去年的時候開發了一個自動化運維的小系統,用的就是Ansible 的python API,其中動態從數據庫獲取主機組合主機變量的功能,着實費了老大的勁,最後用了個很雞肋的方法實現了。最近幾個月把Ansible的官方文檔通看了一遍,哎,想死的心都有了,文檔裏面已經寫的很清楚如何實現動態inventory文件了,就怪當時自己太着急,沒仔細看文檔。
自己開發的動態inventory腳本文件,只需要支持兩個參數即可:
--list 返回所有的主機組信息,每個組都應該包含字典形式的主機列表,子組列表,如果需要的話還應該組變量,最簡單的信息是隻包含主機列表,返回的數據格式要是JSON格式的。
--host <hostname> 返回該主機的變量列表,或者是返回一個空的字典,JSON格式。
參數--list訪問腳本的時候,返回數據結果:
{ "databases" : { "hosts" : [ "host1.example.com", "host2.example.com" ], "vars" : { "a" : true } }, "webservers" : [ "host2.example.com", "host3.example.com" ], "atlanta" : { "hosts" : [ "host1.example.com", "host4.example.com", "host5.example.com" ], "vars" : { "b" : false }, "children": [ "marietta", "5points" ] }, "marietta" : [ "host6.example.com" ], "5points" : [ "host7.example.com" ] }
databses,webservers,atlanta,marietta,5points爲主機組名稱,hosts爲該主機組下的主機列表,vars爲主機組的變量,children爲該主機組的子組列表。
參數--host <hostname>訪問腳本的時候,返回的數據結果,如果沒有主機變量則返回一個空的字典:
{ "favcolor" : "red", "ntpserver" : "wolf.example.com", "monitoring" : "pack.example.com" }
但是如果針對每個主機都調用一次`--host`的話,將會產生大量的資源開銷。在Ansible1.3或之後的版本中,如果腳本返回了一個稱爲`_meta`的頂級元素,該元素中如果包含着`hostvars`關鍵字,那麼就可以將所有主機的變量都返回。腳本也不會再爲每個主機都調用一次`--host`,這講節省大量的資源開銷,同時也方便的客戶端的緩存。
返回的`_meta`類似如下格式:
{ # results of inventory script as above go here # ... "_meta" : { "hostvars" : { "moocow.example.com" : { "asdf" : 1234 }, "llama.example.com" : { "asdf" : 5678 }, } } }
所返回的變量可以在模板中引用。
示例:
動態腳本內容
[root@web1 ~]# cat /etc/ansible/getHosts.py #!/usr/bin/python import argparse try: import json except ImportError: import simplejson as json '''這裏是模擬數據,工作上一般該數據都是從數據庫或者緩存中讀取的''' mockData = { "webservers":{ "hosts": ["192.168.1.65"], "vars":{ "http_port":8888, "max_clients":789 } }, "databases":{ "hosts":["192.168.1.65"], "vars":{ "action":"Restart MySQL server." } } } '''模擬數據結束''' def getList(): '''get list hosts group''' print json.dumps(mockData) def getVars(host): '''Get variables about a specific host''' print json.dumps(mockData[host]["vars"]) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('--list',action='store_true',dest='list',help='get all hosts') parser.add_argument('--host',action='store',dest='host',help='get all hosts') args = parser.parse_args() if args.list: getList() if args.host: getVars(args.host)
該腳本支持兩個參數--list和--host。
playbook內容:
[root@web1 ~]# cat /etc/ansible/test.yml --- - hosts: webservers remote_user: root tasks: - name: config httpd.file template: src=/etc/ansible/httpd.j2 dest=/etc/httpd.conf
執行命令:
[root@web1 ansible]# ansible-playbook -i /etc/ansible/getHosts.py /etc/ansible/test.yml PLAY [webservers] ************************************************************* GATHERING FACTS *************************************************************** ok: [192.168.1.65] TASK: [config httpd.file] ***************************************************** changed: [192.168.1.65] PLAY RECAP ******************************************************************** 192.168.1.65 : ok=2 changed=1 unreachable=0 failed=0 [root@web1 ansible]# ansible -i /etc/ansible/getHosts.py databases -m shell -a "echo {{ action }}"192.168.1.65 | success | rc=0 >> Restart MySQL server.
Python API調用動態inventory:
[root@web1 ~]# cat an.py #!/usr/bin/env python import ansible.runner runner = ansible.runner.Runner( module_name='shell', module_args='echo {{ action }}', pattern='databases', host_list='/etc/ansible/getHosts.py', forks=10 ) data = runner.run() print data [root@web1 ~]# python an.py {'dark': {}, 'contacted': {u'192.168.1.65': {u'cmd': u'echo Restart MySQL server.', u'end': u'2015-08-07 15:26:02.141350', u'stdout': u'Restart MySQL server.', u'changed': True, u'start': u'2015-08-07 15:26:02.128109', u'delta': u'0:00:00.013241', u'stderr': u'', u'rc': 0, 'invocation': {'module_name': 'shell', 'module_args': u'echo Restart MySQL server.'}, u'warnings': []}}}
總的來說,只要腳本文件支持--list和--host <hostname>參數,並且返回的結果是文檔所指定的格式,就不會出現什麼問題。
好吧,就寫到這了,趕緊更新自己的運維繫統中的動態腳本去.......