From 596498b14db611b02b18f039d6798d5f7c3bc651 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Thu, 28 May 2026 12:24:44 -0600 Subject: [PATCH 1/4] requirements: Add AppArmor chkpwd check Signed-off-by: Zack Cerza --- ceph_devstack/host.py | 4 ++++ ceph_devstack/requirements.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/ceph_devstack/host.py b/ceph_devstack/host.py index 1fcca5f4..6cfd3878 100644 --- a/ceph_devstack/host.py +++ b/ceph_devstack/host.py @@ -107,6 +107,10 @@ async def get_sysctl_value(self, name: str) -> int: out = await proc.stdout.read() return int(out.decode().strip()) + async def apparmor_enabled(self) -> bool: + proc = await host.arun(["aa-enabled", "-q"]) + return await proc.wait() == 0 + class LocalHost(Host): pass diff --git a/ceph_devstack/requirements.py b/ceph_devstack/requirements.py index 090dc804..97b987b7 100644 --- a/ceph_devstack/requirements.py +++ b/ceph_devstack/requirements.py @@ -217,6 +217,19 @@ class FuseOverlayfsPresence(FixableRequirement): fix_cmd = ["sudo", "dnf", "install", "-y", "fuse-overlayfs"] +class AppArmorProfile(FixableRequirement): + _profile_path = "/etc/apparmor.d/local/unix-chkpwd" + _profile_content = '"capability dac_override,"' + check_cmd = ["test", "-f", _profile_path] + suggest_msg = "Did not find required apparmor profile" + fix_cmd = [ + "sudo", + "bash", + "-c", + f"echo -e {_profile_content} > {_profile_path} && systemctl reload apparmor", + ] + + async def check_requirements(): if not await PodmanPlatform().evaluate(): return False @@ -248,6 +261,10 @@ async def check_requirements(): result = result and await SELinuxBoolean("container_manage_cgroup").evaluate() result = result and await SELinuxBoolean("container_use_devices").evaluate() + # AppArmor + if await host.apparmor_enabled(): + result = result and await AppArmorProfile().evaluate() + # podman DNS plugin if not await PodmanVersion("5.0").evaluate(): result = result and await PodmanDNSPlugin().evaluate() From 732171905f6aeb5b23d1cb364fceebf4bd909051 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Thu, 28 May 2026 12:25:35 -0600 Subject: [PATCH 2/4] host: Add return type to Host.arun Signed-off-by: Zack Cerza --- ceph_devstack/host.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ceph_devstack/host.py b/ceph_devstack/host.py index 6cfd3878..6f50f8fd 100644 --- a/ceph_devstack/host.py +++ b/ceph_devstack/host.py @@ -1,3 +1,4 @@ +import asyncio import logging import os import pathlib @@ -44,7 +45,7 @@ async def arun( cwd: Optional[pathlib.Path] = None, env: Optional[Dict] = None, stream_output: bool = False, - ): + ) -> asyncio.subprocess.Process: return await self.cmd( args, cwd=cwd, env=env, stream_output=stream_output ).arun() From bf97a363394250ba7d7c4745da8f6619fc40420e Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Thu, 28 May 2026 12:31:33 -0600 Subject: [PATCH 3/4] ci: Check testnode SSH access before waiting So that if SSH access is broken, we don't have to wait several minutes for feedback. Signed-off-by: Zack Cerza --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b652dd4..eae183e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,11 @@ jobs: run: ./venv/bin/ceph-devstack -v start - name: Check Status run: podman ps -a + - name: Check ssh access to testnode container + run: sleep 10 && podman exec teuthology ssh -oBatchMode=yes -v "cm@$(podman inspect --format '{{ slice .ID 0 12 }}' testnode_0)" whoami + - name: Dump testnode journal + if: failure() + run: podman exec testnode_0 journalctl - name: Wait run: ./venv/bin/ceph-devstack wait teuthology - name: Dump logs From 32bdca5b9a2c458bb2d062d01119d530197a5cf6 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Thu, 28 May 2026 13:43:08 -0600 Subject: [PATCH 4/4] requirements: Fix suggested command quoting Signed-off-by: Zack Cerza --- ceph_devstack/requirements.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ceph_devstack/requirements.py b/ceph_devstack/requirements.py index 97b987b7..2d71ba0e 100644 --- a/ceph_devstack/requirements.py +++ b/ceph_devstack/requirements.py @@ -1,3 +1,4 @@ +import shlex import sys from pathlib import Path @@ -35,7 +36,7 @@ async def evaluate(self) -> bool: async def suggest(self): if hasattr(self, "suggest_msg"): - logger.error(f"{self.suggest_msg}. Try: {' '.join(self.fix_cmd)}") + logger.error(f"{self.suggest_msg}. Try: {shlex.join(self.fix_cmd)}") async def fix(self) -> bool: assert self.fix_cmd, "Attempted to fix without a fix command"