Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Install packages
run: sudo apt install podman golang-github-containernetworking-plugin-dnsname sqlite3 jq
run: sudo apt-get update && sudo apt-get install podman golang-github-containernetworking-plugin-dnsname sqlite3 jq
- name: Create virtualenv
run: python3 -m venv venv
- name: Install
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install tox
run: pip install tox
- name: Install pipx
run: pip install pipx
- name: Install uv
run: pipx install uv
- name: Run pipx ensurepath
run: pipx ensurepath
- name: Run unit tests
run: tox -e py3
run: uv run --extra test tox
46 changes: 23 additions & 23 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -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
17 changes: 6 additions & 11 deletions ceph_devstack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import tomlkit.exceptions

from pathlib import Path
from typing import List, Optional, Union
from typing import List


VERBOSE = 15
Expand Down Expand Up @@ -117,8 +117,8 @@ def parse_args(args: List[str]) -> argparse.Namespace:

def deep_merge(*maps):
result = {}
for map in maps:
for k, v in map.items():
for mapping in maps:
for k, v in mapping.items():
if isinstance(v, dict):
v = deep_merge(result.get(k, {}), v)
result[k] = v
Expand All @@ -128,7 +128,7 @@ def deep_merge(*maps):
class Config(dict):
__slots__ = ["user_obj", "user_path"]

def load(self, config_path: Optional[Path] = None):
def load(self, config_path: Path | None = None):
parsed = tomlkit.parse((Path(__file__).parent / "config.toml").read_text())
self.update(parsed)
if config_path:
Expand Down Expand Up @@ -165,13 +165,7 @@ def set_value(self, name: str, value: str):
obj = self.user_obj
i = 0
last_index = len(path) - 1
item: Union[tomlkit.items.Item, str] = value
try:
item = tomlkit.value(item)
except tomlkit.exceptions.UnexpectedCharError:
pass
except tomlkit.exceptions.InternalParserError:
pass
item = tomlkit.value(value)
while i <= last_index:
if i < last_index:
obj = obj.setdefault(path[i], {})
Expand All @@ -181,6 +175,7 @@ def set_value(self, name: str, value: str):
self.user_path.parent.mkdir(exist_ok=True)
self.user_path.write_text(tomlkit.dumps(self.user_obj).strip())
i += 1
return item


config = Config()
Expand Down
Binary file modified ceph_devstack/ceph_devstack.pp
Binary file not shown.
9 changes: 9 additions & 0 deletions ceph_devstack/ceph_devstack.te
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 ==============
Expand Down Expand Up @@ -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;
50 changes: 30 additions & 20 deletions ceph_devstack/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,39 @@
from ceph_devstack.requirements import check_requirements
from ceph_devstack.resources.ceph import CephDevStack

CONFIG_HANDLERS = {
"dump": lambda config, args: print(config.dump()),
"get": lambda config, args: print(config.get_value(args.name)),
"set": lambda config, args: print(config.set_value(args.name, args.value)),
}

def main(): # noqa: C901
COMMAND_HANDLERS = {
"doctor": None,
"apply": lambda args, obj: obj.apply(args.command),
"pull": lambda _, obj: obj.pull(),
"build": lambda _, obj: obj.build(),
"create": lambda _, obj: obj.create(),
"remove": lambda _, obj: obj.remove(),
"start": lambda _, obj: obj.start(),
"stop": lambda _, obj: obj.stop(),
"watch": lambda _, obj: obj.watch(),
"wait": lambda args, obj: obj.wait(container_name=args.container),
"logs": lambda args, obj: obj.logs(
run_name=args.run_name, job_id=args.job_id, locate=args.locate
),
}


def main() -> int:
args = parse_args(sys.argv[1:])
config.load(args.config_file)
if args.verbose:
for handler in logging.getLogger("root").handlers:
if not isinstance(handler, logging.FileHandler):
handler.setLevel(VERBOSE)
if args.command == "config":
if args.config_op == "dump":
print(config.dump())
if args.config_op == "get":
print(config.get_value(args.name))
elif args.config_op == "set":
config.set_value(args.name, args.value)
return
CONFIG_HANDLERS[args.config_op](config, args)
return 0
config["args"] = vars(args)
data_path = Path(config["data_dir"]).expanduser()
data_path.mkdir(parents=True, exist_ok=True)
Expand All @@ -35,20 +52,13 @@ async def run():
obj.check_requirements(),
):
logger.error("Requirements not met!")
sys.exit(1)
if args.command == "doctor":
return
elif args.command == "wait":
return await obj.wait(container_name=args.container)
elif args.command == "logs":
return await obj.logs(
run_name=args.run_name, job_id=args.job_id, locate=args.locate
)
else:
await obj.apply(args.command)
return 0
return 1
handler = COMMAND_HANDLERS.get(args.command)
if handler:
return await handler(args, obj)

try:
sys.exit(asyncio.run(run()))
except KeyboardInterrupt:
logger.debug("Exiting!")
return 130 # 128 + SIGINT
2 changes: 1 addition & 1 deletion ceph_devstack/config.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
5 changes: 4 additions & 1 deletion ceph_devstack/exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion ceph_devstack/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
5 changes: 3 additions & 2 deletions ceph_devstack/resources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@ 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:
self._name = name

@property
def name(self) -> str:
if hasattr(self, "_name"):
if self._name is not None:
return self._name
return self.__class__.__name__.lower()

Expand Down Expand Up @@ -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})"
12 changes: 6 additions & 6 deletions ceph_devstack/resources/ceph/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
import contextlib
import os
import pathlib
import tempfile

from subprocess import CalledProcessError
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -229,9 +231,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:
Expand All @@ -250,7 +250,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:
Expand Down
19 changes: 10 additions & 9 deletions ceph_devstack/resources/ceph/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,26 +99,26 @@ 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",
"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
Expand Down Expand Up @@ -150,11 +150,12 @@ class Pulpito(Container):
]
env_vars = {
"PULPITO_PADDLES_ADDRESS": "http://paddles:8080",
"VITE_MACHINE_TYPE": "testnode",
}


class TestNode(Container):
cmd_vars: List[str] = ["name", "image"]
_image_name = "teuthology-testnode"
capabilities = [
"SYS_ADMIN",
"NET_ADMIN",
Expand Down Expand Up @@ -367,7 +368,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}",
Expand Down
1 change: 1 addition & 0 deletions ceph_devstack/resources/ceph/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")


Expand Down
Loading
Loading