Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix virtual grains for VMs running on Nutanix AHV (bsc#1234022) #697

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
1 change: 1 addition & 0 deletions changelog/67180.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix virtual grains for VMs running on Nutanix AHV
14 changes: 14 additions & 0 deletions salt/grains/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,9 @@ def _windows_virtual(osdata):
# Manufacturer: Parallels Software International Inc.
elif "Parallels" in manufacturer:
grains["virtual"] = "Parallels"
elif "Nutanix" in manufacturer and "AHV" in product_name:
grains["virtual"] = "kvm"
grains["virtual_subtype"] = "Nutanix AHV"
# Apache CloudStack
elif "CloudStack KVM Hypervisor" in productname:
grains["virtual"] = "kvm"
Expand Down Expand Up @@ -927,6 +930,10 @@ def _virtual(osdata):
elif "parallels" in line:
grains["virtual"] = "Parallels"
break
elif "nutanix" in line:
grains["virtual"] = "kvm"
grains["virtual_subtype"] = "Nutanix AHV"
break
elif "hyperv" in line:
grains["virtual"] = "HyperV"
break
Expand Down Expand Up @@ -978,6 +985,9 @@ def _virtual(osdata):
grains["virtual"] = "Parallels"
elif "Manufacturer: Google" in output:
grains["virtual"] = "kvm"
elif "Manufacturer: Nutanix" in output and "Product Name: AHV" in output:
grains["virtual"] = "kvm"
grains["virtual_subtype"] = "Nutanix AHV"
# Proxmox KVM
elif "Vendor: SeaBIOS" in output:
grains["virtual"] = "kvm"
Expand Down Expand Up @@ -1234,6 +1244,7 @@ def _virtual(osdata):
grains["virtual"] = "virtual"

# Try to detect if the instance is running on Amazon EC2
# or Nutanix AHV
if grains["virtual"] in ("qemu", "kvm", "xen", "amazon"):
dmidecode = salt.utils.path.which("dmidecode")
if dmidecode:
Expand All @@ -1253,6 +1264,9 @@ def _virtual(osdata):
elif re.match(r".*Version: [^\r\n]+\.amazon.*", output, flags=re.DOTALL):
grains["virtual_subtype"] = "Amazon EC2"

elif "Manufacturer: Nutanix" in output and "Product Name: AHV" in output:
grains["virtual_subtype"] = "Nutanix AHV"

for command in failed_commands:
log.info(
"Although '%s' was found in path, the current user "
Expand Down
184 changes: 184 additions & 0 deletions tests/pytests/unit/grains/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2770,6 +2770,10 @@ def test_virtual_has_virtual_grain():
{"kernel": "Windows", "manufacturer": "Parallels Software"},
{"virtual": "Parallels"},
),
(
{"kernel": "Windows", "manufacturer": "Nutanix", "product_name": "AHV"},
{"virtual": "kvm", "virtual_subtype": "Nutanix AHV"},
),
],
)
def test__windows_virtual(osdata, expected):
Expand Down Expand Up @@ -3453,6 +3457,186 @@ def _mock_open(filename, *args, **kwargs):
assert virt_grains == {"virtual": "physical"}


@pytest.mark.skip_unless_on_linux
def test_virtual_nutanix_virt_what():
osdata = {}

(
osdata["kernel"],
osdata["nodename"],
osdata["kernelrelease"],
osdata["kernelversion"],
osdata["cpuarch"],
_,
) = platform.uname()

which_mock = MagicMock(
side_effect=[
# Check with virt-what
"/usr/sbin/virt-what",
"/usr/sbin/virt-what",
None,
"/usr/sbin/dmidecode",
]
)
cmd_run_all_mock = MagicMock(
side_effect=[
# Check with virt-what
{"retcode": 0, "stderr": "", "stdout": "nutanix_ahv"},
{
"retcode": 0,
"stderr": "",
"stdout": "\n".join(
[
"dmidecode 3.4",
"Getting SMBIOS data from sysfs.",
"SMBIOS 2.8 present.",
"",
"Handle 0x0001, DMI type 1, 27 bytes",
"System Information",
" Manufacturer: Nutanix",
" Product Name: AHV",
" Version: Not Specified",
" Serial Number: 01234567-dcba-1234-abcd-abcdef012345",
" UUID: 12345678-abcd-4321-dcba-0123456789ab",
" Wake-up Type: Power Switch",
" SKU Number: Not Specified",
" Family: Not Specified",
"",
"Handle 0x2000, DMI type 32, 11 bytes",
"System Boot Information",
" Status: No errors detected",
]
),
},
]
)

with patch("salt.utils.path.which", which_mock), patch.dict(
core.__salt__,
{
"cmd.run": salt.modules.cmdmod.run,
"cmd.run_all": cmd_run_all_mock,
"cmd.retcode": salt.modules.cmdmod.retcode,
"smbios.get": salt.modules.smbios.get,
},
):

virtual_grains = core._virtual(osdata.copy())

assert virtual_grains["virtual"] == "kvm"
assert virtual_grains["virtual_subtype"] == "Nutanix AHV"


@pytest.mark.skip_unless_on_linux
def test_virtual_nutanix_dmidecode():
osdata = {}

(
osdata["kernel"],
osdata["nodename"],
osdata["kernelrelease"],
osdata["kernelversion"],
osdata["cpuarch"],
_,
) = platform.uname()

which_mock = MagicMock(
side_effect=[
# Check with virt-what
None,
None,
None,
"/usr/sbin/dmidecode",
None,
"/usr/sbin/dmidecode",
]
)
cmd_run_all_mock = MagicMock(
side_effect=[
{
"retcode": 0,
"stderr": "",
"stdout": "\n".join(
[
"dmidecode 3.4",
"Getting SMBIOS data from sysfs.",
"SMBIOS 2.8 present.",
"",
"Handle 0x0001, DMI type 1, 27 bytes",
"System Information",
" Manufacturer: Nutanix",
" Product Name: AHV",
" Version: Not Specified",
" Serial Number: 01234567-dcba-1234-abcd-abcdef012345",
" UUID: 12345678-abcd-4321-dcba-0123456789ab",
" Wake-up Type: Power Switch",
" SKU Number: Not Specified",
" Family: Not Specified",
"",
"Handle 0x2000, DMI type 32, 11 bytes",
"System Boot Information",
" Status: No errors detected",
]
),
},
{
"retcode": 0,
"stderr": "",
"stdout": "\n".join(
[
"dmidecode 3.4",
"Getting SMBIOS data from sysfs.",
"SMBIOS 2.8 present.",
"",
"Handle 0x0001, DMI type 1, 27 bytes",
"System Information",
" Manufacturer: Nutanix",
" Product Name: AHV",
" Version: Not Specified",
" Serial Number: 01234567-dcba-1234-abcd-abcdef012345",
" UUID: 12345678-abcd-4321-dcba-0123456789ab",
" Wake-up Type: Power Switch",
" SKU Number: Not Specified",
" Family: Not Specified",
"",
"Handle 0x2000, DMI type 32, 11 bytes",
"System Boot Information",
" Status: No errors detected",
]
),
},
]
)

def _mock_is_file(filename):
if filename in (
"/proc/1/cgroup",
"/proc/cpuinfo",
"/sys/devices/virtual/dmi/id/product_name",
"/proc/xen/xsd_kva",
"/proc/xen/capabilities",
):
return False
return True

with patch("salt.utils.path.which", which_mock), patch.dict(
core.__salt__,
{
"cmd.run": salt.modules.cmdmod.run,
"cmd.run_all": cmd_run_all_mock,
"cmd.retcode": salt.modules.cmdmod.retcode,
"smbios.get": salt.modules.smbios.get,
},
), patch("os.path.isfile", _mock_is_file), patch(
"os.path.isdir", return_value=False
):
virtual_grains = core._virtual(osdata.copy())

assert virtual_grains["virtual"] == "kvm"
assert virtual_grains["virtual_subtype"] == "Nutanix AHV"


@pytest.mark.skip_unless_on_linux
def test_virtual_set_virtual_ec2():
osdata = {}
Expand Down
Loading