Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __call__(self, task, inventory, _plugin_data):
node = task.node
sys_data = inventory.get("system_vendor", {})

serial_number = sys_data.get("sku", sys_data.get("serial_number"))
serial_number = sys_data.get("serial_number")
if serial_number is None:
raise exception.InvalidNodeInventory(
node=node.uuid, reason="No serial number found in inventory data."
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import pytest
from ironic.common import exception
from oslo_utils import uuidutils

from ironic_understack.inspect_hook_node_name_check import InspectHookNodeNameCheck
from ironic_understack.inspect_hook_node_name_check import _manufacturer_slug


def _make_task(mocker, node_name):
node = mocker.Mock(id=1234, uuid=uuidutils.generate_uuid())
node.name = node_name
return mocker.Mock(node=node)


class TestInspectHookNodeNameCheck:
def test_matching_name_passes(self, mocker):
task = _make_task(mocker, "Dell Inc._SN123")
inventory = {
"system_vendor": {
"serial_number": "SN123",
"manufacturer": "Dell Inc.",
}
}

# Should not raise
InspectHookNodeNameCheck()(task, inventory, {})

def test_mismatched_name_raises(self, mocker):
task = _make_task(mocker, "Wrong-Name")
inventory = {
"system_vendor": {
"serial_number": "SN123",
"manufacturer": "Dell Inc.",
}
}

with pytest.raises(RuntimeError, match="Hardware Identity Crisis"):
InspectHookNodeNameCheck()(task, inventory, {})

def test_missing_serial_number_raises(self, mocker):
task = _make_task(mocker, "Dell-SN123")
inventory = {
"system_vendor": {
"manufacturer": "Dell Inc.",
}
}

with pytest.raises(exception.InvalidNodeInventory, match="No serial number"):
InspectHookNodeNameCheck()(task, inventory, {})

def test_missing_manufacturer_raises(self, mocker):
task = _make_task(mocker, "Dell-SN123")
inventory = {
"system_vendor": {
"serial_number": "SN123",
}
}

with pytest.raises(exception.InvalidNodeInventory, match="No manufacturer"):
InspectHookNodeNameCheck()(task, inventory, {})

def test_sku_field_is_not_used_for_serial(self, mocker):
"""serial_number comes only from serial_number, not sku."""
task = _make_task(mocker, "Dell-SKU999")
inventory = {
"system_vendor": {
"sku": "SKU999",
"manufacturer": "Dell Inc.",
}
}

with pytest.raises(exception.InvalidNodeInventory, match="No serial number"):
InspectHookNodeNameCheck()(task, inventory, {})

def test_empty_system_vendor(self, mocker):
task = _make_task(mocker, "Dell-SN123")
inventory = {"system_vendor": {}}

with pytest.raises(exception.InvalidNodeInventory, match="No serial number"):
InspectHookNodeNameCheck()(task, inventory, {})


class TestManufacturerSlug:
def test_dell(self):
assert _manufacturer_slug("Dell Inc.") == "Dell"

def test_dell_uppercase(self):
assert _manufacturer_slug("DELL") == "Dell"

def test_hp(self):
assert _manufacturer_slug("HPE") == "HP"

def test_hp_lowercase(self):
assert _manufacturer_slug("hp") == "HP"

def test_other_replaces_spaces(self):
assert _manufacturer_slug("Super Micro") == "Super_Micro"
11 changes: 4 additions & 7 deletions python/understack-workflows/tests/test_nautobot_device_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ def test_populate_from_inventory_full(self, device_info):

assert device_info.manufacturer == "Dell"
assert device_info.model == "PowerEdge R7615"
assert device_info.service_tag == "ABC1234"
assert device_info.serial_number == "SN123456"

def test_populate_from_inventory_agent_format(self, device_info):
Expand All @@ -155,14 +154,12 @@ def test_populate_from_inventory_agent_format(self, device_info):

_populate_from_inventory(device_info, inventory)

assert device_info.service_tag == "SERVICETAG123"
assert device_info.serial_number is None # Only set when sku exists
assert device_info.serial_number == "SERVICETAG123"

def test_populate_from_inventory_empty(self, device_info):
_populate_from_inventory(device_info, None)

assert device_info.model is None
assert device_info.service_tag is None

def test_populate_from_inventory_system_product_name(self, device_info):
"""Test that 'System' product name is ignored."""
Expand Down Expand Up @@ -202,7 +199,7 @@ def test_generate_name_with_both_fields(self):
device_info = DeviceInfo(
uuid="test-uuid",
manufacturer="Dell",
service_tag="ABC1234",
serial_number="ABC1234",
)

_generate_device_name(device_info)
Expand All @@ -212,14 +209,14 @@ def test_generate_name_with_both_fields(self):
def test_generate_name_missing_manufacturer(self):
device_info = DeviceInfo(
uuid="test-uuid",
service_tag="ABC1234",
serial_number="ABC1234",
)

_generate_device_name(device_info)

assert device_info.name is None

def test_generate_name_missing_service_tag(self):
def test_generate_name_missing_serial_number(self):
device_info = DeviceInfo(
uuid="test-uuid",
manufacturer="Dell",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ class DeviceInfo:
# Identity
name: str | None = None
serial_number: str | None = None
service_tag: str | None = None

# Hardware
manufacturer: str | None = None
Expand Down Expand Up @@ -170,20 +169,14 @@ def _populate_from_inventory(device_info: DeviceInfo, inventory: dict | None) ->
if product_name and product_name != "System":
device_info.model = re.sub(r" \(.*\)", "", str(product_name))

# Service tag: sku (REDFISH) or serial_number (AGENT)
service_tag = system_vendor.get("sku") or system_vendor.get("serial_number")
if service_tag:
device_info.service_tag = service_tag

# Serial number: only if sku exists (REDFISH has both)
if system_vendor.get("sku"):
if system_vendor.get("serial_number"):
device_info.serial_number = system_vendor.get("serial_number")


def _generate_device_name(device_info: DeviceInfo) -> None:
"""Generate device name from manufacturer and service tag."""
if device_info.manufacturer and device_info.service_tag:
device_info.name = f"{device_info.manufacturer}-{device_info.service_tag}"
"""Generate device name from manufacturer and serial number."""
if device_info.manufacturer and device_info.serial_number:
device_info.name = f"{device_info.manufacturer}-{device_info.serial_number}"


def _set_location_from_switches(
Expand Down
Loading