るつぼっと

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

[TextFSM] Rule Actionsの仕組みと使い方

はじめに

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']

動作確認

各Ruleには下記のアクションを指定できる。
指定する際はLine Actions.Record Action State Transitionの形式。
何も指定しないとNext.NoRecordのアクションを取る。

  • Line Actions
    • Next
    • Continue
  • Record Actions
    • NoRecord
    • Record
    • Clear
    • Clearall
  • State Transition
  • Error Action

Line Actions:Next

今回のインプット行について解析を終了し次のインプット行の処理に移る。
Line Actionを定義しない場合このアクションとなる。

Template
  • 明示的にNextアクションを定義
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

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

元々デフォルトでNextアクションなので明示しても挙動は変わらない。

Line Actions:Continue

今回のインプット行について解析を終了せず、次行以降のルールと照合を続ける。

Template(いったんContinue無し)
  • 2行目にルールを追加。ひとまずContinueは無し
  • 話がややこしくなるので明示的なEOF Stateも追加
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

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

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

2行目にRecordがないので日付無しのインプット行は出力されない。
3行目のルールは辿り着くことがないので無意味。

Template(Continue付き)
  • 先ほどの2行目のルールにContinueを追加
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

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

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

今度は日付無しのインプット行も出力された。
Continueにより3行目のルールまで処理が到達したためこうなる。

Record Actions:NoRecord

収集したValueを記録しない。Actionを定義しない場合この動作となる。
記録しないものの内部的にはValueを収集している点に注意。

Template
  • 各ルールにNoRecordを指定
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> NoRecord
  ^\s*${Item}\s*${Qty} -> NoRecord
結果
['Date', 'Item', 'Qty']
['2022_12_31', 'carrot ', '300']

3行目のDateと4行目のItem,Qtyが混ざった値が出力された。
各インプット行がルールと合致する度に各Value上書きして保持されるらしい。
つまり下記のように動作している。

インプット1行目処理時点…['2022_01_01', 'banana ', '100']
インプット2行目処理時点…['2022_01_01', 'orange ', '200'] * Item,QtyのValueを上書き
インプット3行目処理時点…['2022_12_31', 'banana ', '200'] * 全てのValueを上書き
インプット4行目処理時点…['2022_12_31', 'carrot ', '300'] * Item,QtyのValueを上書き
→暗黙のEOF Stateにより出力される。

Record Actions:Record

収集したValueを記録する。
改めて試すべきこともないので挙動確認は省略。

Record Actions:Clear

収集したがまだRecordしておらず、内部保持しているValueを開放するアクション。
ただしFilldownのValueは開放しない。

Template(Filldown無し)
  • 1行目のルールにContinue.Clearを指定
  • 2行目のルールは何も収集せずRecord(^\dは日付有り行が該当する)
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> Continue.Clear
  ^\d -> Record
  ^\s*${Item}\s*${Qty} -> Record

EOF
結果
['Date', 'Item', 'Qty']
['', 'orange ', '200']
['', 'carrot ', '300']

日付有りのインプット行については下記の流れで処理される。
その結果として日付無し行のみ出力される。

  1. 1行目のルールに引っかかる
  2. Valueが収集される
  3. 収集した値がClearアクションで解放される
  4. Continueアクションにより2行目のルールへ進む
  5. 2行目のルールに引っかかる
  6. Recordアクションが動作するが、先ほどClearしたため格納するものがない
Template(Filldown有り)
  • ValueにFilldownオプションを付与
Value Filldown Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> Continue.Clear
  ^\d -> Record
  ^\s*${Item}\s*${Qty} -> Record

EOF
結果
['Date', 'Item', 'Qty']
['2022_01_01', '', '']
['2022_01_01', 'orange ', '200']
['2022_12_31', '', '']
['2022_12_31', 'carrot ', '300']

動作の流れは先ほどと同じだが、FilldownオプションのついたDateは
Clear対象外のため各行で出力される。

Record Actions:Clearall

Clearに近いが、こちらはFilldownオプション付きのValueも開放する。

Template
  • Clear検証時(Filldown有り)のものをClearallに変えただけ
Value Filldown Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

Start
  ^${Date}\s*${Item}\s*${Qty} -> Continue.Clearall
  ^\d -> Record
  ^\s*${Item}\s*${Qty} -> Record

EOF
結果
['Date', 'Item', 'Qty']
['', 'orange ', '200']
['', 'carrot ', '300']

Clear検証時にFilldownオプションを使わなかったときと同じ出力になった。

State Transition

あるStateから他のStateへ移行する。
以降のインプット行の解析は移行後のStateで実施される。
他のアクションの後に半角スペースを空けて定義する。

Template
  • ルールを2つのStateへ分割
  • Start StateのルールにState Transitionを定義
Value Date (\d+_\d+_\d+)
Value Item (\D+)
Value Qty (\d+)

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

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

EOF
結果
['Date', 'Item', 'Qty']
['2022_01_01', 'banana ', '100']
['', 'orange ', '200']
['', 'carrot ', '300']

1行目のRecordが終わった後にTest1 Stateへ移行する。
インプット2~4行目はTest1 Stateで解析されるため
結果として2,4行目だけがマッチし出力される。

Error Action

ルールにマッチした場合、Errorとして解析処理を止める。 これまでRecordした内容もすべて破棄する。

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

Start
  ^${Date}\s*${Item}\s*${Qty} -> Record
  ^\s*${Item}\s*${Qty} -> Error
結果
textfsm.parser.TextFSMError: State Error raised. Rule Line: 7. Input Line:            orange 200

インプットorange 200がTemplateの7行目でErrorと判定された。

所感

疲れた。誰かのお役に立つことを期待します。(そもそも内容に誤りがないことを祈る)