Ansible 回调插件简单使用

2017.04.11

Developing Plugins

插件是增强ansible核心功能的代码片段,我们可以很方便的使用插件,编写插件代码。如果我们想要对ansible的执行结果进行分析,根据返回结果发送邮件,写入日志等都可以通过插件实现。

插件列表

  • Action plugins are front ends to modules and can execute actions on the controller before calling the modules themselves.(操作插件,调用模块之前执行操作)
  • Cache plugins are used to keep a cache of ‘facts’ to avoid costly fact-gathering operations.(缓存插件,缓存主机facts变量)
  • Callback plugins enable you to hook into Ansible events for display or logging purposes.(回调插件,对事件进行显示和记录,这个常用)
  • Connection plugins define how to communicate with inventory hosts.(连接插件,定义如何与节点主机通信)
  • Filters plugins allow you to manipulate data inside Ansible plays and/or templates. This is a Jinja2 feature; Ansible ships extra filter plugins.(过滤插件,Jinja2功能)
  • Lookup plugins are used to pull data from an external source. These are implemented using a custom Jinja2 function.(查找插件用于从外部源提取数据)
  • Strategy plugins control the flow of a play and execution logic.(策略插件)
  • Shell plugins deal with low-level commands and formatting for the different shells Ansible can encounter on remote hosts.(shell插件处理低级别命令和格式化)
  • Test plugins allow you to validate data inside Ansible plays and/or templates. This is a Jinja2 feature; Ansible ships extra test plugins.(测试插件)
  • Vars plugins inject additional variable data into Ansible runs that did not come from an inventory, playbook, or the command line.(Vars插件)

Callback plugins

在日常开发中使用回调插件比较多一点,通过callback插件,可以实现回调功能,里面定义了若干场景,如主机不可达,执行任务失败,执行任务成功等,分别对应不同的方法,这样就可以实现在不同的场景触发不同的操作。

设置回调功能参数

该功能ansible默认是关闭的

$ cat /etc/ansible/ansible.cfg 
...
bin_ansible_callbacks = True #加载功能

# set plugin path directories here, separate with colons 定义插件位置
action_plugins     = /usr/share/ansible_plugins/action_plugins
callback_plugins   = /usr/share/ansible_plugins/callback_plugins
connection_plugins = /usr/share/ansible_plugins/connection_plugins
lookup_plugins     = /usr/share/ansible_plugins/lookup_plugins
vars_plugins       = /usr/share/ansible_plugins/vars_plugins
filter_plugins     = /usr/share/ansible_plugins/filter_plugins
...

编写回调脚本

编写回调脚本放到配置文件中定义的目录中,并赋予可执行权限。我们可以将执行结果写入本地文件、数据库或发邮件等等。在日常开发中我们会将结果写入日志或跟踪记录执行的进度和异常。

写入本地文件

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

import json

result_file = '/tmp/result'

class CallbackModule(object):
    def runner_on_ok(self, host, res):
        with open(result_file,'a') as f:
            f.write('success\n')
            f.write(str(host))
            f.write(json.dumps(res, sort_keys=True, indent=4, separators=(',', ': ')))
            f.write('\n')

    def runner_on_failed(self, host, res, ignore_errors=False):
        with open(result_file, 'a') as f:
            f.write('failed\n')
            f.write(str(host))
            f.write(json.dumps(res, sort_keys=True, indent=4, separators=(',', ': ')))
            f.write('\n')

    def runner_on_unreachable(self,host,res):
        with open(result_file, 'a') as f:
            f.write('unreachable\n')
            f.write(str(host))
            f.write(json.dumps(res, sort_keys=True, indent=4, separators=(',', ': ')))
            f.write('\n')

测试

$ ansible-playbook  -i hosts test.yml

PLAY [default_group] **********************************************************

TASK: [test | cp test auto install script] ************************************
ok: [172.31.30.178]

TASK: [test | install test] ***************************************************
changed: [172.31.30.178]

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

#制造一个unreachable错误
$ ansible-playbook  -i hosts  test.yml

PLAY [default_group] **********************************************************

TASK: [test | cp test auto install script] ************************************
fatal: [172.31.30.178] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/root/startoftwares.retry

172.31.30.178              : ok=0    changed=0    unreachable=1    failed=0

#查看结果
$ cat /tmp/result
success
172.31.30.178{
    "changed": false,
    "dest": "/tmp/test.sh",
    "gid": 1000,
    "group": "ubuntu",
    "invocation": {
        "module_args": "src=test.sh dest=/tmp/test.sh owner=ubuntu group=ubuntu mode=0755",
        "module_name": "copy"
    },
    "md5sum": "da1bd2e811a9909b96b09c3988deaec6",
    "mode": "0755",
    "owner": "ubuntu",
    "path": "/tmp/test.sh",
    "size": 58,
    "state": "file",
    "uid": 1000
}
success
172.31.30.178{
    "changed": true,
    "cmd": "bash /tmp/test.sh",
    "delta": "0:00:03.004104",
    "end": "2017-04-11 10:48:20.429600",
    "invocation": {
        "module_args": "bash /tmp/test.sh",
        "module_name": "shell"
    },
    "rc": 0,
    "start": "2017-04-11 10:48:17.425496",
    "stderr": "",
    "stdout": ""
}
unreachable
172.31.30.178"SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue"

发邮件

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

import smtplib
from email.mime.text import MIMEText



mail_host="smtp.xxx.com"
mail_user="xxxxx@xxx.com"
mail_pass="xxxxxxx"
user_to = "xxxxx@xxx.com"


def send_mail(context):
    msg = MIMEText(context, 'plain', 'utf-8')
    msg['From'] = mail_user
    msg['To'] = user_to
    msg['Subject'] = 'Ansible error mail'
    server = smtplib.SMTP(mail_host, 25)
    server.login(mail_user, mail_pass)
    server.sendmail(mail_user, user_to, msg.as_string())
    server.quit()

class CallbackModule(object):
    def runner_on_ok(self, host, res):
        pass

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

    def runner_on_unreachable(self,host,res):
        if isinstance(res,basestring):
            context = 'An error occured for host ' + host + ' with the following message:\n\n' + res
        else:
            context = 'An error occured for host' + host + ' with the following message:\n\n' + str(res)
        send_mail(context)

测试

$ ansible-playbook  -i hosts  test.yml

PLAY [default_group] **********************************************************

TASK: [test | cp test auto install script] ************************************
fatal: [172.31.30.178] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/root/startoftwares.retry

172.31.30.178              : ok=0    changed=0    unreachable=1    failed=0

查收邮件

以上脚本都写的比较简单,建议参考:官方插件代码样例

参考

官方文档 中文文档配置文件参数详解 ansible调用callbacks插件实现结果nosql输出回调