diff options
author | Mina Galić <freebsd@igalic.co> | 2023-02-16 16:11:50 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-16 09:11:50 -0700 |
commit | 29faf66f69dfe80757365b2e45e6e5e5331695ce (patch) | |
tree | 539fc22c4440458c465579a1f507508dd4faf148 | |
parent | e4f56efce3e04e128c065ae15762b7ef5c02bcc9 (diff) | |
download | cloud-init-git-29faf66f69dfe80757365b2e45e6e5e5331695ce.tar.gz |
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 <brett.holman@canonical.com>
-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): |