From e8aff6301d20d02d528c4156b562e0d72c28749b Mon Sep 17 00:00:00 2001 From: lovkeshsharma702 Date: Thu, 13 Aug 2026 15:57:51 +0530 Subject: [PATCH 1/5] bached request implementation --- aci-preupgrade-validation-script.py | 22 ++-- ...st_n9k_c93180yc_fx3_switch_memory_check.py | 101 +++++++++++++++++- 2 files changed, 114 insertions(+), 9 deletions(-) diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 9492c968..cd6cdf07 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -6702,19 +6702,25 @@ def n9k_c93180yc_fx3_switch_memory_check(fabric_nodes, **kwargs): msg = 'No N9K-C93180YC-FX3 switches found. Skipping.' else: node_ids = [node['fabricNode']['attributes']['id'] for node in affected_nodes] - node_filter = 'or({})'.format(','.join( - 'wcard(procMemUsage.dn,"node-{}/")'.format(nid) for nid in node_ids - )) - query = 'procMemUsage.json?query-target-filter=and({},wcard(procMemUsage.dn,"memusage-sup"),lt(procMemUsage.Total,"{}"))'.format( - node_filter, min_memory_kb - ) - proc_mem_mos = icurl('class', query) - node_id_to_attrs = { node['fabricNode']['attributes']['id']: node['fabricNode']['attributes'] for node in affected_nodes } + # APIC caps query-target-filter at 20 expressions total; use 16 per batch to + # leave headroom in case and()/or() wrappers also count toward that limit. + batch_size = 16 + proc_mem_mos = [] + for i in range(0, len(node_ids), batch_size): + batch_ids = node_ids[i:i + batch_size] + node_filter = 'or({})'.format(','.join( + 'wcard(procMemUsage.dn,"node-{}/")'.format(nid) for nid in batch_ids + )) + query = 'procMemUsage.json?query-target-filter=and({},wcard(procMemUsage.dn,"memusage-sup"),lt(procMemUsage.Total,"{}"))'.format( + node_filter, min_memory_kb + ) + proc_mem_mos += icurl('class', query) + for memory_mo in proc_mem_mos: attrs = memory_mo['procMemUsage']['attributes'] dn_match = re.search(node_regex, attrs['dn']) diff --git a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py index 45f46006..cca366e7 100644 --- a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py +++ b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py @@ -79,4 +79,103 @@ def test_logic(run_check, mock_icurl, fabric_nodes, expected_result, expected_ms if result.data: assert result.data == expected_data else: - assert result.unformatted_data == expected_data \ No newline at end of file + assert result.unformatted_data == expected_data + + +# --- Batch query coverage --- +# The check queries at most `batch_size` (15) node IDs per icurl call to stay +# under APIC's 20 filter-expression limit, with headroom in case the and()/or() +# wrappers also count. These helpers/tests build queries the same way the check +# does, so they mirror the real query strings. +BATCH_SIZE = 15 + + +def _fx3_fabric_nodes(node_ids): + return [ + { + "fabricNode": { + "attributes": { + "dn": "topology/pod-1/node-{}".format(nid), + "id": nid, + "name": "leaf{}".format(nid), + "model": "N9K-C93180YC-FX3", + "role": "leaf", + } + } + } + for nid in node_ids + ] + + +def _batch_query(node_ids, min_memory_kb=32000000): + node_filter = 'or({})'.format(','.join( + 'wcard(procMemUsage.dn,"node-{}/")'.format(nid) for nid in node_ids + )) + return 'procMemUsage.json?query-target-filter=and({},wcard(procMemUsage.dn,"memusage-sup"),lt(procMemUsage.Total,"{}"))'.format( + node_filter, min_memory_kb + ) + + +def _proc_mem_usage(node_id, total_kb): + return [ + { + "procMemUsage": { + "attributes": { + "dn": "topology/pod-1/node-{}/sys/procmem/memusage-sup".format(node_id), + "Modname": "sup", + "Total": str(total_kb), + } + } + } + ] + + +def test_batch_at_boundary_issues_single_query(run_check, monkeypatch): + node_ids = [str(nid) for nid in range(101, 101 + BATCH_SIZE)] # exactly 15 nodes + fabric_nodes = _fx3_fabric_nodes(node_ids) + query = _batch_query(node_ids) + + calls = [] + + def _mock_icurl(apitype, q, page=0, page_size=100000): + calls.append(q) + assert q == query + return [] + + monkeypatch.setattr(script, "icurl", _mock_icurl) + + result = run_check(fabric_nodes=fabric_nodes) + + assert len(calls) == 1 + assert result.result == script.PASS + + +def test_batch_split_across_two_queries(run_check, monkeypatch): + # 20 affected nodes -> batch1 has 15 IDs, batch2 has the remaining 5. + node_ids = [str(nid) for nid in range(101, 121)] + fabric_nodes = _fx3_fabric_nodes(node_ids) + batch1_ids, batch2_ids = node_ids[:BATCH_SIZE], node_ids[BATCH_SIZE:] + query1, query2 = _batch_query(batch1_ids), _batch_query(batch2_ids) + + # One low-memory node in each batch to confirm results from both queries are merged. + responses = { + query1: _proc_mem_usage(batch1_ids[0], 16000000), + query2: _proc_mem_usage(batch2_ids[-1], 16000000), + } + calls = [] + + def _mock_icurl(apitype, q, page=0, page_size=100000): + calls.append(q) + return responses[q] + + monkeypatch.setattr(script, "icurl", _mock_icurl) + + result = run_check(fabric_nodes=fabric_nodes) + + assert sorted(calls) == sorted([query1, query2]) + assert result.result == script.FAIL_O + expected_data = [ + [batch1_ids[0], "leaf{}".format(batch1_ids[0]), "N9K-C93180YC-FX3", 16.0], + [batch2_ids[-1], "leaf{}".format(batch2_ids[-1]), "N9K-C93180YC-FX3", 16.0], + ] + assert result.data == expected_data \ No newline at end of file From db16b22a5a097ae7925054fb79f143200e9c997f Mon Sep 17 00:00:00 2001 From: lovkeshsharma702 Date: Sat, 15 Aug 2026 07:36:59 +0530 Subject: [PATCH 2/5] rtc-check-rebase --- aci-preupgrade-validation-script.py | 3 +-- .../test_n9k_c93180yc_fx3_switch_memory_check.py | 11 ++++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index cd6cdf07..7d7cb50c 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -6707,8 +6707,7 @@ def n9k_c93180yc_fx3_switch_memory_check(fabric_nodes, **kwargs): for node in affected_nodes } - # APIC caps query-target-filter at 20 expressions total; use 16 per batch to - # leave headroom in case and()/or() wrappers also count toward that limit. + # APIC caps query-target-filter at 20 expressions total (16 node wcards + 2 leaf predicates + 2 wrappers = 20) batch_size = 16 proc_mem_mos = [] for i in range(0, len(node_ids), batch_size): diff --git a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py index cca366e7..4903ab33 100644 --- a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py +++ b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py @@ -83,11 +83,8 @@ def test_logic(run_check, mock_icurl, fabric_nodes, expected_result, expected_ms # --- Batch query coverage --- -# The check queries at most `batch_size` (15) node IDs per icurl call to stay -# under APIC's 20 filter-expression limit, with headroom in case the and()/or() -# wrappers also count. These helpers/tests build queries the same way the check -# does, so they mirror the real query strings. -BATCH_SIZE = 15 +# Mirrors the check's batch_size so tests break if the constant drifts. +BATCH_SIZE = 16 def _fx3_fabric_nodes(node_ids): @@ -131,7 +128,7 @@ def _proc_mem_usage(node_id, total_kb): def test_batch_at_boundary_issues_single_query(run_check, monkeypatch): - node_ids = [str(nid) for nid in range(101, 101 + BATCH_SIZE)] # exactly 15 nodes + node_ids = [str(nid) for nid in range(101, 101 + BATCH_SIZE)] # exactly BATCH_SIZE nodes fabric_nodes = _fx3_fabric_nodes(node_ids) query = _batch_query(node_ids) @@ -151,7 +148,7 @@ def _mock_icurl(apitype, q, page=0, page_size=100000): def test_batch_split_across_two_queries(run_check, monkeypatch): - # 20 affected nodes -> batch1 has 15 IDs, batch2 has the remaining 5. + # 20 affected nodes -> batch1 has 16 IDs, batch2 has the remaining 4. node_ids = [str(nid) for nid in range(101, 121)] fabric_nodes = _fx3_fabric_nodes(node_ids) batch1_ids, batch2_ids = node_ids[:BATCH_SIZE], node_ids[BATCH_SIZE:] From 55b77549df13d32a35aa3ec3fa7cea7624d84c41 Mon Sep 17 00:00:00 2001 From: lovkeshsharma702 Date: Sat, 15 Aug 2026 08:14:04 +0530 Subject: [PATCH 3/5] Your descriptive commit message here --- aci-preupgrade-validation-script.py | 4 ++-- .../test_n9k_c93180yc_fx3_switch_memory_check.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py index 7d7cb50c..aacc6ec7 100644 --- a/aci-preupgrade-validation-script.py +++ b/aci-preupgrade-validation-script.py @@ -6707,8 +6707,8 @@ def n9k_c93180yc_fx3_switch_memory_check(fabric_nodes, **kwargs): for node in affected_nodes } - # APIC caps query-target-filter at 20 expressions total (16 node wcards + 2 leaf predicates + 2 wrappers = 20) - batch_size = 16 + # batch_size=20 for APIC's 20-expression cap + batch_size = 20 proc_mem_mos = [] for i in range(0, len(node_ids), batch_size): batch_ids = node_ids[i:i + batch_size] diff --git a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py index 4903ab33..1d3758cd 100644 --- a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py +++ b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py @@ -84,7 +84,7 @@ def test_logic(run_check, mock_icurl, fabric_nodes, expected_result, expected_ms # --- Batch query coverage --- # Mirrors the check's batch_size so tests break if the constant drifts. -BATCH_SIZE = 16 +BATCH_SIZE = 20 def _fx3_fabric_nodes(node_ids): @@ -148,8 +148,8 @@ def _mock_icurl(apitype, q, page=0, page_size=100000): def test_batch_split_across_two_queries(run_check, monkeypatch): - # 20 affected nodes -> batch1 has 16 IDs, batch2 has the remaining 4. - node_ids = [str(nid) for nid in range(101, 121)] + # 25 affected nodes -> batch1 has 20 IDs, batch2 has the remaining 5. + node_ids = [str(nid) for nid in range(101, 126)] fabric_nodes = _fx3_fabric_nodes(node_ids) batch1_ids, batch2_ids = node_ids[:BATCH_SIZE], node_ids[BATCH_SIZE:] query1, query2 = _batch_query(batch1_ids), _batch_query(batch2_ids) From 0ac263f19286c7d505d18eb586eeea3064055644 Mon Sep 17 00:00:00 2001 From: lovkeshsharma702 Date: Sat, 15 Aug 2026 08:25:16 +0530 Subject: [PATCH 4/5] batch implementation --- .../test_n9k_c93180yc_fx3_switch_memory_check.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py index 1d3758cd..003e2bf5 100644 --- a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py +++ b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py @@ -148,8 +148,8 @@ def _mock_icurl(apitype, q, page=0, page_size=100000): def test_batch_split_across_two_queries(run_check, monkeypatch): - # 25 affected nodes -> batch1 has 20 IDs, batch2 has the remaining 5. - node_ids = [str(nid) for nid in range(101, 126)] + # 20 affected nodes -> batch1 has 16 IDs, batch2 has the remaining 4. + node_ids = [str(nid) for nid in range(101, 121)] fabric_nodes = _fx3_fabric_nodes(node_ids) batch1_ids, batch2_ids = node_ids[:BATCH_SIZE], node_ids[BATCH_SIZE:] query1, query2 = _batch_query(batch1_ids), _batch_query(batch2_ids) From 05e0c701b12d4ed1622fdbace540670ab7b803ff Mon Sep 17 00:00:00 2001 From: lovkeshsharma702 Date: Sat, 15 Aug 2026 21:51:25 +0530 Subject: [PATCH 5/5] batch implementation --- .../test_n9k_c93180yc_fx3_switch_memory_check.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py index 003e2bf5..1d3758cd 100644 --- a/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py +++ b/tests/checks/n9k_c93180yc_fx3_switch_memory_check/test_n9k_c93180yc_fx3_switch_memory_check.py @@ -148,8 +148,8 @@ def _mock_icurl(apitype, q, page=0, page_size=100000): def test_batch_split_across_two_queries(run_check, monkeypatch): - # 20 affected nodes -> batch1 has 16 IDs, batch2 has the remaining 4. - node_ids = [str(nid) for nid in range(101, 121)] + # 25 affected nodes -> batch1 has 20 IDs, batch2 has the remaining 5. + node_ids = [str(nid) for nid in range(101, 126)] fabric_nodes = _fx3_fabric_nodes(node_ids) batch1_ids, batch2_ids = node_ids[:BATCH_SIZE], node_ids[BATCH_SIZE:] query1, query2 = _batch_query(batch1_ids), _batch_query(batch2_ids)