Ansible Callback
非api模式下自定义callback
ansible.cfg中开启callback功能
callback_plugins = /usr/share/ansible/plugins/callback #指定路径后,callback文件要放到这个目录 bin_ansible_callbacks = True callback_whitelist = timer #指定脚本中定义的callback的名称
官网示例:
# cat /usr/local/lib/python3.5/dist-packages/ansible/plugins/callback/log_plays.py #默认路径
# Make coding more python3-ish from __future__ import (absolute_import, division, print_function) __metaclass__ = type from datetime import datetime from ansible.plugins.callback import CallbackBase class CallbackModule(CallbackBase): """ This callback module tells you how long your plays ran for. """ CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'aggregate' CALLBACK_NAME = 'timer' #配置文件中定义的callback名称 CALLBACK_NEEDS_WHITELIST = True def __init__(self): super(CallbackModule, self).__init__() self.start_time = datetime.now() def days_hours_minutes_seconds(self, runtime): minutes = (runtime.seconds // 60) % 60 r_seconds = runtime.seconds - (minutes * 60) return runtime.days, runtime.seconds // 3600, minutes, r_seconds def playbook_on_stats(self, stats): self.v2_playbook_on_stats(stats) def v2_playbook_on_stats(self, stats): end_time = datetime.now() runtime = end_time - self.start_time self._display.display("Playbook run took %s days, %s hours, %s minutes, %s seconds" % (self.days_hours_minutes_seconds(runtime)))
效果图:
ansible自带的log插件
from __future__ import (absolute_import, division, print_function) __metaclass__ = type import os import time import json from ansible.module_utils._text import to_bytes from ansible.plugins.callback import CallbackBase class CallbackModule(CallbackBase): """ logs playbook results, per host, in /var/log/ansible/hosts """ CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'log_plays' #callback name,需要在ansible配置文件中指定 如果下面参数为False,可忽略 CALLBACK_NEEDS_WHITELIST = True TIME_FORMAT="%b %d %Y %H:%M:%S" MSG_FORMAT="%(now)s - %(category)s - %(data)s\n\n" def __init__(self): super(CallbackModule, self).__init__() if not os.path.exists("/var/log/ansible/hosts"): os.makedirs("/var/log/ansible/hosts") def log(self, host, category, data): if type(data) == dict: if '_ansible_verbose_override' in data: # avoid logging extraneous data data = 'omitted' else: data = data.copy() invocation = data.pop('invocation', None) data = json.dumps(data) if invocation is not None: data = json.dumps(invocation) + " => %s " % data path = os.path.join("/var/log/ansible/hosts", host) now = time.strftime(self.TIME_FORMAT, time.localtime()) msg = to_bytes(self.MSG_FORMAT % dict(now=now, category=category, data=data)) with open(path, "ab") as fd: fd.write(msg) def runner_on_failed(self, host, res, ignore_errors=False): self.log(host, 'FAILED', res) def runner_on_ok(self, host, res): self.log(host, 'OK', res) def runner_on_skipped(self, host, item=None): self.log(host, 'SKIPPED', '...') def runner_on_unreachable(self, host, res): self.log(host, 'UNREACHABLE', res) def runner_on_async_failed(self, host, res, jid): self.log(host, 'ASYNC_FAILED', res) def playbook_on_import_for_host(self, host, imported_file): self.log(host, 'IMPORTED', imported_file) def playbook_on_not_import_for_host(self, host, missing_file): self.log(host, 'NOTIMPORTED', missing_file)
以上示例为将执行结果写入到 /var/log/ansible/hosts目录以host为名的文件里,
#修改log_plays脚本并进行简单说明
#!/usr/bin/env python #-*-coding:utf-8 -*-
from __future__ import (absolute_import, division, print_function) __metaclass__ = type import os import time import json from ansible.module_utils._text import to_bytes from ansible.plugins.callback import CallbackBase class CallbackModule(CallbackBase): """ logs playbook results, per host, in /var/log/ansible/hosts """ CALLBACK_VERSION = 2.0 CALLBACK_TYPE = 'notification' CALLBACK_NAME = 'log_plays' #此处为callback_name,需要在配置文件中指定 CALLBACK_NEEDS_WHITELIST = True #为True时,以上必做
#定义的格式,自定义即可 TIME_FORMAT="%b %d %Y %H:%M:%S" MSG_FORMAT="%(now)s - %(category)s - %(data)s\n\n" MSG_FORMAT1="%(data)s\n\n" def __init__(self): super(CallbackModule, self).__init__() if not os.path.exists("/var/log/ansible/hosts"): os.makedirs("/var/log/ansible/hosts")
#定义log函数的目的是将处理后的执行结果写到文件,我这里直接display在屏幕上,这里可以自定义一个写入到数据库的函数, def log(self, host, category, data): #默认的执行结果为一个字典,即data在这里为一个字典 result_last = json.dumps(self.option_result(data)) #定义一个函数,接收执行的结果,由于结果不支持字典数据,所以只能dumps成str self._display.display(result_last) #将执行结果在屏幕上显示出来(不支持print打印)
#以下为相关格式化内容
path = os.path.join("/var/log/ansible/hosts", host) now = time.strftime(self.TIME_FORMAT, time.localtime()) # msg = to_bytes(self.MSG_FORMAT % dict(now=now, category=category, data=data)) # msg1 = to_bytes(self.MSG_FORMAT1 % dict(data=data)) # with open(path, "ab") as fd: # fd.write(msg) #定义函数,解析执行结果,并返回给log函数(注:此函数里代码可直接写到log函数里,此处为了区分清楚,单写一个) def option_result(self,msg): result = {} result['stderr_lines'] = data_result['stderr_lines'] result['start_time'] = data_result['start'] result['end_time'] = data_result['end'] result['stderr'] = data_result['stderr'] return result
def runner_on_failed(self, host, res, ignore_errors=False): self.log(host, 'FAILED', res) def runner_on_ok(self, host, res): self.log(host, 'OK', res) def runner_on_skipped(self, host, item=None): self.log(host, 'SKIPPED', '...') def runner_on_unreachable(self, host, res): self.log(host, 'UNREACHABLE', res) def runner_on_async_failed(self, host, res, jid): self.log(host, 'ASYNC_FAILED', res) def playbook_on_import_for_host(self, host, imported_file): self.log(host, 'IMPORTED', imported_file) def playbook_on_not_import_for_host(self, host, missing_file): self.log(host, 'NOTIMPORTED', missing_file)
结果(没有经过解析处理的完整内容):
经过解析:
#然后可以自定义脚本,将结果发送邮件给管理员,或者写入到数据库,此处不再详细介绍
Api模式下使用CallBack功能
首先看一下简单示例(ad-hoc),python_version == 2.3.1
#!/usr/bin/env python # -*-coding:utf-8 -*- import json from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.vars import VariableManager from ansible.inventory import Inventory from ansible.playbook.play import Play from ansible.executor.task_queue_manager import TaskQueueManager from ansible.plugins.callback import CallbackBase # 自定义运行callback,即运行完api后调用v2_runner_on_ok函数,打印host和result 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 # 创建一个Options类型,列表里为属性,这里用来设置ansible执行时的参数,如ask_pass,sudo_user等等(namedtuple创建一个和tuple类似的对象,而且对象拥有可以访问的属性) Options = namedtuple('Options', ['connection', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'diff']) # initialize needed objects loader = DataLoader() # 可以通过options.(属性) 的方法去获取属性的值,如获取connection的值可以直接写成options.connection options = Options(connection='ssh', module_path='/path/to/mymodules', forks=100, become=None, become_method=None, become_user=None, check=False, diff=False) passwords = dict(vault_pass='secret') # Instantiate our ResultCallback for handling results as they come in results_callback = ResultsCallback() # 管理变量的类,包括主机、组、扩展等变量 variable_manager = VariableManager() # 加载inventory变量,这里的sources的值可以是hosts文件,也可以是ip列表,如['xxx.xxx.xx.x','xx.xx.xx.xx'] inventory = Inventory(loader=loader, variable_manager=variable_manager, host_list='/etc/ansible/hosts') # 创建一个任务 play_source = dict( name="Ansible Play", hosts='test', gather_facts='no', tasks=[ dict(action=dict(module='shell', args='ls /home'), register='shell_out'), dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}'))) ] ) play = Play().load(play_source, variable_manager=variable_manager, loader=loader) # actually run it tqm = None try: tqm = TaskQueueManager( inventory=inventory, variable_manager=variable_manager, loader=loader, options=options, passwords=passwords, stdout_callback=results_callback, ) result = tqm.run(play) finally: if tqm is not None: tqm.cleanup() #print (results_callback.host_ok.items()) #打印执行成功后的结果 #执行的结果: dict_items([('192.168.132.130', <ansible.executor.task_result.TaskResult object at 0x7f8eeba6d710>), ('10.10.10.11', <ansible.executor.task_result.TaskResult object at 0x7f8eeba6db38>)] ##定义字典用于接收或者处理结果 result_raw = {'success':{},'failed':{},'unreachable':{}} #循环打印这个结果,success,failed,unreachable需要每个都定义一个 for host,result in results_callback.host_ok.items(): result_raw['success'][host] = result._result #结果下面有__host和_result属性较为常用
for host,result in results_callback.host_failed.items(): result_raw['failed'][host] = result._result for host,result in results_callback.host_unreachable.items(): result_raw['unreachable'][host] = result._result print (result_raw)
结果
{'success': {'10.10.10.11': {'changed': False, 'msg': 'detail\nerror.logs\nmanage.py\nMyDevOps\noperations\nreports\nscanhosts\nstatic\ntemplates\ntest_ansible.py\nusers', '_ansible_verbose_always': True, 'failed': False, '_ansible_no_log': False},
'192.168.132.130': {'changed': False, 'msg': 'detail\nerror.logs\nmanage.py\nMyDevOps\noperations\nreports\nscanhosts\nstatic\ntemplates\ntest_ansible.py\nusers', '_ansible_verbose_always': True, 'failed': False, '_ansible_no_log': False}}, 'unreachable': {}, 'failed': {}}
playbook 自定义示例
#!/usr/bin/env python import json from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.vars import VariableManager from ansible.inventory import Inventory from ansible.executor.playbook_executor import PlaybookExecutor from ansible.plugins.callback import CallbackBase class ResultsCallback(CallbackBase): def __init__(self,*args,**kwargs): super(ResultsCallback,self).__init__(*args,**kwargs) self.task_ok = {} self.task_unreachable = {} self.task_failed = {} self.task_skipped = {} self.task_stats = {} def v2_runner_on_unreachable(self, result): self.task_unreachable[result._host.get_name()] = result 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_skipped(self, result, *args, **kwargs): self.task_skipped[result._host.get_name()] = result def v2_runner_on_stats(self, result, *args, **kwargs): self.task_stats[result._host.get_name()] = result ## 用来加载解析yml文件或者json内容 loader = DataLoader() variable_manager = VariableManager() results_callback = ResultsCallback() # 根据inventory加载对应变量,此处host_list参数可以有两种格式: # 1: hosts文件(需要), # 2: 可以是IP列表,此处使用IP列表 inventory = Inventory(loader=loader, variable_manager=variable_manager,host_list='/etc/ansible/hosts') variable_manager.set_inventory(inventory) passwords=None #初始化 Options = namedtuple('Options', ['connection', 'remote_user', 'verbosity', 'ack_pass', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'listhosts', 'listtasks', 'listtags', 'syntax',]) #初始化需要的对象 options = Options(connection='smart', remote_user='root', ack_pass=None, forks=100, verbosity=5, module_path=None, become=True, become_method='sudo', become_user='root', check=None, listhosts=None, listtasks=None, listtags=None, syntax=None) playbook = PlaybookExecutor(playbooks=['/root/test.yml'],inventory=inventory, variable_manager=variable_manager, loader=loader,options=options,passwords=passwords) playbook._tqm._stdout_callback = results_callback result = playbook.run() ##定义字典用于接收或者处理结果 result_raw = {'success':{},'failed':{},'unreachable':{},'skipped':{},'status':{}} #循环打印这个结果,success,failed,unreachable需要每个都定义一个 for host,result in results_callback.task_ok.items(): result_raw['success'][host] = result._result for host,result in results_callback.task_failed.items(): result_raw['failed'][host] = result._result for host,result in results_callback.task_unreachable.items(): result_raw['unreachable'][host] = result._result for host,result in results_callback.task_skipped.items(): result_raw['skipped'][host] = result._result for host,result in results_callback.task_stats.items(): result_raw['status'][host] = result._result print (result_raw)
打印结果
{'status': {}, 'failed': {'10.10.10.11': {'changed': True, 'rc': 127, 'stdout_lines': [], 'stderr_lines': ['/bin/sh: ifcsonfig: 未找到命令'], 'end': '2018-06-05 16:47:46.441073', 'delta': '0:00:00.011661', 'invocation': {'module_args': {'chdir': None, 'warn': True, 'executable': None, '_uses_shell': True, 'creates': None, '_raw_params': 'ifcsonfig', 'removes': None}}, 'stderr': '/bin/sh: ifcsonfig: 未找到命令', '_ansible_parsed': True, 'start': '2018-06-05 16:47:46.429412', 'cmd': 'ifcsonfig', '_ansible_no_log': False, 'failed': True, 'stdout': ''}}, 'success': {}, 'skipped': {}, 'unreachable': {}}
简单整合2.3.1
#!/usr/bin/env python import json from collections import namedtuple from ansible.parsing.dataloader import DataLoader from ansible.vars import VariableManager from ansible.inventory import Inventory from ansible.executor.playbook_executor import PlaybookExecutor from ansible.plugins.callback import CallbackBase from ansible.playbook.play import Play from ansible.executor.task_queue_manager import TaskQueueManager class ResultsCallback(CallbackBase): def __init__(self,*args,**kwargs): super(ResultsCallback,self).__init__(*args,**kwargs) self.task_ok = {} self.task_unreachable = {} self.task_failed = {} self.task_skipped = {} self.task_stats = {} # self.host_ok = {} # self.host_unreachable = {} # self.host_failed = {} def v2_runner_on_unreachable(self, result): self.task_unreachable[result._host.get_name()] = result 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_skipped(self, result, *args, **kwargs): self.task_skipped[result._host.get_name()] = result def v2_runner_on_stats(self, result, *args, **kwargs): self.task_stats[result._host.get_name()] = result #chushihua class Runner(object): def __init__(self,*args,**kwargs): self.loader = DataLoader() self.variable_manager = VariableManager() self.results_callback = ResultsCallback() self.inventory = Inventory(loader=self.loader, variable_manager=self.variable_manager, host_list='/etc/ansible/hosts') self.variable_manager.set_inventory(self.inventory) self.passwords = None Options = namedtuple('Options', ['connection', 'remote_user', 'verbosity', 'ack_pass', 'module_path', 'forks', 'become', 'become_method', 'become_user', 'check', 'listhosts', 'listtasks', 'listtags', 'syntax', ]) # 初始化需要的对象 self.options = Options(connection='smart', remote_user='root', ack_pass=None, forks=100, verbosity=5, module_path=None, become=True, become_method='sudo', become_user='root', check=None, listhosts=None, listtasks=None, listtags=None, syntax=None) def run_ad_hoc(self): play_source = dict( name="Ansible Play", hosts='test', gather_facts='no', tasks=[ dict(action=dict(module='shell', args='ls /home'), register='shell_out'), dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}'))) ] ) play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader) tqm = None try: tqm = TaskQueueManager( inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=self.passwords, stdout_callback=self.results_callback, ) result = tqm.run(play) finally: if tqm is not None: tqm.cleanup() ##定义字典用于接收或者处理结果 result_raw = {'success': {}, 'failed': {}, 'unreachable': {}, 'skipped': {}, 'status': {}} # 循环打印这个结果,success,failed,unreachable需要每个都定义一个 for host, result in self.results_callback.task_ok.items(): result_raw['success'][host] = result._result for host, result in self.results_callback.task_failed.items(): result_raw['failed'][host] = result._result for host, result in self.results_callback.task_unreachable.items(): result_raw['unreachable'][host] = result._result return result_raw def run_playbook(self): playbook = PlaybookExecutor(playbooks=['/root/test.yml'], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=self.passwords) playbook._tqm._stdout_callback = self.results_callback results = playbook.run() ##定义字典用于接收或者处理结果 result_raw = {'success': {}, 'failed': {}, 'unreachable': {}, 'skipped': {}, 'status': {}} # 循环打印这个结果,success,failed,unreachable需要每个都定义一个 for host, result in self.results_callback.task_ok.items(): result_raw['success'][host] = result._result for host, result in self.results_callback.task_failed.items(): result_raw['failed'][host] = result._result for host, result in self.results_callback.task_unreachable.items(): result_raw['unreachable'][host] = result._result for host, result in self.results_callback.task_skipped.items(): result_raw['skipped'][host] = result._result for host, result in self.results_callback.task_stats.items(): result_raw['status'][host] = result._result return result_raw c = Runner() print (c.run_ad_hoc(),c.run_playbook())
2.4.1
#!/usr/bin/env python import json 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.plugins.callback import CallbackBase from ansible.playbook.play import Play from ansible.executor.task_queue_manager import TaskQueueManager class ResultsCallback(CallbackBase): def __init__(self,*args,**kwargs): super(ResultsCallback,self).__init__(*args,**kwargs) self.task_ok = {} self.task_unreachable = {} self.task_failed = {} self.task_skipped = {} self.task_stats = {} # self.host_ok = {} # self.host_unreachable = {} # self.host_failed = {} def v2_runner_on_unreachable(self, result): self.task_unreachable[result._host.get_name()] = result 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_skipped(self, result, *args, **kwargs): self.task_skipped[result._host.get_name()] = result def v2_runner_on_stats(self, result, *args, **kwargs): self.task_stats[result._host.get_name()] = result #chushihua class Runner(object): def __init__(self,*args,**kwargs): self.loader = DataLoader() self.results_callback = ResultsCallback() self.inventory = InventoryManager(loader=self.loader,sources=['/etc/ansible/hosts']) self.variable_manager = VariableManager(loader=self.loader, inventory=self.inventory) self.passwords = None 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']) # 初始化需要的对象 self.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) def run_ad_hoc(self): play_source = dict( name="Ansible Play ad-hoc", hosts='test', gather_facts='no', tasks=[ dict(action=dict(module='shell', args='ls /home'), register='shell_out'), #dict(action=dict(module='debug', args=dict(msg='{{shell_out.stdout}}'))) ] ) play = Play().load(play_source, variable_manager=self.variable_manager, loader=self.loader) tqm = None try: tqm = TaskQueueManager( inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=self.passwords, stdout_callback=self.results_callback, ) result = tqm.run(play) finally: if tqm is not None: tqm.cleanup() ##定义字典用于接收或者处理结果 result_raw = {'success': {}, 'failed': {}, 'unreachable': {}, 'skipped': {}, 'status': {}} # 循环打印这个结果,success,failed,unreachable需要每个都定义一个 for host, result in self.results_callback.task_ok.items(): result_raw['success'][host] = result._result for host, result in self.results_callback.task_failed.items(): result_raw['failed'][host] = result._result for host, result in self.results_callback.task_unreachable.items(): result_raw['unreachable'][host] = result._result return result_raw def run_playbook(self): playbook = PlaybookExecutor(playbooks=['/root/test.yml'], inventory=self.inventory, variable_manager=self.variable_manager, loader=self.loader, options=self.options, passwords=self.passwords) playbook._tqm._stdout_callback = self.results_callback results = playbook.run() ##定义字典用于接收或者处理结果 result_raw = {'success': {}, 'failed': {}, 'unreachable': {}, 'skipped': {}, 'status': {}} # 循环打印这个结果,success,failed,unreachable需要每个都定义一个 for host, result in self.results_callback.task_ok.items(): result_raw['success'][host] = result._result for host, result in self.results_callback.task_failed.items(): result_raw['failed'][host] = result._result for host, result in self.results_callback.task_unreachable.items(): result_raw['unreachable'][host] = result._result for host, result in self.results_callback.task_skipped.items(): result_raw['skipped'][host] = result._result for host, result in self.results_callback.task_stats.items(): result_raw['status'][host] = result._result return result_raw c = Runner() print (c.run_ad_hoc(),c.run_playbook())