はじめに
以前の記事で、TTP(Template Text Parser)というPythonのパーサーライブラリを使って、L2SWのConfigファイルをパースし、ポート管理表の自動生成を行いました。
L2SWのConfigからポート管理表を自動生成してみた
今回は、このパーサーをAnsibleのカスタムフィルターとして取り込み、xxx_command
モジュールで取得したshowコマンド結果をパースできるようにしてみました。
※ フィルタープラグイン自作例は、以前こちらでも紹介しています。
※ 私が知る限り、パース用フィルタープラグインとして、他にもTextFSMを利用したparse_cli_textfsm
や、pyATS/Genieを利用したparse_genie
(Ansible Galaxyからインストールが必要。詳細はGitHub/parse_genieに記載。)があります。
セットアップ
Python3.6.7の仮想環境内にインストールしたAnsibleを使いました。当初、バージョン2.9.0でテストしていたのですが、Logging error(KeyError)メッセージが複数出たため、2.8.4を使っています。
追加でTTPのインストールが必要です。
(venv) [centos@localhost ansible]$ pip install ttp
カスタムフィルター
以下のPythonスクリプトを作成しました。
from ansible.errors import AnsibleError
# ttp、jsonのインポート。インポートに失敗した場合、後続の処理でエラー出力できるようにする。
try:
from ttp import ttp
HAS_TTP = True
except ImportError:
HAS_TTP = False
try:
import json
HAS_JSON = True
except ImportError:
HAS_JSON = False
class FilterModule(object):
def parse_cli_ttp(self, cli_output, template_file):
if not HAS_TTP:
raise AnsibleError('parse_cli_ttp filter requires TTP library to be installed')
if not HAS_JSON:
raise AnsibleError('parse_cli_ttp filter requires JSON library to be installed')
with open(template_file, 'rt') as ft:
ttp_template = ft.read()
# create parser object and parse data using template
parser = ttp(data=cli_output, template=ttp_template)
parser.parse()
# return result in JSON format
results = parser.result(format='json')[0]
return results
def filters(self):
return {
# 左側がPlaybook内で使用するフィルター名、右側が紐付ける関数名。
'parse_cli_ttp': self.parse_cli_ttp,
}
ベストプラクティスに従い、Playbookを格納しているディレクトリ配下に、filter_pluginsディレクトリを作成し、その中に本ファイルを格納しました。
また、Ansibleがこのカスタムフィルターを認識できるよう、ansible.cfgの設定を以下の通り書き換えました。
[defaults]
filter_plugins = [Playbook格納ディレクトリのフルパス]/filter_plugins
Playbook
ios_command
モジュールでRunning Configを取得し、続くdebug
モジュール内で、取得したConfigと以前の記事で作成したL2インターフェース設定用テンプレートのファイルパスを指定しました。シンプルですね!
---
- hosts: cisco
gather_facts: no
connection: network_cli
tasks:
- name: run show command on remote devices
ios_command:
commands: show running-config
register: result
- name: display parsed output
debug:
msg: "{{ result.stdout[0] | parse_cli_ttp('catalyst2960_template_ttp2.txt') }}"
出力結果
前回とConfigが異なるため結果に違いはありますが、パース自体は問題なくできています。
$ ansible-playbook -i inventory_2960.ini playbook_ttp.yml
PLAY [cisco] *************************************************************************************************
TASK [run show command on remote devices] ********************************************************************
ok: [hqdist1A]
TASK [display parsed output] *********************************************************************************
ok: [hqdist1A] => {
"msg": [
{
"l2_interfaces": [
{
"description": "<< Connect hqdist1 and hqdist2 >>",
"duplex": "auto",
"mode": "trunk",
"port_no": "Port-channel1",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "1,101"
},
{
"description": "<< To hqborder1 Fa1 >>",
"duplex": "full",
"mode": "access",
"port_no": "FastEthernet0/1",
"portfast": "x",
"speed": "100",
"status": "o",
"vlan": "200"
},
{
"description": "<< To hqborder2 Fa1 >>",
"duplex": "full",
"mode": "access",
"port_no": "FastEthernet0/2",
"portfast": "x",
"speed": "100",
"status": "o",
"vlan": "202"
},
{
"description": "<< To hqaccess1 Fa0/23 >>",
"duplex": "full",
"mode": "access",
"port_no": "FastEthernet0/3",
"portfast": "x",
"speed": "100",
"status": "o",
"vlan": "100"
},
{
"duplex": "auto",
"mode": "trunk",
"port_no": "FastEthernet0/4",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/5",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/6",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/7",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/8",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/9",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/10",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/11",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/12",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "203"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/13",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "100"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/14",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/15",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/16",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/17",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/18",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/19",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/20",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/21",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "FastEthernet0/22",
"portfast": "x",
"speed": "auto",
"status": "x",
"vlan": "1"
},
{
"description": "<< To hqdist2 Fa0/23 >>",
"duplex": "auto",
"mode": "trunk",
"port_no": "FastEthernet0/23",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "1,101"
},
{
"description": "<< To hqdist2 Fa0/24 >>",
"duplex": "auto",
"mode": "trunk",
"port_no": "FastEthernet0/24",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "1,101"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "GigabitEthernet0/1",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "1"
},
{
"duplex": "auto",
"mode": "access",
"port_no": "GigabitEthernet0/2",
"portfast": "x",
"speed": "auto",
"status": "o",
"vlan": "1"
}
]
}
]
}
PLAY RECAP ***************************************************************************************************
hqdist1A : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
最後に
今回作ったプラグインはGitHub/ansible-ttpにアップしています。Configだけでなく、他のshowコマンドの出力結果も比較的簡単にパース出来ますので、ぜひ使ってみて頂ければと思います。