From 29faf66f69dfe80757365b2e45e6e5e5331695ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mina=20Gali=C4=87?= Date: Thu, 16 Feb 2023 16:11:50 +0000 Subject: add function is_virtual to distro/FreeBSD (#1957) - is_virtual property identifies identify if the thing we're running is any kind of virtualization - virtual() identifies what kind of virtualisation we're dealing with - is_container() tells us if we're running in a container, or in FreeBSD's case, in a jail. - the helper functions are @lru_cached, since this is very unlikely to change Sponsored by: The FreeBSD Foundation Co-authored-by: Brett Holman --- cloudinit/distros/freebsd.py | 39 +++++++++++++++++++++++++++++- tests/unittests/distros/test__init__.py | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) 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): -- cgit v1.2.1