ansible的核心类

1. 给予Django的ansible web框架设计

 

2. 核心类介绍

归纳

核心类 用途 所在的模块路径
DataLoader 用于读取yaml、json格式的文件 ansible.parsing.dataloader
Play 存储执行hosts的角色信息 ansible.playbook.play
TaskQueueManager ansible底层用到的任务队列 ansible.executor.task_queue_manager
PlaybookExecutor 核心类执行playbook剧本 ansible.executor.playbook_executor
CallbackBase 状态回调,各种成功失败的状态 ansible.plugins.callback
InventoryManager 用于导入inventory文件 ansible.inventory.manager
VariableManager 用于存储各类变量信息 ansible.var.manager
Host,Group 操作单个主机或者主机组信息 ansible.inventory.host

 

2.1 InventoryManager 

功能:用来管理主机和主机组相关的资源设备信息

from ansible.parsing.dataloader import DataLoader
from ansible.inventory.manager import InventoryManager
from ansible.vars.manager import VariableManager


# InventoryManager类
loader = DataLoader() # 实例化对象
inv = InventoryManager(loader=loader,sources=['auto_hosts'])

# add_host()方法,添加主机到指定的主机组 inv.add_host(host='192.168.1.112',port=22,group='test_group3') # get_groups_dict()方法,查看主机组资源 inv.get_groups_dict() """ {'all': ['192.168.1.112', '192.168.1.101', '192.168.1.110'], 'test_group1': ['192.168.1.112', '192.168.1.101', '192.168.1.110'], 'test_group2': ['192.168.1.112', '192.168.1.101'], 'test_group3': ['192.168.1.110', '192.168.1.112'], 'ungrouped': []} """ # get_hosts() 获取所有的主机信息,返回的为列表 inv.get_hosts # [192.168.1.112, 192.168.1.101, 192.168.1.110] # get_host() 获取指定的主机对象 inv.get_host(hostname='192.168.1.112') # 192.168.1.112

 

2.2 VariableManager

功能:进行主机变量的读取

from ansible.parsing.dataloader import DataLoader
from ansible.inventory.manager import InventoryManager
from ansible.vars.manager import VariableManager


# InventoryManager类
loader = DataLoader() # 实例化对象
inv = InventoryManager(loader=loader,sources=['auto_hosts'])
#VariableManager类
variable_manager = VariableManager(loader=loader,inventory=inv)

# get_vars() # 查看变量方法
variable_manager.get_vars()
"""
{'ansible_playbook_python': '/usr/bin/python3',
 'groups': {'all': ['192.168.1.112', '192.168.1.101', '192.168.1.110'],
  'test_group1': ['192.168.1.112', '192.168.1.101', '192.168.1.110'],
  'test_group2': ['192.168.1.112', '192.168.1.101'],
  'test_group3': ['192.168.1.110', '192.168.1.112'],
  'ungrouped': []},
 'omit': '__omit_place_holder__04e40184623ea65c38973233a8c804a29215f21d',
 'playbook_dir': '/u01/autoops/script'}
"""

# get_vars() # 查询主机指定的变量信息
host = inv.get_host(hostname='192.168.1.110')
variable_manager.get_vars(host=host)
"""
{'ansible_playbook_python': '/usr/bin/python3',
 'ansible_port': 22,
 'ansible_ssh_pass': 123456,
 'ansible_ssh_user': 'root',
 'group_names': ['test_group1', 'test_group3'],
 'groups': {'all': ['192.168.1.112', '192.168.1.101', '192.168.1.110'],
  'test_group1': ['192.168.1.112', '192.168.1.101', '192.168.1.110'],
  'test_group2': ['192.168.1.112', '192.168.1.101'],
  'test_group3': ['192.168.1.110', '192.168.1.112'],
  'ungrouped': []},
 'inventory_dir': '/u01/autoops/script',
 'inventory_file': '/u01/autoops/script/auto_hosts',
 'inventory_hostname': '192.168.1.110',
 'inventory_hostname_short': '192',
 'omit': '__omit_place_holder__3d8bb8c134c30295abd201bee6ef34093e580c2f',
 'playbook_dir': '/u01/autoops/script'}
"""

# set_host_variable() # 修改指定主机的变量信息
variable_manager.set_host_variable(host=host,varname='ansible_ssh_pass',value='111111')
variable_manager.get_vars(host=host)
"""
{'ansible_playbook_python': '/usr/bin/python3',
 'ansible_port': 22,
 'ansible_ssh_pass': '111111',
 'ansible_ssh_user': 'root',
 'group_names': ['test_group1', 'test_group3'],
 'groups': {'all': ['192.168.1.112', '192.168.1.101', '192.168.1.110'],
  'test_group1': ['192.168.1.112', '192.168.1.101', '192.168.1.110'],
  'test_group2': ['192.168.1.112', '192.168.1.101'],
  'test_group3': ['192.168.1.110', '192.168.1.112'],
  'ungrouped': []},
 'inventory_dir': '/u01/autoops/script',
 'inventory_file': '/u01/autoops/script/auto_hosts',
 'inventory_hostname': '192.168.1.110',
 'inventory_hostname_short': '192',
 'omit': '__omit_place_holder__3d8bb8c134c30295abd201bee6ef34093e580c2f',
 'playbook_dir': '/u01/autoops/script'}
""" # extra_vars={} # 添加指定对象的扩展变量,全局有效 variable_manager.extra_vars={'myweb':'jd.com','myname':'jacob'} variable_manager.get_vars(host=host) """ {'ansible_playbook_python': '/usr/bin/python3', 'ansible_port': 22, 'ansible_ssh_pass': '111111', 'ansible_ssh_user': 'root', 'group_names': ['test_group1', 'test_group2', 'test_group3'], 'groups': {'all': ['192.168.1.112', '192.168.1.101', '192.168.1.110'], 'test_group1': ['192.168.1.112', '192.168.1.101', '192.168.1.110'], 'test_group2': ['192.168.1.112', '192.168.1.101'], 'test_group3': ['192.168.1.110', '192.168.1.112'], 'ungrouped': []}, 'inventory_dir': '/u01/autoops/script', 'inventory_file': '/u01/autoops/script/auto_hosts', 'inventory_hostname': '192.168.1.110', 'inventory_hostname_short': '192', 'myname': 'jacob', 'myweb': 'jd.com', 'omit': '__omit_place_holder__04e40184623ea65c38973233a8c804a29215f21d', 'playbook_dir': '/u01/autoops/script'}
"""

 

2.3 ad-hoc模式调用场景

ansible的ad-hoc模式的调用示例:

 

ad-hoc模式一般用于批量执行简单命令,文件替换等。此处的重点是执行对象和模块,资源资产配置清单,执行选项。

所需类及其调用关系为:

 

2.3.1 namedtuple的用法回顾

from collections import namedtuple

# User = namedtuple('User', 'name age id')
User = namedtuple('User', ['name', 'age', 'id'])
user = User('tester', '22', '24242432')
print(user)
# User(name='tester', age='22', id='24242432')
namedtuple用法

 

 2.3.2 ansible的ad-hoc模式options总结

-v, --verbose:输出更详细的执行过程信息,-vvv可得到所有执行过程信息。

-i PATH, --inventory=PATH:指定inventory信息,默认/etc/ansible/hosts。

-f NUM, --forks=NUM:并发线程数,默认5个线程。

--private-key=PRIVATE_KEY_FILE:指定密钥文件。

-m NAME, --module-name=NAME:指定执行使用的模块。

-M DIRECTORY, --module-path=DIRECTORY:指定模块存放路径,默认/usr/share/ansible,也可以通过ANSIBLE_LIBRARY设定默认路径。

-a 'ARGUMENTS', --args='ARGUMENTS':模块参数。

-k, --ask-pass SSH:认证密码。

-K, --ask-sudo-pass sudo:用户的密码(—sudo时使用)。

-o, --one-line:标准输出至一行。

-s, --sudo:相当于Linux系统下的sudo命令。

-t DIRECTORY, --tree=DIRECTORY:输出信息至DIRECTORY目录下,结果文件以远程主机名命名。

-T SECONDS, --timeout=SECONDS:指定连接远程主机的最大超时,单位是:秒。

-B NUM, --background=NUM:后台执行命令,超NUM秒后kill正在执行的任务。

-P NUM, --poll=NUM:定期返回后台任务进度。

-u USERNAME, --user=USERNAME:指定远程主机以USERNAME运行命令。

-U SUDO_USERNAME, --sudo-user=SUDO_USERNAM:E使用sudo,相当于Linux下的sudo命令。

-c CONNECTION, --connection=CONNECTION:指定连接方式,可用选项paramiko (SSH), ssh, local。Local方式常用于crontab 和 kickstarts。

-l SUBSET, --limit=SUBSET:指定运行主机。

-l ~REGEX, --limit=~REGEX:指定运行主机(正则)。

--list-hosts:列出符合条件的主机列表,不执行任何其他命令
options

 

2.3.3 结合options和play类的使用的简单案例

# -*- coding: utf-8 -*-
#!/usr/bin/env python

#核心类
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

#InventoryManager类
loader = DataLoader()
inventory = InventoryManager(loader=loader,sources=['auto_hosts'])

#VariableManager类
variable_manager = VariableManager(loader=loader,inventory=inventory)

#Options 执行选项
Options = namedtuple('Options',
                     ['connection',     
                      'remote_user',   
                      'ask_sudo_pass', 
                      'verbosity',
                      'ack_pass',
                      'module_path',
                      'forks',         
                      'become',
                      'become_method',
                      'become_user',
                      'check',
                      'listhosts',
                      'listtasks',
                      'listtags',
                      'syntax',
                      'sudo_user',      
                      'sudo',
                      'diff'])
options = Options(connection='smart',   
                       remote_user=None,
                       ack_pass=None,
                       sudo_user=None,
                       forks=5,
                       sudo=None,
                       ask_sudo_pass=False,
                       verbosity=5,
                       module_path=None,
                       become=None,
                       become_method=None,
                       become_user=None,
                       check=False,
                       diff=False,
                       listhosts=None,
                       listtasks=None,
                       listtags=None,
                       syntax=None)
#Play 执行对象和模块
play_source =  dict(
        name = "Ansible Play ad-hoc test", # 任务执行的名称
        hosts = '192.168.102.101', # # 控制着任务执行的目标主机,可以通过逗号填入多台主机,或者正则匹配,或者主机组
        gather_facts = 'no', # 执行任务之前去获取响应主机的相关信息,建议关闭,提高执行效率
        tasks = [
            # 以dict的方式实现,一个任务一个dict,可以写多个,module 为对应模块,args为传入的参数
            dict(action=dict(module='shell', args='touch /tmp/ad_hoc_test1')),
            # dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
         ]
    )
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)


passwords = dict() # 没有实际作用,实际中密码已经写在文件中了
tqm = TaskQueueManager(
          inventory=inventory,
          variable_manager=variable_manager,
          loader=loader,
          options=options,
          passwords=passwords,
      )
result = tqm.run(play)
ansible_api_k1.py
[root@test01 script]# python3 ansible_api_k1.py 

PLAY [Ansible Play ad-hoc test] *******************************************************************************************************************************************

TASK [command] ************************************************************************************************************************************************************
 [WARNING]: Consider using file module with state=touch rather than running touch

changed: [192.168.102.101]

# 且在192.168.102.101生成/tmp/ad_hoc_test1文件
输出结果

 

2.4 playbook模式调用场景

ansible的paybook模式的调用示例:

palybook模式一般用于批量执行复杂的自动化任务,如批量安装等。此处的重点是资源资产配置清单,执行选项等。

所需类及其调用关系为:

2.4.1 playbook结合options和play类的使用的简单案例

# -*- coding: utf-8 -*-
#!/usr/bin/env python

#核心类
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager


#InventoryManager类
loader = DataLoader()
inventory = InventoryManager(loader=loader,sources=['auto_hosts'])

#VariableManager类
variable_manager = VariableManager(loader=loader,inventory=inventory)

#Options 执行选项
Options = namedtuple('Options',
                     ['connection',
                      'remote_user',
                      'ask_sudo_pass',
                      'verbosity',
                      'ack_pass',
                      'module_path',
                      'forks',
                      'become',
                      'become_method',
                      'become_user',
                      'check',
                      'listhosts',
                      'listtasks',
                      'listtags',
                      'syntax',
                      'sudo_user',
                      'sudo',
                      'diff'])
options = Options(connection='smart',
                       remote_user=None,
                       ack_pass=None,
                       sudo_user=None,
                       forks=5,
                       sudo=None,
                       ask_sudo_pass=False,
                       verbosity=5,
                       module_path=None,
                       become=None,
                       become_method=None,
                       become_user=None,
                       check=False,
                       diff=False,
                       listhosts=None,
                       listtasks=None,
                       listtags=None,
                       syntax=None)


#PlaybookExecutor 执行playbook
passwords = dict()
playbook = PlaybookExecutor(playbooks=['touch.yml'], # 注意路径,多个基本直接添加即可
                            inventory=inventory,
                            variable_manager=variable_manager,
                            loader=loader,
                            options=options,
                            passwords=passwords,
                            )

playbook.run()
ansible_api_k2.py
---
- hosts: 192.168.102.101
  remote_user: root
  vars:
      touch_file: touch.file
  tasks:
      - name: touch file
        shell: "touch /tmp/{{touch_file}}"
touch.yml
PLAY [192.168.102.101] ****************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************
ok: [192.168.102.101]

TASK [touch file] *********************************************************************************************************************************************************
 [WARNING]: Consider using file module with state=touch rather than running touch

changed: [192.168.102.101]

PLAY RECAP ****************************************************************************************************************************************************************
192.168.102.101            : ok=2    changed=1    unreachable=0    failed=0

# 会在192.168.102.101上生成/tmp/touch.file
输出结果

 

2.5 callback的改写

通过上面脚本的执行我们会发现脚本执行结果并不是很友善,比如执行任务的时间,执行任务所耗费时间,执行了什么,特别是工程中我们将输出结果发送到前端进行展示以及后端日志保存,如果只是简单地字符串则就不易操作,此时就要使用callback的改写。

 

2.5.1 为什么要重写callback

为了自定义格式输出

 

2.5.2 怎么改写callback

1. 通过子类继承父类(callbackbase)

2. 通过子类改写父类的部分方法

      v2_runner_on_unreachable

     v2_runner_on_ok

     v2_runner_on_failed

 

2.5.3 重写ad-hoc模式的类

# -*- coding: utf-8 -*-
# !/usr/bin/env python

# 核心类
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

# InventoryManager类
loader = DataLoader()
inventory = InventoryManager(loader=loader, sources=['auto_hosts'])

# VariableManager类
variable_manager = VariableManager(loader=loader, inventory=inventory)

# Options 执行选项
Options = namedtuple('Options',
                     ['connection',
                      'remote_user',
                      'ask_sudo_pass',
                      'verbosity',
                      'ack_pass',
                      'module_path',
                      'forks',
                      'become',
                      'become_method',
                      'become_user',
                      'check',
                      'listhosts',
                      'listtasks',
                      'listtags',
                      'syntax',
                      'sudo_user',
                      'sudo',
                      'diff'])
options = Options(connection='smart',
                  remote_user=None,
                  ack_pass=None,
                  sudo_user=None,
                  forks=5,
                  sudo=None,
                  ask_sudo_pass=False,
                  verbosity=5,
                  module_path=None,
                  become=None,
                  become_method=None,
                  become_user=None,
                  check=False,
                  diff=False,
                  listhosts=None,
                  listtasks=None,
                  listtags=None,
                  syntax=None)
# Play 执行对象和模块
play_source = dict(
    name="Ansible Play ad-hoc test",  # 任务执行的名称
    hosts='192.168.102.101',  # 控制着任务执行的目标主机,可以通过逗号填入多台主机,或者正则匹配,或者主机组
    gather_facts='no',  # 执行任务之前去获取响应主机的相关信息,建议关闭,提高执行效率
    tasks=[
        # 以dict的方式实现,一个任务一个dict,可以写多个,module 为对应模块,args为传入的参数
        dict(action=dict(module='shell', args='touch /tmp/ad_hoc_test1')),
        # dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}')))
    ]
)
play = Play().load(play_source, variable_manager=variable_manager, loader=loader)


class ModelResultsCollector(CallbackBase):  # 继承父类CallbackBase
    """
    重写callbackBase类的部分方法
    """

    def __init__(self, *args, **kwargs):
        super(ModelResultsCollector, self).__init__(*args, **kwargs)  # 初始化父类方法
        self.host_ok = {}
        self.host_unreachable = {}
        self.host_failed = {}

    def v2_runner_on_unreachable(self, result):  # result 为父类中获取所有执行结果信息的对象
        self.host_unreachable[result._host.get_name()] = result

    def v2_runner_on_ok(self, result):
        self.host_ok[result._host.get_name()] = result

    def v2_runner_on_failed(self, result):
        self.host_failed[result._host.get_name()] = result


callback = ModelResultsCollector()

passwords = dict()
tqm = TaskQueueManager(
    inventory=inventory,
    variable_manager=variable_manager,
    loader=loader,
    options=options,
    passwords=passwords,  # 没有实际的作用
    stdout_callback=callback,
)
result = tqm.run(play)

print(callback.host_ok.items())
# [(u"192.168.102.101",<ansible.executor.task_result.TaskResult object at 0x10406b790>)]

result_raw = {'success': {}, 'failed': {}, 'unreachable': {}}

for host, result in callback.host_ok.items():
    result_raw['success'][host] = result._result # _result属性来获取任务执行的结果
for host, result in callback.host_failed.items():
    result_raw['failed'][host] = result._result
for host, result in callback.host_unreachable.items():
    result_raw['unreachable'][host] = result._result
    
print(result_raw)
ansible_api_k1.py
{
    'success': {
        '192.168.102.101': {
            'changed': True,
            'end': '2019-03-03 21:24:29.426184',
            'stdout': '',
            'cmd': 'touch /tmp/ad_hoc_test1',
            'rc': 0,
            'start': '2019-03-03 21:24:29.422987',
            'stderr': '',
            'delta': '0:00:00.003197',
            'invocation': {
                'module_args': {
                    'warn': True,
                    'executable': None,
                    '_uses_shell': True,
                    '_raw_params': 'touch /tmp/ad_hoc_test1',
                    'removes': None,
                    'creates': None,
                    'chdir': None,
                    'stdin': None
                }
            },
            'warnings': ['Consider using file module with state=touch rather than running touch'],
            '_ansible_parsed': True,
            'stdout_lines': [],
            'stderr_lines': [],
            '_ansible_no_log': False,
            'failed': False
        }
    },
    'failed': {},
    'unreachable': {}
}
输出结果

 

2.5.4 重写playbook模式的类

# -*- coding: utf-8 -*-
# !/usr/bin/env python

# 核心类
from collections import namedtuple
from ansible.parsing.dataloader import DataLoader
from ansible.vars.manager import VariableManager
from ansible.inventory.manager import InventoryManager
from ansible.executor.playbook_executor import PlaybookExecutor
from ansible.playbook.play import Play
from ansible.executor.task_queue_manager import TaskQueueManager
from ansible.plugins.callback import CallbackBase

# InventoryManager类
loader = DataLoader()
inventory = InventoryManager(loader=loader, sources=['auto_hosts'])

# VariableManager类
variable_manager = VariableManager(loader=loader, inventory=inventory)

# Options 执行选项
Options = namedtuple('Options',
                     ['connection',
                      'remote_user',
                      'ask_sudo_pass',
                      'verbosity',
                      'ack_pass',
                      'module_path',
                      'forks',
                      'become',
                      'become_method',
                      'become_user',
                      'check',
                      'listhosts',
                      'listtasks',
                      'listtags',
                      'syntax',
                      'sudo_user',
                      'sudo',
                      'diff'])
options = Options(connection='smart',
                  remote_user=None,
                  ack_pass=None,
                  sudo_user=None,
                  forks=5,
                  sudo=None,
                  ask_sudo_pass=False,
                  verbosity=5,
                  module_path=None,
                  become=None,
                  become_method=None,
                  become_user=None,
                  check=False,
                  diff=False,
                  listhosts=None,
                  listtasks=None,
                  listtags=None,
                  syntax=None)


# CallbackBase改写
class PlayBookResultsCollector(CallbackBase):
    CALLBACK_VERSION = 2.0

    def __init__(self, *args, **kwargs):
        super(PlayBookResultsCollector, self).__init__(*args, **kwargs)
        self.task_ok = {}
        self.task_skipped = {}
        self.task_failed = {}
        self.task_status = {}
        self.task_unreachable = {}

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

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

    def v2_runner_on_unreachable(self, result):
        self.task_unreachable[result._host.get_name()] = result

    def v2_runner_on_skipped(self, result):
        self.task_ok[result._host.get_name()] = result

    def v2_playbook_on_stats(self, stats):
        hosts = sorted(stats.processed.keys())
        for h in hosts:
            t = stats.summarize(h)
            self.task_status[h] = {
                "ok": t['ok'],
                "changed": t['changed'],
                "unreachable": t['unreachable'],
                "skipped": t['skipped'],
                "failed": t['failures']
            }


callback = PlayBookResultsCollector()

# PlaybookExecutor 执行playbook
passwords = dict()
playbook = PlaybookExecutor(playbooks=['touch.yml'],  # 如果有多个剧本则在列表中写入
                            inventory=inventory,
                            variable_manager=variable_manager,
                            loader=loader,
                            options=options,
                            passwords=passwords,
                            )
playbook._tqm._stdout_callback = callback # 此处调用与ad-hoc模式不同
playbook.run()

results_raw = {'skipped': {}, 'failed': {}, 'success': {}, "status": {}, 'unreachable': {}, "changed": {}}


for host, result in callback.task_ok.items():
    results_raw['success'][host] = result._result # _result属性来获取任务执行的结果
for host, result in callback.task_failed.items():
    results_raw['failed'][host] = result._result    
for host, result in callback.task_unreachable.items():
    results_raw['unreachable'][host] = result._result


print(results_raw)
ansible_api_k2.py
{
    'skipped': {},
    'failed': {},
    'success': {
        '192.168.102.101': {
            'changed': True,
            'end': '2019-03-03 21:49:44.851042',
            'stdout': '',
            'cmd': 'touch /tmp/touch.file',
            'rc': 0,
            'start': '2019-03-03 21:49:44.848321',
            'stderr': '',
            'delta': '0:00:00.002721',
            'invocation': {
                'module_args': {
                    'warn': True,
                    'executable': None,
                    '_uses_shell': True,
                    '_raw_params': 'touch /tmp/touch.file',
                    'removes': None,
                    'creates': None,
                    'chdir': None,
                    'stdin': None
                }
            },
            'warnings': ['Consider using file module with state=touch rather than running touch'],
            '_ansible_parsed': True,
            'stdout_lines': [],
            'stderr_lines': [],
            '_ansible_no_log': False,
            'failed': False
        }
    },
    'status': {},
    'unreachable': {},
    'changed': {}
}
输出结果

 

posted @ 2019-05-13 11:16  324小熊  阅读(656)  评论(0编辑  收藏  举报