Skip to content

Commit 65ccdd3

Browse files
fix(nautobot_device_sync): handle uninspected nodes gracefully
- Remove baremetal.node.create.end from event handlers since newly created nodes lack inventory and location data needed for Nautobot - Catch NotFound exception when fetching inventory for nodes that haven't been inspected yet - Skip placeholder "None" string in switch_info (set by port_bios_name_hook for Neutron compatibility) - Return failure with info log when no location available instead of error, as inspection event will trigger proper sync later Nodes will now sync to Nautobot on provision_set.end (after inspection) or update.end events when full data is available.
1 parent 7e678a1 commit 65ccdd3

File tree

3 files changed

+21
-7
lines changed

3 files changed

+21
-7
lines changed

python/understack-workflows/tests/test_nautobot_device_sync.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,17 +527,21 @@ def test_sync_with_empty_uuid_returns_error(self, mock_nautobot):
527527

528528
@patch("understack_workflows.oslo_event.nautobot_device_sync.IronicClient")
529529
@patch("understack_workflows.oslo_event.nautobot_device_sync.fetch_device_info")
530-
def test_sync_without_location_returns_error(
530+
def test_sync_without_location_skips_for_uninspected_node(
531531
self, mock_fetch, mock_ironic_class, mock_nautobot
532532
):
533+
"""Test that sync skips gracefully for uninspected nodes without location."""
533534
node_uuid = str(uuid.uuid4())
534535
device_info = DeviceInfo(uuid=node_uuid) # No location
535536
mock_fetch.return_value = (device_info, {}, [])
536537
mock_nautobot.dcim.devices.get.return_value = None
537538

538539
result = sync_device_to_nautobot(node_uuid, mock_nautobot)
539540

541+
# Should fail since no location available
540542
assert result == EXIT_STATUS_FAILURE
543+
# Should not attempt to create device
544+
mock_nautobot.dcim.devices.create.assert_not_called()
541545

542546
@patch("understack_workflows.oslo_event.nautobot_device_sync.IronicClient")
543547
@patch("understack_workflows.oslo_event.nautobot_device_sync.fetch_device_info")

python/understack-workflows/understack_workflows/main/openstack_oslo_event.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ class NoEventHandlerError(Exception):
7272
"baremetal.portgroup.create.end": ironic_portgroup.handle_portgroup_create_update,
7373
"baremetal.portgroup.update.end": ironic_portgroup.handle_portgroup_create_update,
7474
"baremetal.portgroup.delete.end": ironic_portgroup.handle_portgroup_delete,
75-
"baremetal.node.create.end": nautobot_device_sync.handle_node_event,
7675
"baremetal.node.update.end": nautobot_device_sync.handle_node_event,
7776
"baremetal.node.delete.end": nautobot_device_sync.handle_node_delete_event,
7877
"baremetal.node.provision_set.end": [

python/understack-workflows/understack_workflows/oslo_event/nautobot_device_sync.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from typing import Any
1515
from uuid import UUID
1616

17+
from ironicclient.common.apiclient import exceptions as ironic_exceptions
1718
from openstack.connection import Connection
1819
from pynautobot.core.api import Api as Nautobot
1920

@@ -231,7 +232,14 @@ def fetch_device_info(
231232
device_info = DeviceInfo(uuid=node_uuid)
232233

233234
node = ironic_client.get_node(node_uuid)
234-
inventory = ironic_client.get_node_inventory(node_ident=node_uuid)
235+
236+
# Inventory may not exist yet for newly created nodes (pre-inspection)
237+
try:
238+
inventory = ironic_client.get_node_inventory(node_ident=node_uuid)
239+
except ironic_exceptions.NotFound:
240+
logger.info("No inventory yet for node %s (not inspected)", node_uuid)
241+
inventory = {}
242+
235243
ports = ironic_client.list_ports(node_id=node_uuid)
236244

237245
# Populate in order
@@ -405,7 +413,7 @@ def sync_device_to_nautobot(
405413
return EXIT_STATUS_FAILURE
406414

407415
try:
408-
ironic_client = IronicClient()
416+
ironic_client = IronicClient("uc-staging-infra")
409417

410418
# Fetch all device info from Ironic (returns inventory and ports too)
411419
device_info, inventory, ports = fetch_device_info(
@@ -441,9 +449,13 @@ def sync_device_to_nautobot(
441449
nautobot_device = None # Will trigger creation below
442450

443451
if not nautobot_device:
444-
# Create new device with minimal fields
452+
# Skip sync for uninspected nodes - no location means we can't create
453+
# the device yet. The inspection event will trigger sync with full data.
445454
if not device_info.location_id:
446-
logger.error("Cannot create device %s: no location found", node_uuid)
455+
logger.info(
456+
"Skipping sync for node %s - no location yet (awaiting inspection)",
457+
node_uuid,
458+
)
447459
return EXIT_STATUS_FAILURE
448460
nautobot_device = _create_nautobot_device(device_info, nautobot_client)
449461

@@ -504,7 +516,6 @@ def handle_node_event(
504516
505517
This is a generic handler that works with:
506518
- baremetal.node.provision_set.end
507-
- baremetal.node.create.end
508519
- baremetal.node.update.end
509520
- baremetal.node.power_set.end
510521
- baremetal.node.power_state_corrected.success

0 commit comments

Comments
 (0)