Skip to content

adjust parser for IOSXE 'show running-config aaa username' to be less rigid #937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
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
204 changes: 94 additions & 110 deletions src/genie/libs/parser/iosxe/show_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -2738,7 +2738,9 @@ class ShowRunningConfigAAAUsernameSchema(MetaParser):
Optional('common_criteria_policy'): str,
Optional('view'): str,
Optional('type'): str,
Optional('autocommand'): str,
Optional('onetime'): bool,
Optional('nopassword'): bool,
Optional('secret'): {
Optional('type'): int,
Optional('secret'): str,
Expand Down Expand Up @@ -2884,139 +2886,121 @@ def cli(self, output=None):
else:
out = output

# username testuser password 0 lab
p1 = re.compile(r'^username +(?P<username>\S+) +password +(?P<type>\d) +(?P<password>.*)$')
# NOTE: All of the following regular expressions should be anchored to
# the begining of the line ('^'). As each is used the line will be
# shortened. Think of this as popping arguments (and their parameters)
# off of a stack (the front of the line).
#
# There are some arguments that cannot have any subsequent arguments.
# These are:
# 1) password
# 2) secret
# 3) autocommand
# These arguments shall also match the end of the line ('$').
#
# All arguments that are not matched to the end of the line shall match
# an optional trailing space (' ?').

# username testuser
username_cmd = re.compile(r'^username (?P<username>\S+) ?')

# username testuser common-criteria-policy Test-CC password 0 password
p2 = re.compile(
r'^username +(?P<username>\S+) +common-criteria-policy +(?P<common_criteria_policy>.*) '
r'+password +(?P<type>\d) +(?P<password>.*)$')
# common-criteria-policy Test-CC
common_criteria_policy = re.compile(r'^common-criteria-policy (?P<common_criteria_policy>\S+) ?')

# username testuser secret 9 $9$A2OfV.30kNlIhE$ZEJQIT6aUj.TfCzqGQr.h4AmjQd/bWikQaGRlaLv0nQ
p3 = re.compile(r'^username +(?P<username>\S+) +secret +(?P<type>\d) +(?P<secret>.*)$')
# secret 9 $9$A2OfV.30kNlIhE$ZEJQIT6aUj.TfCzqGQr.h4AmjQd/bWikQaGRlaLv0nQ
secret = re.compile(r'^secret (?P<type>\d) (?P<secret>.*)$')

# username testuser one-time secret 9 $9$AuJ8xgW8aBBuF.$HyAzLk.3ILFsKrEvd4YjaAHbtonVMLikXw2pnrlkYJY
p4 = re.compile(
r'^username +(?P<username>\S+) +one-time +(?P<Onetime>)\s*secret +(?P<type>\d+) +(?P<secret>.*)$')
# privilege 15
privilege = re.compile(r'^privilege (?P<privilege>\d+) ?')

# username testuser privilege 15 password 0 lab
p5 = re.compile(
r'^username +(?P<username>\S+) +privilege +(?P<privilege>\d+) +password +(?P<type>\d) +(?P<password>.*)$')
# one-time
onetime = re.compile(r'^one-time ?')

# username testuser common-criteria-policy Test-CC secret 9 $9$7K9qbCZMJa2Vuk$6bS3.Bv7AkBXhTHpTH9V9fhMnJCQe1a9O7xBWHtOKo.
p6 = re.compile(
r'^username +(?P<username>\S+) +common-criteria-policy +(?P<common_criteria_policy>.*) '
r'+secret +(?P<type>\d) +(?P<secret>.*)$')
# nopassword
nopassword = re.compile(r'^nopassword ?')

# username testuser one-time password 0 password
p7 = re.compile(
r'^username +(?P<username>\S+) +one-time +(?P<Onetime>)\s*password +(?P<type>\d) +(?P<password>.*)$')
# password 0 lab
password = re.compile(r'^password (?P<type>\d) (?P<password>.*)$')

# username developer privilege 15 secret 9 $9$oNguEA9um9vRx.$MsDk0DOy1rzBjKAcySWdNjoKcA7GetG9YNnKOs8S67A
p8 = re.compile(r'^username +(?P<username>\S+) +privilege +(?P<privilege>\d+) +secret +(?P<secret_type>\d+) +(?P<secret>\S+)$')
# autocommand show ip bgp summary
autocommand = re.compile(r'^autocommand (?P<autocommand>.*)$')

# Initial return dictionary
ret_dict = {}

for line in out.splitlines():
line = line.strip()

# username testuser password 0 lab
m = p1.match(line)
if m:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})
pass_dict = users_dict.setdefault('password', {})
pass_dict['type'] = int(group['type'])
pass_dict['password'] = group['password']
# username testuser
m = username_cmd.match(line)
if not m:
# CLAIM: This is not a line with a 'username' command.
continue

# username testuser common-criteria-policy Test-CC password 0 password
m = p2.match(line)
if m:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})
users_dict['common_criteria_policy'] = group['common_criteria_policy']
pass_dict = users_dict.setdefault('password', {})
pass_dict['type'] = int(group['type'])
pass_dict['password'] = group['password']
continue
# CLAIM: this is a username line
# GOAL: extract the specified username and switch to that
# sub-dictionary:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})

# username testuser secret 9 $9$A2OfV.30kNlIhE$ZEJQIT6aUj.TfCzqGQr.h4AmjQd/bWikQaGRlaLv0nQ
m = p3.match(line)
if m:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})
secret_dict = users_dict.setdefault('secret', {})
secret_dict['type'] = int(group['type'])
secret_dict['secret'] = group['secret']
continue
# GOAL: remove the matched portion from the begining of the line
# so that we can match the subsequent argument (if any):
line = line[m.end():]

# username testuser one-time secret 9 $9$AuJ8xgW8aBBuF.$HyAzLk.3ILFsKrEvd4YjaAHbtonVMLikXw2pnrlkYJY
m = p4.match(line)
if m:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})
users_dict['onetime'] = True
secret_dict = users_dict.setdefault('secret', {})
secret_dict['type'] = int(group['type'])
secret_dict['secret'] = group['secret']
continue
while line:
# GOAL: parse through the line an argument at a time,
# shortening the line as we go.

# username testuser privilege 15 password 0 lab
m = p5.match(line)
if m:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})
users_dict['privilege'] = int(group['privilege'])
pass_dict = users_dict.setdefault('password', {})
pass_dict['type'] = int(group['type'])
pass_dict['password'] = group['password']
continue
if m := common_criteria_policy.match(line):
group = m.groupdict()
users_dict['common_criteria_policy'] = group['common_criteria_policy']
line = line[m.end():]
continue

# username testuser common-criteria-policy Test-CC secret 9 $9$7K9qbCZMJa2Vuk$6bS3.Bv7AkBXhTHpTH9V9fhMnJCQe1a9O7xBWHtOKo.
m = p6.match(line)
if m:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})
users_dict['common_criteria_policy'] = group['common_criteria_policy']
secret_dict = users_dict.setdefault('secret', {})
secret_dict['type'] = int(group['type'])
secret_dict['secret'] = group['secret']
continue
if m := privilege.match(line):
group = m.groupdict()
users_dict['privilege'] = int(group['privilege'])
line = line[m.end():]
continue

# username testuser one-time password 0 password
m = p7.match(line)
if m:
group = m.groupdict()
username = group['username']
users_dict = ret_dict.setdefault('username', {}).setdefault(username, {})
users_dict['onetime'] = True
pass_dict = users_dict.setdefault('password', {})
pass_dict['type'] = int(group['type'])
pass_dict['password'] = group['password']
continue
if m := secret.match(line):
group = m.groupdict()
pass_dict = users_dict.setdefault('secret', {})
pass_dict['type'] = int(group['type'])
pass_dict['secret'] = group['secret']
line = line[m.end():]
continue

# username developer privilege 15 secret 9 $9$oNguEA9um9vRx.$MsDk0DOy1rzBjKAcySWdNjoKcA7GetG9YNnKOs8S67A
m = p8.match(line)
if m:
group = m.groupdict()
user_dict = ret_dict.setdefault('username', {}).setdefault(group['username'], {})
user_dict.update({
'privilege': int(group['privilege']),
})
if m := onetime.match(line):
group = m.groupdict()
users_dict['onetime'] = True
line = line[m.end():]
continue

secret_dict = user_dict.setdefault('secret', {})
secret_dict.update({
'type': int(group['secret_type']),
'secret': group['secret']
})
if m := nopassword.match(line):
group = m.groupdict()
users_dict['nopassword'] = True
line = line[m.end():]
continue

if m := autocommand.match(line):
group = m.groupdict()
users_dict['autocommand'] = group['autocommand']
line = line[m.end():]
continue

if m := password.match(line):
group = m.groupdict()
pass_dict = users_dict.setdefault('password', {})
pass_dict['type'] = int(group['type'])
pass_dict['password'] = group['password']
line = line[m.end():]
continue

# CLAIM: There is an unhandled argument.
Common.log.warning(f"Unhandled argument in parser 'show running-config aaa username': {line}")
break

return ret_dict

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
expected_output = {
"username": {
"testuser07": {
"nopassword": True,
"privilege": 3
},
"testuser08": {
"common_criteria_policy": "Test-CC",
"privilege": 15,
"secret": {
"secret": "$9$oNguEA9um9vRx.$MsDk0DOy1rzBjKAcySWdNjoKcA7GetG9YNnKOs8S67A",
"type": 9
}
},
"testuser09": {
"autocommand": "show ip bgp summary",
"privilege": 15,
"secret": {
"secret": "$9$UuxZCcqGu2IgBU$teHrzSPJK5FgLH0YAnUezoA1JwaqGBcJI4Xb6c3S7tU",
"type": 9
}
},
"testuser10": {
"common_criteria_policy": "Test-CC",
"password": {
"password": "lab",
"type": 0
},
"privilege": 15
},
"testuser11": {
"privilege": 15
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
9400-HA#show running-config aaa username
!
! You may also need to setup a common criteria policy for testing:
! aaa new-model
! aaa common-criteria policy Test-CC
! min-length 1
username testuser07 privilege 3 nopassword
username testuser08 privilege 15 common-criteria-policy Test-CC secret 9 $9$oNguEA9um9vRx.$MsDk0DOy1rzBjKAcySWdNjoKcA7GetG9YNnKOs8S67A
! Some usernames can span multiple lines:
username testuser09 privilege 15 secret 9 $9$UuxZCcqGu2IgBU$teHrzSPJK5FgLH0YAnUezoA1JwaqGBcJI4Xb6c3S7tU
username testuser09 autocommand show ip bgp summary
username testuser10 privilege 15 common-criteria-policy Test-CC password 0 lab
! username with privilege and no password can happen if SSH pubkey auth is used:
username testuser11 privilege 15