From fbe4c04e50efae2a4198fbc3a7d11eb7f2d10432 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Fri, 17 Apr 2026 11:42:58 -0600 Subject: [PATCH 01/11] PodmanResource: _name is always present Signed-off-by: Zack Cerza --- ceph_devstack/resources/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ceph_devstack/resources/__init__.py b/ceph_devstack/resources/__init__.py index 1c9e39d67..65051cc32 100644 --- a/ceph_devstack/resources/__init__.py +++ b/ceph_devstack/resources/__init__.py @@ -38,6 +38,7 @@ class PodmanResource: stop_cmd: List[str] = [] cmd_vars: List[str] = ["name"] log: Dict[str, Set[str]] = {} + _name: str | None = None def __init__(self, name: str = ""): if name: @@ -45,7 +46,7 @@ def __init__(self, name: str = ""): @property def name(self) -> str: - if hasattr(self, "_name"): + if self._name is not None: return self._name return self.__class__.__name__.lower() @@ -110,5 +111,5 @@ async def remove(self): await self.cmd(self.format_cmd(self.remove_cmd)) def __repr__(self): - param_str = "" if not hasattr(self, "_name") else f'name="{self._name}"' + param_str = "" if self._name is None else f'name="{self._name}"' return f"{self.__class__.__name__}({param_str})" From 0ff4fbd9e8cdd8ec6ccd6dc583b70c17a82229b7 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Fri, 17 Apr 2026 12:05:44 -0600 Subject: [PATCH 02/11] pre-commit: Update mypy Signed-off-by: Zack Cerza --- .pre-commit-config.yaml | 46 ++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 99d5da011..c2392d4e1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,26 +1,26 @@ exclude: 'ceph_devstack/ceph_devstack\.(te|pp)' repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 - hooks: - - id: check-yaml - - id: end-of-file-fixer - - id: trailing-whitespace -- repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.11 - hooks: - - id: ruff-check - - id: ruff-format -- repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.0 - hooks: - - id: mypy - exclude: ^docs/conf.py - additional_dependencies: - - types-dataclasses >= 0.1.3 - - types-PyYAML - - tomli >= 0.2.6, < 2.0.0 - - types-typed-ast >= 1.4.1 - - click >= 8.1.0 - - platformdirs >= 2.1.0 + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.12.11 + hooks: + - id: ruff-check + - id: ruff-format + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.20.1 + hooks: + - id: mypy + exclude: ^docs/conf.py + additional_dependencies: + - types-dataclasses >= 0.1.3 + - types-PyYAML + - tomli >= 0.2.6, < 2.0.0 + - types-typed-ast >= 1.4.1 + - click >= 8.1.0 + - platformdirs >= 2.1.0 From e8335105d6b090fe8877c6d7d645283b31ecdb76 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Fri, 17 Apr 2026 12:09:31 -0600 Subject: [PATCH 03/11] Fix type errors reported by mypy pre-commit must have somehow been skipped. Signed-off-by: Zack Cerza --- ceph_devstack/exec.py | 5 ++++- ceph_devstack/resources/ceph/__init__.py | 6 ++---- ceph_devstack/resources/ceph/utils.py | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ceph_devstack/exec.py b/ceph_devstack/exec.py index 9e3bff729..bfeb93636 100644 --- a/ceph_devstack/exec.py +++ b/ceph_devstack/exec.py @@ -60,7 +60,10 @@ def run(self) -> subprocess.Popen: async def arun(self) -> asyncio.subprocess.Process: logger.log(VERBOSE, self._make_log_msg()) loop = asyncio.get_running_loop() - protocol_factory: functools.partial[SubprocessStreamProtocol] + protocol_factory: ( + functools.partial[SubprocessStreamProtocol] + | functools.partial[LoggingStreamProtocol] + ) if self.stream_output: protocol_factory = functools.partial( LoggingStreamProtocol, diff --git a/ceph_devstack/resources/ceph/__init__.py b/ceph_devstack/resources/ceph/__init__.py index 289197f1d..d8fce242f 100644 --- a/ceph_devstack/resources/ceph/__init__.py +++ b/ceph_devstack/resources/ceph/__init__.py @@ -229,9 +229,7 @@ async def wait(self, container_name: str): logger.error(f"Could not find container {container_name}") return 1 - async def logs( - self, run_name: str = None, job_id: str = None, locate: bool = False - ): + async def logs(self, run_name: str = "", job_id: str = "", locate: bool = False): try: log_file = self.get_log_file(run_name, job_id) except FileNotFoundError: @@ -250,7 +248,7 @@ async def logs( while chunk := f.read(buffer_size): print(chunk, end="") - def get_log_file(self, run_name: str = None, job_id: str = None): + def get_log_file(self, run_name: str, job_id: str): archive_dir = Teuthology().archive_dir.expanduser() if not run_name: diff --git a/ceph_devstack/resources/ceph/utils.py b/ceph_devstack/resources/ceph/utils.py index 6f96f5aa0..438343822 100644 --- a/ceph_devstack/resources/ceph/utils.py +++ b/ceph_devstack/resources/ceph/utils.py @@ -10,6 +10,7 @@ def get_logtimestamp(dirname: str) -> datetime: match_ = RUN_DIRNAME_PATTERN.search(dirname) + assert match_ return datetime.strptime(match_.group("timestamp"), "%Y-%m-%d_%H:%M:%S") From 3a381b0641081defa1c9360e4fcd5e0e44945530 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Fri, 17 Apr 2026 12:10:48 -0600 Subject: [PATCH 04/11] testnode: Tolerate broken ssh auth socket Signed-off-by: Zack Cerza --- ceph_devstack/resources/ceph/containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ceph_devstack/resources/ceph/containers.py b/ceph_devstack/resources/ceph/containers.py index dab877a91..799611a02 100644 --- a/ceph_devstack/resources/ceph/containers.py +++ b/ceph_devstack/resources/ceph/containers.py @@ -367,7 +367,7 @@ def create_cmd(self): f"{ansible_inv}/secrets:/etc/ansible/secrets", ] ssh_auth_socket = os.environ.get("SSH_AUTH_SOCK") - if ssh_auth_socket: + if ssh_auth_socket and Path(ssh_auth_socket).exists(): cmd += [ "-v", f"{ssh_auth_socket}:{ssh_auth_socket}", From cfe8c59dbddb68eb514c74ef40a1499b576b23e7 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Fri, 17 Apr 2026 12:11:58 -0600 Subject: [PATCH 05/11] ssh: Don't use deprecated mktemp Signed-off-by: Zack Cerza --- ceph_devstack/resources/ceph/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ceph_devstack/resources/ceph/__init__.py b/ceph_devstack/resources/ceph/__init__.py index d8fce242f..d40a15aa7 100644 --- a/ceph_devstack/resources/ceph/__init__.py +++ b/ceph_devstack/resources/ceph/__init__.py @@ -1,6 +1,7 @@ import asyncio import contextlib import os +import pathlib import tempfile from subprocess import CalledProcessError @@ -67,12 +68,13 @@ async def _get_ssh_keys(self): privkey_path = os.environ.get("SSH_PRIVKEY_PATH") self.pubkey_path = "/dev/null" if not privkey_path: - privkey_path = tempfile.mktemp( + temp_dir = tempfile.mkdtemp( prefix="teuthology-ssh-key-", dir="/tmp", ) + privkey_path = pathlib.Path(temp_dir) / self.__class__.privkey_path await self.cmd( - ["ssh-keygen", "-t", "rsa", "-N", "", "-f", privkey_path], + ["ssh-keygen", "-t", "rsa", "-N", "", "-f", str(privkey_path)], check=True, force_local=True, ) From bdd476bd5b7e8b8ed63f45b9da24b5825553314c Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Fri, 17 Apr 2026 12:13:37 -0600 Subject: [PATCH 06/11] testnode: Use a consistent image name Signed-off-by: Zack Cerza --- ceph_devstack/resources/ceph/containers.py | 2 +- ceph_devstack/resources/container.py | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ceph_devstack/resources/ceph/containers.py b/ceph_devstack/resources/ceph/containers.py index 799611a02..e772b9cb3 100644 --- a/ceph_devstack/resources/ceph/containers.py +++ b/ceph_devstack/resources/ceph/containers.py @@ -154,7 +154,7 @@ class Pulpito(Container): class TestNode(Container): - cmd_vars: List[str] = ["name", "image"] + _image_name = "teuthology-testnode" capabilities = [ "SYS_ADMIN", "NET_ADMIN", diff --git a/ceph_devstack/resources/container.py b/ceph_devstack/resources/container.py index c0c6d9ef8..49662b3c0 100644 --- a/ceph_devstack/resources/container.py +++ b/ceph_devstack/resources/container.py @@ -11,12 +11,12 @@ class Container(PodmanResource): network: str secret: List[str] - cmd_vars: List[str] = ["name", "image", "image_tag"] + cmd_vars: List[str] = ["name", "image", "image_name", "image_tag"] build_cmd: List[str] = [ "podman", "build", "-t", - "{name}:{image_tag}", + "{image_name}:{image_tag}", ".", ] create_cmd: List[str] = ["podman", "container", "create", "{name}"] @@ -27,6 +27,7 @@ class Container(PodmanResource): pull_cmd: List[str] = ["podman", "pull", "{image}"] wait_cmd: List[str] = ["podman", "wait", "{name}"] env_vars: Dict[str, Optional[str]] = {} + _image_name: str | None = None def __init__(self, name: str = ""): super().__init__(name) @@ -48,10 +49,16 @@ def add_env_to_args(self, args: List): def config(self): return config["containers"].get(self.__class__.__name__.lower(), {}) + @property + def image_name(self) -> str: + if self._image_name is not None: + return self._image_name + return self.__class__.__name__.lower() + @property def image(self): if self.repo: - return f"localhost/{self.name}" + return f"localhost/{self.image_name}" return self.config["image"] @property From e66d4588d199e95d3f57e0ec7f7067d55e0a533a Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Tue, 21 Apr 2026 17:32:54 -0600 Subject: [PATCH 07/11] Update SELinux policy Signed-off-by: Zack Cerza --- ceph_devstack/ceph_devstack.pp | Bin 13915 -> 14537 bytes ceph_devstack/ceph_devstack.te | 9 +++++++++ 2 files changed, 9 insertions(+) diff --git a/ceph_devstack/ceph_devstack.pp b/ceph_devstack/ceph_devstack.pp index f507aedebe8698367515a3773f8f62b24b02c05c..04433ecef86d4a68b9b12e652fea257810972f09 100644 GIT binary patch delta 1451 zcmZWpO^6&t6yBQt|DFEZ`RVDnoy~YySi(SpN)m7dK@ks%Ab5zqVbY^RlG)8n$0R7S zLJl4@j{6A;imYA)$zj8)c+f*mL5R2r;z19RbC5(Yh6u5$d#aLPLDBE~-uGU;dRX<@G& z;$|%(i!)0{D*40{XiL(};rMh`XjJ}?pwCMdSrBMT3L#4gev&*A3igg<6NMvta(*(H zWQ9Z17@Wj2vQOp(IxE*nSD?@3h}Z)CEQfefOPu5RYr}q?6|9-D@GdD4v2e?5(!J<7 zs+hIH!&^3o5$Oo&yNZnqwAQZ`pX}qTJ2*4RGoIVSerW-9H6kh(;Bi%>)m#axluL@G z7VHN!gzl(FtryJXypex=*>7V##v z8Rv#E4*H9@PVufE;;EpGk0|~!sPM00o8{RIsx&vFXUk=o^p$z)Dw`UY;j7^?4Xw)N z{&&KA`1MAcm&lsO{t4eDlYh|^?y{2maj&_Ijrl5-*XZK+`KlbRu~L@PYeuAoC#**W zk_BSBWZnIzQN=T@UV2}(%L8pztQi}wHk delta 1123 zcmY*WNoW*76iqojGrdo5vvp6plOSNi$45?Itb`eADsf2_4ARO`ub>fwnY5E<#x7i{SHd)PC1|CSq*xu(f(bz^ zVL`}{c3x4E4v{$OP1;1|XgH~oJV(D$3VdHm8nh4Lm6|BycT&cn*s7jASHE11?px&I z{**}qj>c0aCh4G;zCqIHmI5&xsk3{_=uQXdOzR+02?J@BxI8c*C2>71<3z?mSrFMW z>LK;BW%%6jCT$X%qn|XD;iyd%=!<8C05#FUq{XwC5>2ASUEhle$uXu3)rP`KXw!~5 zS6ih#cBu~PX$=RZtz;b*B@aJb2fpl}t5`roUK{h8+~j>)8U1qGvepAvDaF3KatT+J zNSiN|F~5xUY!f$>EGkL>U5j!JWgyP8xx-pCm-*JI9`5N;KgMrSzHOPW+;eV~f061n zt?7TNd338y?&$%#jA-thUPfOw(vBKAY$~{Mc7?CZX8du+#UZolHw+guW(Ph}?N7$S z4%3Bib>N{H@z3--VRb^Va`0^%JFP7GR1x2-X#Rwq$B-fGS>_i}l?-f7sF*35I3U>c>kxu>#Vtfc!hMA!f(YH)hepo8F|QAGDMghB nUIqEmMw~3|CGFFXN;e2z_yWEJGLHE7VFwNL`}*|PV4?62?qp1m diff --git a/ceph_devstack/ceph_devstack.te b/ceph_devstack/ceph_devstack.te index 7451a521d..f661669bd 100644 --- a/ceph_devstack/ceph_devstack.te +++ b/ceph_devstack/ceph_devstack.te @@ -1,10 +1,13 @@ module ceph_devstack 1.0; require { + type unconfined_t; + type container_t; type container_init_t; type proc_t; type sysfs_t; type tmpfs_t; + type user_tmp_t; type devpts_t; class filesystem mount; class filesystem unmount; @@ -74,6 +77,9 @@ require { type fuse_device_t; type tun_tap_device_t; + + class sock_file write; + class unix_stream_socket connectto; } #============= container_init_t ============== @@ -116,3 +122,6 @@ allow container_init_t lvm_control_t:chr_file mounton; allow container_init_t fuse_device_t:chr_file mounton; allow container_init_t fixed_disk_device_t:blk_file mounton; allow container_init_t tun_tap_device_t:chr_file mounton; + +allow container_t user_tmp_t:sock_file write; +allow container_t unconfined_t:unix_stream_socket connectto; From 5406155b65873aa8b629b196957429e5838d4b4a Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Tue, 21 Apr 2026 17:33:05 -0600 Subject: [PATCH 08/11] host: Fix SELinux boolean logic Signed-off-by: Zack Cerza --- ceph_devstack/host.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ceph_devstack/host.py b/ceph_devstack/host.py index 1fcca5f45..d4646fbb4 100644 --- a/ceph_devstack/host.py +++ b/ceph_devstack/host.py @@ -99,7 +99,7 @@ async def check_selinux_bool(self, name: str): proc = await host.arun(["getsebool", name]) assert proc.stdout is not None out = await proc.stdout.read() - return out.decode().strip() != f"{name} --> on" + return out.decode().strip() == f"{name} --> on" async def get_sysctl_value(self, name: str) -> int: proc = await host.arun(["sysctl", "-b", name]) From 11a1d54736d046449d44c1334912edeaa3053b88 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Tue, 21 Apr 2026 17:33:25 -0600 Subject: [PATCH 09/11] Set VITE_MACHINE_TYPE for pulpito This is for the in-progress pulpito-ng SSR implementation Signed-off-by: Zack Cerza --- ceph_devstack/resources/ceph/containers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ceph_devstack/resources/ceph/containers.py b/ceph_devstack/resources/ceph/containers.py index e772b9cb3..72e45bb80 100644 --- a/ceph_devstack/resources/ceph/containers.py +++ b/ceph_devstack/resources/ceph/containers.py @@ -150,6 +150,7 @@ class Pulpito(Container): ] env_vars = { "PULPITO_PADDLES_ADDRESS": "http://paddles:8080", + "VITE_MACHINE_TYPE": "testnode", } From 8889069cdc505675586a60e66eb0ac70355799f2 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Tue, 21 Apr 2026 17:33:39 -0600 Subject: [PATCH 10/11] Archive: extend cmd_vars To allow building the archive image locally. Signed-off-by: Zack Cerza --- ceph_devstack/resources/ceph/containers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ceph_devstack/resources/ceph/containers.py b/ceph_devstack/resources/ceph/containers.py index 72e45bb80..d0332fddb 100644 --- a/ceph_devstack/resources/ceph/containers.py +++ b/ceph_devstack/resources/ceph/containers.py @@ -99,7 +99,7 @@ class Paddles(Container): class Archive(Container): - cmd_vars: List[str] = ["name", "image", "archive_dir"] + cmd_vars = Container.cmd_vars + ["archive_dir"] create_cmd = [ "podman", "container", From 3459953bbd90a1eedd4046bdcbbbba6a82859988 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Mon, 27 Apr 2026 17:24:27 -0600 Subject: [PATCH 11/11] Use darkhttpd for log archive This is mainly because python's server doesn't easily let us correct MIME types, which prevents reading log files in-browser. This image is also far smaller. Signed-off-by: Zack Cerza --- ceph_devstack/config.toml | 2 +- ceph_devstack/resources/ceph/containers.py | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ceph_devstack/config.toml b/ceph_devstack/config.toml index 147a729eb..2c2c08f75 100644 --- a/ceph_devstack/config.toml +++ b/ceph_devstack/config.toml @@ -1,7 +1,7 @@ data_dir = "~/.local/share/ceph-devstack" [containers.archive] -image = "python:alpine" +image = "docker.io/alpinelinux/darkhttpd:latest" [containers.beanstalk] image = "quay.io/ceph-infra/teuthology-beanstalkd:main" diff --git a/ceph_devstack/resources/ceph/containers.py b/ceph_devstack/resources/ceph/containers.py index d0332fddb..1eabf6d4b 100644 --- a/ceph_devstack/resources/ceph/containers.py +++ b/ceph_devstack/resources/ceph/containers.py @@ -104,21 +104,21 @@ class Archive(Container): "podman", "container", "create", - "-i", "--network", "ceph-devstack", "-p", - "8000:8000", + "8000:8080", "-v", "{archive_dir}:/archive" + ARCHIVE_MOUNT_SUFFIX, "--name", "{name}", + "--entrypoint", + "darkhttpd", "{image}", - "python3", - "-m", - "http.server", - "-d", "/archive", + "--no-server-id", + "--default-mimetype", + "text/plain", ] @property