本文基于Ansible 2.7
Ansible官方文档提供的示例如下:
#!/usr/bin/env python
import json
import shutil
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase
import ansible.constants as C
class ResultCallback(CallbackBase):
"""A sample callback plugin used for performing an action as results come in
If you want to collect all results into a single object for processing at
the end of the execution, look into utilizing the ``json`` callback plugin
or writing your own custom callback plugin
"""
def v2_runner_on_ok(self, result, **kwargs):
"""Print a json representation of the result
This method could store the result in an instance attribute for retrieval later
"""
host = result._host
print(json.dumps({host.name: result._result}, indent=4))
# since API is constructed for CLI it expects certain options to always be set, named tuple 'fakes' the args parsing options object
Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff'])
options = Options(connection='local', module_path=['/to/mymodules'], forks=10, become=None, become_method=None, become_user=None, check=False, diff=False)
# initialize needed objects
loader = DataLoader() # Takes care of finding and reading yaml, json and ini files
passwords = dict(vault_pass='secret')
# Instantiate our ResultCallback for handling results as they come in. Ansible expects this to be one of its main display outlets
results_callback = ResultCallback()
# create inventory, use path to host config file as source or hosts in a comma separated string
inventory = InventoryManager(loader=loader, sources='localhost,')
# variable manager takes care of merging all the different sources to give you a unifed view of variables available in each context
variable_manager = VariableManager(loader=loader, inventory=inventory)
# create datastructure that represents our play, including tasks, this is basically what our YAML loader does internally.
play_source = dict(
name = "Ansible Play",
hosts = 'localhost',
gather_facts = 'no',
tasks = [
dict(action=dict(module='shell', args='ls'), register='shell_out'),
dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
]
)
# Create play object, playbook objects use .load instead of init or new methods,
# this will also automatically create the task objects from the info provided in play_source
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)
# Run it - instantiate task queue manager, which takes care of forking and setting up all objects to iterate over host list and tasks
tqm = None
try:
tqm = TaskQueueManager(
inventory=inventory,
variable_manager=variable_manager,
loader=loader,
options=options,
passwords=passwords,
stdout_callback=results_callback, # Use our custom callback instead of the ``default`` callback plugin, which prints to stdout
)
result = tqm.run(play) # most interesting data for a play is actually sent to the callback's methods
finally:
# we always need to cleanup child procs and the structres we use to communicate with them
if tqm is not None:
tqm.cleanup()
# Remove ansible tmpdir
shutil.rmtree(C.DEFAULT_LOCAL_TMP, True)
可以看到
inventory = InventoryManager(loader=loader, sources='localhost,')
这里使用的是静态清单,localhost实际指向的是本地默认的清单文件(/etc/ansible/hosts),但如果我们想使用动态清单呢?
在简单的需求中,是比较方便的。
#假设我们有一个IP地址的列表(这个列表可以通过合适的其他服务获得,或者从数据库直接查询)
host_list = ['192.168.1.1','192.168.1.2']
#建立一个空的InventoryManager(sources默认就是None,此处就是明确一下)
inventory = InventoryManager(loader=loader, sources=None)
#下面开始向Inventory中添加Host
for host in host_list:
inventory.add_host(host)
#也可以分组,但此分组必须先显式通过add_group方法添加
#inventory.add_host(host,group='groupName')
这里有一个地方需要注意,通过add_host添加的host,不能通过‘all’分组取到。也就是说,如果add_host的时候不指定分组,在play_source中指定 hosts = ‘all’ 是取不到host的,必须显式的指定:
inventory.add_host(host,group='all')
如果我们需要使用host variable,就会麻烦一些。
InventoryManager.add_host这个方法,host参数必须是个字符串。而InventoryManager.get_host方法的返回值是一个Host对象:
def get_host(self, hostname):
return self._inventory.get_host(hostname)
def add_host(self, host, group=None, port=None):
return self._inventory.add_host(host, group, port)
其中 self._inventory 是一个 InventoryData 对象
def add_host(self, host, group=None, port=None):
''' adds a host to inventory and possibly a group if not there already '''
if host:
g = None
if group:
if group in self.groups:
g = self.groups[group]
else:
raise AnsibleError("Could not find group %s in inventory" % group)
if host not in self.hosts:
h = Host(host, port)
self.hosts[host] = h
if self.current_source: # set to 'first source' in which host was encountered
self.set_variable(host, 'inventory_file', self.current_source)
self.set_variable(host, 'inventory_dir', basedir(self.current_source))
else:
self.set_variable(host, 'inventory_file', None)
self.set_variable(host, 'inventory_dir', None)
display.debug("Added host %s to inventory" % (host))
# set default localhost from inventory to avoid creating an implicit one. Last localhost defined 'wins'.
if host in C.LOCALHOST:
if self.localhost is None:
self.localhost = self.hosts[host]
display.vvvv("Set default localhost to %s" % h)
else:
display.warning("A duplicate localhost-like entry was found (%s). First found localhost was %s" % (h, self.localhost.name))
else:
h = self.hosts[host]
if g:
g.add_host(h)
self._groups_dict_cache = {}
display.debug("Added host %s to group %s" % (host, group))
else:
raise AnsibleError("Invalid empty host name provided: %s" % host)
def get_host(self, hostname):
''' fetch host object using name deal with implicit localhost '''
matching_host = self.hosts.get(hostname, None)
# if host is not in hosts dict
if matching_host is None and hostname in C.LOCALHOST:
# might need to create implicit localhost
matching_host = self._create_implicit_localhost(hostname)
return matching_host
def __init__(self, name=None, port=None, gen_uuid=True):
self.vars = {}
self.groups = []
self._uuid = None
self.name = name
self.address = name
if port:
self.set_variable('ansible_port', int(port))
if gen_uuid:
self._uuid = get_unique_id()
self.implicit = False
可以看到Host的name和address值都是取形参name的值
所以我们只能通过下面这种办法来使用host variable
host_list = ['192.168.1.1','192.168.1.2']
#建立一个空的InventoryManager(sources默认就是None,此处就是明确一下)
inventory = InventoryManager(loader=loader, sources=None)
#下面开始向Inventory中添加Host
for host in host_list:
inventory.add_host(host)
inventory.get_host(host).vars['var_name'] = 'var_val'
写法十分僵硬。