【Ansible】textfsmのparserは簡単に作れる～基礎編～

### はじめに
<バージョン>
ansible 2.9.7
textfsm 1.1.0
　ネットワーク機器のshowコマンド取得結果はそのままだと機械処理に向いていないので、
parserなどを使って機械処理に向いたデータに変換する必要があります。
以下はCisco ASAにおけるshow route結果をparserで変換した例になります。
※[networktocode/ntc-templates - GitHub](https://github.com/networktocode/ntc-templates) のcisco_asa_show_route.textfsmを使用しました。

```text:入力
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area 
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2, V - VPN
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, + - replicated route
       SI - Static InterVRF
Gateway of last resort is 172.31.16.1 to network 0.0.0.0

S*       0.0.0.0 0.0.0.0 [1/0] via 172.31.16.1, management
C        172.31.16.0 255.255.240.0 is directly connected, management
L        172.31.19.91 255.255.255.255 is directly connected, management
```

```yml:出力
- DISTANCE: '1'
  MASK: 0.0.0.0
  METRIC: '0'
  NETWORK: 0.0.0.0
  NEXTHOPIF: management
  NEXTHOPIP: 172.31.16.1
  PROTOCOL: S
  TYPE: ''
  UPTIME: ''
- DISTANCE: ''
  MASK: 255.255.240.0
  METRIC: ''
  NETWORK: 172.31.16.0
  NEXTHOPIF: management
  NEXTHOPIP: ''
  PROTOCOL: C
  TYPE: ''
  UPTIME: ''
- DISTANCE: ''
  MASK: 255.255.255.255
  METRIC: ''
  NETWORK: 172.31.19.91
  NEXTHOPIF: management
  NEXTHOPIP: ''
  PROTOCOL: L
  TYPE: ''
  UPTIME: ''
```

showコマンド結果の抽出はパターンが多いので難しいのですが、以下のように
__parseする各データが1行に入っており、検索パターンが少ない場合__は
__正規表現の知識だけでparserが作れる__ので実際にやってみましょう。

```text:
id name age comment
1 Taro 20 hello
2 Hanako 22
3 Satoru 1
````

### 0. 下準備
　今回はtextfsmを使用するのでインストールしておきましょう。

```text:
pip install textfsm
```

### 1. 必要な変数(出力する変数)の数を把握する
　サンプルデータより、必要な変数は4つであるとわかります。
変数がわかったら以下のように正規表現でまとめましょう。

|変数|正規表現|
|---|---|
|id|\d+|
|name|[A-Z][a-z]+|
|age|\d+|
|comment|\w+|


### 2. データ行の検索パターンを把握する
　今回は以下の2通りであるとわかります。
（1）末尾にコメントがある場合
（2）末尾にコメントがない場合
確実にヒットさせるために、行頭を表す「^」と行末を表す「$$(2個)」を入れておきましょう。

```text:
^${id} ${name} ${age} ${comment}$$
^${id} ${name} ${age}$$
```

### 3. textfsmファイルを作成する
　ここまでまとめてきた内容をtextfsmファイル（fsm_sample.textfsm）に書き起こしましょう。
__ValueやRecordなど決まっている変数の先頭は大文字にしないとエラー__になります。

- 出力する変数を記入する部分
  - __Value 変数名 (正規表現)__と記入
- マッチさせるパターンを記入する部分
  - Startと書いた行の下に、__検索条件 -> Record__ と記入
  - Recordの部分で変数が出力され、変数がリセットされる
  

```text:fsm_sample.textfsm
# 出力する変数を記入
Value id (\d+)
Value name ([A-Z][a-z]+)
Value age (\d+)
Value comment (\w+)

# マッチさせるパターンを記入
Start
  ^${id} ${name} ${age} ${comment}$$ -> Record
  ^${id} ${name} ${age}$$ -> Record
```

### 4. parse用playbook作成
　今回parseしたい内容は、sample_input内に記入しました。
parseする際は、「parse_cli_textfsm(fsmファイルのパス)」と記入してください。

```yaml:fsm_test.yml
---
- hosts: localhost
  gather_facts: false
  vars:
    sample_input: |
      id name age comment
      1 Taro 20 hello
      2 Hanako 22
      3 Satoru 1
  tasks:
    - name: debug
      debug:
        var: sample_input | parse_cli_textfsm('./fsm_sample.textfsm')
```

### 5. 実行結果
　想定通りparseすることが出来ました。

```text:出力
(venv) [centos@ip-<ip-addr> ansible]$ ansible-playbook fsm_test.yml 

PLAY [localhost] *******************************************************************************************************

TASK [debug] ***********************************************************************************************************
ok: [localhost] => 
  sample_input | parse_cli_textfsm('./fsm_sample.textfsm'):
  - age: '20'
    comment: hello
    id: '1'
    name: Taro
  - age: '22'
    comment: ''
    id: '2'
    name: Hanako
  - age: '1'
    comment: ''
    id: '3'
    name: Satoru

PLAY RECAP *************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
```

### まとめ
　簡単なparserなら正規表現の知識さえあれば作れることが分かったと思います。
parserがあると機械処理の幅が広がるので、自分でも作ってみましょう。

### 関連記事
[ネットワーク機器のコマンド結果をパースする parse_cli_textfsm フィルタープラグインを試す](https://tekunabe.hatenablog.jp/entry/2017/09/23/parse_cli_textfsm)
[networktocode/ntc-templates - GitHub](https://github.com/networktocode/ntc-templates)
[google/textfsm - GitHub](https://github.com/google/textfsm/wiki/TextFSM)
[【Ansible】textfsmのparserは簡単に作れる～応用編～](https://qiita.com/masa2223/items/b4ec5164e404689f55bf)