Skip to content

Commit 68eb242

Browse files
committed
utils/nvme: Enhanced get_ns_status to support namespace-based topology fallback
The 'nvme show-topology /dev/<controller>' form fails on some nvme-cli builds, returning {"error": "Invalid device name"} instead of a valid JSON topology. Additionally, in multi-path subsystems where two controllers (e.g. nvme0 and nvme3) share a namespace (e.g. nvme3n1), the namespace block device is named after only one of the controllers, making a namespace name constructed from controller_name unreliable. Enhance get_ns_status with a two-stage approach: Primary path (unchanged behaviour): nvme show-topology /dev/<controller_name> -o json Parses the original JSON structure where paths["Name"] matches the controller name. Returns immediately on success. Fallback path (new): nvme show-topology -o json (whole-system, no device argument) Triggered when the primary path returns an error payload (dict instead of list), raises JSONDecodeError, or yields no match. Locates the correct path by matching NSID and Controller.Name inside the Paths[].Controller[] array, which is unambiguous across multi-controller subsystems and avoids any namespace device name construction. Both paths return [State, ANAState] preserving the existing API.
1 parent 74b7379 commit 68eb242

1 file changed

Lines changed: 57 additions & 12 deletions

File tree

avocado/utils/nvme.py

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -394,24 +394,69 @@ def create_namespaces(controller_name, ns_count, shared_ns=False):
394394

395395
def get_ns_status(controller_name, ns_id):
396396
"""
397-
Returns the status of namespaces on the specified controller
397+
Returns the status of namespaces on the specified controller.
398398
399-
:param controller_name: name of the controller like nvme0
400-
:param ns_id: ID of namespace for which we need the status
399+
Tries ``nvme show-topology /dev/<controller_name>`` first. If that
400+
command returns an error payload (observed with some nvme-cli builds
401+
where passing a controller node is rejected) or yields no match, the
402+
function falls back to ``nvme show-topology -o json`` (whole-system)
403+
and locates the path by matching ``NSID`` and ``Controller.Name``.
404+
This correctly handles multi-path subsystems where the namespace block
405+
device is named after a peer controller (e.g. ``nvme0`` and ``nvme3``
406+
both serving ``nvme3n1``).
401407
402-
:rtype: list
408+
:param controller_name: name of the controller, e.g. ``nvme0``
409+
:param ns_id: NSID of the namespace whose status is required (int)
410+
411+
:rtype: list -- ``[State, ANAState]`` for the matching path, or ``[]``
403412
"""
404413
stat = []
414+
405415
cmd = f"nvme show-topology /dev/{controller_name} -o json"
406416
data = process.run(cmd, ignore_status=True, sudo=True, shell=True).stdout_text
407-
json_data = json.loads(data)
408-
for data in json_data:
409-
for subsystem in data["Subsystems"]:
410-
for namespace in subsystem["Namespaces"]:
411-
nsid = namespace["NSID"]
412-
for paths in namespace["Paths"]:
413-
if nsid == ns_id and paths["Name"] == controller_name:
414-
stat.extend([paths["State"], paths["ANAState"]])
417+
try:
418+
json_data = json.loads(data)
419+
except json.JSONDecodeError:
420+
json_data = None
421+
422+
if isinstance(json_data, list):
423+
for entry in json_data:
424+
for subsystem in entry.get("Subsystems", []):
425+
for namespace in subsystem.get("Namespaces", []):
426+
nsid = namespace.get("NSID")
427+
for path in namespace.get("Paths", []):
428+
if nsid == ns_id and path.get("Name") == controller_name:
429+
stat.extend([path["State"], path["ANAState"]])
430+
if stat:
431+
return stat
432+
433+
LOGGER.debug(
434+
"get_ns_status: primary show-topology for %s failed or returned no "
435+
"match; retrying with whole-system show-topology",
436+
controller_name,
437+
)
438+
cmd = "nvme show-topology -o json"
439+
data = process.run(cmd, ignore_status=True, sudo=True, shell=True).stdout_text
440+
try:
441+
json_data = json.loads(data)
442+
except json.JSONDecodeError:
443+
LOGGER.warning(
444+
"get_ns_status: could not parse whole-system show-topology output"
445+
)
446+
return stat
447+
448+
if isinstance(json_data, list):
449+
for entry in json_data:
450+
for subsystem in entry.get("Subsystems", []):
451+
for namespace in subsystem.get("Namespaces", []):
452+
if namespace.get("NSID") != ns_id:
453+
continue
454+
for path in namespace.get("Paths", []):
455+
for controller in path.get("Controller", []):
456+
if controller.get("Name") == controller_name:
457+
stat.extend(
458+
[controller["State"], path["ANAState"]]
459+
)
415460
return stat
416461

417462

0 commit comments

Comments
 (0)