diff options
-rw-r--r-- | cloudinit/distros/freebsd.py | 39 | ||||
-rw-r--r-- | tests/unittests/distros/test__init__.py | 42 |
2 files changed, 80 insertions, 1 deletions
diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py index 4268abe6..706d0743 100644 --- a/cloudinit/distros/freebsd.py +++ b/cloudinit/distros/freebsd.py @@ -6,7 +6,9 @@ import os import re +from functools import lru_cache from io import StringIO +from typing import Optional import cloudinit.distros.bsd from cloudinit import log as logging @@ -192,5 +194,40 @@ class Distro(cloudinit.distros.bsd.BSD): freq=PER_INSTANCE, ) + @lru_cache() + def is_container(self) -> bool: + """return whether we're running in a container. + Cached, because it's unlikely to change.""" + jailed, _ = subp.subp(["sysctl", "-n", "security.jail.jailed"]) + if jailed.strip() == "0": + return False + return True + + @lru_cache() + def virtual(self) -> str: + """return the kind of virtualisation system we're running under. + Cached, because it's unlikely to change.""" + if self.is_container(): + return "jail" + # map FreeBSD's kern.vm_guest to systemd-detect-virt, just like we do + # in ds-identify + VM_GUEST_TO_SYSTEMD = { + "hv": "microsoft", + "vbox": "oracle", + "generic": "vm-other", + } + vm, _ = subp.subp(["sysctl", "-n", "kern.vm_guest"]) + vm = vm.strip() + if vm in VM_GUEST_TO_SYSTEMD: + return VM_GUEST_TO_SYSTEMD[vm] + return vm + + @property + def is_virtual(self) -> Optional[bool]: + """Detect if running on a virtual machine or bare metal. -# vi: ts=4 expandtab + This can fail on some platforms, so the signature is Optional[bool] + """ + if self.virtual() == "none": + return False + return True diff --git a/tests/unittests/distros/test__init__.py b/tests/unittests/distros/test__init__.py index 4201c687..ea017d58 100644 --- a/tests/unittests/distros/test__init__.py +++ b/tests/unittests/distros/test__init__.py @@ -275,6 +275,48 @@ class TestGenericDistro(helpers.FilesystemMockingTestCase): d.is_virtual, "Reflect unknown state on ProcessExecutionError" ) + def test_virtualization_on_freebsd(self): + # This test function is a bit unusual: + # We need to first mock away the `ifconfig -a` subp call + # Then, we can use side-effects to get the results of two subp calls + # needed for is_container()/virtual() which is_virtual depends on. + # We also have to clear cache between each of those assertions. + + cls = distros.fetch("freebsd") + with mock.patch( + "cloudinit.distros.subp.subp", return_value=("", None) + ): + d = cls("freebsd", {}, None) + # This mock is called by `sysctl -n security.jail.jailed` + with mock.patch( + "cloudinit.distros.subp.subp", + side_effect=[("0\n", None), ("literaly any truthy value", None)], + ): + self.assertFalse(d.is_container()) + d.is_container.cache_clear() + self.assertTrue(d.is_container()) + d.is_container.cache_clear() + + # This mock is called by `sysctl -n kern.vm_guest` + with mock.patch( + "cloudinit.distros.subp.subp", + # fmt: off + side_effect=[ + ("0\n", None), ("hv\n", None), # virtual + ("0\n", None), ("none\n", None), # physical + ("0\n", None), ("hv\n", None) # virtual + ], + # fmt: on + ): + self.assertEqual(d.virtual(), "microsoft") + d.is_container.cache_clear() + d.virtual.cache_clear() + self.assertEqual(d.virtual(), "none") + d.is_container.cache_clear() + d.virtual.cache_clear() + + self.assertTrue(d.is_virtual) + class TestGetPackageMirrors: def return_first(self, mlist): |