Skip to content
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
101 changes: 101 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6293,6 +6293,106 @@ def multipod_modular_spine_bootscript_check(tversion, fabric_nodes, username, pa
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)


@check_wrapper(check_title='N9300 Switch Memory')
def n9300_switch_memory_check(fabric_nodes, **kwargs):
result = PASS
headers = ["NodeId", "Name", "Model", "Memory Detected (GB)"]
data = []
recommended_action = 'Increase the switch memory to at least 32GB on affected N9K-C93180YC-FX3 switches.'
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#n9300-switch-memory'
min_memory_kb = 32 * 1000 * 1000
msg = ''

affected_nodes = [
node for node in fabric_nodes
if node.get('fabricNode', {}).get('attributes', {}).get('model', '') == 'N9K-C93180YC-FX3'
]

if not affected_nodes:
result = NA
msg = 'No N9K-C93180YC-FX3 switches found. Skipping.'
else:
proc_mem_mos = icurl('class', 'procMemUsage.json')
node_total_kb = {}
parse_errors = []

for memory_mo in proc_mem_mos:
attrs = memory_mo.get('procMemUsage', {}).get('attributes', {})
total = attrs.get('Total')
mem_dn = attrs.get('dn', '')
if not total or '/memusage-sup' not in mem_dn:
continue
dn_match = re.search(node_regex, mem_dn)
if not dn_match:
continue
try:
total_kb = int(total)
except (TypeError, ValueError):
parse_errors.append([mem_dn, total])
continue

node_id = dn_match.group('node')
if node_id not in node_total_kb:
node_total_kb[node_id] = total_kb

if parse_errors:
result = ERROR
msg = 'Failed to parse procMemUsage Total for one or more nodes.'
headers = ['DN', 'Total']
data = parse_errors
recommended_action = ''
else:
missing_nodes = []

for node in affected_nodes:
node_id = node['fabricNode']['attributes']['id']
total_kb = node_total_kb.get(node_id)
if total_kb is None:
missing_nodes.append([
node_id,
node['fabricNode']['attributes'].get('name', ''),
node['fabricNode']['attributes'].get('model', ''),
])
continue

if total_kb < min_memory_kb:
memory_in_gb = round(total_kb / 1000000, 2)
result = MANUAL
data.append([
node_id,
node['fabricNode']['attributes'].get('name', ''),
node['fabricNode']['attributes'].get('model', ''),
memory_in_gb,
])

if missing_nodes and data:
result = MANUAL
msg = (
'Some N9K-C93180YC-FX3 nodes have insufficient memory and others are missing '
'procMemUsage data. Please manually verify the memory on all affected nodes.\n'
'Nodes with insufficient memory: {}\n'
'Nodes with missing data: {}'.format(
', '.join(str(row[0]) for row in data),
', '.join(str(row[0]) for row in missing_nodes),
)
)
headers = ['NodeId', 'Name', 'Model', 'Memory Detected (GB)']
data = data + [row + ['N/A'] for row in missing_nodes]
elif missing_nodes:
result = ERROR
msg = 'Missing procMemUsage data for one or more affected N9K-C93180YC-FX3 nodes.'
headers = ['NodeId', 'Name', 'Model']
data = missing_nodes
recommended_action = ''
elif data:
msg = (
'One or more N9K-C93180YC-FX3 switches have less than 32GB of memory. '
'An outage is not guaranteed but can occur. Please verify and upgrade the memory on affected nodes.'
)

return Result(result=result, msg=msg, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)


# ---- Script Execution ----


Expand Down Expand Up @@ -6384,6 +6484,7 @@ class CheckManager:
validate_32_64_bit_image_check,
fabric_link_redundancy_check,
apic_downgrade_compat_warning_check,
n9300_switch_memory_check,

# Faults
apic_disk_space_faults_check,
Expand Down
10 changes: 10 additions & 0 deletions docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ Items | This Script
[Fabric Link Redundancy][g17] | :white_check_mark: | :no_entry_sign:
[APIC Database Size][g18] | :white_check_mark: | :no_entry_sign:
[APIC downgrade compatibility when crossing 6.2 release][g19]| :white_check_mark: | :no_entry_sign:
[N9300 Switch Memory][g20] | :white_check_mark: | :no_entry_sign:

[g1]: #compatibility-target-aci-version
[g2]: #compatibility-cimc-version
Expand All @@ -57,6 +58,7 @@ Items | This Script
[g17]: #fabric-link-redundancy
[g18]: #apic-database-size
[g19]: #apic-downgrade-compatibility-when-crossing-62-release
[g20]: #n9300-switch-memory

### Fault Checks
Items | Faults | This Script | APIC built-in
Expand Down Expand Up @@ -2725,6 +2727,14 @@ To avoid this risk, consider disabling Auto Firmware Update before upgrading to
!!! note
This issue occurs because older switch firmware versions are not compatible with switch images 6.0(3) or newer. The APIC version is not a factor.

### N9300 Switch Memory

This check applies to N9K-C93180YC-FX3 switches only. It reviews `procMemUsage` and flags nodes with less than 32GB memory installed. This check is not version dependent and runs for all upgrade versions.

Impact: Running an N9K-C93180YC-FX3 switch with less than 32GB memory can lead to memory pressure and increase the risk of service instability.

If any N9K-C93180YC-FX3 switch is flagged by this check, upgrade the switch memory to at least 32GB.


### Rogue EP Exception List missing on switches

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"procMemUsage": {
"attributes": {
"dn": "topology/pod-1/node-101/sys/procmem/memusage-sup",
"Modname": "sup",
"Total": "33554432"
}
}
},
{
"procMemUsage": {
"attributes": {
"dn": "topology/pod-1/node-102/sys/procmem/memusage-sup",
"Modname": "sup",
"Total": "33554432"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"procMemUsage": {
"attributes": {
"dn": "topology/pod-1/node-101/sys/procmem/memusage-sup",
"Modname": "sup",
"Total": "33554432"
}
}
}
]
20 changes: 20 additions & 0 deletions tests/checks/n9300_switch_memory_24g_check/procMemUsage_mixed.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"procMemUsage": {
"attributes": {
"dn": "topology/pod-1/node-101/sys/procmem/memusage-sup",
"Modname": "sup",
"Total": "33554432"
}
}
},
{
"procMemUsage": {
"attributes": {
"dn": "topology/pod-1/node-102/sys/procmem/memusage-sup",
"Modname": "sup",
"Total": "22535444"
}
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import os
import pytest
import logging
import importlib
from helpers.utils import read_data

script = importlib.import_module("aci-preupgrade-validation-script")

log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))

test_function = "n9300_switch_memory_check"

# icurl queries
proc_mem_query = 'procMemUsage.json'


@pytest.mark.parametrize(
"fabric_nodes, icurl_outputs, tversion, expected_result, expected_msg, expected_data",
[
# No nodes returned
(
[],
{},
"6.0(3c)",
script.NA,
'No N9K-C93180YC-FX3 switches found. Skipping.',
[],
),
# Non-N9K-C93180YC-FX3 node
(
read_data(dir, "fabricNode_non_n9300.json"),
{
proc_mem_query: read_data(dir, "procMemUsage_node201_gt32gb.json"),
},
"6.0(3c)",
script.NA,
'No N9K-C93180YC-FX3 switches found. Skipping.',
[],
),
# N9K-C93180YC-FX3 node with >=32GB memory
(
read_data(dir, "fabricNode_one.json"),
{
proc_mem_query: read_data(dir, "procMemUsage_gt32gb.json"),
},
"6.0(3c)",
script.PASS,
'',
[],
),
# Multiple nodes, only N9K-C93180YC-FX3 checked, all >=32GB
(
read_data(dir, "fabricNode_two.json"),
{
proc_mem_query: read_data(dir, "procMemUsage_all_gt24gb.json"),
},
"6.0(3c)",
script.PASS,
'',
[],
),
# Invalid procMemUsage Total value
(
read_data(dir, "fabricNode_one.json"),
{
proc_mem_query: read_data(dir, "procMemUsage_invalid_total.json"),
},
"6.0(3c)",
script.ERROR,
'Failed to parse procMemUsage Total for one or more nodes.',
[["topology/pod-1/node-101/sys/procmem/memusage-sup", "unknown"]],
),
# Missing procMemUsage data for affected node
(
read_data(dir, "fabricNode_one.json"),
{
proc_mem_query: read_data(dir, "procMemUsage_missing_affected_node.json"),
},
"6.0(3c)",
script.ERROR,
'Missing procMemUsage data for one or more affected N9K-C93180YC-FX3 nodes.',
[["101", "leaf101", "N9K-C93180YC-FX3"]],
),
# N9K-C93180YC-FX3 node with <32GB memory
(
read_data(dir, "fabricNode_two.json"),
{
proc_mem_query: read_data(dir, "procMemUsage_mixed.json"),
},
"6.0(3c)",
script.PASS,
'',
[],
),
# N9K-C93180YC-FX3 node with <32GB memory (fail case)
(
read_data(dir, "fabricNode_one.json"),
{
proc_mem_query: read_data(dir, "procMemUsage_lt32gb.json"),
},
"6.0(3c)",
script.FAIL_O,
'',
[["101", "leaf101", "N9K-C93180YC-FX3", 25.31]],
),
],
)
def test_logic(run_check, mock_icurl, fabric_nodes, tversion, expected_result, expected_msg, expected_data):
result = run_check(
tversion=script.AciVersion(tversion),
fabric_nodes=fabric_nodes,
)
assert result.result == expected_result
assert result.msg == expected_msg
assert result.data == expected_data
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"fabricNode": {
"attributes": {
"dn": "topology/pod-1/node-201",
"id": "201",
"name": "leaf201",
"model": "N9K-C9508",
"role": "leaf"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/n9300_switch_memory_32g_check/fabricNode_one.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"fabricNode": {
"attributes": {
"dn": "topology/pod-1/node-101",
"id": "101",
"name": "leaf101",
"model": "N9K-C93180YC-FX3",
"role": "leaf"
}
}
}
]
24 changes: 24 additions & 0 deletions tests/checks/n9300_switch_memory_32g_check/fabricNode_two.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[
{
"fabricNode": {
"attributes": {
"dn": "topology/pod-1/node-101",
"id": "101",
"name": "leaf101",
"model": "N9K-C93180YC-FX3",
"role": "leaf"
}
}
},
{
"fabricNode": {
"attributes": {
"dn": "topology/pod-1/node-102",
"id": "102",
"name": "leaf102",
"model": "N9K-C9364C",
"role": "leaf"
}
}
}
]
Loading