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
29 changes: 28 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Install packages
run: sudo apt install podman golang-github-containernetworking-plugin-dnsname
run: sudo apt install podman golang-github-containernetworking-plugin-dnsname sqlite3 jq
- name: Create virtualenv
run: python3 -m venv venv
- name: Install
run: ./venv/bin/pip3 install -e .
- name: Set owner for /dev/loop-control
run: sudo chown $(whoami) /dev/loop-control
- name: Configure
run: ./venv/bin/ceph-devstack config set containers.postgres.count 0
- name: Doctor
run: ./venv/bin/ceph-devstack -v doctor --fix
- name: Build
Expand All @@ -37,6 +39,31 @@ jobs:
- name: Dump logs
if: success() || failure()
run: podman logs -f teuthology
- name: Create archive
if: success() || failure()
run: |
mkdir -p /tmp/artifacts
- name: Dump job data
if: success() || failure()
run: |
podman cp paddles:/paddles/dev.db /tmp/
sqlite3 /tmp/dev.db ".output stdout" ".mode json" "select * from jobs" | jq | tee /tmp/artifacts/jobs.json
- name: Upload jobs.json
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: jobs
path: /tmp/artifacts/jobs.json
- name: Create tarball of log archive
if: success() || failure()
run: |
tar -czf /tmp/artifacts/archive.tar ~/.local/share/ceph-devstack/archive/
- name: Upload log archive
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: archive
path: /tmp/artifacts/archive.tar
- name: Stop
run: ./venv/bin/ceph-devstack -v stop
- name: Remove
Expand Down
2 changes: 1 addition & 1 deletion Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pipeline {
else
python3 -c "import yaml; print(yaml.safe_dump({'containers': {'teuthology': {'repo': '${env.WORKSPACE}/teuthology'}}, 'data_dir': '${env.WORKSPACE}/data'}))" > ${env.CDS_CONF}
fi
ceph-devstack --config-file ${env.CDS_CONF} show-conf
ceph-devstack --config-file ${env.CDS_CONF} config dump
ceph-devstack --config-file ${env.CDS_CONF} doctor
"""
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ python3 -m pip install git+https://github.com/zmc/ceph-devstack.git
## Configuration
`ceph-devstack` 's default configuration is [here](./ceph_devstack/config.yml). It can be extended by placing a file at `~/.config/ceph-devstack/config.yml` or by using the `--config-file` flag.

`ceph-devstack show-conf` will output the current configuration.
`ceph-devstack config dump` will output the current configuration.

As an example, the following configuration will use a local image for paddles with the tag `TEST`; it will also create ten testnode containers; and will build its teuthology container from the git repo at `~/src/teuthology`:
```
Expand Down
95 changes: 67 additions & 28 deletions ceph_devstack/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import argparse
import logging.config
import yaml
import tomlkit
import tomlkit.items
import tomlkit.exceptions

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


VERBOSE = 15
Expand All @@ -12,17 +14,7 @@
logger = logging.getLogger("ceph-devstack")

PROJECT_ROOT = Path(__file__).parent
DEFAULT_CONFIG_PATH = Path("~/.config/ceph-devstack/config.yml")


def represent_path(dumper: yaml.dumper.SafeDumper, data: PosixPath) -> yaml.Node:
return dumper.represent_scalar("tag:yaml.org,2002:str", str(data))


yaml.SafeDumper.add_representer(
PosixPath,
represent_path,
)
DEFAULT_CONFIG_PATH = Path("~/.config/ceph-devstack/config.toml")


def parse_args(args: List[str]) -> argparse.Namespace:
Expand Down Expand Up @@ -50,6 +42,14 @@ def parse_args(args: List[str]) -> argparse.Namespace:
help="Path to the ceph-devstack config file",
)
subparsers = parser.add_subparsers(dest="command")
parser_config = subparsers.add_parser("config", help="Get or set config items")
subparsers_config = parser_config.add_subparsers(dest="config_op")
subparsers_config.add_parser("dump", help="show the configuration")
parser_config_get = subparsers_config.add_parser("get")
parser_config_get.add_argument("name")
parser_config_set = subparsers_config.add_parser("set")
parser_config_set.add_argument("name")
parser_config_set.add_argument("value")
parser_doc = subparsers.add_parser(
"doctor", help="Check that the system meets requirements"
)
Expand Down Expand Up @@ -104,7 +104,6 @@ def parse_args(args: List[str]) -> argparse.Namespace:
"container",
help="The container to wait for",
)
subparsers.add_parser("show-conf", help="show the configuration")
return parser.parse_args(args)


Expand All @@ -119,21 +118,61 @@ def deep_merge(*maps):


class Config(dict):
__slots__ = ["user_obj", "user_path"]

def load(self, config_path: Optional[Path] = None):
self.update(yaml.safe_load((Path(__file__).parent / "config.yml").read_text()))
parsed = tomlkit.parse((Path(__file__).parent / "config.toml").read_text())
self.update(parsed)
if config_path:
user_path = config_path.expanduser()
if user_path.exists():
user_obj = yaml.safe_load(user_path.read_text()) or {}
self.update(deep_merge(config, user_obj))
elif user_path != DEFAULT_CONFIG_PATH.expanduser():
raise OSError(f"Config file at {user_path} not found!")


yaml.SafeDumper.add_representer(
Config,
yaml.representer.SafeRepresenter.represent_dict,
)
self.user_path = config_path.expanduser()
if self.user_path.exists():
self.user_obj: dict = tomlkit.parse(self.user_path.read_text()) or {}
self.update(deep_merge(config, self.user_obj))
elif self.user_path != DEFAULT_CONFIG_PATH.expanduser():
raise OSError(f"Config file at {self.user_path} not found!")
else:
self.user_obj = {}

def dump(self):
return tomlkit.dumps(self)

def get_value(self, name: str) -> str:
path = name.split(".")
obj = config
i = 0
while i < len(path):
sub_path = path[i]
try:
obj = obj[sub_path]
except KeyError:
logger.error(f"{name} not found in config")
raise
i += 1
if isinstance(obj, (str, int, bool)):
return str(obj)
return tomlkit.dumps(obj).strip()

def set_value(self, name: str, value: str):
path = name.split(".")
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
while i <= last_index:
if i < last_index:
obj = obj.setdefault(path[i], {})
elif i == last_index:
obj[path[i]] = item
self.update(self.user_obj)
self.user_path.parent.mkdir(exist_ok=True)
self.user_path.write_text(tomlkit.dumps(self.user_obj).strip())
i += 1


config = Config()
Expand Down
10 changes: 7 additions & 3 deletions ceph_devstack/cli.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import asyncio
import logging
import sys
import yaml

from pathlib import Path

Expand All @@ -17,8 +16,13 @@ def main():
for handler in logging.getLogger("root").handlers:
if not isinstance(handler, logging.FileHandler):
handler.setLevel(VERBOSE)
if args.command == "show-conf":
print(yaml.safe_dump(config))
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["args"] = vars(args)
data_path = Path(config["data_dir"]).expanduser()
Expand Down
23 changes: 23 additions & 0 deletions ceph_devstack/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
data_dir = "~/.local/share/ceph-devstack"

[containers.archive]
image = "python:alpine"

[containers.beanstalk]
image = "quay.io/ceph-infra/teuthology-beanstalkd:latest"

[containers.paddles]
image = "quay.io/ceph-infra/paddles:latest"

[containers.postgres]
image = "quay.io/ceph-infra/teuthology-postgresql:latest"

[containers.pulpito]
image = "quay.io/ceph-infra/pulpito:latest"

[containers.testnode]
count = 3
image = "quay.io/ceph-infra/teuthology-testnode:latest"

[containers.teuthology]
image = "quay.io/ceph-infra/teuthology-dev:latest"
17 changes: 0 additions & 17 deletions ceph_devstack/config.yml

This file was deleted.

Loading