Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ Value Required INTERFACE (\S+)
Value List IP_HELPER (\d+\.\d+\.\d+\.\d+|\S+)

Start
^DHCP
^Interface -> Continue.Record
^Interface:\s+${INTERFACE}$$
^\s+DHCP\s+Smart
^\s+DHCP\s+servers:\s+${IP_HELPER}$$
^\s+${IP_HELPER}$$
^$$
^. -> Error
^DHCP
^Interface -> Continue.Record
^Interface:\s+${INTERFACE}$$
^\s+DHCP\s+Smart
^\s+DHCP\s+servers:\s+${IP_HELPER}$$
^\s+${IP_HELPER}$$
^$$
^. -> Error
16 changes: 8 additions & 8 deletions ntc_templates/templates/aruba_os_show_arp.textfsm
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ Value INTERFACE (\S+)
Value AGE (\d+)

Start
^Codes:\s+\*\s+-\s+Local\s+Addresses,\s+S\s+-\s+Static,\s+A\s+-\s+Auth
^Total\s+ARP\s+entries:\s+\d+
^IPV4\s+ARP\s+Table
^-+
^\s+Protocol\s+IP\s+Address\s+Hardware\s+Address\s+Interface\s+Age
^\s+${PROTOCOL}\s+${IP_ADDRESS}\s+${MAC_ADDRESS}\s+${INTERFACE}\s+${AGE} -> Record
^\s*$$
^. -> Error
^Codes:\s+\*\s+-\s+Local\s+Addresses,\s+S\s+-\s+Static,\s+A\s+-\s+Auth
^Total\s+ARP\s+entries:\s+\d+
^IPV4\s+ARP\s+Table
^-+
^\s+Protocol\s+IP\s+Address\s+Hardware\s+Address\s+Interface\s+Age
^\s+${PROTOCOL}\s+${IP_ADDRESS}\s+${MAC_ADDRESS}\s+${INTERFACE}\s+${AGE} -> Record
^\s*$$
^. -> Error
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,3 @@ Start
^Port Name|Unit\/Port Name
^-+ -+
^${PORT}\s+${NAME} -> Record

Done
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,3 @@ Start
^\s+MAC Address.*
^-+ -+ -+ -+
^${MAC_ADDRESS}\s+${VID}\s+${TYPE}\s+\w*:*\s*${PORT}* -> Record

Done
2 changes: 0 additions & 2 deletions ntc_templates/templates/avaya_vsp_show_software.textfsm
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,3 @@ Start
^${PRIMARY_RELEASE}\s+\(Primary Release\)
^Auto Commit\s+:\s+${AUTO_COMMIT}
^Commit Timeout\s+:\s+${COMMIT_TIMEOUT}

Done
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ Start
^Dynamic\sArp\sParameters
^IP\sAddress\s+Mac\sAddress
^${IP_ADDRESS}\s+${MAC_ADDRESS} -> Record
^\s*$$
^. -> Error
^\s*$$
^. -> Error
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,27 @@ Value ACTIVE_SA (\d+)
Value ORIGIN (.+)

Start
^Crypto\s+.*
^Code:
^K\s+-
^X\s+-
^R\s+-
^S\s+-
^Interface: -> Continue.Record
^Interface:\s+${INTERFACE}
^Profile:\s+${PROFILE}
^Session\s+status:\s+${SESSION_STATUS}
^Uptime:\s+${UPTIME}
^Peer:\s+${PEER}\s+port\s+${PORT}\s+fvrf:\s+${FVRF}\s+ivrf:\s+${IVRF}
^\s+Desc:\s+${DESCRIPTION}
^\s+Phase1_id:\s+${PHASE1_ID}
^\s+Session\s+ID:\s+${SESSION_ID}
^\s+IKEv[1|2]\s+SA:\s+local\s+${LOCAL_IP}/${LOCAL_PORT}\s+remote\s+${REMOTE_IP}/${REMOTE_PORT}\s+${IKEV1_STATUS}
^\s+Capabilities:${CAPABILITIES}\s+connid:${CONN_ID}\s+lifetime:${LIFETIME}
^\s+IPSEC\s+FLOW:\s+permit\s+${PERMIT}\s+host\s+${SRC_HOST}\s+host\s+${DST_HOST}
^\s+IPSEC\s+FLOW:\s+permit\s+${PERMIT}\s+${SRC_HOST}/${SRC_MASK}\s+${DST_HOST}/${DST_MASK}
^\s+Active\s+SAs:\s+${ACTIVE_SA},\s+origin:\s+${ORIGIN}
^\s+Inbound:\s+#.*
^\s+Outbound:\s+#.*
^\s*$$
^. -> Error
^Crypto\s+.*
^Code:
^K\s+-
^X\s+-
^R\s+-
^S\s+-
^Interface: -> Continue.Record
^Interface:\s+${INTERFACE}
^Profile:\s+${PROFILE}
^Session\s+status:\s+${SESSION_STATUS}
^Uptime:\s+${UPTIME}
^Peer:\s+${PEER}\s+port\s+${PORT}\s+fvrf:\s+${FVRF}\s+ivrf:\s+${IVRF}
^\s+Desc:\s+${DESCRIPTION}
^\s+Phase1_id:\s+${PHASE1_ID}
^\s+Session\s+ID:\s+${SESSION_ID}
^\s+IKEv[1|2]\s+SA:\s+local\s+${LOCAL_IP}/${LOCAL_PORT}\s+remote\s+${REMOTE_IP}/${REMOTE_PORT}\s+${IKEV1_STATUS}
^\s+Capabilities:${CAPABILITIES}\s+connid:${CONN_ID}\s+lifetime:${LIFETIME}
^\s+IPSEC\s+FLOW:\s+permit\s+${PERMIT}\s+host\s+${SRC_HOST}\s+host\s+${DST_HOST}
^\s+IPSEC\s+FLOW:\s+permit\s+${PERMIT}\s+${SRC_HOST}/${SRC_MASK}\s+${DST_HOST}/${DST_MASK}
^\s+Active\s+SAs:\s+${ACTIVE_SA},\s+origin:\s+${ORIGIN}
^\s+Inbound:\s+#.*
^\s+Outbound:\s+#.*
^\s*$$
^. -> Error
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,3 @@ Start
^${BGP_NEIGH}\s+${BGP_VER}\s+${NEIGH_AS}\s+${MSG_RCVD}\s+${MSG_SENT}\s+${TBLVER}\s+${IN_QUEUE}\s+${OUT_QUEUE}\s+${UP_DOWN}\s+${STATE_PFXRCD}\s*$$ -> Record
# Match lines that are spaces
^\s+$$

Done
^.*
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ Start
^\s*Vlan\s*Root\sID\s*Cost\s*Time\s*Age\s*Dly\s*Root\s*Port$$
^\s*-
^\s*${VLAN_ID}\s+${PRIORITY}\s+${ROOT_ID}+\s+${ROOT_COST}+\s+${HELLO_TIME}+\s+${MAX_AGE}+\s+${FWD_DLY}+\s+${ROOT_PORT} -> Record
^. -> Error
^. -> Error
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ Start
^\s*Module\s*Number\s*Operational\s*Mode\s*$$
^\s*${MODULE_NUMBER}\s*${OPERATIONAL_MODE}\s*$$ -> Record
^\s*$$
^. -> Error
^. -> Error
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ Value DEFAULT_ROUTE_DISTANCE (\d+)
Value AUTHENTICATION (\S+)

Start
^${INDEX}\s+(${FLAGS}\s+)?name=${NAME}\s+apn=${APN}\s+use-peer-dns=${USE_PEER_DNS}\s+use-network-apn=${USE_NETWORK_APN}\s+add-default-route=${ADD_DEFAULT_ROUTE}\s+default-route-distance=${DEFAULT_ROUTE_DISTANCE}\s+ip-type=${IP_TYPE}\s+authentication=${AUTHENTICATION}\s*$$
^${INDEX}\s+(${FLAGS}\s+)?name=${NAME}\s+apn=${APN}\s+use-peer-dns=${USE_PEER_DNS}\s+use-network-apn=${USE_NETWORK_APN}\s+add-default-route=${ADD_DEFAULT_ROUTE}\s+default-route-distance=${DEFAULT_ROUTE_DISTANCE}\s+ip-type=${IP_TYPE}\s+authentication=${AUTHENTICATION}\s*$$
^\s*$$
^. -> Error
3 changes: 0 additions & 3 deletions ntc_templates/templates/ruckus_fastiron_show_vlan.textfsm
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,3 @@ Start
^\s+Monitoring:\s+
^\s*$$
^.+ -> Error

Done
^.*
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Value PFX_SENT (\d+)
Value PFX_ADV (\d+)

Start
^BGP neighbor.* -> Continue.Record
^BGP neighbor is ${NEIGHBOR_ID},\s+remote AS ${REMOTE_AS}
^BGP state = ${STATE}
^Hold time is ${HOLD_INTERVAL}, Keep alive interval is ${KEEPALIVE_INTERVAL}
^\s+Prefixes received ${PFX_RECV} sent ${PFX_SENT} advertised ${PFX_ADV}
^BGP neighbor.* -> Continue.Record
^BGP neighbor is ${NEIGHBOR_ID},\s+remote AS ${REMOTE_AS}
^BGP state = ${STATE}
^Hold time is ${HOLD_INTERVAL}, Keep alive interval is ${KEEPALIVE_INTERVAL}
^\s+Prefixes received ${PFX_RECV} sent ${PFX_SENT} advertised ${PFX_ADV}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ Value METRIC (\d+)
Value NEXTHOP (\d+(\.\d+){3})

Start
^${PROTOCOL}\s+${TYPE}\s+${IP_ADDRESS}\/${PREFIX_LENGTH}\s+\[${DISTANCE}\/${METRIC}\]\s+via\s+${NEXTHOP} -> Next.Record
^${PROTOCOL}\s+${TYPE}\s+${IP_ADDRESS}\/${PREFIX_LENGTH}\s+\[${DISTANCE}\/${METRIC}\]\s+via\s+${NEXTHOP} -> Next.Record
59 changes: 59 additions & 0 deletions tests/test_template_syntax.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#!/usr/bin/env python

"""Run syntax tests against all the *.textfsm template files."""

import glob
import re

import pytest
import textfsm

# Rules and comments inside a state must be indented with exactly 2 spaces.
RE_INDENTED_LINE = re.compile(r"^([ \t]+)(\S)")
VALID_INDENTED_LINES = {(" ", "^"), (" ", "#")}
# States that are valid without being referenced by another rule.
IMPLICIT_STATES = {"Start", "End", "EOF"}


def return_template_files():
"""Return a list of all the *.textfsm template files."""
return glob.glob("./ntc_templates/templates/*.textfsm")


@pytest.fixture(scope="function", params=return_template_files())
def load_template_files(request):
"""Return each *.textfsm file to run the syntax tests on."""
return request.param


def test_template_compiles(load_template_files):
"""Test that each template compiles with the textfsm parser.

Compilation catches invalid rule indentation, references to undefined
states, a missing Start state, and duplicate state names.
"""
with open(load_template_files, encoding="utf-8") as fh:
textfsm.TextFSM(fh)


def test_rule_indentation(load_template_files):
"""Test that every rule or comment line is indented with exactly 2 spaces."""
bad_lines = []
with open(load_template_files, encoding="utf-8") as fh:
for line_num, line in enumerate(fh, start=1):
match = RE_INDENTED_LINE.match(line)
if match and (match.group(1), match.group(2)) not in VALID_INDENTED_LINES:
bad_lines.append(line_num)

assert not bad_lines, f"Rules must be indented with exactly 2 spaces, see line(s): {bad_lines}"


def test_no_unused_states(load_template_files):
"""Test that every defined state is reachable from another rule."""
with open(load_template_files, encoding="utf-8") as fh:
fsm = textfsm.TextFSM(fh)

referenced_states = {rule.new_state for rules in fsm.states.values() for rule in rules}
unused_states = set(fsm.states) - IMPLICIT_STATES - referenced_states

assert not unused_states, f"State(s) defined but never referenced by a rule: {sorted(unused_states)}"
Loading