ansible支持python版本_【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 -*-

importosimportsysfrom collections importnamedtuplefrom ansible.parsing.dataloader importDataLoaderfrom ansible.vars importVariableManagerfrom ansible.inventory importInventoryfrom ansible.inventory.group importGroupfrom ansible.inventory.host importHostfrom ansible.playbook.play importPlayfrom ansible.executor.task_queue_manager importTaskQueueManagerfrom ansible.executor.playbook_executor importPlaybookExecutorfrom ansible.plugins.callback importCallbackBaseclassResultsCollector(CallbackBase):def __init__(self, *args, **kwargs):

super(ResultsCollector, self).__init__(*args, **kwargs)

self.host_ok={}

self.host_unreachable={}

self.host_failed={}defv2_runner_on_unreachable(self, result):

self.host_unreachable[result._host.get_name()]=resultdef v2_runner_on_ok(self, result, *args, **kwargs):

self.host_ok[result._host.get_name()]=resultdef v2_runner_on_failed(self, result, *args, **kwargs):

self.host_failed[result._host.get_name()]=resultclassMyInventory(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

ifgroupvars:for key, value ingroupvars.iteritems():

my_group.set_variable(key, value)#add hosts to group

for host inhosts:#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 inhost.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)defgen_inventory(self):"""add hosts to inventory."""

ifisinstance(self.resource, list):

self.my_add_group(self.resource,'default_group')elifisinstance(self.resource, dict):for groupname, hosts_and_vars inself.resource.iteritems():

self.my_add_group(hosts_and_vars.get("hosts"), groupname, hosts_and_vars.get("vars"))classAnsibleAPI(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)defrun(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 notNone:

tqm.cleanup()defrun_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 notos.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 inhost_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()exceptException as e:print "error:",e.messagedefget_result(self):

self.results_raw= {'success': {}, 'failed': {}, 'unreachable': {}}for host, result inself.callback.host_ok.items():

self.results_raw['success'][host] =result._resultfor host, result inself.callback.host_failed.items():

self.results_raw['failed'][host] = result._result.get('msg') orresult._resultfor host, result inself.callback.host_unreachable.items():

self.results_raw['unreachable'][host] = result._result['msg']return self.results_raw

简单的用法是这样的:

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

from ansible_api importAnsibleAPI

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模块美化一下

importjsonprint 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"}

}

View Code

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值