前言:
????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.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數據的。
原文: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?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)
如果不想發郵件,又不想搞到數據庫里面,怎么辦? 那來點低端的。 直接寫入到文件里面。
官方給出一個例子,大家照著模板寫就行了。
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?
也可以把結果以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):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?
咱們可以簡單看看ansible的callbacks源碼。
規定了兩個類,一個是供應ansible-playbook用的,還有一個是供應ansible,也就是cli。 根據各種的情況,調用不同的函數,首先會打到終端,再log日志,最后是自定義的callbacks的插件。?
好了,就這樣了 !!!!?
總結
以上是生活随笔為你收集整理的ansible调用callbacks插件实现结果nosql输出回调的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。