NW機器のconfigを作るとき別のconfigを置換するのもいいんですが、jinja2でテンプレートから読み込んで作るという方法を覚えてから精度とスピードが格段に上がり、以前にはもう戻れません。
csvから読み込むようにするとcsvだけ管理すればいいので楽チンです。
今回いわゆる1:1と1:Nの値が複合したパラメータをレンダリングが出来たのでメモしておきます。
職場で使っているのをそのまま書くわけには行きませんので仮値での再現です。
ケース
今回はBIG-IP LTMのプール作成の際に本件に直面しました。
本番、テスト環境といったバーチャルサーバをいろいろ立てるけどそれぞれメンバー数が違ったりね。
BIG-IPはGUIでポチポチ設定するのは面倒くさいので、なるべくコマンドで流すようにしています。
メンバーの様な可変になる値もcsvから読み込んで、テンプレートからこういう出力を作りたいわけです。
- poolA
- node1:80
- node2:80
- node3:80
- poolA_test
- node4:80
- poolB
- node5:80
これを1つのテンプレートから起こしたい。
やりたいことの整理
LBの設計次第ですが、今回のケースは以下です。
- 固定のパラメータ作成
- プール名
- バランシング方式
- ポート
- モニター
- 可変のパラメータ作成
- プールメンバー
- 上記をまとめたパラメータをテンプレートに渡す
固定の方は完全に1:1書き方になるので、この2つのcsvは分けないといけませんね。
最終形を整理する
最終形を最初に想定できると書くべき内容が想像しやすいのでいいですね。
csvから読み込むと辞書型で格納されます。
テンプレートに送る段階で各値の{key1: value1, key2: value2 ... }が格納されていればOKということになりますね。
jinja2触りたての頃はいろんな変数がごっちゃになっていましたがようやく見るべきところがわかってきました。
やったこと
- 固定パラメータ用の変数作成
- 可変パラメータ用の変数作成
- 可変パラメータ用変数を加工
- テンプレートに送る用の変数にマージ
- ループさせてrenderに送る
各ファイル
- createpool.py
処理の本体です。
from jinja2 import Environment, FileSystemLoader import csv env = Environment(loader=FileSystemLoader('./', encoding='utf8')) template = env.get_template('temple.txt') # 1. 固定パラメータ用の変数作成 with open('poolsetting.csv', 'r') as f: data = csv.DictReader(f) settings = [ x for x in data ] # 2. 可変パラメータ用の変数作成 with open('poolmember.csv', 'r') as f: data = csv.DictReader(f) members = [x for x in data] poolmember = {} # 3. 可変パラメータ用変数を加工 # キーとリストを格納 for k in members[0]: poolmember[k] = [] # 各キーに中身を追加 for m in members: for k,v in m.items(): poolmember[k].append(v) # 4. テンプレートに送る用の変数にマージ for s in settings: for k,v in poolmember.items(): # キーが同じだったらマージ if s['poolname'] == k: s.update({k:v}) # テンプレートに渡すようにリネーム s['member'] = s.pop(k) # 5. ループさせてrenderに送る for param in settings: print(template.render(param))
- poolsetting.csv
固定パラメータ用のファイル
poolname,port,monitor POOLA,80,tcp POOLA_test,80,tcp POOLB,8080,tcp
- poolmember.csv
可変パラメータ用のファイル
プール名がキーになるので書き方が先ほどと逆になります。
POOLA,POOLA_test,POOLB node1,node4,node6 node2,node5 node3
- temple.txt
値を渡すテンプレートです。
私はltmのshellからそのまま入力しますので、tmshコマンドから書いてます。
投入パラメータが見やすいように改行してます。
### create {{ poolname }} tmsh create ltm pool {{ poolname }} { \ load-balancing-mode least-connections-members \ {%- for node in member %} {%- if node != None %} members add { \ {{ node }}:{{ port }} \ } \ {%- endif %} {%- endfor %} monitor {{ monitor }} \ }
debug(手動で)
各処理を見ていこうと思いましたが、本件が長くなってしまうので次回に切り出したいと思います。
詳細はこちらに記載しました。
pythonのjinja2で1:Nの可変パラメータをテンプレートに渡した時のメモ debug編 - paloma blog
最終形の出力だけ書きます。
>>> settings[0] {'poolname': 'POOL1', 'port': '80', 'monitor': 'tcp', 'member': ['node1', 'node2', 'node3']}
想定通りの形になりました。
settingsをループで回して各キーがテンプレートに渡りますね。
member内の値はtemple.txt内のループで更に渡されると。
実行
実行します。
(python3) masashi@PC-ubuntu:~/python3$ python createpool.py ### create POOLA tmsh create ltm pool POOLA { \ load-balancing-mode least-connections-members \ members add { \ node1:80 \ } \ members add { \ node2:80 \ } \ members add { \ node3:80 \ } \ monitor tcp \ } ### create POOLA_test tmsh create ltm pool POOLA_test { \ load-balancing-mode least-connections-members \ members add { \ node4:80 \ } \ members add { \ node5:80 \ } \ monitor tcp \ } ### create POOLB tmsh create ltm pool POOLB { \ load-balancing-mode least-connections-members \ members add { \ node6:8080 \ } \ monitor tcp \ }
OK!
それぞれのプールでメンバーがバラけてます。
これをファイルにリダイレクトすれば投入Configの出来上がりです。
リダイレクトはshellでもpython内に書いてもどちらでもいいですね。
まとめ
jijna2を使ってパラメータを1:1で渡す分にはそれほど難しくないのですが、今回の様な可変の値を入れ込もうとするとアイデアが浮かばず頓挫していました。
ある日パラメータをマージするという知見(辞書型のupdate)を得ましたので、今回望む結果を得ることができました。
ciscoのACLでも使えそうですが、テンプレファイルがブレースだらけで汚くなりそうなので使わないでおこうと思いますw
参考サイト
おまけ
調べたらbuilt-inのstring.Templateという似たクラスがあるようですね。
こっち先に出会っていたらこれで試していたかもな。