From e3307e4d8cd7ccf352c7662783b472545e8ecc56 Mon Sep 17 00:00:00 2001
From: Chad Smith <chad.smith@canonical.com>
Date: Wed, 6 Apr 2022 15:15:21 -0600
Subject: [PATCH] ds-identify: detect LXD for VMs launched from host with >
 5.10 kernel (#1370)

Launching KVM instances from a host system with > 5.10 kernel results
in LXC passing `hv_passthrough` to the kvm instance being launched.
Systemd < 251 will incorrectly detect the CPU in this case as
"qemu" instead of "kvm".

ds-identify needs to properly interpret systems with
sytemd-detect-virt="qemu"  and /sys/class/dmi/id/board_name="LXD"

This functionality can be dropped once systemd 251 support is
available on all supported distributions.

LP: #1968085
---
 tests/unittests/test_ds_identify.py | 25 +++++++++++++++++++++++++
 tools/ds-identify                   |  6 +++++-
 2 files changed, 30 insertions(+), 1 deletion(-)

--- a/tests/unittests/test_ds_identify.py
+++ b/tests/unittests/test_ds_identify.py
@@ -95,6 +95,10 @@ MOCK_VIRT_IS_CONTAINER_OTHER = {
 }
 MOCK_NOT_LXD_DATASOURCE = {"name": "dscheck_LXD", "ret": 1}
 MOCK_VIRT_IS_KVM = {"name": "detect_virt", "RET": "kvm", "ret": 0}
+# qemu support for LXD is only for host systems > 5.10 kernel as lxd
+# passed `hv_passthrough` which causes systemd < v.251 to misinterpret CPU
+# as "qemu" instead of "kvm"
+MOCK_VIRT_IS_KVM_QEMU = {"name": "detect_virt", "RET": "qemu", "ret": 0}
 MOCK_VIRT_IS_VMWARE = {"name": "detect_virt", "RET": "vmware", "ret": 0}
 # currenty' SmartOS hypervisor "bhyve" is unknown by systemd-detect-virt.
 MOCK_VIRT_IS_VM_OTHER = {"name": "detect_virt", "RET": "vm-other", "ret": 0}
@@ -338,6 +342,20 @@ class TestDsIdentify(DsIdentifyBase):
         """LXD KVM has race on absent /dev/lxd/socket. Use DMI board_name."""
         self._test_ds_found("LXD-kvm")
 
+    def test_lxd_kvm_jammy(self):
+        """LXD KVM on host systems with a kernel > 5.10 need to match "qemu".
+        LXD provides `hv_passthrough` when launching kvm instances when host
+        kernel is > 5.10. This results in systemd being unable to detect the
+        virtualized CPUID="Linux KVM Hv" as type "kvm" and results in
+        systemd-detect-virt returning "qemu" in this case.
+
+        Assert ds-identify can match systemd-detect-virt="qemu" and
+        /sys/class/dmi/id/board_name = LXD.
+        Once systemd 251 is available on a target distro, the virtualized
+        CPUID will be represented properly as "kvm"
+        """
+        self._test_ds_found("LXD-kvm-qemu-kernel-gt-5.10")
+
     def test_lxd_containers(self):
         """LXD containers will have /dev/lxd/socket at generator time."""
         self._test_ds_found("LXD")
@@ -1031,6 +1049,13 @@ VALID_CFG = {
         "mocks": [{"name": "is_socket_file", "ret": 1}, MOCK_VIRT_IS_KVM],
         "no_mocks": ["dscheck_LXD"],  # Don't default mock dscheck_LXD
     },
+    "LXD-kvm-qemu-kernel-gt-5.10": {  # LXD host > 5.10 kvm launch virt==qemu
+        "ds": "LXD",
+        "files": {P_BOARD_NAME: "LXD\n"},
+        # /dev/lxd/sock does not exist and KVM virt-type
+        "mocks": [{"name": "is_socket_file", "ret": 1}, MOCK_VIRT_IS_KVM_QEMU],
+        "no_mocks": ["dscheck_LXD"],  # Don't default mock dscheck_LXD
+    },
     "LXD": {
         "ds": "LXD",
         # /dev/lxd/sock exists
--- a/tools/ds-identify
+++ b/tools/ds-identify
@@ -823,7 +823,11 @@ dscheck_LXD() {
     # On LXD KVM instances, /dev/lxd/sock is not yet setup by
     # lxd-agent-loader's systemd lxd-agent.service.
     # Rely on DMI product information that is present on all LXD images.
-    if [ "${DI_VIRT}" = "kvm" ]; then
+    # Note "qemu" is returned on kvm instances launched from a host kernel
+    # kernels >=5.10, due to `hv_passthrough` option.
+    # systemd v. 251 should properly return "kvm" in this scenario
+    # https://github.com/systemd/systemd/issues/22709
+    if [ "${DI_VIRT}" = "kvm" -o "${DI_VIRT}" = "qemu" ]; then
         [ "${DI_DMI_BOARD_NAME}" = "LXD" ] && return ${DS_FOUND}
     fi
     return ${DS_NOT_FOUND}
