【Ansible API】

  Ansible本身就是由python写成,所有其对python形式的API的支持应该不错。

  其API分不同的版本,这个版本也就是ansible本身的版本,可以通过ansible --version命令查看或者在python中import ansible然后查看anisble.__version__。

  在2.0的版本以前,ansible的API十分简单。通过大概十几行代码就可以模拟通过ad-hoc的方式运行annsible命令的。

  但是在2.0及以后的版本,难度突然陡增,与此同时更多的更加底层的东西被开放给一般开发,虽然复杂了,但是也更加灵活了。

  对于一些简单的需求,可能我们还是喜欢老版API,因此这里有个网上别人封装好的2.0版本API简化文件,通过这里的代码我们可以依然简单地运行ansible的ad-hoc。同时,它的源码支持修改,从而达到更个性化的改造。

  *** 需要注意,下面这个代码只在2.0开头的几个版本中适用。至少到2.4.0之后,API又有了一些改动,下面的代码运行会出错。我懒得再研究2.4了,就干脆pip install ansible==2.0来配合这个库

  ansible_api.py:

# -*- coding:utf-8 -*-

import os
import sys
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars import VariableManager
from ansible.inventory import Inventory
from ansible.inventory.group import Group
from ansible.inventory.host import Host
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.plugins.callback import CallbackBase class ResultsCollector(CallbackBase):
def __init__(self, *args, **kwargs):
super(ResultsCollector, self).__init__(*args, **kwargs)
self.host_ok = {}
self.host_unreachable = {}
self.host_failed = {} def v2_runner_on_unreachable(self, result):
self.host_unreachable[result._host.get_name()] = result def v2_runner_on_ok(self, result, *args, **kwargs):
self.host_ok[result._host.get_name()] = result def v2_runner_on_failed(self, result, *args, **kwargs):
self.host_failed[result._host.get_name()] = result class MyInventory(Inventory):
"""
this is my ansible inventory object.
""" def __init__(self, resource, loader, variable_manager):
"""
resource的数据格式是一个列表字典,比如
{
"group1": {
"hosts": [{"hostname": "10.0.0.0", "port": "22", "username": "test", "password": "pass"}, ...],
"vars": {"var1": value1, "var2": value2, ...}
}
} 如果你只传入1个列表,这默认该列表内的所有主机属于my_group组,比如
[{"hostname": "10.0.0.0", "port": "22", "username": "test", "password": "pass"}, ...]
"""
self.resource = resource
self.inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list=[])
self.gen_inventory() def my_add_group(self, hosts, groupname, groupvars=None):
"""
add hosts to a group
"""
my_group = Group(name=groupname) # if group variables exists, add them to group
if groupvars:
for key, value in groupvars.iteritems():
my_group.set_variable(key, value) # add hosts to group
for host in hosts:
# set connection variables
hostname = host.get("hostname")
hostip = host.get('ip', hostname)
hostport = host.get("port")
username = host.get("username")
password = host.get("password")
ssh_key = host.get("ssh_key")
my_host = Host(name=hostname, port=hostport)
my_host.set_variable('ansible_ssh_host', hostip)
my_host.set_variable('ansible_ssh_port', hostport)
my_host.set_variable('ansible_ssh_user', username)
my_host.set_variable('ansible_ssh_pass', password)
my_host.set_variable('ansible_ssh_private_key_file', ssh_key) # set other variables
for key, value in host.iteritems():
if key not in ["hostname", "port", "username", "password"]:
my_host.set_variable(key, value)
# add to group
my_group.add_host(my_host) self.inventory.add_group(my_group) def gen_inventory(self):
"""
add hosts to inventory.
"""
if isinstance(self.resource, list):
self.my_add_group(self.resource, 'default_group')
elif isinstance(self.resource, dict):
for groupname, hosts_and_vars in self.resource.iteritems():
self.my_add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars")) class AnsibleAPI(object):
"""
This is a General object for parallel execute modules.
""" def __init__(self, resource, *args, **kwargs):
self.resource = resource
self.inventory = None
self.variable_manager = None
self.loader = None
self.options = None
self.passwords = None
self.callback = None
self.__initializeData()
self.results_raw = {} def __initializeData(self):
"""
初始化ansible
"""
Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'timeout', 'remote_user',
'ask_pass', 'private_key_file', 'ssh_common_args', 'ssh_extra_args',
'sftp_extra_args',
'scp_extra_args', 'become', 'become_method', 'become_user', 'ask_value_pass',
'verbosity',
'check', 'listhosts', 'listtasks', 'listtags', 'syntax']) # initialize needed objects
self.variable_manager = VariableManager()
self.loader = DataLoader()
self.options = Options(connection='smart', module_path='/usr/share/ansible', forks=100, timeout=10,
remote_user='root', ask_pass=False, private_key_file=None, ssh_common_args=None,
ssh_extra_args=None,
sftp_extra_args=None, scp_extra_args=None, become=None, become_method=None,
become_user='root', ask_value_pass=False, verbosity=None, check=False, listhosts=False,
listtasks=False, listtags=False, syntax=False) self.passwords = dict(sshpass=None, becomepass=None)
self.inventory = MyInventory(self.resource, self.loader, self.variable_manager).inventory
self.variable_manager.set_inventory(self.inventory) def run(self, host_list, module_name, module_args):
"""
run module from andible ad-hoc.
module_name: ansible module_name
module_args: ansible module args
"""
# create play with tasks
play_source = dict(
name="Ansible Play",
hosts=host_list,
gather_facts='no',
tasks=[dict(action=dict(module=module_name, args=module_args))]
)
play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader) # actually run it
tqm = None
self.callback = ResultsCollector()
try:
tqm = TaskQueueManager(
inventory=self.inventory,
variable_manager=self.variable_manager,
loader=self.loader,
options=self.options,
passwords=self.passwords,
)
tqm._stdout_callback = self.callback
tqm.run(play)
finally:
if tqm is not None:
tqm.cleanup() def run_playbook(self, host_list, role_name, role_uuid, temp_param):
"""
run ansible palybook
"""
try:
self.callback = ResultsCollector()
filenames = ['' + '/handlers/ansible/v1_0/sudoers.yml'] # playbook的路径
template_file = '' # 模板文件的路径
if not os.path.exists(template_file):
sys.exit() extra_vars = {} # 额外的参数 sudoers.yml以及模板中的参数,它对应ansible-playbook test.yml --extra-vars "host='aa' name='cc' "
host_list_str = ','.join([item for item in host_list])
extra_vars['host_list'] = host_list_str
extra_vars['username'] = role_name
extra_vars['template_dir'] = template_file
extra_vars['command_list'] = temp_param.get('cmdList')
extra_vars['role_uuid'] = 'role-%s' % role_uuid
self.variable_manager.extra_vars = extra_vars
# actually run it
executor = PlaybookExecutor(
playbooks=filenames, inventory=self.inventory, variable_manager=self.variable_manager,
loader=self.loader,
options=self.options, passwords=self.passwords,
)
executor._tqm._stdout_callback = self.callback
executor.run()
except Exception as e:
print "error:",e.message def get_result(self):
self.results_raw = {'success': {}, 'failed': {}, 'unreachable': {}}
for host, result in self.callback.host_ok.items():
self.results_raw['success'][host] = result._result for host, result in self.callback.host_failed.items():
self.results_raw['failed'][host] = result._result.get('msg') or result._result for host, result in self.callback.host_unreachable.items():
self.results_raw['unreachable'][host] = result._result['msg'] return self.results_raw

  简单的用法是这样的:

# -*- coding:utf-8 -*-

from ansible_api import AnsibleAPI

resource = [
{'hostname':'localtest','ip','192.168.178.59','username':'root','password':'xxx'},
{'hostname':'localtest2','ip':'192.168.178.141','username':'root','password':'yyy'} #有个小坑,hostname中不能有空格,否则这个host会被ansible无视
] api = AnsibleAPI(resource) # 开始模拟以ad-hoc方式运行ansible命令
api.run(
['localtest','localtest2'], # 指出本次运行涉及的主机,在resource中定义
'command', # 本次运行使用的模块
'hostname' # 模块的参数
) # 获取结果,是一个字典格式,如果是print可以用json模块美化一下
import json
print json.dumps(api.get_result(),indent=4)

  从逻辑上看,首先我们声明了一个resource,在这是一个list结构,其中包含了各个被操作主机的信息。hostname,ip,username,password这些是作为ansible连接所用的最基本的几个参数,此外port也可指定。resource被api类加载,这个过程其实就是生成了一个动态的inventory。从源码中的90多行可以看出,当传入一个list时api默认将其中所有host信息都放入一个default_group的inventory组,当传入一个dict就默认这个dict的各个键值对分别是组名和组中host信息。

  run方法是真正的执行方法,其参数从前往后三个分别是host_list, module, module_args。command的话args比较简单。像类似于copy这类模块的参数可以这么写:

  api.run(['test'],'copy','src="/tmp/testfille" dest="/tmp/newfile"')

  而file就可以这样:

  api.run(['test'],'path="/tmp/test.py" mode=0755 owner="tmpuser"')

  通过这两个例子基本就可以看清楚如何向run方法传递ansible模块的参数了。

  api在执行run方法之后并不会主动输出结果,需要我们手动地get_result()。result在空的情况下是这样一个结构:

{
"success": {},
"failed": {},
"unreachable":{}
}

  不难看出,从主机层面上,主机们被分成了执行成功,执行失败,和连接不到三类,分别给出结果。

  下面给出一个返回结果的示例

{
"failed": {
"node-one": {
"cmd": [
"cat",
"/tmp/test"
],
"end": "2018-05-08 16:27:29.327685",
"_ansible_no_log": false,
"stdout": "",
"changed": true,
"failed": true,
"delta": "0:00:00.003369",
"stderr": "cat: /tmp/test: \u6ca1\u6709\u90a3\u4e2a\u6587\u4ef6\u6216\u76ee\u5f55",
"rc": 1,
"invocation": {
"module_name": "command",
"module_args": {
"creates": null,
"executable": null,
"chdir": null,
"_raw_params": "cat /tmp/test",
"removes": null,
"warn": true,
"_uses_shell": false
}
},
"stdout_lines": [],
"start": "2018-05-08 16:27:29.324316",
"warnings": []
}
},
"success": {
"localtest": {
"cmd": [
"cat",
"/tmp/test"
],
"end": "2018-05-08 16:27:30.692616",
"_ansible_no_log": false,
"stdout": "",
"changed": true,
"start": "2018-05-08 16:27:30.689329",
"delta": "0:00:00.003287",
"stderr": "",
"rc": 0,
"invocation": {
"module_name": "command",
"module_args": {
"creates": null,
"executable": null,
"chdir": null,
"_raw_params": "cat /tmp/test",
"removes": null,
"warn": true,
"_uses_shell": false
}
},
"stdout_lines": [],
"warnings": []
}
},
"unreachable": {
"node-two": "ERROR! SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue"
}
}

  localtest执行成功,返回中有changed,delta,start/end,stdout等可能要在后续处理中用到的各种数据

  node-one执行失败,所以可以读取stderr中的内容。由于返回是中文,在这里以unicode的形式展现

  node-two无法连接,只给出了简单的无法连接的提示信息

  基本使用就说到这里吧。下面我要使用了,过程中可能会要回过头来改源码。

【Ansible】的python api的更多相关文章

  1. Python API:openstack

    OpenStack 是一个越来越流行的.用于部署基础架构即服务 (IaaS) 云的开源解决方案.OpenStack 附带了一个仪表板 Web 应用程序,非常适合执行手动任务,比如启动单个虚拟机 (VM ...

  2. Appium python API 总结

    Appium python api 根据testerhome的文章,再补充一些文章里面没有提及的API [TOC] [1]find element driver 的方法 注意:这几个方法只能通过sel ...

  3. The novaclient Python API

    The novaclient Python API Usage First create a client instance with your credentials: >>> f ...

  4. Openstack python api 学习文档 api创建虚拟机

    Openstack python api 学习文档 转载请注明http://www.cnblogs.com/juandx/p/4953191.html 因为需要学习使用api接口调用openstack ...

  5. BotVS开发基础—Python API

    代码 import json def main(): # python API列表 https://www.botvs.com/bbs-topic/443 #状态信息 LogStatus(" ...

  6. 《Spark Python API 官方文档中文版》 之 pyspark.sql (一)

    摘要:在Spark开发中,由于需要用Python实现,发现API与Scala的略有不同,而Python API的中文资料相对很少.每次去查英文版API的说明相对比较慢,还是中文版比较容易get到所需, ...

  7. 《Spark Python API 官方文档中文版》 之 pyspark.sql (二)

    摘要:在Spark开发中,由于需要用Python实现,发现API与Scala的略有不同,而Python API的中文资料相对很少.每次去查英文版API的说明相对比较慢,还是中文版比较容易get到所需, ...

  8. HBase Python API

    HBase Python API HBase通过thrift机制可以实现多语言编程,信息通过端口传递,因此Python是个不错的选择 吐槽 博主在Mac上配置HBase,奈何Zoomkeeper一直报 ...

  9. 二、Blender/Python API总览

    原文:https://docs.blender.org/api/blender_python_api_current/info_overview.html Python in Blender  Ble ...

  10. Appium+python自动化8-Appium Python API

    Appium+python自动化8-AppiumPython API   前言: Appium Python API全集,不知道哪个大神整理的,这里贴出来分享给大家. 1.contexts conte ...

随机推荐

  1. OSINT系列:网站信任评估WOT

     OSINT系列:网站信任评估WOT Web of Trust(WOT)是芬兰的一家网站信任评估服务公司.它通过收集用户对网站的评价,来评估网站的可信任度.在该公司网站www.mywot.com,用户 ...

  2. [清橙A1210]光棱坦克

    [清橙A1210]光棱坦克 题目大意: 平面上放置了\(n(n\le7000)\)个反射装置,光纤将从某个装置出发,在经过一处装置时发生反射,若经过的装置坐标依次为\((x_1,y_1),(x_2,y ...

  3. 本地搭建Wordpress博客网站(Windows)

    最近在写一些web功能测试的一个主题分享,里边有一个分类是数据库测试,那么数据库测试有几个点的方法,其中有一个是学会看数据库的日志.由于公司内部的数据库日志我们测试人员暂时不开放查看,所以打算自己在本 ...

  4. null与“ ”

    http://blog.csdn.net/eroswang/article/details/8529817 MySQL数据库是一个基于结构化数据的开源数据库.SQL语句是mysql数据库中核心语言.不 ...

  5. 回顾MySQL中的事务特征

    一.事务定义Transaction事务:一个最小的不可再分的工作单元:通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)一个完整的业务需要批量的DML(insert. ...

  6. Linux--信号阻塞与屏蔽

    1. sigprocmask函数提供屏蔽和解除屏蔽信号的功能. 从而实现关键代码的运行不被打断. 函数声明如下: int sigprocmask(int how, const sigset_t *se ...

  7. Revit API创建房间

    start [Transaction(TransactionMode.Manual)] [Regeneration(RegenerationOption.Manual)] );             ...

  8. Windows下Phalcon的安装以及phpstorm识别phalcon语法及提示

    1.由于Phalcon是C语言写的一个扩展,所以需要安装这个扩展php_phalcon.dll,下载地址https://github.com/phalcon/cphalcon/releases, 然后 ...

  9. Mongodb 笔记 - 性能及Java代码

    性能 以下数据都是在千兆网络下测试的结果 写入 数据量的增大会导致内存占满, 因为mongodb会将数据尽可能地载入内存, 索引占用的空间也很可观非安全模式下, 速度取决于内存是否占满能差一个数量级, ...

  10. webpack中file-loader和url-loader的关系

    url-loader把资源文件转换为URL,file-loader也是一样的功能. 不同之处在于url-loader更加灵活,它可以把小文件转换为base64格式的URL,从而减少网络请求次数.url ...