diff --git a/src/genie/libs/parser/iosxe/show_run.py b/src/genie/libs/parser/iosxe/show_run.py index 4efe0b8eae..733a4be5ec 100644 --- a/src/genie/libs/parser/iosxe/show_run.py +++ b/src/genie/libs/parser/iosxe/show_run.py @@ -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, @@ -2884,36 +2886,44 @@ def cli(self, output=None): else: out = output - # username testuser password 0 lab - p1 = re.compile(r'^username +(?P\S+) +password +(?P\d) +(?P.*)$') + # 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\S+) ?') - # username testuser common-criteria-policy Test-CC password 0 password - p2 = re.compile( - r'^username +(?P\S+) +common-criteria-policy +(?P.*) ' - r'+password +(?P\d) +(?P.*)$') + # common-criteria-policy Test-CC + common_criteria_policy = re.compile(r'^common-criteria-policy (?P\S+) ?') - # username testuser secret 9 $9$A2OfV.30kNlIhE$ZEJQIT6aUj.TfCzqGQr.h4AmjQd/bWikQaGRlaLv0nQ - p3 = re.compile(r'^username +(?P\S+) +secret +(?P\d) +(?P.*)$') + # secret 9 $9$A2OfV.30kNlIhE$ZEJQIT6aUj.TfCzqGQr.h4AmjQd/bWikQaGRlaLv0nQ + secret = re.compile(r'^secret (?P\d) (?P.*)$') - # username testuser one-time secret 9 $9$AuJ8xgW8aBBuF.$HyAzLk.3ILFsKrEvd4YjaAHbtonVMLikXw2pnrlkYJY - p4 = re.compile( - r'^username +(?P\S+) +one-time +(?P)\s*secret +(?P\d+) +(?P.*)$') + # privilege 15 + privilege = re.compile(r'^privilege (?P\d+) ?') - # username testuser privilege 15 password 0 lab - p5 = re.compile( - r'^username +(?P\S+) +privilege +(?P\d+) +password +(?P\d) +(?P.*)$') + # 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\S+) +common-criteria-policy +(?P.*) ' - r'+secret +(?P\d) +(?P.*)$') + # nopassword + nopassword = re.compile(r'^nopassword ?') - # username testuser one-time password 0 password - p7 = re.compile( - r'^username +(?P\S+) +one-time +(?P)\s*password +(?P\d) +(?P.*)$') + # password 0 lab + password = re.compile(r'^password (?P\d) (?P.*)$') - # username developer privilege 15 secret 9 $9$oNguEA9um9vRx.$MsDk0DOy1rzBjKAcySWdNjoKcA7GetG9YNnKOs8S67A - p8 = re.compile(r'^username +(?P\S+) +privilege +(?P\d+) +secret +(?P\d+) +(?P\S+)$') + # autocommand show ip bgp summary + autocommand = re.compile(r'^autocommand (?P.*)$') # Initial return dictionary ret_dict = {} @@ -2921,102 +2931,76 @@ def cli(self, output=None): 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 diff --git a/src/genie/libs/parser/iosxe/tests/ShowRunningConfigAAAUsername/cli/equal/golden2_expected.py b/src/genie/libs/parser/iosxe/tests/ShowRunningConfigAAAUsername/cli/equal/golden2_expected.py new file mode 100644 index 0000000000..daa5a405f5 --- /dev/null +++ b/src/genie/libs/parser/iosxe/tests/ShowRunningConfigAAAUsername/cli/equal/golden2_expected.py @@ -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 + } + } +} diff --git a/src/genie/libs/parser/iosxe/tests/ShowRunningConfigAAAUsername/cli/equal/golden2_output.txt b/src/genie/libs/parser/iosxe/tests/ShowRunningConfigAAAUsername/cli/equal/golden2_output.txt new file mode 100644 index 0000000000..ef53f209bf --- /dev/null +++ b/src/genie/libs/parser/iosxe/tests/ShowRunningConfigAAAUsername/cli/equal/golden2_output.txt @@ -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