cli.administration.inventory.provision package¶
Submodules¶
cli.administration.inventory.provision.cli module¶
- cli.administration.inventory.provision.cli.main(argv: list[str] | None = None) int¶
cli.administration.inventory.provision.credentials_generator module¶
- cli.administration.inventory.provision.credentials_generator.generate_credentials_for_roles(application_ids: list[str], roles_dir: Path, host_vars_file: Path, vault_password_file: Path, project_root: Path, env: dict[str, str] | None, workers: int = 4, app_variants: dict[str, int] | None = None) None¶
cli.administration.inventory.provision.filters module¶
- cli.administration.inventory.provision.filters.filter_dynamic_inventory(dyn_inv: dict[str, Any], include_filter: set[str] | None, exclude_filter: set[str] | None, legacy_roles_filter: set[str] | None) dict[str, Any]¶
- Apply include/exclude/legacy role filters in the same order as before:
include -> exclude -> legacy roles
- cli.administration.inventory.provision.filters.parse_roles_list(raw_roles: list[str] | None) set[str] | None¶
- Parse a list of IDs supplied on the CLI. Supports:
–include web-app-nextcloud web-app-mastodon –include web-app-nextcloud,web-app-mastodon
Same logic is reused for –exclude and –roles.
cli.administration.inventory.provision.host_vars module¶
- cli.administration.inventory.provision.host_vars.apply_vars_overrides(host_vars_file: Path, json_str: str) None¶
- cli.administration.inventory.provision.host_vars.apply_vars_overrides_from_file(host_vars_file: Path, vars_file: Path) None¶
cli.administration.inventory.provision.inventory_generator module¶
- cli.administration.inventory.provision.inventory_generator.generate_dynamic_inventory(host: str, tmp_inventory: Path, env: dict[str, str] | None) dict[str, Any]¶
- Generate a dynamic inventory by executing the generator as a module:
python -m cli.administration.inventory.devices …
This avoids fragile file path construction and fixes ‘cli/cli/…’ issues.
cli.administration.inventory.provision.mirror_overrides module¶
cli.administration.inventory.provision.passwords module¶
- cli.administration.inventory.provision.passwords.generate_declared_user_password(username, algorithm, validation)¶
Return a password satisfying what the user’s own declaration demands.
- Parameters:
username – the user the password belongs to, for the failure message.
algorithm – a ValueGenerator algorithm name, or None for the shell-safe default every user gets when its declaration asks for nothing.
validation – a regular expression the value must match, or None.
An application whose registration rejects the shell-safe alphabet states that in its own meta/users.yml rather than carrying a second credential beside the account it already owns.
- cli.administration.inventory.provision.passwords.generate_random_password(length: int = 64) str¶
- cli.administration.inventory.provision.passwords.generate_user_password(length: int = 64) str¶
Return a shell-safe password for a declared user.
- Parameters:
length – characters to generate, at least 16.
- Returns:
letters, digits and the marks
-_., never a leading-, with one of each class the Keycloak realm policyspecialChars(1)counts.
cli.administration.inventory.provision.project module¶
- cli.administration.inventory.provision.project.build_env_with_project_root(project_root: Path) dict[str, str]¶
Return an environment dict where PYTHONPATH includes the project root. This makes utils and other top-level packages importable in subprocesses.
- cli.administration.inventory.provision.project.detect_project_root(start_file: Path) Path¶
Detect repo root by walking upwards from a file path until we find a typical repo layout.
- We require:
cli/ directory
roles/ directory
group_vars/ directory
This avoids brittle parents[N] assumptions and fixes “cli/cli/…” path bugs.
cli.administration.inventory.provision.reset module¶
Rotate the credentials an inventory generated, so a redeploy has to carry them.
Provisioning fills a missing credential and leaves an existing one alone. That keeps a redeploy stable, and it also means no deploy ever runs against a secret that moved: every pass reads the value the first pass wrote. Dropping the generated values and regenerating them between two deploy passes turns the second pass into a test of the update path, where each role has to push its new secret into the application and the database it already created.
- cli.administration.inventory.provision.reset.reset_credentials(application_ids: list[str], roles_dir: Path, host_vars_file: Path, vault_password_file: Path, project_root: Path, env: dict[str, str] | None, schema: bool, users: bool, exclude: set[str], workers: int = 4, app_variants: dict[str, int] | None = None, credential_ids: list[str] | None = None) int¶
Replace every generated credential in
host_vars_filewith a fresh one.- Parameters:
application_ids – the roles resolved into this inventory.
roles_dir – directory the roles live in.
host_vars_file – inventory file the credentials are rewritten in.
vault_password_file – vault password used to encrypt each value.
project_root – repository root the role resolver works from.
env – environment the credential subprocesses inherit.
schema – rotate
applications.<app>.credentials.*.users – rotate
users.<name>.password.exclude – application ids and user keys to leave untouched.
workers – worker threads for credentials generation.
app_variants – variant index per app, as at provision time.
credential_ids – application ids whose credentials rotate;
Nonerotates all ofapplication_ids. User passwords ignore it and always resolve through the fullapplication_ids.
- Returns:
How many values were rotated.
cli.administration.inventory.provision.role_resolver module¶
- cli.administration.inventory.provision.role_resolver.resolve_role_path(application_id: str, roles_dir: Path, project_root: Path, env: dict[str, str] | None) Path | None¶
- Resolve role path by calling:
python -m cli.meta.roles.applications.role_name <app_id> -r <roles_dir>
- The helper may print:
bare folder name (e.g. web-app-nextcloud)
relative path (e.g. roles/web-app-nextcloud)
absolute path
Returns None when the id belongs to no application role.
cli.administration.inventory.provision.ruamel_io module¶
Round-trip YAML access for the inventory files this package rewrites.
The counterpart to yaml_io, which reads values. Everything here keeps
comments, quoting and vault tags intact, because host_vars is hand-edited as
often as it is generated.
- cli.administration.inventory.provision.ruamel_io.as_commented_map(data: Any) CommentedMap¶
Return
dataas a CommentedMap, empty when it holds no mapping.- Parameters:
data – whatever a round-trip load returned.
- cli.administration.inventory.provision.ruamel_io.dump_document(path: Path, document: CommentedMap) None¶
Write
documenttopath, creating the parent directory.- Parameters:
path – the YAML file to write.
document – the round-trip document to serialise.
- cli.administration.inventory.provision.ruamel_io.ensure_map(node: CommentedMap, key: str) CommentedMap¶
Return
node[key]as a CommentedMap, creating it when absent.- Parameters:
node – the parent mapping.
key – the child key to resolve.
- cli.administration.inventory.provision.ruamel_io.load_document(path: Path) CommentedMap¶
Return the round-trip document at
path, empty when it does not exist.- Parameters:
path – the YAML file to read.
- cli.administration.inventory.provision.ruamel_io.round_trip_yaml() YAML¶
Return a YAML handler configured the way every writer here needs it.
- cli.administration.inventory.provision.ruamel_io.vault_value(vault_password_file: Path | str, plain: str, name: str) Any¶
Return
plainas a parsed!vaultnode ready to assign.- Parameters:
vault_password_file – vault password used to encrypt.
plain – the secret to encrypt.
name – the key ansible-vault names the snippet after.
ansible-vault emits a YAML snippet rather than a value, so the encrypted node has to be read back out of it before it can be assigned anywhere.
cli.administration.inventory.provision.services_disabler module¶
- exception cli.administration.inventory.provision.services_disabler.ServicesDisabledConflictError¶
Bases:
RuntimeErrorRaised when disable conflicts with an existing inventory.
- cli.administration.inventory.provision.services_disabler.apply_services_disabled(host_vars_file: Path, services: list[str], roles_dir: Path, inventory_file: Path | None = None) None¶
For every role under roles_dir whose meta/services.yml defines a service listed in services, set enabled: false and shared: false in host_vars_file under applications.<app_id>.services.<svc_name>. Missing application or services blocks are created as needed.
Per the materialised path is
applications.<app>.services.<svc>(nocompose.serviceswrapper).If inventory_file is provided, also removes the provider role for each service from the inventory (devices.yml).
- cli.administration.inventory.provision.services_disabler.apply_services_disabled_from_env(host_vars_file: Path, roles_dir: Path, inventory_file: Path | None = None) None¶
Read disable from the environment and apply to host_vars and inventory.
- cli.administration.inventory.provision.services_disabler.assert_services_disabled_inventory_consistency_from_env(inventory_dir: Path, roles_dir: Path) None¶
Fail fast when disable conflicts with the existing inventory.
- cli.administration.inventory.provision.services_disabler.expand_disabled_tokens(tokens: list[str], roles_dir: Path) list[str]¶
Backwards-compatible alias for the shared
utils.roles.applications.services.registry.expand_service_tokens().
- cli.administration.inventory.provision.services_disabler.find_provider_roles(services: list[str], roles_dir: Path) dict[str, str]¶
Return mapping of service_name -> application_id for the provider role of each requested service. Resolution goes through the canonical service registry, so
provides:aliases (e.g.mailu.provides: email) and shared entity-name services are both recognized.
- cli.administration.inventory.provision.services_disabler.find_roles_with_service(service_name: str, roles_dir: Path) set[str]¶
Return all role IDs whose meta/services.yml declares the given service as a consumer/provider — i.e. the entry carries at least one of
enabled/shared/provides. Pure metadata entries (e.g. a primary entity that only carrieslifecycle/run_after) are intentionally excluded so disable doesn’t add bogusenabled: falseoverrides that turn metadata into spurious provider declarations downstream.Per the file root of meta/services.yml IS the services map (no compose.services wrapper).
- cli.administration.inventory.provision.services_disabler.find_services_disabled_conflicts(inventory_dir: Path, services: list[str], roles_dir: Path) list[str]¶
Return human-readable conflicts when disable disagrees with the existing inventory/host_vars state.
- cli.administration.inventory.provision.services_disabler.parse_services_disabled(env_value: str) list[str]¶
Parse a space- or comma-separated list of service names.
cli.administration.inventory.provision.subprocess_runner module¶
- cli.administration.inventory.provision.subprocess_runner.run_subprocess(cmd: list[str], capture_output: bool = False, env: dict[str, str] | None = None, ok_returncodes: tuple[int, ...] = (0,)) CompletedProcess¶
Run a subprocess command and either stream output or capture it. Raise SystemExit on a return code outside ok_returncodes.
cli.administration.inventory.provision.tor_node module¶
Provision the node onion address into services.tor.node.
The onion identity (key files) is minted/reused by
cli.administration.inventory.onion; this provisioner is the single spot that
writes the resulting address into
applications.svc-net-tor.services.tor.node of the host_vars — so consumers
read it via lookup('config', 'svc-net-tor', 'services.tor.node') with no env
indirection.
cli.administration.inventory.provision.users_generator module¶
Pin a password on every user the resolved role set needs.
A user definition that declares no password falls back to
{{ 42 | strong_password }}. That is a template, not a value: it is
re-rendered at every use site, so the task that creates an account and the
task that later authenticates as it read two different secrets. Inventory
creation is where the value gets decided once, next to the role credentials
that are already generated there.
- cli.administration.inventory.provision.users_generator.generate_user_passwords(roles_dir: Path, application_ids: list[str], host_vars_file: Path, vault_password_file: Path) int¶
Write a vaulted password for every required user that has none yet.
- Parameters:
roles_dir – directory the roles live in.
application_ids – the roles resolved into this inventory.
host_vars_file – inventory file the passwords are written into.
vault_password_file – vault password used to encrypt each value.
- Returns:
How many users received a freshly generated password.
- cli.administration.inventory.provision.users_generator.required_user_policies(roles_dir: Path, application_ids: list[str]) dict[str, dict[str, str | None]]¶
Return every declared username with the password policy it asks for.
- Parameters:
roles_dir – directory the roles live in.
application_ids – the roles resolved into this inventory.
A role that declares no users contributes none, so the result follows the deployment rather than everything the repository could ever deploy. Two roles sharing a user must agree on its policy: the account is one thing, so silently picking one declaration would hand the other role a password its application rejects.
cli.administration.inventory.provision.yaml_io module¶
Module contents¶
- cli.administration.inventory.provision.main(argv: list[str] | None = None) int¶