技術メモ

ネットワークエンジニアがpython, perl等々を気楽に使うための覚え書き

python textfsm(part3) - ラッパー関数で整理

textfsmの整理

ciscoのconfigやlogといった解析対象は雑多なコマンド出力が一緒にまとめられたファイルを扱うことが多いため、以下のtextfsm_wrapper.pyを書いてみた。

  • concat_target() glob形式で対象ログを指定し、コマンド開始・終了を判定する正規表現を与えると、解析用のtextデータを返す関数
  • parse() textfsmの定義ファイル(テンプレート)読み込み・解析およびcsv出力を行う関数

textfsm_wrapper.py

#!/usr/bin/env python3
import glob
import re
import textfsm
import csv

def concat_target(target_file, target_start, target_end,):
    target_start = re.compile(target_start)
    target_end = re.compile(target_end)
    target_text = ''
    for fname in glob.glob(target_file):
        in_target = False
        with open(fname, 'r') as f:
            for line in f:
                if in_target == False:
                    if target_start.search(line):
                        target_text += line
                        in_target = True
                else:
                    target_text += line
                    if target_end.search(line): break
    return target_text

def parse(template_file, output_file, target_text):
    with open(template_file, 'r') as f:
        table = textfsm.TextFSM(f)
    with open(output_file, 'w', newline = '') as f:
        writer = csv.writer(f, delimiter = ',', quoting = csv.QUOTE_ALL)
        writer.writerow(table.header)
        writer.writerows(table.ParseText(target_text))
    return

利用例

sample.py

#!/usr/bin/env python3
from textfsm_wrapper import concat_target, parse

# show running-config sample for IOS and NX-OS
target = concat_target(
    target_file     = r"./log/*.*",
    target_start    = r"sh(?:ow?)? (?:run|conf|start)",
    target_end      = r"^end$",
)
parse(
    target_text     = target,
    template_file   = r"./template_interface.txt",
    output_file     = r"./result_interface.csv",
)

# show inventory sample for IOS and NX-OS
target = concat_target(
    target_file     = r"./log/*.*",
    target_start    = r"sh(?:ow?)? inv",
    target_end      = r"^[\w\-\.]+[#>]|^-{10,} show ",
)
parse(
    target_text     = target,
    template_file   = r"./template_inventory.txt",
    output_file     = r"./result_inventory.csv",
)

# show interface transceiver sample for NX-OS(Ethernet)
target = concat_target(
    target_file     = r"./log/*.*",
    target_start    = r"sh(?:ow?)? int(?:.*) trans",
    target_end      = r"^[\w\-\.]+[#>]|^-{10,} show ",
)
parse(
    target_text     = target,
    template_file   = r"./template_transceiver.txt",
    output_file     = r"./result_transceiver.csv",
)

template_interface.txt
多分、これでios、nx-osとも対応出来るはず・・・

# parse interface settings from configuration.
Value Filldown hostname (\S+)
Value Required interface (\S+)
Value status ((?:no )?shutdown)
Value description (.*)
Value channel_group (\d+)
Value speed (\w+)
Value duplex (\w+)
Value mdix_auto (no mdix auto)
Value untagged_vlan (\d+)
Value List tagged_vlan ([\d\,\-]+)
Value primary_ip ((?:\d{1,3}\.){3}\d{1,3} (?:\d{1,3}\.){3}\d{1,3}|(?:\d{1,3}\.){3}\d{1,3}/\d{1,2})
Value secondary_ip ((?:\d{1,3}\.){3}\d{1,3} (?:\d{1,3}\.){3}\d{1,3}|(?:\d{1,3}\.){3}\d{1,3}/\d{1,2})
Value standby_group (\d+)
Value standby_ip ((?:\d{1,3}\.){3}\d{1,3})
Value standby_priority (\d+)
Value List standby_option (preempt.*|track.*|auth.*|timers.*)

Start
  ^hostname ${hostname}
  ^interface ${interface}
  ^ +${status}
  ^ +description ${description}
  ^ +channel-group ${channel_group}
  ^ +speed ${speed}
  ^ +duplex ${duplex}
  ^ +${mdix_auto}
  ^ +switchport access vlan ${untagged_vlan}
  ^ +switchport trunk allowed vlan (?:add )?${tagged_vlan}
  ^ +encapsulation dot1Q ${tagged_vlan}
  ^ +ip address ${primary_ip}(?! secondary)
  ^ +ip address ${secondary_ip} secondary
  ^ +standby ${standby_group} ip ${standby_ip}
  ^ +standby \d+ priority ${standby_priority}
  ^ +standby \d+ ${standby_option}
  ^ +hsrp ${standby_group}
  ^ +ip ${standby_ip}
  ^ +priority ${standby_priority}
  ^ +${standby_option}
  ^!?$$|\w -> Record

template_inventory.txt

# parse show inventory outputs
Value Filldown hostname (\S+)
Value NAME ([^"]+)
Value DESCR ([^"]+)
Value PID (\S*)
Value VID (\S*)
Value Required SN (\S+)

Start
  ^${hostname}[>#]\s*sh(?:ow?)? inv
  ^NAME: "${NAME}" *, +DESCR: "${DESCR}"
  ^PID: ${PID} *, +VID: ${VID} *, +SN: ${SN} -> Record

template_transceiver.txt

# parse show interface transceiver outputs
Value Filldown hostname (\S+)
Value interface (Ethernet.*)
Value type (\S*)
Value name (\S*)
Value part (\S*)
Value revision (\S*)
Value Required serial (\S+)

Start
  ^${hostname}[>#]\s*sh(?:ow?)? int(?:.*) tran
  ^${interface}
  ^ +type is ${type}
  ^ +name is ${name}
  ^ +part number is ${part}
  ^ +revision is ${revision}
  ^ +serial number is ${serial}\s*$$ -> Record