genie dqのcustom_filter自作してみた
ansible.utils.cli_parseで出力した辞書から任意のkeyだったりvalueだったりを取り出すのにgenieのdqを使ってみたくなりました
実装するには自分でcustom_filter作るしかなかったので試してみました
Dqの説明は以下
公式ドキュメント
https://pubhub.devnetcloud.com/media/genie-docs/docs/userguide/utils/index.html
環境
ansible 2.10.7
genie 21.2.3
参考にしたGithub
こちらのfilter_pluginを参考に作成しました github.com
from __future__ import absolute_import, division, print_function __metaclass__ = type from ansible.module_utils.six import PY3 from ansible.errors import AnsibleError, AnsibleFilterError try: from genie.utils import Dq HAS_GENIE = True except ImportError: HAS_GENIE = False class FilterModule(object): def __init__(self): if not PY3: raise AnsibleFilterError("Genie requires Python 3") if not HAS_GENIE: raise AnsibleFilterError("Genie not found. Run 'pip install genie'") def genie_dq_contains(self, output,value): try: parsed_output = Dq(output).contains(value).reconstruct() except Exception as e: raise AnsibleFilterError("DQ Error: {0}".format(e)) if parsed_output: return parsed_output else: return None ~~ snip~~ def filters(self): return { 'genie_dq_contains': self.genie_dq_contains, 'genie_dq_get_values': self.genie_dq_get_values, 'genie_dq_contains_key_value': self.genie_dq_contains_key_value, 'genie_dq_value_operator': self.genie_dq_value_operator, }
genie_dq_contrains
のメソッドにfilterとして呼び出された際の処理を記載しています、
filters
メソッドはfilter呼び出し時のkeywordを定義しています。
Playbookでこのgenie_dq_contrainsを呼び出すときは以下のようになります
"{{ res_show | genie_dq_contrains('route') }}"
res_showの変数が一番目の変数(output)として入り、valueは2番目の変数(value)としてfilterに渡されます
引き渡された変数はGenieのDqで特定のデータを抽出します
同じ要領で以下4つのメソッドを作成してみました 1. genie_dq_contains 2. genie_dq_get_values 3. genie_dq_contains_key_value 4. genie_dq_value_operator
それでは実際に動かしてみます
下準備
custom_filterを使うには ansible.cfgにfilterパスを追加する必要があります filter_pluginにcustom_filterが配置されているパスを定義します。
[defaults] filter_plugins = filter_plugins
これで下準備は完了です
使い方
動作確認のため、以下のようなPlaybookを書きました
処理の内容としては、ansible.utils.cli_parseでshow ip routeを送信しparser pyatsでパースをします。
この時の戻り値をres_show
に格納し、debugモジュールで表示する際にcustom_filterにかけていきます。
--- - hosts: ios01 gather_facts: false tasks: - name: コマンド送信 ansible.utils.cli_parse: command: show ip route parser: name: ansible.netcommon.pyats register: res_show - name: routeの一覧 ansible.builtin.debug: msg: "{{ res_show | genie_dq_get_values(value) }}" vars: value: 'routes' - name: 対象のrouteの取得 ansible.builtin.debug: msg: "{{ res_show | genie_dq_contains_key_value(key,value) }}" vars: key: 'routes' value: "0.0.0.0/0" - name: 想定のNexthopのrouteをListにしてから対象のrouteを表示 ansible.builtin.debug: msg: "{{ res_show | genie_dq_contains_key_value(key,value) | genie_dq_contains(route) }}" vars: key: 'next_hop' value: "10.10.20.254" route: "0.0.0.0/0" - name: routeのpreference値が01のrouteのみ表示 ansible.builtin.debug: msg: "{{ res_show | genie_dq_value_operator(arg1,arg2,arg3) }}" vars: arg1: 'route_preference' arg2: '==' arg3: '1'
実行対象はいつも通り、Devnetのalwaysonのリソースを使わせていただいています。
実行
さっそく実行してみます。 ログが長くなってしまったので、分割して結果について紹介していきます。
custom_filter_genie_dq# ansible-playbook -i inventory/hosts.ini dq_sample.yml PLAY [ios01] ************************************************************************************************************************************************* TASK [コマンド送信] ************************************************************************************************************************************************ ok: [ios01] TASK [routeの一覧] ********************************************************************************************************************************************** ok: [ios01] => { "msg": [ "0.0.0.0/0", "10.10.20.0/24", "10.10.20.48/32", "10.255.255.0/24", "10.255.255.1/32" ] }
上記はgenie_dq_get_valuesで'route'のvalueをlistとして格納しています。
単純にルートの数などが知りたいときに使えるかなぁ
TASK [対象のrouteの取得] ******************************************************************************************************************************************* ok: [ios01] => { "msg": { "parsed": { "vrf": { "default": { "address_family": { "ipv4": { "routes": { "0.0.0.0/0": { "active": true, "metric": 0, "next_hop": { "next_hop_list": { "1": { "index": 1, "next_hop": "10.10.20.254", "outgoing_interface": "GigabitEthernet1" } } }, "route": "0.0.0.0/0", "route_preference": 1, "source_protocol": "static", "source_protocol_codes": "S*" } } } } } } } } }
続いてgenie_dq_contains_key_value
でkeyとvalueの組み合わせを指定しデータを抽出します
routeの中の0.0.0.0/0のデータのみ抜き出しています。
もちろん存在しない場合は空っぽで応答されます。
TASK [想定のNexthopのrouteをListにしてから対象のrouteを表示] ***************************************************************************************************************** ok: [ios01] => { "msg": { "parsed": { "vrf": { "default": { "address_family": { "ipv4": { "routes": { "0.0.0.0/0": { "next_hop": { "next_hop_list": { "1": { "next_hop": "10.10.20.254" } } } } } } } } } } } }
続いてはgenie_dq_contains_key_valueとgenie_dq_containsの合わせ技になります
genie_dq_contains_key_valueでnext_hopが10.10.20.254のデータのみを抽出し、その後
genie_dq_containsでrouteが0.0.0.0/0のものを抽出しています。
(| をつないで複数のfilterをつなぎ合わせることも可能ってのを見せたかった例)
TASK [routeのpreference値が01のrouteのみ表示] ************************************************************************************************************************ ok: [ios01] => { "msg": { "parsed": { "vrf": { "default": { "address_family": { "ipv4": { "routes": { "0.0.0.0/0": { "route_preference": 1 } } } } } } } } } PLAY RECAP *************************************************************************************************************************************************** ios01 : ok=5 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
最後はgenie_dq_value_operatorです、
route_preferenceが1のデータのみ抽出しています
まとめ
はじめてcustom_filterを作成したが思ったよりも簡単に実装することができました。 パパっとできた割には結構柔軟に使えて便利なfilterになりました
なるべく既存のものを利用し、自分でガリガリ作らないほうがいいってポリシーなんですが
作ってみると楽しくて、これもcustom_filterでできそうだなと考えてしまう頭になってしまいそうです。
ただライセンス周りがいまいちわからず(このブログもダイジョブなんかなとか思ったり) githubで公開できるように勉強中です・・・ (詳しい方いたら教えてください・・・)