ansible调用callbacks插件实现结果nosql输出回调

前言:

ansible的结果默认是输出到cli终端和日志里面的,用惯了saltsatck的returners数据回调后,也很是喜欢ansible也有,一开始不知道有这个功能,自己也简单实现了这样的功能。

我的实现方式是,在模块里面做一些输出的逻辑。当使用ansible runner api的时候,是在后面runner代码,最后加了一段往redis输出的逻辑。 这里实现数据的输出有些独特,但是只能是在模块和 api方面搞 。 如果是用playbook的话,按照我以前的思路的话,再继续改ansbile的源码。  这两天听沈灿说,ansible有个callback_plugins的功能,可以对于执行的状态做一些判断,比如,执行成功,执行失败,异步执行,异步执行失败,playbook开始,结束等等。

沈灿这货先写了关于ansible callbacks的文章,我看到了后,才知道有而一个东西。大家可以看看 。

http://www.shencan.net/index.php/2014/07/17/ansible-%E6%8F%92%E4%BB%B6%E4%B9%8Bcallback_plugins-%EF%BC%88%E5%AE%9E%E6%88%98%EF%BC%89/

我也不说复杂了,就简单说一个例子,把执行的结果,都推到redis里面,也可以暂存到sqlite数据库里面,只是这段代码我给屏蔽了,有兴趣的朋友再搞搞。对于redis里面的数据可以写一个页面展现下,专门记录错误的问题,成功的就pass掉。

原文:http://rfyiamcool.blog.51cto.com/1030776/1440624

#xiaorui.cc

import os
import time
import sqlite3
import redis
import json

dbname = ‘/tmp/setup.db‘
TIME_FORMAT=‘%Y-%m-%d %H:%M:%S‘

try:
    con = sqlite3.connect(dbname)
    cur = con.cursor()
except:
    pass

def log(host, data):

#    if type(data) == dict:
#        invocation = data.pop(‘invocation‘, None)
#        if invocation.get(‘module_name‘, None) != ‘setup‘:
#            return
#
#    facts = data.get(‘ansible_facts‘, None)
#
#    now = time.strftime(TIME_FORMAT, time.localtime())
#
#    try:
#        # `host` is a unique index
#        cur.execute("REPLACE INTO inventory (now, host, arch, dist, distvers, sys,kernel) VALUES(?,?,?,?,?,?,?);",
#        (
#            now,
#            facts.get(‘ansible_hostname‘, None),
#            facts.get(‘ansible_architecture‘, None),
#            facts.get(‘ansible_distribution‘, None),
#            facts.get(‘ansible_distribution_version‘, None),
#            facts.get(‘ansible_system‘, None),
#            facts.get(‘ansible_kernel‘, None)
#        ))
#        con.commit()
#    except:
#        pass
#
class CallbackModule(object):
    def runner_on_ok(self, host, res):
        r = redis.Redis(host=‘127.0.0.1‘, port=6379, db=0) 
        r.set(host,str(res))

        f = open(‘/tmp/11‘,‘a‘)
        f.write(str(host))
        f.write(str(res))
        f.close()
        log(host, res)
    def runner_on_failed(self, host, res, ignore_errors=False):
        f = open(‘/tmp/11‘,‘a‘)
        f.write(‘\nbad\n‘)
        f.close()
        log(host, res)

还是可以接收所有的facts数据的。

原文:http://rfyiamcool.blog.51cto.com/1030776/1440624

原文:http://rfyiamcool.blog.51cto.com/1030776/1440624

虽然我上面的例子用了redis,sqlite数据库,其实我个人推荐用mongodb这样的文档数据库的。因为ansible主runner函数,给callbacks传递了一个叫res的变量,他本身就是一个dict对象,如果放到redis的hash,sqlite的各种字段,够你烦的了,如果直接mongo,那就简单了,直接insert ! 欧了

这里在show一个邮件的callbacks代码,场景是,非常消耗时间的任务,当执行完成后,查看结果咋办?  但是你也可以在终端继续看,既然咱们讲了callbacks_plugins,就可以把结果push到你的邮箱里面,当然只给你发错误的,有问题的。 下面的callback代码需要自己替换成自己用的邮箱、密码、smtp服务器。

#xiaorui.cc
原文:http://rfyiamcool.blog.51cto.com/1030776/1440624 
 
import smtplib

def mail(subject=‘Ansible error mail‘, sender=‘<root>‘, to=‘root‘, cc=None, bcc=None, body=None):
    if not body:
        body = subject

    smtp = smtplib.SMTP(‘localhost‘)

    content = ‘From: %s\n‘ % sender
    content += ‘To: %s\n‘ % to
    if cc:
        content += ‘Cc: %s\n‘ % cc
    content += ‘Subject: %s\n\n‘ % subject
    content += body

    addresses = to.split(‘,‘)
    if cc:
        addresses += cc.split(‘,‘)
    if bcc:
        addresses += bcc.split(‘,‘)

    for address in addresses:
        smtp.sendmail(sender, address, content)

    smtp.quit()

class CallbackModule(object):

    """
    This Ansible callback plugin mails errors to interested parties.
    """

    def runner_on_failed(self, host, res, ignore_errors=False):
        if ignore_errors:
            return
        sender = ‘"Ansible: %s" <root>‘ % host
        subject = ‘Failed: %(module_name)s %(module_args)s‘ % res[‘invocation‘]
        body = ‘The following task failed for host ‘ + host + ‘:\n\n%(module_name)s %(module_args)s\n\n‘ % res[‘invocation‘]
        if ‘stdout‘ in res.keys() and res[‘stdout‘]:
            subject = res[‘stdout‘].strip(‘\r\n‘).split(‘\n‘)[-1]
            body += ‘with the following output in standard output:\n\n‘ + res[‘stdout‘] + ‘\n\n‘
        if ‘stderr‘ in res.keys() and res[‘stderr‘]:
            subject = res[‘stderr‘].strip(‘\r\n‘).split(‘\n‘)[-1]
            body += ‘with the following output in standard error:\n\n‘ + res[‘stderr‘] + ‘\n\n‘
        if ‘msg‘ in res.keys() and res[‘msg‘]:
            subject = res[‘msg‘].strip(‘\r\n‘).split(‘\n‘)[0]
            body += ‘with the following message:\n\n‘ + res[‘msg‘] + ‘\n\n‘
        body += ‘A complete dump of the error:\n\n‘ + str(res)
        mail(sender=sender, subject=subject, body=body)
                  
    def runner_on_unreachable(self, host, res):
        sender = ‘"Ansible: %s" <root>‘ % host
        if isinstance(res, basestring):
            subject = ‘Unreachable: %s‘ % res.strip(‘\r\n‘).split(‘\n‘)[-1]
            body = ‘An error occured for host ‘ + host + ‘ with the following message:\n\n‘ + res
        else:
            subject = ‘Unreachable: %s‘ % res[‘msg‘].strip(‘\r\n‘).split(‘\n‘)[0]
            body = ‘An error occured for host ‘ + host + ‘ with the following message:\n\n‘ +                    res[‘msg‘] + ‘\n\nA complete dump of the error:\n\n‘ + str(res)
        mail(sender=sender, subject=subject, body=body)

    def runner_on_async_failed(self, host, res, jid):
        sender = ‘"Ansible: %s" <root>‘ % host
        if isinstance(res, basestring):
            subject = ‘Async failure: %s‘ % res.strip(‘\r\n‘).split(‘\n‘)[-1]
            body = ‘An error occured for host ‘ + host + ‘ with the following message:\n\n‘ + res
        else:
            subject = ‘Async failure: %s‘ % res[‘msg‘].strip(‘\r\n‘).split(‘\n‘)[0]
            body = ‘An error occured for host ‘ + host + ‘ with the following message:\n\n‘ +                    res[‘msg‘] + ‘\n\nA complete dump of the error:\n\n‘ + str(res)
        mail(sender=sender, subject=subject, body=body)

如果不想发邮件,又不想搞到数据库里面,怎么办? 那来点低端的。  直接写入到文件里面。

官方给出一个例子,大家照着模板写就行了。

import os
import time
import json

TIME_FORMAT="%b %d %Y %H:%M:%S"
MSG_FORMAT="%(now)s - %(category)s - %(data)s\n\n"

if not os.path.exists("/var/log/ansible/hosts"):
    os.makedirs("/var/log/ansible/hosts")

def log(host, category, data):
    if type(data) == dict:
        if ‘verbose_override‘ in data:
            # avoid logging extraneous data from facts
            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(TIME_FORMAT, time.localtime())
    fd = open(path, "a")
    fd.write(MSG_FORMAT % dict(now=now, category=category, data=data))
    fd.close()

class CallbackModule(object):
    """
    logs playbook results, per host, in /var/log/ansible/hosts
    """

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

    def runner_on_failed(self, host, res, ignore_errors=False):
        log(host, ‘FAILED‘, res)

    def runner_on_ok(self, host, res):
        log(host, ‘OK‘, res)

    def runner_on_skipped(self, host, item=None):
        log(host, ‘SKIPPED‘, ‘...‘)

    def runner_on_unreachable(self, host, res):
        log(host, ‘UNREACHABLE‘, res)

    def runner_on_no_hosts(self):
        pass

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

    def runner_on_async_ok(self, host, res, jid):
        pass

    def runner_on_async_failed(self, host, res, jid):
        log(host, ‘ASYNC_FAILED‘, res)

    def playbook_on_start(self):
        pass

    def playbook_on_notify(self, host, handler):
        pass

    def playbook_on_no_hosts_matched(self):
        pass

    def playbook_on_no_hosts_remaining(self):
        pass

    def playbook_on_task_start(self, name, is_conditional):
        pass

    def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
        pass

    def playbook_on_setup(self):
        pass

    def playbook_on_import_for_host(self, host, imported_file):
        log(host, ‘IMPORTED‘, imported_file)

    def playbook_on_not_import_for_host(self, host, missing_file):
        log(host, ‘NOTIMPORTED‘, missing_file)

    def playbook_on_play_start(self, name):
        pass

    def playbook_on_stats(self, stats):
        pass

原文: http://rfyiamcool.blog.51cto.com/1030776/1440624

也可以把结果以webhooks钩子的方式,做些你想做的东西。

callbacks的各种状态还是很多的,每个函数的字眼还是很好理解的。

比如:

on_any  哪都有他 !任何的状态他触发。

runner_on_failed 失败

runner_on_ok  成功

runner_on_unreachable 网络不可达

runner_on_no_hosts 没有主机

runner_on_async_poll 任务的异步执行

playbook_on_start  playbook执行的时候

等等。。。。  自己尝试吧 !

class CallbackModule(object):

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

    def runner_on_failed(self, host, res, ignore_errors=False):
        log(host, ‘FAILED‘, res)

    def runner_on_ok(self, host, res):
        log(host, ‘OK‘, res)

    def runner_on_skipped(self, host, item=None):
        log(host, ‘SKIPPED‘, ‘...‘)

    def runner_on_unreachable(self, host, res):
        log(host, ‘UNREACHABLE‘, res)

    def runner_on_no_hosts(self):
        pass

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

    def runner_on_async_ok(self, host, res, jid):
        pass

    def runner_on_async_failed(self, host, res, jid):
        log(host, ‘ASYNC_FAILED‘, res)

    def playbook_on_start(self):
        pass

    def playbook_on_notify(self, host, handler):
        pass

    def playbook_on_no_hosts_matched(self):
        pass

    def playbook_on_no_hosts_remaining(self):
        pass

    def playbook_on_task_start(self, name, is_conditional):
        pass

    def playbook_on_vars_prompt(self, varname, private=True, prompt=None, encrypt=None, confirm=False, salt_size=None, salt=None, default=None):
        pass

    def playbook_on_setup(self):
        pass

    def playbook_on_import_for_host(self, host, imported_file):
        log(host, ‘IMPORTED‘, imported_file)

    def playbook_on_not_import_for_host(self, host, missing_file):
        log(host, ‘NOTIMPORTED‘, missing_file)

    def playbook_on_play_start(self, name):
        pass

    def playbook_on_stats(self, stats):
        pass

原文: http://rfyiamcool.blog.51cto.com/1030776/1440624

咱们可以简单看看ansible的callbacks源码。

规定了两个类,一个是供应ansible-playbook用的,还有一个是供应ansible,也就是cli。 根据各种的情况,调用不同的函数,首先会打到终端,再log日志,最后是自定义的callbacks的插件。

好了,就这样了 !!!!

时间: 2024-10-10 02:04:18

ansible调用callbacks插件实现结果nosql输出回调的相关文章

ansible playbook callbacks

大家都知道ansible的playbook是不会显示标准输出的. 如何能让ansible像commands line 一样有标准输出呢? ansible Boss+Merchant+Web -m shell -a "hostname;ip a" 192.168.6.210 | success | rc=0 >> Pay-Boss+Merchant+Web 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue 

任意输入一个4位正整数,调用函数(函数自定义)输出该正整数数的各位数 字组成的最大数。

/*任意输入一个4位正整数,调用函数(函数自定义)输出该正整数数的各位数字组成的最大数.*/#include <stdio.h>int numb(int a){ int i = 0, j = 0,sum = 0, b[4]; b[0] = a%10; b[1] = a/10%10; b[2] = a/100%10; b[3] = a/1000; for (i = 0 ;i < 4; i++) { for(j = 0;j < 3 - i; j++) { if(b[j] > b[

ThinkPHP自动获取关键词(调用第三方插件)

ThinkPHP自动获取关键词调用在线discuz词库 先按照下图路径放好插件 方法如下 /** * 自动获取关键词(调用第三方插件) * @return [type] [description] * www.shouce.ren */ public function keyword() { Vendor('autokeyword.AutoKeyword'); $keyword = new \AutoKeyword(); $str='自动获取关键词并发大数据我们大家好吃饭啦调用第三方插件'; $

jquery ajax 调用kkpager插件 异步加载重新生成分页后,点击页数还是跟首次加载一样

kkpager的用法网上有很多,可以百度的到. 但是在调用动态数据的时候发现两个问题 1.Ajax数据变化但是页码不变的问题,方法来自网上 2.按查询条件重新生成数据和分页,点击分页事件后totalpage 和totalrecord 和没加条件查询的数据一样. 主要原因是因为客户端不会帮你保留总页码数和总条数, 所以在按查询条件重新生成数据时,用hidden按钮绑定你的总页数和总条数 下面是ajax 调用后台数据,返回的总页数和总条数,都存放在hidden里了 $.ajax({ type: "g

如何在 静态编译的QT 5.5.1 中 使用数据库插件连接 ODBC(调用静态插件)

前段时间由于工作的关系,需要编写一个将数据插入到 Sql server 2012 的桌面软件. 由于之前使用的是MFC,偶然间接触到了Qt后,被它的简洁惊艳到了,于是便毅然而然的转投到了Qt的怀抱,哈哈…… 废话不多说,我使用的是最新的Qt 5.5.1版本(Qt 5.5.1 for Windows 32-bit MinGW), 在一路查看帮助文档后, 终于是把程序编译出来,正常运行了.正当我满心欢喜的交付出去的时候,遇到问题了,程序在对方的电脑上运行时, 提示缺少动态库!而且不同电脑缺少的库还不

ionic2 调用自定义插件之研究

摘要 最近在研发一个移动项目,架构已经定型,使用Ionic2开发,虽然ionic2 已经提供了ionic native插件,但是当遇到一些特别的需求大多时候还是需要我们自己封装插件. cordova机制我在此就不提了,我们使用Typescript调用cordova plugin就如同调用第三方库是一个道理,那么这里就少不了书写declare文件,下面我就把几种封装调用的几种方式介绍一下. 方式一,编写全局declare文件 现在我们有一个“残峰(我的同事)”封装好的签名版插件,cordova p

咏南中间件修正了一处BUG,调用中间件插件会报:非法访问

咏南中间件修正了一处BUG,调用中间件插件会报:非法访问将以下方法修改成如下的代码即可function TServerMethods1.GetSvrData(const accountNo, defineId: WideString; inParams: OleVariant): OleVariant;// 调用中间件的插件// defineId=3位插件编号+2位自定义编号var p: TfrmPlugBase; plugClass: string; ps: TPersistent;begin

Unity 调用android插件

1. Unity的Bundle Identifier必须和你的android报名一致 Activity和View的区别: Activity应该是一个展示页面,View是页面上一些按钮视图等等. 如何调用Android插件:  (如果你把方法主入口中的) AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); //参数不变 AndroidJavaObject jo = jc.GetStat

C#调用大漠插件

大漠插件是一个很不错的东西,在按键精灵和易语言里面用得很多,可以后台找图找字,写游戏自动脚本用得特别多.前面写一个微信的自动脚本,查了一些资料,易语言不太熟悉,按键精灵功能上可能不好实现,就找了些资料,大漠插件是可以用在C#里的调用的.这里分享下方法. 一.引用Ddm.dll .NET里面可以直接引用,引用方法,添加引用--COM--找到DM,双击即可 二.注册大漠插件 //这是注册DLL到系统的一个方法,注册大漠则调用 AutoRegCom("regsvr32 -s dm.dll")