ansible源码开发
一、使用插件
1 回调插件介绍
1.1 修改默认的回调插件
同时只能有一个回调插件作为主要的管理者,用于输出到屏幕。
如果想替换,应该在这个插件中修改 CALLBACK_TYPE = stdout
,
之后在 ansible.cfg
中配置 stdout 插件。
[defaults]
stdout_callback = json # 以 JSON 的格式输出结果
或使用自定义的回调:
[defaults]
stdout_callback = mycallback
默认 情况下这仅对 playbook 生效,如果想让 ad-hoc 方式生效应该在 ansible.cfg
文件中做如下设置:
[defaults]
bin_ansible_callbacks = True
示例演示
[root@qfedu.com ~]# ansible-playbook -i hosts checkhosts.yml --limit dbservers
{
"custom_stats": {},
"global_custom_stats": {},
"plays": [
{
"play": {
"duration": {
"end": "2020-04-23T02:32:44.163630Z",
"start": "2020-04-23T02:32:44.131390Z"
},
"id": "0242ac12-0002-b0c7-074b-00000000000d",
"name": "all"
},
"tasks": [
{
"hosts": {
"172.18.0.3": {
"_ansible_no_log": false,
"_ansible_verbose_always": true,
"action": "debug",
"ansible_distribution": "VARIABLE IS NOT DEFINED!",
"changed": false
}
},
"task": {
"duration": {
"end": "2020-04-23T02:32:44.163630Z",
"start": "2020-04-23T02:32:44.137440Z"
},
"id": "0242ac12-0002-b0c7-074b-00000000000f",
"name": "debug"
}
}
]
}
],
"stats": {
"172.18.0.3": {
"changed": 0,
"failures": 0,
"ignored": 0,
"ok": 1,
"rescued": 0,
"skipped": 0,
"unreachable": 0
}
}
}
1.2 启用其他内置的回调插件
大部分情况下,无论是内置的回调插件还是自定义的回调插件,都需要在 ansible.cfg
中添加到白名单中,从而才能启用。
callback_whitelist = timer, mail, profile_roles, custom_callback
timer
这个回调插件可以计算整个 playbook 的运行时间mail
这个回调插件可以实现发送邮件的功能profile_roles
这个插件是在执行中提添加用时时间custom_callback
是自定义的插件,稍后会讲
1.3 获取帮助
ansible-doc -t callback -l
可以查看当前可用的回调插件列表
ansible-doc -t callback <callback plugins name>
可查看具体回调插件的帮助文档
比如:
[root@qfedu ~]# ansible-doc -t callback timer
> TIMER (/usr/lib/python2.7/site-packages/ansible/plugins/callback/timer.py)
This callback just adds total play duration to the play stats.
* This module is maintained by The Ansible Community
REQUIREMENTS: whitelist in configuration
CALLBACK_TYPE: aggregate
METADATA:
status:
- preview
supported_by: community
2 回调插件类型
回调插件类型在回调插件类中定义:
class CallbackModule(CallbackBase):
CALLBACK_TYPE = 'notification'
不同的回调类型对于 playbook 的输出有不一样的效果
stdout
标准输出类型,用在回调的主管理者aggregate
聚合类型, 把此类型插件处理的结果和stdout
类型插件合并一起输出到标准输出。比如 :timer
,profile_tasks
等。notification
通知类型,不参与标准输出,也不影响标准输出插件的正常输出,只是会把执行 playbook 的返回值写的指定的媒介中。
比如: log_plays
,mail
。假如自定义把执行playbook 的结果输出到数据库中就可以使用此类型。
查看所有默认的查看类型
[root@qfedu ~]# grep 'CALLBACK_TYPE =.*' /usr/lib/python2.7/site-packages/ansible/plugins/callback/*.py |cut -d: -f 2 |sort -u
CALLBACK_TYPE = 'aggregate'
CALLBACK_TYPE = 'notification'
CALLBACK_TYPE = 'stdout
3 把返回结果输出到日志中
内置的回调插件 log_plays
会将 playbook 的返回信息输出到 /var/log/ansible/hosts
目录中。
可以在 ansible.cfg
中配置指定的目录,使用 log_folder
比如,把日志存到 /tmp/ansible/hosts/
目录下
在 ansible.cfg
文件的最后添加如下配置
配置日志存放的目录
[callback_log_plays]
log_folder=/tmp/ansible/hosts/
配置到白名单
ansible.cfg
callback_whitelist = log_plays
Inventory
[root@qfedu.com ~]# cat hosts
[dbservers]
172.18.0.3
[webservers]
172.18.0.4
172.18.0.5
[allservers:children]
dbservers
webservers
playbook
remoteDate.yml
- hosts: all
gather_facts: no
tasks:
- name: test
shell: date +"%F %T"
执行 playbook
[root@qfedu.com ~]# ansible-playbook -i hosts remoteDate.yml
PLAY [all] *********************************************************************
TASK [test] ********************************************************************
fatal: [172.18.0.5]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host 172.18.0.5 port 22: Connection refused", "unreachable": true}
changed: [172.18.0.3]
changed: [172.18.0.4]
PLAY RECAP *********************************************************************
172.18.0.3 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
172.18.0.4 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
172.18.0.5 : ok=0 changed=0 unreachable=1 failed=0 skipped=0 rescued=0 ignored=0
Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds
查看输出结果
[root@qfedu.com ~]# ls /tmp/ansible/hosts/
172.18.0.3 172.18.0.4 172.18.0.5
[root@qfedu.com ~]# cat /tmp/ansible/hosts/172.18.0.3
Apr 24 2020 06:43:57 - OK - {"module_args": {"data": "pong"}} => {"changed": false, "ping": "pong", "_ansible_no_log": false, "ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}}
Apr 24 2020 06:45:11 - OK - {"module_args": {"warn": true, "executable": null, "_uses_shell": true, "strip_empty_ends": true, "_raw_params": "date +\"%F %T\"", "removes": null, "argv": null, "creates": null, "chdir": null, "stdin_add_newline": true, "stdin": null}} => {"stderr_lines": [], "cmd": "date +\"%F %T\"", "end": "2020-04-24 06:45:11.110025", "_ansible_no_log": false, "stdout": "2020-04-24 06:45:11", "changed": true, "rc": 0, "start": "2020-04-24 06:45:10.878037", "stderr": "", "delta": "0:00:00.231988", "stdout_lines": ["2020-04-24 06:45:11"], "ansible_facts": {"discovered_interpreter_python": "/usr/bin/python"}}
二、开发自定义插件
1 log_plays 插件源码分析
# (C) 2012, Michael DeHaan, <michael.dehaan@gmail.com>
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = '''
callback: log_plays
type: notification
short_description: write playbook output to log file
version_added: historical
description:
- This callback writes playbook output to a file per host in the `/var/log/ansible/hosts` directory
requirements:
- Whitelist in configuration
- A writeable /var/log/ansible/hosts directory by the user executing Ansible on the controller
options:
log_folder:
version_added: '2.9'
default: /var/log/ansible/hosts
description: The folder where log files will be created.
env:
- name: ANSIBLE_LOG_FOLDER
ini:
- section: callback_log_plays
key: log_folder
'''
import os
import time
import json
from ansible.utils.path import makedirs_safe
from ansible.module_utils._text import to_bytes
from ansible.module_utils.common._collections_compat import MutableMapping
from ansible.parsing.ajson import AnsibleJSONEncoder
from ansible.plugins.callback import CallbackBase
# NOTE: in Ansible 1.2 or later general logging is available without
# this plugin, just set ANSIBLE_LOG_PATH as an environment variable
# or log_path in the DEFAULTS section of your ansible configuration
# file. This callback is an example of per hosts logging for those
# that want it.
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_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__()
def set_options(self, task_keys=None, var_options=None, direct=None):
super(CallbackModule, self).set_options(task_keys=task_keys, var_options=var_options, direct=direct)
self.log_folder = self.get_option("log_folder")
if not os.path.exists(self.log_folder):
makedirs_safe(self.log_folder)
def log(self, host, category, data):
if isinstance(data, MutableMapping):
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, cls=AnsibleJSONEncoder)
if