Skip to content

Latest commit

 

History

History
138 lines (113 loc) · 8 KB

File metadata and controls

138 lines (113 loc) · 8 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project overview

Koan ("kickstart-over-a-network") is the client-side companion to Cobbler. It runs on a target machine and talks to a Cobbler server to either provision new virtualized guests (Xen, KVM/qemu, VMware, OpenVZ) or re-provision (re-kickstart) an existing physical/virtual system. It ships two console scripts, koan and cobbler-register; both entrypoints (main() and register_main()) live in koan/cli.py and use argparse, while the actual provisioning/registration logic lives in the Koan class (app.py) and Register class (register.py) respectively — see Architecture below.

Commands

Development install and dependency groups are declared in setup.cfg ([options.extras_require]: build, lint, test, docs).

# Install with test + lint extras
pip install -e .[lint,test]

# Run the full test suite with coverage (this is what CI runs)
pytest --cov=./koan

# Run a single test file / test
pytest tests/test_utils.py
pytest tests/test_utils.py::test_os_release

# Lint (pyflakes only checks top-level *.py and koan/*.py, not tests/)
pyflakes *.py koan/*.py

# Format (black is also enforced via pre-commit and CI)
black --safe .

# Import sorting (isort is also enforced via pre-commit and CI; profile=black keeps it black-compatible)
isort --profile black .

make qa runs pyflakes + black + isort if available locally. make doc builds the Sphinx docs in docs/. make release (clean → qa → authors → sdist → bdist → doc) is the full local release build; make rpms / make debs build native packages and depend on setuptools_scm-derived versioning being consistent — see the comments above pin-spec-version and debs in Makefile if touching packaging.

Versioning is entirely derived from git tags via setuptools_scm (pyproject.toml, koan/_version.py is generated — never hand-edit it). There is no manual version bump.

Commit messages / releases

Commits must follow Conventional Commits — enforced by the commitlint CI job (see CONTRIBUTING.md and commitlint.config.js). Merges to main are released automatically by semantic-release: the version, tag, GitHub release, and CHANGELOG.md are all generated from commit messages, so feat:/fix:/!/BREAKING CHANGE: prefixes directly control the next release. Malformed commit messages will fail CI and won't release correctly, so this matters more here than in most repos.

The package.json / node_modules tooling in this repo (semantic-release, commitlint) is release automation only — koan itself has no JavaScript runtime code.

License headers

Source files use SPDX headers, not the older GPL docstring blocks (converted in a recent PR):

# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: Copyright <year> <holder>

Add these to any new source file; match the existing pattern (module docstring first, then the SPDX comment block, then imports).

Architecture

Everything lives in the koan/ package, mostly flat aside from the koan/virt/ subpackage:

  • cli.pyargparse-based entrypoints for both console scripts. main() (koan) builds the ~30 flags, copies the parsed Namespace onto a fresh Koan() instance's attributes 1:1, and calls k.run(); register_main() (cobbler-register) does the same for Register. Both wrap the call in a try/except that special-cases the from_koan marker (see cexceptions.py below) to print a clean one-line message instead of a traceback. main() also defines --version (reads koan.__version__, generated by setuptools_scm). Gotcha when adding new store_true flags here: argparse's store_true defaults to False, not None like the optparse code this replaced — pass default=None explicitly for any flag whose absence needs to be distinguishable from an explicit "off" (e.g. -n/--nogfx, checked via is not None against -g/--graphics's always-set default).
  • app.py — the real core of the program: the Koan class implements almost all of the koan CLI's behavior as a plain business-logic class with no argument parsing of its own — cli.py's main() populates its attributes and calls Koan.run(), which drives a single top-level action (--virt, --replace-self, --update-files, --update-config, --list, etc). Key method groups:
    • net_install() / get_distro_files() / calc_kernel_args() — fetch profile/system data from the Cobbler server and compute kernel/initrd/kernel-options for network installs and --replace-self (kexec) reinstalls.
    • virt_net_install() / load_virt_modules() / virt_choose() — dispatch to the per-hypervisor module in koan/virt/ based on virt_type.
    • The many calc_virt_*() methods (calc_virt_ram, calc_virt_disk-adjacent calc_virt_filesize*, calc_virt_mac, calc_virt_uuid, calc_virt_path*, ...) resolve per-VM settings from Cobbler profile/system data plus command-line overrides plus defaults — this layered override pattern (CLI arg > profile data > hardcoded default) is used throughout and is the main thing to preserve when touching option handling.
    • update_files() / update_config() — pull config-management templates/packages/repos from Cobbler for an already-installed system and hand off to configurator.KoanConfigure.
    • get_install_tree_from_autoinst() still uses optparse.OptionParser internally — that's parsing url=/nfs= args embedded in a downloaded kickstart/autoinst file, unrelated to the koan command line, so it wasn't touched by the cli.py extraction/argparse migration.
  • configurator.pyKoanConfigure: applies repo/package/file configuration pushed down from Cobbler's config-management data to the local system (currently only implements YUM repo configuration; yum import is optional/best-effort).
  • virtinstall.py — builds a virt-install/libvirt command line (disk/NIC sanitization, build_commandline()) for KVM/qemu and Xen-via-libvirt guests.
  • koan/virt/{xen,qemu,openvz,vmw,image}.py — one module per hypervisor/guest backend, each exposing a start_install(*args, **kwargs) entry point that app.py calls into via virt_choose()/load_virt_modules(). vmw.py additionally has helpers to build VMX files and register/start guests through VMware. When adding a new virt backend, follow this same start_install(**kwargs) contract so it plugs into app.py unchanged.
  • utils.py — shared grab-bag: XML-RPC connection to the Cobbler server (connect_to_server), network/OS introspection (get_network_info, os_release, is_uefi_system), subprocess helpers, and misc file/MAC/UUID utilities used across the other modules.
  • register.py — a second, much smaller business-logic class (Register) backing the cobbler-register command (its argparse entrypoint is cli.py's register_main()), which just registers the current system with a Cobbler server (distinct from the main provisioning flow in app.py).
  • cexceptions.py — exception hierarchy. KoanException/InfoException set a from_koan marker so cli.py's main()/register_main() can print a clean one-line message instead of a traceback for expected/user-facing errors; anything else prints a full traceback.

Tests

tests/ currently only covers utils.py (test_utils.py), using pytest-mock to stub os.path/distro/subprocess calls rather than touching a real system or network — follow that mocking style for new tests, since most of koan's code paths require root, libvirt, or a live Cobbler server to exercise for real. tests/conftest.py provides a does_not_raise() context manager for parametrized tests that assert "no exception" as one case among several pytest.raises(...) cases. tests/virtinstall.py is not a test_*.py file, so pytest won't collect it as a test module.