Skip to content

[Bug] NoneType crash in inventory plugins when VM fields are explicitly None #939

@abhinavbansal29

Description

@abhinavbansal29

Describe the bug

All three inventory plugins crash with 'NoneType' object has no attribute 'get' when processing VMs (or hosts) that have fields like ownership_info, project, or cluster set to None in the API response.

The root cause is the use of dict.get("key", {}).get(...) throughout the inventory plugins. In Python, dict.get("key", {}) only returns the default {} when the key is missing from the dict. If the key exists but its value is explicitly None (as returned by the SDK's to_dict()), Python returns None — and the subsequent .get() call fails.

Affected files:

  • plugins/inventory/ntnx_prism_vm_inventory_v2.py (6 locations)
  • plugins/inventory/ntnx_prism_vm_inventory.py (8 locations)
  • plugins/inventory/ntnx_prism_host_inventory_v2.py (6 locations)

To Reproduce

  1. Create a VM with minimal configuration (no description, no project, no ownership):
- name: Create minimal VM
  nutanix.ncp.ntnx_vms:
    nutanix_host: "{{ nutanix_host }}"
    nutanix_username: "{{ nutanix_username }}"
    nutanix_password: "{{ nutanix_password }}"
    validate_certs: false
    state: present
    name: "test-vm-minimal"
    cluster:
      uuid: "{{ cluster_uuid }}"
    vcpus: 1
    cores_per_vcpu: 1
    memory_gb: 1
  1. Run the inventory plugin:
ansible-inventory -i nutanix.yml --graph -vvv
  1. The plugin crashes when iterating over VMs because the SDK returns fields like ownership_info: None, project: None in the VM dict.

Stack trace

[WARNING]: Failed to parse inventory with
  'ansible_collections.nutanix.ncp.plugins.inventory.ntnx_prism_vm_inventory_v2'
  plugin: 'NoneType' object has no attribute 'get'

Failed to parse inventory with
  'ansible_collections.nutanix.ncp.plugins.inventory.ntnx_prism_vm_inventory_v2' plugin.

<<< caused by >>>
'NoneType' object has no attribute 'get'

Crash locations

ntnx_prism_vm_inventory_v2.py

# line 621
cluster_ext_id = vm.get("cluster", {}).get("ext_id")  # crashes if cluster is None

# line 636-637
owner_ext_id = vm.get("ownership_info", {}).get("owner", {}).get("ext_id")  # crashes if ownership_info is None
project_ext_id = vm.get("project", {}).get("ext_id")  # crashes if project is None

# line 390-393 (_resolve_custom_ansible_host)
vm.get("cluster", {}).get("ext_id")  # crashes if cluster is None

# line 599 (parse)
for cluster in list_clusters.data:  # crashes if data is None

ntnx_prism_vm_inventory.py

# line 234-238
cluster = entity.get("status", {}).get("cluster_reference", {}).get("name")  # crashes if status is None
vm_resources = entity.get("status", {}).get("resources", {}).copy()  # crashes if status is None

# line 306-311
entity.get("metadata", {}).get("categories")  # crashes if metadata is None

# line 328-330
filter_vars.update(entity.get("status", {}))  # crashes if status is None

# line 437
entity.get("metadata", {}).get("project_reference", {})  # crashes if metadata is None

ntnx_prism_host_inventory_v2.py

# line 343-351
hypervisor = host.get("hypervisor", {})  # returns None if hypervisor key exists with None value
external_addr = hypervisor.get("external_address", {})  # crashes

controller_vm = host.get("controller_vm", {})  # same issue
external_addr = controller_vm.get("external_address", {})  # crashes

# line 507-508
cluster_ext_id = host.get("cluster", {}).get("uuid")  # crashes if cluster is None
cluster_name = host.get("cluster", {}).get("name")  # crashes if cluster is None

Expected behavior

The inventory plugins should gracefully handle None values for nested fields and continue processing VMs/hosts without crashing.

Fix

Replace all unsafe dict.get("key", {}).get(...) patterns with (dict.get("key") or {}).get(...). The or {} ensures None values are coerced to an empty dict before the chained .get() call.

Example:

# Before (crashes when ownership_info is None)
owner_ext_id = vm.get("ownership_info", {}).get("owner", {}).get("ext_id")

# After (safe)
owner_ext_id = ((vm.get("ownership_info") or {}).get("owner") or {}).get("ext_id")

Proof:

>>> vm = {"ownership_info": None, "project": None, "cluster": None}

>>> # OLD CODE - crashes
>>> vm.get("ownership_info", {}).get("owner", {}).get("ext_id")
AttributeError: 'NoneType' object has no attribute 'get'

>>> # NEW CODE - safe
>>> ((vm.get("ownership_info") or {}).get("owner") or {}).get("ext_id")
None  # returns None gracefully

Additional context

  • Affects nutanix.ncp collection version 2.4.0
  • Reproduced with ansible-core 2.20.0 (Python 3.13) and ansible-core 2.16.0 (Python 3.10)
  • The issue manifests when VMs are created with minimal configuration (common in automated/test environments) or when the PC returns None for optional nested fields
  • All 3 inventory plugins (ntnx_prism_vm_inventory, ntnx_prism_vm_inventory_v2, ntnx_prism_host_inventory_v2) are affected by the same pattern

Metadata

Metadata

Labels

bugSomething isn't workingv2.5.0

Type

Projects

Status

Release Ready

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions