Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions ceph_devstack/resources/ceph/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,19 +218,7 @@ def create_cmd(self):
"/dev/fuse:/dev/fuse",
"-v",
"/dev/disk:/dev/disk",
# cephadm tries to access these DMI-related files, and by default they
# have 600 permissions on the host. It appears to be ok if they are
# empty, though.
# The below was bizarrely causing this error message:
# No such file or directory: OCI runtime attempted to invoke a command that was
# not found
# That was causing the container to fail to start up.
"-v",
"/dev/null:/sys/class/dmi/id/board_serial",
"-v",
"/dev/null:/sys/class/dmi/id/chassis_serial",
"-v",
"/dev/null:/sys/class/dmi/id/product_serial",
*self._get_available_dmi_mounts(),
*self.additional_volumes,
"--device",
"/dev/net/tun",
Expand Down Expand Up @@ -329,6 +317,26 @@ def device_name(self, index: int):
def device_image(self, device: str):
return f"{self.name}-{device.removeprefix('/dev/loop')}"

def _get_available_dmi_mounts(self) -> List[str]:
"""Return volume mount args for DMI files that exist on the host.

cephadm tries to access DMI-related files, and by default they have 600
permissions on the host. We mount /dev/null to make them accessible but empty.
However, in nested virtualization these files may not exist at all, so we
only mount them if they're present on the host.
"""
dmi_files = [
"board_serial",
"chassis_serial",
"product_serial",
]
mounts = []
for dmi_file in dmi_files:
dmi_path = f"/sys/class/dmi/id/{dmi_file}"
if host.path_exists(dmi_path):
mounts.extend(["-v", f"/dev/null:{dmi_path}"])
return mounts


class Teuthology(Container):
cmd_vars: List[str] = ["name", "image", "image_tag", "archive_dir"]
Expand Down
64 changes: 64 additions & 0 deletions tests/resources/ceph/test_testnode.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pathlib import Path
from unittest.mock import patch

import pytest

Expand Down Expand Up @@ -35,3 +36,66 @@ def test_testnode_devices_is_based_on_loop_device_count_config(self, cls):
"/dev/loop6",
"/dev/loop7",
]

def test_get_available_dmi_mounts_with_all_files_present(self, cls):
testnode = cls("testnode_1")
with patch(
"ceph_devstack.resources.ceph.containers.host.path_exists"
) as mock_exists:
mock_exists.return_value = True
mounts = testnode._get_available_dmi_mounts()
assert mounts == [
"-v",
"/dev/null:/sys/class/dmi/id/board_serial",
"-v",
"/dev/null:/sys/class/dmi/id/chassis_serial",
"-v",
"/dev/null:/sys/class/dmi/id/product_serial",
]

def test_get_available_dmi_mounts_with_no_files_present(self, cls):
testnode = cls("testnode_1")
with patch(
"ceph_devstack.resources.ceph.containers.host.path_exists"
) as mock_exists:
mock_exists.return_value = False
mounts = testnode._get_available_dmi_mounts()
assert mounts == []

def test_get_available_dmi_mounts_with_partial_files_present(self, cls):
testnode = cls("testnode_1")
with patch(
"ceph_devstack.resources.ceph.containers.host.path_exists"
) as mock_exists:
# Only board_serial exists
mock_exists.side_effect = lambda path: "board_serial" in path
mounts = testnode._get_available_dmi_mounts()
assert mounts == [
"-v",
"/dev/null:/sys/class/dmi/id/board_serial",
]

def test_create_cmd_includes_dmi_mounts_when_available(self, cls):
config.load(Path(__file__).parent.joinpath("fixtures", "testnode-config.toml"))
testnode = cls("testnode_1")
with patch(
"ceph_devstack.resources.ceph.containers.host.path_exists"
) as mock_exists:
mock_exists.return_value = True
create_cmd = testnode.create_cmd
assert "/dev/null:/sys/class/dmi/id/board_serial" in " ".join(create_cmd)
assert "/dev/null:/sys/class/dmi/id/chassis_serial" in " ".join(create_cmd)
assert "/dev/null:/sys/class/dmi/id/product_serial" in " ".join(create_cmd)

def test_create_cmd_excludes_dmi_mounts_when_unavailable(self, cls):
config.load(Path(__file__).parent.joinpath("fixtures", "testnode-config.toml"))
testnode = cls("testnode_1")
with patch(
"ceph_devstack.resources.ceph.containers.host.path_exists"
) as mock_exists:
mock_exists.return_value = False
create_cmd = testnode.create_cmd
cmd_str = " ".join(create_cmd)
assert "/sys/class/dmi/id/board_serial" not in cmd_str
assert "/sys/class/dmi/id/chassis_serial" not in cmd_str
assert "/sys/class/dmi/id/product_serial" not in cmd_str