CI-Framework is an Ansible collection (cifmw.general) that bootstraps
development and CI environments for RHOSO (Red Hat OpenStack Services on
OpenShift). It is not intended for production or long-lived deployments.
The upstream repository lives at
https://github.com/openstack-k8s-operators/ci-framework.
- Ansible collection (requires
ansible-core >= 2.15). - Python 3 modules and plugins under
plugins/. - Molecule + Podman for per-role testing.
- ansible-test for unit, sanity, and integration tests on plugins.
- Sphinx for documentation (hosted on ReadTheDocs).
- Zuul, GitHub Actions, and Prow for CI.
| Path | Description |
|---|---|
roles/ |
Ansible roles. Each has defaults/, tasks/, molecule/, README.md. |
playbooks/ |
Domain-specific playbooks in subdirectories (adoption/, ceph/, bgp/, etc.). Legacy numbered-stage playbooks are deprecated -- orchestration is handled by the cifmw_setup role. |
plugins/ |
Collection plugins: action/, filter/, modules/, module_utils/. Test with ansible-test, not Molecule. |
tests/ |
ansible-test suites: unit/ (pytest), integration/targets/, sanity/ignore.txt. |
ci/ |
CI-only playbooks: content provider, EDPM, kuttl, architecture validation, doc build, log collection. |
scenarios/ |
Scenario-oriented variable packs used by framework flows. |
scripts/ |
Environment setup, Molecule runner, ansible-test runner, Zuul/Molecule generation, snippet checks. |
docs/ |
Sphinx sources under docs/source/. |
hooks/ |
Hook playbooks consumed by the framework. Some hooks have their own roles/ subdirectory. |
custom/ |
Local overrides (gitignored except README.md). Safe for local dev experiments, never committed. |
containerfiles/ |
Podman images for CI (Containerfile.ci, Containerfile.tests). |
group_vars/ |
Shared group variables (e.g., all.yml). Changes here affect every playbook run. |
zuul.d/ |
Zuul job and project definitions. Some files are generated -- see below. |
_skeleton_role_/ |
Template used by ansible-galaxy role init when creating new roles. |
All Ansible role variables must match the pattern ^cifmw_[a-z_][a-z0-9_]*$
where after the cifmw_ prefix comes the role name, then the variable name
(e.g., cifmw_my_role_some_setting).
This is enforced by ansible-lint with strict: true and profile: production.
All module calls must use fully-qualified collection names.
The following FQCN rules are enabled in .ansible-lint:
fqcn-builtins, fqcn[action], fqcn[action-core], fqcn[canonical], fqcn[deep].
The following files are generated by scripts/create_role_molecule.py:
zuul.d/molecule.yamlzuul.d/projects.yaml(molecule section)
To regenerate: make role_molecule. To verify consistency: make check_zuul_files.
If you hand-edit these files, CI will reject the change.
Do not modify these paths directly:
| Path | Reason |
|---|---|
zuul.d/molecule.yaml |
Generated by scripts/create_role_molecule.py. |
zuul.d/projects.yaml |
Generated (molecule section). |
custom/ |
Gitignored. Local-only overrides, never committed. |
hooks/playbooks/roles/ |
Excluded from ansible-lint. Owned by hook authors. |
All other paths (roles/, playbooks/, plugins/, group_vars/, scenarios/,
hooks/playbooks/, ci/, scripts/, docs/) are safe to edit following the
conventions in this file.
Use block/rescue/always for complex task sequences. Dump relevant
variables in the rescue block, then ansible.builtin.fail to stop
execution. This makes CI failures much easier to diagnose.
There is no need to provide explanation for each variable in each task file. It is enough when the variable description is available in the role README file. Add comments only to complex code.
Balance the amount of variables: there is no need to create additional variables especially for static values that are only used once by another module or role. In that case, fewer variables means more clarity.
Tasks should not do too many things in a single step -- on failure that becomes difficult to debug. In some cases, adding more small, fast tasks is better than one large task. Add debug messages only in very complex places, not everywhere.
Do not rely on the playbooks/ directory as the primary orchestration layer.
The numbered-stage playbooks (01-bootstrap.yml, 02-infra.yml, etc.) are
deprecated -- orchestration is now handled by the cifmw_setup role.
The playbooks/ directory still contains:
- Subdirectories (
adoption/,ceph/,bgp/,dcn/,multi-namespace/) with domain-specific flows. - Standalone playbooks (
hooks.yml,update.yml,dcn.yml,nfs.yml,switches_config.yml, etc.) for specific operations.
Always use the Makefile:
make new_role ROLE_NAME=my_role
This generates the skeleton, Molecule config, and updates Zuul jobs. Every new role must have:
- A
README.mddocumenting its parameters. - Molecule test scenarios.
- Documentation that builds cleanly (checked in CI).
If the role cannot be tested via Molecule, remove the molecule/ directory
and run make role_molecule to regenerate Zuul jobs. Add a note in the
role's README.md explaining why.
| Command | What it does |
|---|---|
make pre_commit |
Runs pre-commit hooks (shellcheck, black, ansible-lint) with dependency install. |
make molecule |
Runs Molecule tests for all roles with dependency install. |
make ansible_test |
Runs ansible-test (units + sanity + integration) with dependency install. |
make tests |
Runs pre-commit + Molecule. |
make check_zuul_files |
Regenerates Zuul YAML and fails if it differs from committed files. |
make docs |
Builds Sphinx documentation under docs/_build/html/. |
make spelling |
Runs pyspelling on docs. |
make plugin-development-enable |
Rewrites import paths and sets PYTHONPATH for local plugin dev. |
make plugin-development-disable |
Reverts the changes made by plugin-development-enable. |
| Command | What it does |
|---|---|
make run_ctx_pre_commit |
Pre-commit in a container. |
make run_ctx_molecule |
Molecule in a container. |
make run_ctx_ansible_test |
ansible-test in a container. |
make run_ctx_all_tests |
All of the above. |
- Config:
.config/molecule/config_podman.yml(host) orconfig_local.yml(container). - Test a single role:
TEST_SINGLE_ROLE=my_role make moleculeormake run_ctx_molecule. - Molecule scenarios live under
roles/<name>/molecule/.
When verifying a change, run checks in this order:
pre-commit run --all-files— fast lint pass (ansible-lint, black, shellcheck).TEST_SINGLE_ROLE=<role> make molecule— targeted Molecule test.make ansible_test— plugin unit/sanity/integration tests (only if plugins changed).make docs— only if documentation was modified.
The Makefile test targets (make tests, make molecule, make ansible_test,
make setup_tests, make setup_molecule, and their _nodeps variants) do not
work on macOS. The underlying scripts use readlink -f, which is not available
on macOS. These targets are designed for Linux CI environments only.
- ansible-lint:
productionprofile,strict: true. Config in.ansible-lint. - Python: Formatted with
black. - Shell: Checked with
shellcheck(severity=error, excludes SC2071). - Pre-commit: Config in
.pre-commit-config.yaml. Run withmake pre_commitormake run_ctx_pre_commit. - Spelling:
pyspellingon docs. Run withmake spelling.
ansible-lint skips: .github/, scripts/, docs/, containerfiles/, ci/,
and the generated Zuul files (zuul.d/projects.yaml, zuul.d/molecule.yaml).
- Title: Must begin with the role name in brackets or parentheses:
[my_role] Add feature Xor(my_role) Fix bug Y. If changes span multiple roles, use[multiple]or(multiple). For cross-cutting changes use a category:[ci],[docs],[Feature]. - Body: Must be longer than 10 characters and describe why the change was made.
- Sign-off: Required (
git commit --signoff). The sign-off certifies a DCO. AI agents cannot sign off on behalf of a human -- the committer must add it themselves or amend the commit. - AI attribution: Use
Co-Authored-By:for substantial AI-generated code,Assisted-By:for minor AI help. Disclose the scope in the PR description. - Ticket references: Link Jira cards in the commit message body:
Closes: ANVIL-123(resolves the ticket) orRelated-Issue: #OSPRH-12345(related but does not close). - Cross-repo dependencies: When a change depends on an unmerged PR/MR in
another repository, add
Depends-On: <PR-or-MR-URL>in the PR/MR description. Zuul uses this to test the changes together.
To keep a clean git history, prefer a single commit per feature or fix:
- Create the initial commit normally.
- For subsequent changes on the same branch, amend the existing commit
(
git commit --amend) instead of creating new ones. - After amending, use
git push --forceto update the remote branch.
Never push directly to main — it is a protected branch. Always work on
a feature branch. Force pushing is only appropriate for solo feature
branches, never for main or shared branches.
If a change is not directly related to the main goal of the pull request but is required for it to work, add it as a separate commit. When amending, be careful to edit only the commits that belong to the same pull request.
- The default branch is
main. - Feature work happens on topic branches.
- PRs target
mainunless otherwise specified. - Branch names should be descriptive (e.g.,
fix-reproducer-pull-secret,feature/OSPRH-12345-new-role).
- PRs are auto-set to draft on open. To undraft, push a non-
nit:change. - Minimum 2 approvals required (excluding the author).
- Security-sensitive code requires additional maintainer review.
- Ownership is defined in
OWNERSandOWNERS_ALIASES.
The ci-framework-jobs repository holds downstream Zuul job definitions that
consume this repository. Jobs in that repo declare
required-projects: openstack-k8s-operators/ci-framework and
roles: zuul: openstack-k8s-operators/ci-framework so Zuul checks out this
repo and exposes its roles during job execution. Uni jobs orchestrate this
repo's reproducer.yml playbook as their main entry point.
When making changes here that affect CI behavior, coordinate with the
corresponding job definitions in ci-framework-jobs.
To develop collection plugins locally without installing the collection:
make plugin-development-enable
This rewrites import paths and sets PYTHONPATH. Revert with:
make plugin-development-disable
Plugins are tested with ansible-test, not Molecule.
Before searching the web or relying on general knowledge, check local documentation:
docs/source/— Sphinx sources for the ci-framework collection.- Role-level
README.mdfiles underroles/<name>/. - The downstream CI docs repository at
https://gitlab.cee.redhat.com/ci-framework/docscovers job types, pipelines, troubleshooting, and glossary.
Before performing expensive or broad-impact operations, confirm with the user first:
- Running full test suites (
make tests,make molecule) — ask whether a targeted run (TEST_SINGLE_ROLE=<role>) is sufficient. - Modifying
group_vars/all.yml— changes here affect every playbook run. - Editing roles used by multiple playbooks — flag the blast radius.
- Cross-repo changes that require coordinated updates in
ci-framework-jobsorarchitecture.