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
1 change: 1 addition & 0 deletions changelog/69036.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added UUID detection for Xen paravirtualized guests
33 changes: 29 additions & 4 deletions salt/grains/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3259,6 +3259,26 @@ def _hw_data(osdata):
return {}

grains = {}

# For Xen para-virtualized guests read UUID from /sys/hypervisor/uuid.
# This file also exists on Xen Dom0 but contains all-zeros; skip that
# sentinel value so the real DMI/smbios UUID is used on Dom0 hosts.
if osdata["kernel"] == "Linux" and os.path.exists("/sys/hypervisor/uuid"):
try:
with salt.utils.files.fopen("/sys/hypervisor/uuid", "rb") as ifile:
hypervisor_uuid = salt.utils.stringutils.to_unicode(
ifile.read().strip(), errors="replace"
).lower()
# All-zero UUID is the Dom0 sentinel; ignore it.
if hypervisor_uuid and hypervisor_uuid.strip("0-"):
grains["uuid"] = hypervisor_uuid
log.debug(
"Read UUID from /sys/hypervisor/uuid for para-virtualized guest: %s",
grains["uuid"],
)
except OSError as err:
log.debug("Unable to read /sys/hypervisor/uuid: %s", err)

if osdata["kernel"] == "Linux" and os.path.exists("/sys/class/dmi/id"):
# On many Linux distributions basic firmware information is available via sysfs
# requires CONFIG_DMIID to be enabled in the Linux kernel configuration
Expand All @@ -3273,6 +3293,9 @@ def _hw_data(osdata):
"serialnumber": "product_serial",
}
for key, fw_file in sysfs_firmware_info.items():
# Skip UUID if already read from /sys/hypervisor/uuid (Xen PV guests)
if key == "uuid" and "uuid" in grains:
continue
contents_file = os.path.join("/sys/class/dmi/id", fw_file)
if os.path.exists(contents_file):
try:
Expand Down Expand Up @@ -3303,18 +3326,20 @@ def _hw_data(osdata):
):
# On SmartOS (possibly SunOS also) smbios only works in the global zone
# smbios is also not compatible with linux's smbios (smbios -s = print summarized)
uuid = __salt__["smbios.get"]("system-uuid")
if uuid is not None:
uuid = uuid.lower()
else:
uuid = grains.get("uuid")
grains = {
"biosversion": __salt__["smbios.get"]("bios-version"),
"biosvendor": __salt__["smbios.get"]("bios-vendor"),
"productname": __salt__["smbios.get"]("system-product-name"),
"manufacturer": __salt__["smbios.get"]("system-manufacturer"),
"biosreleasedate": __salt__["smbios.get"]("bios-release-date"),
"uuid": __salt__["smbios.get"]("system-uuid"),
"uuid": uuid,
}
grains = {key: val for key, val in grains.items() if val is not None}
uuid = __salt__["smbios.get"]("system-uuid")
if uuid is not None:
grains["uuid"] = uuid.lower()
for serial in (
"system-serial-number",
"chassis-serial-number",
Expand Down
44 changes: 44 additions & 0 deletions tests/pytests/unit/grains/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3560,6 +3560,50 @@ def read(self):
assert core._hw_data({"kernel": "Linux"}) == {}


@pytest.mark.skip_unless_on_linux
def test__hw_data_xen_pv_uuid():
hypervisor_uuid = b"12345678-1234-1234-1234-123456789ABC"
expected_uuid = "12345678-1234-1234-1234-123456789abc"

def _exists_side_effect(path):
if path == "/sys/hypervisor/uuid":
return True
return False

with patch("os.path.exists", side_effect=_exists_side_effect), patch(
"salt.utils.platform.is_proxy", return_value=False
), patch("salt.utils.path.which_bin", return_value=None), patch(
"salt.utils.files.fopen",
mock_open(read_data=hypervisor_uuid),
):
result = core._hw_data({"kernel": "Linux"})
assert result.get("uuid") == expected_uuid


@pytest.mark.skip_unless_on_linux
def test__hw_data_xen_dom0_uuid_ignored():
"""
On Xen Dom0, /sys/hypervisor/uuid contains an all-zero sentinel value.
The grain should not be set from that value so the real DMI UUID can
be used instead.
"""
dom0_uuid = b"00000000-0000-0000-0000-000000000000"

def _exists_side_effect(path):
if path == "/sys/hypervisor/uuid":
return True
return False

with patch("os.path.exists", side_effect=_exists_side_effect), patch(
"salt.utils.platform.is_proxy", return_value=False
), patch("salt.utils.path.which_bin", return_value=None), patch(
"salt.utils.files.fopen",
mock_open(read_data=dom0_uuid),
):
result = core._hw_data({"kernel": "Linux"})
assert result.get("uuid") is None


@pytest.mark.skip_unless_on_windows
def test_kernelparams_return_windows():
"""
Expand Down
Loading