【Ansible】的python api
【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的更多相关文章
- Python API:openstack
OpenStack 是一个越来越流行的.用于部署基础架构即服务 (IaaS) 云的开源解决方案.OpenStack 附带了一个仪表板 Web 应用程序,非常适合执行手动任务,比如启动单个虚拟机 (VM ...
- Appium python API 总结
Appium python api 根据testerhome的文章,再补充一些文章里面没有提及的API [TOC] [1]find element driver 的方法 注意:这几个方法只能通过sel ...
- The novaclient Python API
The novaclient Python API Usage First create a client instance with your credentials: >>> f ...
- Openstack python api 学习文档 api创建虚拟机
Openstack python api 学习文档 转载请注明http://www.cnblogs.com/juandx/p/4953191.html 因为需要学习使用api接口调用openstack ...
- BotVS开发基础—Python API
代码 import json def main(): # python API列表 https://www.botvs.com/bbs-topic/443 #状态信息 LogStatus(" ...
- 《Spark Python API 官方文档中文版》 之 pyspark.sql (一)
摘要:在Spark开发中,由于需要用Python实现,发现API与Scala的略有不同,而Python API的中文资料相对很少.每次去查英文版API的说明相对比较慢,还是中文版比较容易get到所需, ...
- 《Spark Python API 官方文档中文版》 之 pyspark.sql (二)
摘要:在Spark开发中,由于需要用Python实现,发现API与Scala的略有不同,而Python API的中文资料相对很少.每次去查英文版API的说明相对比较慢,还是中文版比较容易get到所需, ...
- HBase Python API
HBase Python API HBase通过thrift机制可以实现多语言编程,信息通过端口传递,因此Python是个不错的选择 吐槽 博主在Mac上配置HBase,奈何Zoomkeeper一直报 ...
- 二、Blender/Python API总览
原文:https://docs.blender.org/api/blender_python_api_current/info_overview.html Python in Blender Ble ...
- Appium+python自动化8-Appium Python API
Appium+python自动化8-AppiumPython API 前言: Appium Python API全集,不知道哪个大神整理的,这里贴出来分享给大家. 1.contexts conte ...
随机推荐
- 虚拟机克隆后导致两台机器的IP都不显示的解决方法
centos7中输入ifconfig出现ens33,没有eth0,也没有ip,不能上网,输入ifconfig后如下图 之前在网上也找了很多的方法,比如删除文件70-persistent-ipoib.r ...
- 探秘Tomcat(一)
前言:有的时候自己不知道自己是井底之蛙,这并没有什么可怕的,因为你只要蜷缩在方寸之间的井里,无数次的生活轨迹无非最终归结还是一个圆形:但是可怕的是有一天你不得不从井里跳出来生活,需要重新审视井以外的生 ...
- mfc调用cmd执行完保留黑框
mfc调用cmd的方法有很多,本文采用 ShellExecute ShellExecute(AfxGetMainWnd()->m_hWnd,L"open",L"cm ...
- BZOJ.3495.[PA2010]Riddle(2-SAT 前缀优化建图)
题目链接 每个城市要么建首都要么不建,考虑2-SAT 这样一个国家内城市两两连边是很显然的,但是边数为O(n^2) 每个国家中仅有一个建首都,考虑新建前缀S[i]=1/0这2n个点表示当前国家的[1, ...
- BZOJ.4903.[CTSC2017]吉夫特(Lucas DP)
题目链接 首先\(C(n,m)\)为奇数当且仅当\(n\&m=m\). 简要证明: 因为是\(mod\ 2\),考虑Lucas定理. 在\(mod\ 2\)的情况下\(C(n,m)\)最后只会 ...
- pwcrack--一款集合多种md5解密的工具
项目开源地址:https://github.com/L-codes/pwcrack-framework Ruby2.5+ (tested with Ruby2.5.3 & Ruby 2.6.3 ...
- 使用time模块,转化时间格式
import time ''' 时间戳:表示1970年开始计算的偏移量.我们运用type(时间戳)是float类型 结构化时间:9个元素组成的数组 格式化时间字符串 ''' '''获取当前时间戳''' ...
- C#基础_循环
1,三元运算符 表达式1?表达式2:表达式3 栗子1:bool result=5>3?true:false; 栗子2: int num=5>3?1:0; 注意事项以及相应规则: 2, 3, ...
- mxnet安装及NDArray初体验
一.mxnet安装 (以下均为mac环境) 有二种方式: 1.1 用conda安装 #创建gluon目录 mkdir gluon-tutorials && cd gluon-tutor ...
- AngularJS中获取数据源的几种方式
在AngularJS中,可以从$rootScope中获取数据源,也可以把获取数据的逻辑封装在service中,然后注入到app.run函数中,或者注入到controller中.本篇就来整理获取数据的几 ...