Skip to content

Conversation

thepurplebuffalo
Copy link
Contributor

@thepurplebuffalo thepurplebuffalo commented Mar 28, 2025

Description

This change adjusts how the IOSXE parser for show running-config aaa username works.

Motivation and Context

I recently encountered a problem where this username line was skipped by the parser:

username testuser8 privilege 15 common-criteria-policy Test-CC secret 9 <hash>

The existing regular expressions (8 of them) each match entire username lines that meet a specific format. The line above did not match any of those expressions. The result was that the username was silently skipped by the parser. None of the 8 existing regular expressions allow for both privilege and common-criteria-policy on the same line.

I have changed the general nature of this parser to treat the username line like a stack. Arguments are popped off of the beginning of the line (along with their parameters) so that the arguments can exist in flexible combinations.

This code is untested as the project Makefile relies on resources internal to Cisco.

Impact (If any)

Screenshots:

None.

Checklist:

  • I have updated the changelog.
  • I have updated the documentation (If applicable).
  • I have added tests to cover my changes (If applicable).
  • All new and existing tests passed.
  • All new code passed compilation.
2025-09-28T13:36:25: %AETEST-INFO: +------------------------------------------------------------------------------+
2025-09-28T13:36:25: %AETEST-INFO: |                   Starting section ShowRunningAAAUserName                    |
2025-09-28T13:36:25: %AETEST-INFO: +------------------------------------------------------------------------------+
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: :               Starting STEP 1: iosxe -> ShowRunningAAAUserName               :
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: :      Starting STEP 1.1: Test Golden -> iosxe -> ShowRunningAAAUserName       :
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: :    Starting STEP 1.1.1: Gold -> iosxe -> ShowRunningAAAUserName -> golden    :
2025-09-28T13:36:25: %AETEST-INFO: :                                   _output                                    :
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: The result of STEP 1.1.1: Gold -> iosxe -> ShowRunningAAAUserName -> golden_output is => PASSED
2025-09-28T13:36:25: %AETEST-INFO: The result of STEP 1.1: Test Golden -> iosxe -> ShowRunningAAAUserName is => PASSED
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: :       Starting STEP 1.2: Test Empty -> iosxe -> ShowRunningAAAUserName       :
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: :    Starting STEP 1.2.1: Empty -> iosxe -> ShowRunningAAAUserName -> empty    :
2025-09-28T13:36:25: %AETEST-INFO: :                                   _output                                    :
2025-09-28T13:36:25: %AETEST-INFO: +..............................................................................+
2025-09-28T13:36:25: %AETEST-INFO: The result of STEP 1.2.1: Empty -> iosxe -> ShowRunningAAAUserName -> empty_output is => PASSED
2025-09-28T13:36:25: %AETEST-INFO: The result of STEP 1.2: Test Empty -> iosxe -> ShowRunningAAAUserName is => PASSED
2025-09-28T13:36:25: %AETEST-INFO: The result of STEP 1: iosxe -> ShowRunningAAAUserName is => PASSED
2025-09-28T13:36:25: %AETEST-INFO: +..........................................................+
2025-09-28T13:36:25: %AETEST-INFO: :                       STEPS Report                       :
2025-09-28T13:36:25: %AETEST-INFO: +..........................................................+
2025-09-28T13:36:25: %AETEST-INFO: STEP 1 - iosxe -> ShowRunningAAAUserName          Passed
2025-09-28T13:36:25: %AETEST-INFO: STEP 1.1 - Test Golden -> iosxe -> ShowRunningAAAUserNamePassed
2025-09-28T13:36:25: %AETEST-INFO: STEP 1.1.1 - Gold -> iosxe -> ShowRunningAAAUserName -> golden_outputPassed
2025-09-28T13:36:25: %AETEST-INFO: STEP 1.2 - Test Empty -> iosxe -> ShowRunningAAAUserNamePassed
2025-09-28T13:36:25: %AETEST-INFO: STEP 1.2.1 - Empty -> iosxe -> ShowRunningAAAUserName -> empty_outputPassed
2025-09-28T13:36:25: %AETEST-INFO: ............................................................
2025-09-28T13:36:25: %AETEST-INFO: The result of section ShowRunningAAAUserName is => PASSED

@thepurplebuffalo thepurplebuffalo requested a review from a team as a code owner March 28, 2025 10:14
Copy link
Collaborator

@ThomasJRyan ThomasJRyan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You've got a failing pipeline which should be resolved through pulling the latest main branch into yours and pushing it upstream

secret_dict['type'] = int(group['type'])
secret_dict['secret'] = group['secret']
continue
while line:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we typically discourage the use of re.search, it might actually be preferable to use it over a while loop

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced that re.search will be able to successfully identify all of the weird user-configurable portions of the line. Consider this abomonation:

Router1(config)#do sh running-config aaa username
!
username testuser12 common-criteria-policy nopassword password 0 nopassword

Comment on lines +7 to +12
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actually interested in this format. Is this entered exactly as the user entered it, or is it rearranged to fit an underlying order. Does the command only allow options to be added in a particular order?

If the order is set in stone, then it's actually possible to write a regular expression that should be able to match anything. It gets a bit difficult if say, both of these are valid

username testuser10 privilege 15 common-criteria-policy Test-CC password 0 lab
username testuser10 common-criteria-policy Test-CC privilege 15 password 0 lab

But if only one is valid, then a regular express like this becomes possible

"^username (?P<username>\S+) (privilege (?P<privilege>\d+) )?(common-criteria-policy (?P<common_criteria_policy>\S+) )?(?P<flags>((one-time|nopassword) ?)* ?)?((?P<password_type>secret|password|autocommand) (((?P<type>\d+) (?P<password>\S+))|(?P<command>.*)))?$"

Admittedly, that does become a tad tedious to maintain. The re.search method or what you currently have might be better to work with

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took all of the provided text from the running-config of a virtual device.

Ordering for these commands does seem to be static. Whether or not it's the same order on all versions of IOS-XE is beyond me. I see your regex and I like where it's going. Consider something like this borked (but accepted) config:

    Router1(config)#do sh run | i username
    username testuser11 privilege 3 common-criteria-policy privilege nopassword
    username testuser11 autocommand 15 borked-command

In this case the 15 is a valid command - it's the session to resume... but borked-command would cause it to fail as session resumption doesn't take any arguments. I think the <type> <password> portion of your regex will greedily capture it. You'd need an even more complex regex (lookbehind?) to handle people doing silly things like this.

Alternatively your regex should work with some nesting. Either way the regex would become significantly more complex.

Also note the name privilege for the applied common criteria policy. Apparently you can put keywords as the name of the policy. I shudder to think that somewhere out there is a config with a policy named secret or password.

continue

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to use or create a logger in this file at the top. The log should output the file location of the logger, which if we use Common's wouldn't be totally accurate to where the warning was raised

@lsheikal lsheikal requested review from lukemanhakkim and removed request for lsheikal September 23, 2025 17:26
Copy link
Collaborator

@ThomasJRyan ThomasJRyan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A hard problem to solve, but I like it overall. So long as the pipeline passes I'm content

@lukemanhakkim lukemanhakkim merged commit 10fdc7f into CiscoTestAutomation:main Oct 2, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants