他の構成管理ツールにはすでにあったり、もしくはAnsibleでも実はあるのかもしれないけど見つからなかったので、「こんなのがあったらいいなあ」レベルのコードを書きましたので投稿します。
なるべく余計なものを入れないようにしたかったのでPythonで書いたのですが、Python初心者でプログラムもちゃんと勉強したわけではないので、重箱の隅どころかいたるところをつつき放題だと思います。
コードは一番最後に置いておきます。
自分は忘れっぽいので、「あのホストにはplaybook適用していたっけ?」となることが多いです。
Ansibleのconfにはいちおうログを取る設定があるのですが、ほとんど実行時に流れるメッセージをそのままログしているだけなので、ログを追いづらいなと思いました。
とにかくホストに対していつ何のplaybookの何のtaskが実行済みかどうか一覧で見たいのです。
で書いたコードによる結果が以下のようになります(インデントは適当です)
見づらくてすいません。あと適当ですみません。とにかくこんなふうにズラッと情報を表示したいのです。
すでにこういう仕組みがあるようならここから先は見ずにソッ……と教えてください。
スクリプトについて
- ansibleのconfでログを取れるようにしておくこと(log= を有効にする。初期設定の/var/log/ansible.logを使用するなら、権限設定を忘れない)
- ログを消すと過去の状態が追えないのでログを消さない(稼働時からログしてないと意味がない)
- でもスクリプト実行時のplaybookの解析でログが増えていくのでログが肥大化します(すいません)
- playbookはひとつのディレクトリにちゃんと.ymlという名前で置いてある前提です。
- hostのレンジ指定(www[0:10].hogeとかのやつ)は対応してません。
- 途中グループ名を取得していますがどうリストに表示したらいいのか考え付かなかったので、取得するだけで終わってます。
では以下コードです。
#-*- coding: utf-8 -*-
import commands
# 文字色の設定
GREEN = '\033[92m'
YELLOW = '\033[93m'
RED ='\033[91m'
ENDC = '\033[0m'
# ログファイルの場所を指定
log=open('/var/log/ansible.log','r')
# hostsファイルの場所を指定
hosts=open('/etc/ansible/hosts', 'r')
# playbookのディレクトリを指定
ymldir='/etc/ansible/'
# まずhostsを解析し、リストを作る
hostline = hosts.readline()
groupnames = []
groupnames.append('default')
hostnames = []
hostname = []
while hostline:
if hostline.startswith("#") or hostline.startswith("\n"):
pass
elif hostline.startswith("["):
groupnames.append(hostline.rstrip(']\n').lstrip('['))
hostnames.append(hostname)
hostname = []
else:
hostname.append(hostline.rstrip('\n'))
hostline = hosts.readline()
else:
hostnames.append(hostname)
# リスト作りここまで。groupname[default, webserver...] hostnames[ [defaultのホスト, defaultのホスト2] , [webserverの..]] に格納。
# 次にPlaybookをチェックして、対応するホストと実行するタスクのリストを作る。
#Playbookのファイル名を格納
playbooks=commands.getoutput('/bin/ls '+ymldir+' | grep .yml').split('\n')
playbookhosts=[]
playbookhost=[]
tasks=[]
task=[]
for i in playbooks:
result = commands.getoutput('/usr/bin/ansible-playbook '+ymldir+i+' --list-hosts --list-tasks').split('\n')
hostcount = 0
taskcount = 0
taskmode = 0
for j in result:
if "host count=" in j:
hostcount = j[(j.rfind("=") + 1):]
hostcount = int(hostcount)
elif hostcount > 0:
playbookhost.append(j.strip())
hostcount -= 1
if hostcount==0 and "play #" in j:
taskmode = 1
elif taskmode==1 and len(j.strip()) > 0:
task.append(j.strip())
playbookhosts.append(playbookhost)
playbookhost=[]
tasks.append(task)
task=[]
# リスト作りここまで。playbooks[playbook1.yml, playbook2.yml...] playbookhosts[ [playbook1のホスト1, playbook2のホスト2], [playbook3の...]]] に格納 tasksも同様
# 必要な情報がそろった(あとはログ側の実行結果と日時なので)ので表にする。
# ホスト プレイブック タスク で全タスク分の表。
disptable=[]
rows=[]
j = 0
for i in playbookhosts:
hcount=len(playbookhosts[playbookhosts.index(i)])
#host個数分繰り返す
while hcount > 0:
hcount -= 1
tcount=len(tasks[j])
#さらにtask個数分繰り返す
while tcount > 0:
tcount -= 1
rows.append(playbookhosts[j][hcount])
rows.append(playbooks[j])
rows.append(tasks[j][tcount])
disptable.append(rows)
rows=[]
j += 1
# ここからログの解析
line = log.readline()
while line:
# 上から順にみていって、チェックじゃないansibleが実行されたとき
if "ansible-playbook" in line and ymldir in line and "--check" not in line and "--list-" not in line:
# たぶん実行されているplaybookを格納
cbook = line[line.index(ymldir)+len(ymldir):line.index(".yml")+4]
try:
if cbook and "TASK" in line:
# taskを格納
ctask = line[line.index("TASK: [")+len("TASK :["):line.index("]")]
except NameError:
pass
try:
if cbook and ctask:
if " changed:" in line:
# hostを取り出す
chost = line[line.index("[")+len("["):line.index("]")]
# hostとtaskが一致するdistpable[[]]に追記したい。何か記録されているなら上書き
for i in disptable:
if chost in i and cbook in i and ctask in i:
try:
if disptable[disptable.index(i)][3]:
disptable[disptable.index(i)][3] = YELLOW + "changed" + ENDC
disptable[disptable.index(i)][4] = line[:line.index(",")]
except IndexError:
disptable[disptable.index(i)].append(YELLOW + "changed" + ENDC)
disptable[disptable.index(i)].append(line[:line.index(",")])
elif " ok:" in line:
# hostを取り出す
chost = line[line.index("[")+len("["):line.index("]")]
# hostとtaskが一致するdisptable[[]]に追記したい。ただしchangedが記録されているなら実行しない(変更日時を知りたいので)
for i in disptable:
if chost in i and cbook in i and ctask in i:
try:
if disptable[disptable.index(i)][3] != "changed":
disptable[disptable.index(i)][3] = GREEN + "ok" + ENDC
disptable[disptable.index(i)][4] = line[:line.index(",")]
except IndexError:
disptable[disptable.index(i)].append(GREEN + "ok" + ENDC)
disptable[disptable.index(i)].append(line[:line.index(",")])
elif " failed:" in line:
# hostを取り出す
chost = line[line.index("[")+len("["):line.index("]")]
# hostとtaskが一致するdistpable[[]]に追記したい。何か記録されているなら上書き
for i in disptable:
if chost in i and cbook in i and ctask in i:
try:
if disptable[disptable.index(i)][3]:
disptable[disptable.index(i)][3] = RED + "failed" + ENDC
disptable[disptable.index(i)][4] = line[:line.index(",")]
except IndexError:
disptable[disptable.index(i)].append(RED + "failed" + ENDC)
disptable[disptable.index(i)].append(line[:line.index(",")])
except NameError:
pass
line = log.readline()
# 表示部分
print "HOST\t\t\tPLAYBOOK\t\tTASK\t\tRESULT\t\tDATE"
disptable.sort()
j = 0;
prvhost=""
prvyml=""
for i in disptable:
nowhost = disptable[j][0]
nowyml = disptable[j][1]
if nowhost==prvhost:
nowhost="\t\t"
if nowyml==prvyml:
nowyml="\t\t"
try:
print nowhost + '\t\t' + nowyml + '\t\t' + disptable[j][2] + '\t\t' + disptable[j][3] + '\t\t' + disptable[j][4]
except IndexError:
print nowhost + '\t\t' + nowyml + '\t\t' + disptable[j][2] + '\t\t' + "no data" + '\t\t' + ""
prvhost = disptable[j][0]
prvyml = disptable[j][1]
j += 1
もっといい方法があったら(絶対あると思う)教えてください。