るつぼっと

NWエンジニアな人たちに向けて

[TextFSM] 予約済みStateの仕組みと使い方

はじめに

ntc-templateを編集するために最近調べたので、自分なりに理解した内容を纏める。
参考にした文献は次のとおり。

TextFSM · google/textfsm Wiki · GitHub
21. Parsing command output with TextFSM — Python for network engineers 1.0 documentation

準備

動作確認用のコード。 解析対象のデータも併せて定義している。

import textfsm

InputData = '''
2022_01_01 banana 100
           orange 200
2022_12_31 banana 200
           carrot 300
'''
with open('test.template') as template:
    fsm = textfsm.TextFSM(template)
    result = fsm.ParseText(InputData)

print(fsm.header)
print(*result, sep='\n')


基本とするTemplate

Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> Record
  ^\s*${Item}\s*${Qty} -> Record


解析結果はこうなる。
インプットと同じく2,4行目は日付が空欄。

['Date', 'Item', 'Qty']
['2022_01_01', 'banana ', '100']
['', 'orange ', '200']
['2022_12_31', 'banana ', '200']
['', 'carrot ', '300']

動作確認

TextFSMには下記の予約済みStateが存在する。それぞれの動作を確認する。

  • Start
  • EOF
  • END

Start State

定義必須のState。
インプット行との照合はこのStateの1行目のRuleから始まる。
ここでは次の2つの動作を確認する。

  • Start Stateを定義しない場合
  • State以外のStateを追加定義した場合
Template(State Stateを未定義)
  • Startの代わりにTest1という名称でStateを定義
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Test1
  ^${Date}\s*${Item}\s*${Qty} -> Record
  ^\s*${Item}\s*${Qty} -> Record
結果
textfsm.parser.TextFSMTemplateError: Missing state 'Start'.

必須のStateがないためErrorで処理が止まった。

Template(他のStateを追加定義)
  • Test1, Test2というStateを定義
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Test1
  ^\s*${Item}\s*${Qty} -> Record

Start
  ^${Date}\s*${Item}\s*${Qty} -> Record

Test2
  ^\s*${Item}\s*${Qty} -> Record
結果
['Date', 'Item', 'Qty']
['2022_01_01', 'banana ', '100']
['2022_12_31', 'banana ', '200']

日付入りの行のみ出力された。
処理はStart Stateから始まり、かつ他のStateへは飛ばないためこうなる。
※ 後ほど確認するState Transition Actionを使えば他のStateへ飛べる

EOF State

インプットの全行について処理を終えた後に自動的に作動するState。
明示的に定義しなかった場合、下記の暗黙のEOF Stateが作動する。

EOF
  ^.* -> Record

これは今現在収集している情報をRecordするという意味を持つ。
ここでは次の2つの動作を確認する。

  • 暗黙のEOF State
  • 明示的なEOF State
Template(暗黙のEOF State)
  • ほぼ元々と同じ形だが、最後のルールにRecordがない点に注目
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> Record
  ^\s*${Item}\s*${Qty}
結果
['Date', 'Item', 'Qty']
['2022_01_01', 'banana ', '100']
['2022_12_31', 'banana ', '200']
['', 'carrot ', '300']

日付入りの行だけが出力されると思いきや、4行目のcarrot,300も出てきた。
^\s*${Item}\s*${Qty}はRecordしないもののItem,QtyのValueを収集しており、
それが暗黙のEOF Stateによって出力されるためこうなる。
このあたりの挙動は下記の記事でも確認している。

[TextFSM] Rule Actionsの挙動確認 - るつぼっと

Template(明示的なEOF State)
  • 最後のルールにRecordがない
  • 新たにEOF Stateを定義
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> Record
  ^\s*${Item}\s*${Qty}

EOF
結果
['Date', 'Item', 'Qty']
['2022_01_01', 'banana ', '100']
['2022_12_31', 'banana ', '200']

先ほどと違い、日付入りの行だけが出力された。
EOF Stateを明示的に定義した場合、先ほどの暗黙的な動作が
取られないためこのような結果となる。

END State

このStateが作動した時点で解析処理を終了するState。
未解析のインプット行が残っていても終了させる。

Template
  • 2行目のルールをRecordからENDへ変更
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> Record
  ^\s*${Item}\s*${Qty} -> END

END
結果
['Date', 'Item', 'Qty']
['2022_01_01', 'banana ', '100']
['', 'orange ', '200']

2行目までが解析および出力された。
どうやらEND StateにはRecordアクションも含まれるようだ。