From 6a4854d03eea0871176084b6220a4d23bf270800 Mon Sep 17 00:00:00 2001 From: Luke Jenkins Date: Wed, 1 Jul 2026 14:35:32 -0600 Subject: [PATCH 1/3] Fix run tests workflow Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/run_tests.yml | 2 +- .gitignore | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 6b3bf318fd..4c7869bbdf 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -4,6 +4,7 @@ name: Run Tests on: + - workflow_dispatch - push - pull_request @@ -32,7 +33,6 @@ jobs: - name: Make develop run: | make develop - make json git status git diff diff --git a/.gitignore b/.gitignore index 67b5a00eb0..008aa4e19d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ __pycache__/ # Developer files .project .pydevproject +.venv/ # Pycharm files .idea From 78f70a4c88c93fbb5c7f2f846695151ec59069fb Mon Sep 17 00:00:00 2001 From: Luke Jenkins Date: Wed, 1 Jul 2026 15:00:26 -0600 Subject: [PATCH 2/3] New Genie parser for 'show ap meraki monitoring summary' command. Extracts serial number, cloud ID, registration status, and MAC addresses per AP. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/genie/libs/parser/iosxe/show_ap.py | 101 +++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/src/genie/libs/parser/iosxe/show_ap.py b/src/genie/libs/parser/iosxe/show_ap.py index 03fc43826a..295c5fa66c 100644 --- a/src/genie/libs/parser/iosxe/show_ap.py +++ b/src/genie/libs/parser/iosxe/show_ap.py @@ -1,4 +1,5 @@ import re +from typing import Any as tAny, Dict from genie.metaparser import MetaParser from genie.metaparser.util.schemaengine import Any, Optional, Or @@ -1254,6 +1255,106 @@ def cli(self, output=None): return ap_cdp_neighbor_dict +# ================================================ +# Schema for: +# * 'show ap meraki monitoring summary' +# ================================================ +class ShowApMerakiMonitoringSummarySchema(MetaParser): + """Schema for show ap meraki monitoring summary.""" + + schema = { + "meraki_monitoring": { + "monitoring_status": str, + "supported_ap_count": int, + Optional("aps"): { + str: { + Optional("ap_model"): str, + Optional("radio_mac"): str, + Optional("mac_address"): str, + Optional("serial_number"): str, + Optional("cloud_id"): str, + Optional("status"): str, + } + }, + } + } + + +# ================================================ +# Parser for: +# * 'show ap meraki monitoring summary' +# ================================================ +class ShowApMerakiMonitoringSummary(ShowApMerakiMonitoringSummarySchema): + """Parser for show ap meraki monitoring summary""" + + cli_command = "show ap meraki monitoring summary" + + def cli(self, output=None): + if output is None: + out = self.device.execute(self.cli_command) + else: + out = output + + result_dict: Dict[str, tAny] = { + "meraki_monitoring": { + "monitoring_status": "", + "supported_ap_count": 0, + } + } + + monitoring_capture = re.compile( + r"^Meraki\s+Monitoring\s*:\s*(?P.+)$" + ) + supported_capture = re.compile( + r"^Number\s+of\s+Supported\s+APs\s*:\s*(?P\d+)" + ) + row_capture = re.compile( + r"^(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P\S+)\s+" + r"(?P.+)" + ) + + aps: Dict[str, Dict[str, tAny]] = {} + + for line in out.splitlines(): + line = line.rstrip() + if not line: + continue + + monitoring_match = monitoring_capture.match(line) + if monitoring_match: + result_dict["meraki_monitoring"][ + "monitoring_status" + ] = monitoring_match.group("status").strip() + continue + + supported_match = supported_capture.match(line) + if supported_match: + result_dict["meraki_monitoring"][ + "supported_ap_count" + ] = int(supported_match.group("count")) + continue + + if line.startswith("AP Name") or line.startswith("---"): + continue + + row_match = row_capture.match(line) + if row_match: + groups = row_match.groupdict() + ap_name = groups.pop("ap_name") + aps[ap_name] = {key: value.strip() for key, value in groups.items()} + continue + + if aps: + result_dict["meraki_monitoring"]["aps"] = aps + + return result_dict + + # ============================= # Schema for: # * 'show ap config general' From a8aed4ae68f4b58f289302442479be7076c09d87 Mon Sep 17 00:00:00 2001 From: Luke Jenkins Date: Wed, 1 Jul 2026 16:48:08 -0600 Subject: [PATCH 3/3] Add unit tests and align ShowApMerakiMonitoringSummary with contribution guidelines Prepares the 'show ap meraki monitoring summary' IOSXE parser for upstream submission: * Add folder-based unit tests (golden + empty) with redacted sample output covering C9136I-B, AIR-AP1562I/E, CW9176I and CW9176D1 access points. * Raise SchemaEmptyParserError on empty output by returning an empty dict instead of pre-populated defaults, matching genie parser conventions. * Remove the typing import and local annotations to match repo style. * Register the parser in parsers.json (make json) and add a changelog entry. Verified against real device output (58 APs) and via the pyats folder-test harness (100% pass). --- ...raki_monitoring_summary_20260701163528.rst | 6 ++ sdk_generator/outputs/github_parser.json | 18 +++- src/genie/libs/parser/iosxe/show_ap.py | 35 ++++---- .../cli/empty/empty_output_output.txt | 0 .../cli/equal/golden_output_expected.py | 88 +++++++++++++++++++ .../cli/equal/golden_output_output.txt | 15 ++++ 6 files changed, 142 insertions(+), 20 deletions(-) create mode 100644 changelog/undistributed/changelog_new_iosxe_show_ap_meraki_monitoring_summary_20260701163528.rst create mode 100644 src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/empty/empty_output_output.txt create mode 100644 src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_expected.py create mode 100644 src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_output.txt diff --git a/changelog/undistributed/changelog_new_iosxe_show_ap_meraki_monitoring_summary_20260701163528.rst b/changelog/undistributed/changelog_new_iosxe_show_ap_meraki_monitoring_summary_20260701163528.rst new file mode 100644 index 0000000000..526509ab99 --- /dev/null +++ b/changelog/undistributed/changelog_new_iosxe_show_ap_meraki_monitoring_summary_20260701163528.rst @@ -0,0 +1,6 @@ +-------------------------------------------------------------------------------- + New +-------------------------------------------------------------------------------- +* IOSXE + * Added ShowApMerakiMonitoringSummary: + * show ap meraki monitoring summary diff --git a/sdk_generator/outputs/github_parser.json b/sdk_generator/outputs/github_parser.json index 07da27e606..b11d88822a 100644 --- a/sdk_generator/outputs/github_parser.json +++ b/sdk_generator/outputs/github_parser.json @@ -116496,5 +116496,21 @@ } } } + }, + "show ap meraki monitoring summary": { + "folders": { + "iosxe": { + "class": "ShowApMerakiMonitoringSummary", + "doc": "Parser for show ap meraki monitoring summary", + "module_name": "iosxe.show_ap", + "package": "genie.libs.parser", + "schema": "{\n 'meraki_monitoring': {\n 'monitoring_status': str,\n 'supported_ap_count': int,\n Optional('aps'): {\n '': {\n Optional('ap_model'): str,\n Optional('radio_mac'): str,\n Optional('mac_address'): str,\n Optional('serial_number'): str,\n Optional('cloud_id'): str,\n Optional('status'): str,\n },\n },\n },\n}", + "tokens": { + "os": "iosxe" + }, + "uid": "show_ap_meraki_monitoring_summary", + "url": "https://github.com/CiscoTestAutomation/genieparser/tree/master/src/genie/libs/parser/iosxe/show_ap.py#L1286" + } + } } -} \ No newline at end of file +} diff --git a/src/genie/libs/parser/iosxe/show_ap.py b/src/genie/libs/parser/iosxe/show_ap.py index 295c5fa66c..dfb96c7293 100644 --- a/src/genie/libs/parser/iosxe/show_ap.py +++ b/src/genie/libs/parser/iosxe/show_ap.py @@ -1,5 +1,4 @@ import re -from typing import Any as tAny, Dict from genie.metaparser import MetaParser from genie.metaparser.util.schemaengine import Any, Optional, Or @@ -1295,19 +1294,18 @@ def cli(self, output=None): else: out = output - result_dict: Dict[str, tAny] = { - "meraki_monitoring": { - "monitoring_status": "", - "supported_ap_count": 0, - } - } + # initial return dictionary + result_dict = {} + # Meraki Monitoring : Disabled monitoring_capture = re.compile( r"^Meraki\s+Monitoring\s*:\s*(?P.+)$" ) + # Number of Supported APs : 58 supported_capture = re.compile( r"^Number\s+of\s+Supported\s+APs\s*:\s*(?P\d+)" ) + # lab-ap-01 C9136I-B 0011.2233.0001 0011.2233.1001 TST00000001 Q2ZZ-0001-AAAA Registered row_capture = re.compile( r"^(?P\S+)\s+" r"(?P\S+)\s+" @@ -1318,8 +1316,6 @@ def cli(self, output=None): r"(?P.+)" ) - aps: Dict[str, Dict[str, tAny]] = {} - for line in out.splitlines(): line = line.rstrip() if not line: @@ -1327,16 +1323,16 @@ def cli(self, output=None): monitoring_match = monitoring_capture.match(line) if monitoring_match: - result_dict["meraki_monitoring"][ - "monitoring_status" - ] = monitoring_match.group("status").strip() + meraki_dict = result_dict.setdefault("meraki_monitoring", {}) + meraki_dict["monitoring_status"] = \ + monitoring_match.group("status").strip() continue supported_match = supported_capture.match(line) if supported_match: - result_dict["meraki_monitoring"][ - "supported_ap_count" - ] = int(supported_match.group("count")) + meraki_dict = result_dict.setdefault("meraki_monitoring", {}) + meraki_dict["supported_ap_count"] = \ + int(supported_match.group("count")) continue if line.startswith("AP Name") or line.startswith("---"): @@ -1346,12 +1342,13 @@ def cli(self, output=None): if row_match: groups = row_match.groupdict() ap_name = groups.pop("ap_name") - aps[ap_name] = {key: value.strip() for key, value in groups.items()} + meraki_dict = result_dict.setdefault("meraki_monitoring", {}) + aps = meraki_dict.setdefault("aps", {}) + aps[ap_name] = { + key: value.strip() for key, value in groups.items() + } continue - if aps: - result_dict["meraki_monitoring"]["aps"] = aps - return result_dict diff --git a/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/empty/empty_output_output.txt b/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/empty/empty_output_output.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_expected.py b/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_expected.py new file mode 100644 index 0000000000..8859148b82 --- /dev/null +++ b/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_expected.py @@ -0,0 +1,88 @@ +expected_output = { + "meraki_monitoring": { + "monitoring_status": "Disabled", + "supported_ap_count": 58, + "aps": { + "lab-ap-01": { + "ap_model": "C9136I-B", + "radio_mac": "0011.2233.0001", + "mac_address": "0011.2233.1001", + "serial_number": "TST00000001", + "cloud_id": "Q2ZZ-0001-AAAA", + "status": "Registered" + }, + "lab-ap-02": { + "ap_model": "AIR-AP1562I-B-K9", + "radio_mac": "0011.2233.0002", + "mac_address": "0011.2233.1002", + "serial_number": "TST00000002", + "cloud_id": "Q2ZZ-0002-AAAB", + "status": "Registered" + }, + "lab-ap-03": { + "ap_model": "AIR-AP3802I-B-K9", + "radio_mac": "0011.2233.0003", + "mac_address": "0011.2233.1003", + "serial_number": "TST00000003", + "cloud_id": "Q2ZZ-0003-AAAC", + "status": "Registered" + }, + "lab-ap-04": { + "ap_model": "AIR-AP3802I-B-K9", + "radio_mac": "0011.2233.0004", + "mac_address": "0011.2233.1004", + "serial_number": "TST00000004", + "cloud_id": "Q2ZZ-0004-AAAD", + "status": "Registered" + }, + "lab-ap-05": { + "ap_model": "AIR-AP3802E-B-K9", + "radio_mac": "0011.2233.0005", + "mac_address": "0011.2233.1005", + "serial_number": "TST00000005", + "cloud_id": "Q2ZZ-0005-AAAE", + "status": "Registered" + }, + "lab-ap-06": { + "ap_model": "CW9176I", + "radio_mac": "0011.2233.0006", + "mac_address": "0011.2233.1006", + "serial_number": "TST00000006", + "cloud_id": "Q5BK-0006-AAAF", + "status": "Registered" + }, + "lab-ap-07": { + "ap_model": "CW9176I", + "radio_mac": "0011.2233.0007", + "mac_address": "0011.2233.1007", + "serial_number": "TST00000007", + "cloud_id": "Q5BK-0007-AAAG", + "status": "Registered" + }, + "lab-ap-08": { + "ap_model": "CW9176D1", + "radio_mac": "0011.2233.0008", + "mac_address": "0011.2233.1008", + "serial_number": "TST00000008", + "cloud_id": "Q5BL-0008-AAAH", + "status": "Registered" + }, + "lab-ap-09": { + "ap_model": "CW9176D1", + "radio_mac": "0011.2233.0009", + "mac_address": "0011.2233.1009", + "serial_number": "TST00000009", + "cloud_id": "Q5BC-0009-AAAJ", + "status": "Registered" + }, + "lab-ap-10": { + "ap_model": "CW9176I", + "radio_mac": "0011.2233.0010", + "mac_address": "0011.2233.1010", + "serial_number": "TST00000010", + "cloud_id": "Q5BK-0010-AAAK", + "status": "Registered" + } + } + } +} diff --git a/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_output.txt b/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_output.txt new file mode 100644 index 0000000000..d57288025f --- /dev/null +++ b/src/genie/libs/parser/iosxe/tests/ShowApMerakiMonitoringSummary/cli/equal/golden_output_output.txt @@ -0,0 +1,15 @@ +Meraki Monitoring : Disabled +Number of Supported APs : 58 + +AP Name AP Model Radio MAC MAC Address AP Serial Number Cloud ID Status +--------------------------------------------------------------------------------------------------------------------------------------------------- +lab-ap-01 C9136I-B 0011.2233.0001 0011.2233.1001 TST00000001 Q2ZZ-0001-AAAA Registered +lab-ap-02 AIR-AP1562I-B-K9 0011.2233.0002 0011.2233.1002 TST00000002 Q2ZZ-0002-AAAB Registered +lab-ap-03 AIR-AP3802I-B-K9 0011.2233.0003 0011.2233.1003 TST00000003 Q2ZZ-0003-AAAC Registered +lab-ap-04 AIR-AP3802I-B-K9 0011.2233.0004 0011.2233.1004 TST00000004 Q2ZZ-0004-AAAD Registered +lab-ap-05 AIR-AP3802E-B-K9 0011.2233.0005 0011.2233.1005 TST00000005 Q2ZZ-0005-AAAE Registered +lab-ap-06 CW9176I 0011.2233.0006 0011.2233.1006 TST00000006 Q5BK-0006-AAAF Registered +lab-ap-07 CW9176I 0011.2233.0007 0011.2233.1007 TST00000007 Q5BK-0007-AAAG Registered +lab-ap-08 CW9176D1 0011.2233.0008 0011.2233.1008 TST00000008 Q5BL-0008-AAAH Registered +lab-ap-09 CW9176D1 0011.2233.0009 0011.2233.1009 TST00000009 Q5BC-0009-AAAJ Registered +lab-ap-10 CW9176I 0011.2233.0010 0011.2233.1010 TST00000010 Q5BK-0010-AAAK Registered