summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMina Galić <freebsd@igalic.co>2023-02-16 16:11:50 +0000
committerGitHub <noreply@github.com>2023-02-16 09:11:50 -0700
commit29faf66f69dfe80757365b2e45e6e5e5331695ce (patch)
tree539fc22c4440458c465579a1f507508dd4faf148
parente4f56efce3e04e128c065ae15762b7ef5c02bcc9 (diff)
downloadcloud-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.py39
-rw-r--r--tests/unittests/distros/test__init__.py42
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):