0
点赞
收藏
分享

微信扫一扫

自动化运维工具Ansible(22)日志插件源码分析

插件源码分析

log_plays插件位置

[root@localhost ~]# ansible-doc -t callback log_plays
> LOG_PLAYS (/usr/lib/python2.7/site-packages/ansible/plugins/callback/log_plays.py)
得到插件具体位置

[root@localhost ~]# vim /usr/lib/python2.7/site-packages/ansible/plugins/callback/log_plays.py


插件源码分析

# (C) 2012, Michael DeHaan, 
# (c) 2017 Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

#兼容py2或py3
from __future__ import (absolute_import, division, print_function)
#定义__metaclass__变量
__metaclass__ = type

#描述文档
DOCUMENTATION = '''
callback: log_plays //声明名称
type: notification //声明类型
short_description: write playbook output to log file //描述
version_added: historical //加入ansible时的版本状态
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 //必须有ansible用户可写入的目录
options:
log_folder: //定义路径
version_added: '2.9'
default: /var/log/ansible/hosts //默认路径
description: The folder where log files will be created. //在路径下创建文件
env: //两种方式env 和 ini 改变路径
- name: ANSIBLE_LOG_FOLDER
ini:
- section: callback_log_plays
key: log_folder
'''

#导入模块
import os //os 系统模块
import time //time 时间模块
import json //json 处理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 //所有回调插件必须继承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
#例:class A(B) A中继承了B的数据信息并可以修改继承的数据
#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_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 invocation is not None:
data = json.dumps(invocation) + " => %s " % data

path = os.path.join(self.log_folder, 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)


查看CallbackBase源码

[root@localhost ~]# pip install ipython
[root@localhost ~]# ipython
In [1]: from ansible.plugins.callback import CafallbackBase
在上方log_plays.py文件中存在CafallbackBase

In [2]: ??CallbackBase
查看CallbackBase

In [3]: !ls /usr/lib/python2.7/site-packages/ansible/plugins/callback/
`ansible-doc -t callback log_plays` 得到所有模块的位置

In [4]: !vim /usr/lib/python2.7/site-packages/ansible/plugins/callback/__init__.py
具体CallbackBase插件源码位置



CallbackBase继承相关信息

。。。。。
import difflib
import json
import os
import sys
import warnings

。。。。。
class CallbackBase(AnsiblePlugin):
。。。。。

def set_option(self, k, v):
self._plugin_options[k] = v
def get_option(self, k):
return self._plugin_options[k]
def set_options(self, task_keys=None, var_options=None, direct=None):

。。。。。

def set_play_context(self, play_context):
pass

def on_any(self, *args, **kwargs):
pass

def runner_on_failed(self, host, res, ignore_errors=False):
pass

def runner_on_ok(self, host, res):
pass

def runner_on_skipped(self, host, item=None):
pass

def runner_on_unreachable(self, host, res):
pass

def runner_on_no_hosts(self):
pass

def runner_on_async_poll(self, host, res, jid, clock):
pass

。。。。。

# V2 METHODS, by default they call v1 counterparts if possible
# 只可在ansible v2版本中运行的函数
def v2_on_any(self, *args, **kwargs):
self.on_any(args, kwargs)

def v2_runner_on_failed(self, result, ignore_errors=False):
host = result._host.get_name()
self.runner_on_failed(host, result._result, ignore_errors)

def v2_runner_on_ok(self, result):
host = result._host.get_name()
self.runner_on_ok(host, result._result)

def v2_runner_on_skipped(self, result):
if C.DISPLAY_SKIPPED_HOSTS:
host = result._host.get_name()

def v2_runner_on_unreachable(self, result):
host = result._host.get_name()
self.runner_on_unreachable(host, result._result)
。。。。。



核心逻辑

###log_play.py后半部分###
#设置选项
def set_options(self, task_keys=None, var_options=None, direct=None):
。。。。。
self.log_folder = self.get_option("log_folder")
#从log_folder获取选项,在描述文档中定义log_folder
#- section: callback_log_play
# key: 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 invocation is not None:
data = json.dumps(invocation) + " => %s " % data

#path保存路径:log_folder配置的路径+host主机名
#now当前时间
path = os.path.join(self.log_folder, 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) //在FAILED状态下调用self.log

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)

举报

相关推荐

0 条评论