pythonregexpython-textfsm

Custom TextFSM Template Issue


I'm having issues with a custom TextFSM Template producing errors and I'm not sure what's wrong, because the RegEx works.

Here's the sample script:

from netmiko import ConnectHandler
import Seeker.helpers.constants

root = Seeker.helpers.constants.get_project_root()
show_mac_template = f'{root}/ntc_textfsm_templates/cisco_ios_show_mac_address-table.textfsm'

device = {
    'host': 'r1.nunya.com',
    'device_type': 'cisco_ios',
    'username': 'cisco',
    'password': 'password',
    'secret': 'secret',
}

ssh = ConnectHandler(**device)
ssh.enable()

show_mac = ssh.send_command(
    "show mac address-table dynamic",
    use_textfsm=True,
    textfsm_template=show_mac_template,
)
ssh.disconnect()

Here's the traceback I'm getting:

"C:\Program Files\Python310\python.exe" "C:/Scripts/Python/Seeker/test1.py"
Traceback (most recent call last):
  File "C:\Scripts\Python\Seeker\test1.py", line 18, in <module>
    show_mac = ssh.send_command(
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\netmiko\utilities.py", line 596, in wrapper_decorator
    return func(self, *args, **kwargs)
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\netmiko\base_connection.py", line 1702, in send_command
    return_val = structured_data_converter(
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\netmiko\utilities.py", line 560, in structured_data_converter
    structured_output_tfsm = get_structured_data_textfsm(
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\netmiko\utilities.py", line 397, in get_structured_data_textfsm
    return _textfsm_parse(
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\netmiko\utilities.py", line 345, in _textfsm_parse
    tfsm_parse(raw_output, templates=template_file)
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\textfsm\clitable.py", line 282, in ParseCmd
    self.table = self._ParseCmdItem(self.raw, template_file=template_files[0])
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\textfsm\clitable.py", line 315, in _ParseCmdItem
    for record in fsm.ParseText(cmd_input):
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\textfsm\parser.py", line 897, in ParseText
    self._CheckLine(line)
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\textfsm\parser.py", line 946, in _CheckLine
    if self._Operations(rule, line):
  File "C:\Users\nunya\AppData\Roaming\Python\Python310\site-packages\textfsm\parser.py", line 1026, in _Operations
    raise TextFSMError('State Error raised. Rule Line: %s. Input Line: %s'
textfsm.parser.TextFSMError: State Error raised. Rule Line: 27. Input Line:    1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      

Process finished with exit code 1

Here's the template, it should be using TYPE2:

Value VLAN (\S+)
Value MAC ([0-9a-fA-F]{4}\.[0-9a-fA-F]{4}\.[0-9a-fA-F]{4})
Value TYPE (\S+)
Value PORT ([^,\s]+)

Start
  ^Vlan\s+Mac\s+Address\s+Type\s+Ports -> TYPE1
  ^\s*vlan\s+mac address\s+type\s+protocols\s+port -> TYPE2

TYPE1
  ^\s*Mac\sAddress\sTable
  ^-+
  ^\s*$$
  ^Vlan\s+Mac Address\s+Type\s+Ports
  ^-+\s+-+\s+-+\s+-+$$
  ^\s*${VLAN}\s+${MAC}\s+${TYPE}\s+${PORT}$$ -> Record
  ^Total\s+Mac\s+Addresses\s+for\s+this\s+criterion\:\s+\d+ -> End
  ^. -> Error

TYPE2
  ^Unicast\sEntries
  ^\s+vlan\s+mac address\s+type\s+protocols\s+port
  ^-+\+-+\+-+\+-+\+-+
  ^\s*${VLAN}\s+${MAC}\s+${TYPE}\s+\S+\s+${PORT}$$ -> Record
  ^\s*$$ -> End
  ^. -> Error

And finally here's an example output of the command ran directly on the device:

r1#show mac address-table dynamic 
Unicast Entries
 vlan     mac address     type        protocols               port
---------+---------------+--------+---------------------+-------------------------
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet4/10     
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/8      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/5      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/5      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/1      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet10/4     
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/5      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/9      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet4/10     
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet10/5     
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet10/5     
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet4/9      
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet10/6     
   1      0000.0000.0000   dynamic ip,ipx,assigned,other TenGigabitEthernet3/4      
 --More--

Any help would be appreciated.


Solution

  • Changing a line in TYPE2 from:

      ^\s*${VLAN}\s+${MAC}\s+${TYPE}\s+\S+\s+${PORT}$$ -> Record
    

    to:

      ^\s*${VLAN}\s+${MAC}\s+${TYPE}\s+\S+\s+${PORT}\s*$$ -> Record
    

    fixes the issue.