前言:
????ansible的結(jié)果默認(rèn)是輸出到cli終端和日志里面的,用慣了saltsatck的returners數(shù)據(jù)回調(diào)后,也很是喜歡ansible也有,一開始不知道有這個(gè)功能,自己也簡(jiǎn)單實(shí)現(xiàn)了這樣的功能。
我的實(shí)現(xiàn)方式是,在模塊里面做一些輸出的邏輯。當(dāng)使用ansible runner api的時(shí)候,是在后面runner代碼,最后加了一段往redis輸出的邏輯。 這里實(shí)現(xiàn)數(shù)據(jù)的輸出有些獨(dú)特,但是只能是在模塊和 api方面搞 。 如果是用playbook的話,按照我以前的思路的話,再繼續(xù)改ansbile的源碼。 ?這兩天聽沈燦說(shuō),ansible有個(gè)callback_plugins的功能,可以對(duì)于執(zhí)行的狀態(tài)做一些判斷,比如,執(zhí)行成功,執(zhí)行失敗,異步執(zhí)行,異步執(zhí)行失敗,playbook開始,結(jié)束等等。?
沈燦這貨先寫了關(guān)于ansible callbacks的文章,我看到了后,才知道有而一個(gè)東西。大家可以看看 。
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/?
我也不說(shuō)復(fù)雜了,就簡(jiǎn)單說(shuō)一個(gè)例子,把執(zhí)行的結(jié)果,都推到redis里面,也可以暫存到sqlite數(shù)據(jù)庫(kù)里面,只是這段代碼我給屏蔽了,有興趣的朋友再搞搞。對(duì)于redis里面的數(shù)據(jù)可以寫一個(gè)頁(yè)面展現(xiàn)下,專門記錄錯(cuò)誤的問(wèn)題,成功的就pass掉。
原文:http://rfyiamcool.blog.51cto.com/1030776/1440624?
#xiaorui.ccimport?os
import?time
import?sqlite3
import?redis
import?jsondbname?=?'/tmp/setup.db'
TIME_FORMAT='%Y-%m-%d?%H:%M:%S'try:con?=?sqlite3.connect(dbname)cur?=?con.cursor()
except:passdef?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數(shù)據(jù)的。
原文:http://rfyiamcool.blog.51cto.com/1030776/1440624?
原文:http://rfyiamcool.blog.51cto.com/1030776/1440624?
雖然我上面的例子用了redis,sqlite數(shù)據(jù)庫(kù),其實(shí)我個(gè)人推薦用mongodb這樣的文檔數(shù)據(jù)庫(kù)的。因?yàn)閍nsible主runner函數(shù),給callbacks傳遞了一個(gè)叫res的變量,他本身就是一個(gè)dict對(duì)象,如果放到redis的hash,sqlite的各種字段,夠你煩的了,如果直接mongo,那就簡(jiǎn)單了,直接insert ! 歐了
這里在show一個(gè)郵件的callbacks代碼,場(chǎng)景是,非常消耗時(shí)間的任務(wù),當(dāng)執(zhí)行完成后,查看結(jié)果咋辦? 但是你也可以在終端繼續(xù)看,既然咱們講了callbacks_plugins,就可以把結(jié)果push到你的郵箱里面,當(dāng)然只給你發(fā)錯(cuò)誤的,有問(wèn)題的。 下面的callback代碼需要自己替換成自己用的郵箱、密碼、smtp服務(wù)器。
#xiaorui.cc
原文:http://rfyiamcool.blog.51cto.com/1030776/1440624?import?smtplibdef?mail(subject='Ansible?error?mail',?sender='<root>',?to='root',?cc=None,?bcc=None,?body=None):if?not?body:body?=?subjectsmtp?=?smtplib.SMTP('localhost')content?=?'From:?%s\n'?%?sendercontent?+=?'To:?%s\n'?%?toif?cc:content?+=?'Cc:?%s\n'?%?cccontent?+=?'Subject:?%s\n\n'?%?subjectcontent?+=?bodyaddresses?=?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:returnsender?=?'"Ansible:?%s"?<root>'?%?hostsubject?=?'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>'?%?hostif?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'?+?reselse: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>'?%?hostif?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'?+?reselse: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)
如果不想發(fā)郵件,又不想搞到數(shù)據(jù)庫(kù)里面,怎么辦? 那來(lái)點(diǎn)低端的?! ≈苯訉懭氲轿募锩妗?/span>
官方給出一個(gè)例子,大家照著模板寫就行了。
import?os
import?time
import?jsonTIME_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?factsdata?=?'omitted'else:data?=?data.copy()invocation?=?data.pop('invocation',?None)data?=?json.dumps(data)if?invocation?is?not?None:data?=?json.dumps(invocation)?+?"?=>?%s?"?%?datapath?=?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):passdef?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):passdef?runner_on_async_poll(self,?host,?res,?jid,?clock):passdef?runner_on_async_ok(self,?host,?res,?jid):passdef?runner_on_async_failed(self,?host,?res,?jid):log(host,?'ASYNC_FAILED',?res)def?playbook_on_start(self):passdef?playbook_on_notify(self,?host,?handler):passdef?playbook_on_no_hosts_matched(self):passdef?playbook_on_no_hosts_remaining(self):passdef?playbook_on_task_start(self,?name,?is_conditional):passdef?playbook_on_vars_prompt(self,?varname,?private=True,?prompt=None,?encrypt=None,?confirm=False,?salt_size=None,?salt=None,?default=None):passdef?playbook_on_setup(self):passdef?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):passdef?playbook_on_stats(self,?stats):pass
原文:?http://rfyiamcool.blog.51cto.com/1030776/1440624?
也可以把結(jié)果以webhooks鉤子的方式,做些你想做的東西。
callbacks的各種狀態(tài)還是很多的,每個(gè)函數(shù)的字眼還是很好理解的。
比如:
on_any ?哪都有他 !任何的狀態(tài)他觸發(fā)。
runner_on_failed 失敗
runner_on_ok ?成功
runner_on_unreachable 網(wǎng)絡(luò)不可達(dá)
runner_on_no_hosts 沒(méi)有主機(jī)
runner_on_async_poll 任務(wù)的異步執(zhí)行
playbook_on_start ?playbook執(zhí)行的時(shí)候
等等。。。。 ?自己嘗試吧 !
class?CallbackModule(object):def?on_any(self,?*args,?**kwargs):passdef?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):passdef?runner_on_async_poll(self,?host,?res,?jid,?clock):passdef?runner_on_async_ok(self,?host,?res,?jid):passdef?runner_on_async_failed(self,?host,?res,?jid):log(host,?'ASYNC_FAILED',?res)def?playbook_on_start(self):passdef?playbook_on_notify(self,?host,?handler):passdef?playbook_on_no_hosts_matched(self):passdef?playbook_on_no_hosts_remaining(self):passdef?playbook_on_task_start(self,?name,?is_conditional):passdef?playbook_on_vars_prompt(self,?varname,?private=True,?prompt=None,?encrypt=None,?confirm=False,?salt_size=None,?salt=None,?default=None):passdef?playbook_on_setup(self):passdef?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):passdef?playbook_on_stats(self,?stats):pass
原文:?http://rfyiamcool.blog.51cto.com/1030776/1440624?
咱們可以簡(jiǎn)單看看ansible的callbacks源碼。
規(guī)定了兩個(gè)類,一個(gè)是供應(yīng)ansible-playbook用的,還有一個(gè)是供應(yīng)ansible,也就是cli。 根據(jù)各種的情況,調(diào)用不同的函數(shù),首先會(huì)打到終端,再log日志,最后是自定義的callbacks的插件。?
好了,就這樣了 !!!!?
總結(jié)
以上是生活随笔為你收集整理的ansible调用callbacks插件实现结果nosql输出回调的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。