【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. scikit-learn全局图

    https://scikit-learn.org/stable/tutorial/machine_learning_map/index.html

  2. 10.17 NOIP模拟赛

    目录 2018.10.17 NOIP模拟赛 A 咒语curse B 神光light(二分 DP) C 迷宫maze(次短路) 考试代码 B 2018.10.17 NOIP模拟赛 时间:1h15min( ...

  3. [Beego模型] 一、ORM 使用方法

    [Beego模型] 一.ORM 使用方法 [Beego模型] 二.CRUD 操作 [Beego模型] 三.高级查询 [Beego模型] 四.使用SQL语句进行查询 [Beego模型] 五.构造查询 [ ...

  4. Hessian学习总结(二)——使用hessian上传文件

    hessian较早版本通过 byte[] 进行文件传输:4.0之后支持 InputStream 作为参数或返回值进行传输. 注意:hessian会读取整个文件,如果文件过大,会导致JVM内存溢出.可以 ...

  5. windows Server 2008 R2 IE增强安全配置正在阻止来自下列网站的内容

    1.在windows Server 2008 R2上访问百度,会出现以下界面 当在Windows Sever 2008 R2中运动IE8的时候会发现默认情况下IE启用了增强的安全配置,为了方便而且是在 ...

  6. [BetterExplained]为什么你应该(从现在开始就)写博客

    (一)为什么你应该(从现在开始就)写博客 用一句话来说就是,写一个博客有很多好处,却没有任何明显的坏处.(阿灵顿的情况属于例外,而非常态,就像不能拿抽烟活到一百岁的英国老太太的个例来反驳抽烟对健康的极 ...

  7. (原)MobileNetV1

    转载请注明出处: https://www.cnblogs.com/darkknightzh/p/9410540.html 论文: MobileNets: Efficient Convolutional ...

  8. 书记查询的App设计

    表示学习Andriod没有头绪,看书看着看着都觉得大部分是些item的介绍,看过去之后会完全忘记,还是自己做个小东西进行实际开发学习和掌握起来会比较快. PC端的软件已经做好,当然因为PC屏幕较大,手 ...

  9. 基于Ubuntu 搭建 VNC 远程桌面服务

    系统要求:Ubuntu 16.04.1 LTS 64 位操作系统 安装.启动 VNC VNC 远程桌面原理 注:本小节内容旨在帮助您更好地了解 Xorg.X11.VNC 等概念和原理,如果你不想了解原 ...

  10. spring-mybatis代码生成插件,与实例展示

    前段时间看了张开涛写的代码生成插件,感觉思路很好,通过连接库然后获取数据库表信息,然后用户在界面中勾选要映射的策略,映射的字段,然后可以自动生成业务代码. 基于开涛的思路,自己写了一个简易插件,去掉了 ...