欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自动化运维 Ansible 2.2.1 playbook api

程序员文章站 2022-03-10 15:16:08
...
最近把Ansible升级到2.2.1,感觉变化比较大,花些时间对playbook api 的调用做下总结

重大更新:
  • ansible.conf中添加了strategy:free属性, 配置该属性可以实现任务的异步执行,再也不用的等待所有机器执行完,才进入下个任务了。
  • ansible api 和 ansible-playbook api 的调用方式


发现的问题:
  • 2.2.1 的执行速度比1.9略慢


Ansible Playbook api的调用

核心类
ansible.executor.task_queue_manager
  • 将各类参数放到"ansible.executor.task_queue_manager"类中
  • 将playbook 拆分成多个task 放入ansible.executor.task_queue_manager中


直接上源码
class TaskQueueManager:

    '''
    This class handles the multiprocessing requirements of Ansible by
    creating a pool of worker forks, a result handler fork, and a
    manager object with shared datastructures/queues for coordinating
    work between all processes.

    The queue manager is responsible for loading the play strategy plugin,
    which dispatches the Play's tasks to hosts.
    '''

    def __init__(self, inventory, variable_manager, loader, options, passwords, stdout_callback=None, run_additional_callbacks=True, run_tree=False):

        self._inventory        = inventory
        self._variable_manager = variable_manager
        self._loader           = loader
        self._options          = options
        self._stats            = AggregateStats()
        self.passwords         = passwords
        self._stdout_callback  = stdout_callback
        self._run_additional_callbacks = run_additional_callbacks
        self._run_tree         = run_tree

        self._callbacks_loaded = False
        self._callback_plugins = []
        self._start_at_done    = False

        # make sure the module path (if specified) is parsed and
        # added to the module_loader object
        
        ...



参数:
  • inventory --> 由ansible.inventory模块创建,用于导入inventory文件 (也可以直接使用实例)
  • variable_manager --> 由ansible.vars模块创建,用于存储各类变量信息
  • loader --> 由ansible.parsing.dataloader模块创建,用于数据解析
  • options --> 存放各类配置信息的数据字典
  • passwords --> 登录密码,可设置加密信息
  • stdout_callback --> 回调函数(可以自定义返回结果)



完整的例子:
class MyInventory(object):
    """
    this is my ansible inventory object
    """

    def __init__(self, loader, variable_manager, host_list, groupname='default_group'):
        """
        :param loader:
        :param variable_manager:
        :param host_list: ex.:[{'hostname':'','ip':'','port':,'username':,'password':,'become_root_passwd':,'become':,}]
        :param groupname:
        """

        self.inventory = Inventory(loader, variable_manager, host_list=[])
        # self.inventory.refresh_inventory()
        self.set_inventory(host_list, groupname)

    def get_inventory(self):
        return self.inventory

    def set_inventory(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")
            become_root_passwd = host.get("become_root_passwd")
            become = host.get("become", True)

            my_host = Host(name=hostname, port=hostport)
            my_host.set_variable('ansible_host', hostip)
            my_host.set_variable('ansible_port', hostport)
            my_host.set_variable('ansible_user', username)
            my_host.set_variable('ansible_ssh_pass', password)
            my_host.set_variable('ansible_become', become)
            my_host.set_variable('ansible_become_method', 'su')
            my_host.set_variable('ansible_become_user', 'root')
            my_host.set_variable('ansible_become_pass', become_root_passwd)

            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)



class ResultsCallBack(CallbackBase):
    def __init__(self, *args, **kwargs):
        super(ResultsCallBack, 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 AnsiblePlaybookTask(object):
    def __init__(self, host_list, **kwargs):
        """

        :param host_list: [{'hostname':'','ip':'','port':,'username':,'password':,'become_root_passwd':,'become':,}]
        """
        Options = namedtuple('Options',
                             ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check',
                              'listtags', 'listtasks', 'listhosts', 'syntax', 'stdout_callback'])
        # initialize needed objects
        self.loader = DataLoader()
        self.variable_manager = VariableManager()
        self.resultCallBack = ResultsCallBack()
        self.options = Options(
            listtags=None,
            listtasks=None,
            listhosts=None,
            syntax=None,
            connection='smart',
            module_path=None,
            forks=100 if not kwargs else kwargs.get('forks', 100),
            become=True, become_method=None,
            stdout_callback=self.resultCallBack,
            become_user=None, check=False)
        myInventory = MyInventory(self.loader, self.variable_manager, host_list)
        self.inventory = myInventory.get_inventory()

    def run_yaml(self, yml_path, extra_vars):
        self.variable_manager.extra_vars = extra_vars
        playbook = Playbook.load(yml_path, variable_manager=self.variable_manager, loader=self.loader)
        p = playbook.get_plays()[0]
        tqm = None
        try:
            tqm = TaskQueueManager(
                inventory=self.inventory,
                variable_manager=self.variable_manager,
                loader=self.loader,
                options=self.options,
                passwords=None,
                stdout_callback=self.options.stdout_callback,
            )
            result = tqm.run(playbook.get_plays()[0])
        finally:
            if tqm is not None:
                tqm.cleanup()
        return result

    def get_result(self):
        self.results_raw = {'ok': {}, 'failed': {}, 'unreachable': {}}
        for host, result in self.resultCallBack.host_ok.items():
            self.results_raw['ok'][host] = result._result

        for host, result in self.resultCallBack.host_failed.items():
            self.results_raw['failed'][host] = result._result

        for host, result in self.resultCallBack.host_unreachable.items():
            self.results_raw['unreachable'][host] = result._result

        return self.results_raw


调用:
playbook = AnsiblePlaybookTask(        [dict(hostname='XXX', ip='XXX', port=XXX, username="XXX",
              password='XXX', become_root_passwd='XXX', become=XXX,
              platform='XXX')         ])
playbook.run_yaml(yml_path='XXX', extra_vars={})

playbook.get_result()



优化点:
  • 可以在MyInventory中设置每个Host的自身属性,并在yml里调用
  • 自定义callback,将结果按需要输出
相关标签: python ansible