Inventory Providers
Inventory in the Syncer is module-pluggable: each module that has a host (or site, or device, …) catalogue can register a provider. The same data is then reachable through one CLI command and one HTTP endpoint, in the format the consumer expects — currently the standard Ansible JSON shape, with room for additional formats next to it.
The architecture is what lets the bundled UI runner, the cmdbsyncer-inventory plugin, and a remote AWX execution environment all see identical data without any of them caring which module produced it.
Concepts
┌─────────────────────────┐
│ Modules (plugins) │
│ │
│ Ansible host inventory │ ← provider 'ansible'
│ Checkmk Sites │ ← provider 'cmk_sites'
│ …future modules │ ← provider '…'
└────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Inventory Registry │
│ application.modules. │
│ inventory │
└────────────┬────────────┘
│
┌───────────────────────┼───────────────────────┐
▼ ▼ ▼
┌───────────┐ ┌───────────┐ ┌────────────┐
│ CLI │ │ HTTP │ │ Future │
│ inventory│ │ /api/v1/ │ │ formats │
│ ansible │ │ inventory│ │ (DCD,…) │
└─────┬─────┘ └─────┬─────┘ └────────────┘
│ │
▼ ▼
shared render function: render_ansible_inventory(provider, host=…)
A provider is any object with two methods:
class MyProvider:
def get_full_inventory(self) -> dict: ...
def get_host_inventory(self, hostname: str) -> dict | False: ...
The shape returned matches the Ansible script contract:
{
"_meta": {"hostvars": {"<host>": {"ansible_host": "10.0.0.1", ...}}},
"all": {"hosts": ["<host>", ...]}
}
A format adapter consumes the registry. Today the only adapter renders Ansible JSON — both for the CLI (cmdbsyncer ansible inventory <provider>) and for the HTTP endpoint (/api/v1/inventory/ansible/<provider>). Both call the same render_ansible_inventory(provider, host=…) function, so changing one cannot drift from the other.
Bundled providers
| Name | Source | Used by |
|---|---|---|
ansible |
application/plugins/ansible/inventory.py:AnsibleInventory — the host catalogue rendered by the Ansible filter / rewrite / custom-attribute rules without a project assignment. |
The default for every playbook in the manifest unless overridden. |
cmk_sites |
application/plugins/ansible/site_syncer.py:SyncSites — the Checkmk site catalogue. |
cmk_server_mngmt.yml and cmk_omd_cleanup.yml (declared in the bundled manifest). |
<project-name> |
Each enabled Ansible Project becomes its own provider, rendered through the rules assigned to that project (strict isolation). | Set the manifest's inventory: field to the project name. |
List what your installation has registered:
cmdbsyncer ansible list-inventory-providers
Or via HTTP:
curl -H "x-login-user: USER:SECRET" https://syncer/api/v1/inventory/ansible
Reaching a provider
CLI (local Syncer)
Honors the standard Ansible inventory-script contract:
cmdbsyncer ansible inventory <provider> --list
cmdbsyncer ansible inventory <provider> --host=<hostname>
This is what the cmdbsyncer-inventory plugin runs in local mode.
HTTP (remote Syncer)
GET /api/v1/inventory/ansible/<provider>
GET /api/v1/inventory/ansible/<provider>?host=<hostname>
GET /api/v1/inventory/ansible # list registered providers
Auth: x-login-user: USER:SECRET header (or basic auth). The user needs the ansible API role.
Per-playbook selection (UI runner)
The Run Playbook runner picks the provider from the manifest entry's inventory: field and exports CMDBSYNCER_INVENTORY_PROVIDER into the spawned ansible-playbook. The cmdbsyncer-inventory plugin reads that env var and serves the matching provider — so the same ansible/syncer.inventory.yml works for every playbook.
# ansible/playbooks.yml
playbooks:
- file: cmk_server_mngmt.yml
name: "Checkmk: Manage OMD Server"
inventory: cmk_sites # ← picks the cmk_sites provider for this run
Registering a provider in your own module
# application/plugins/yourmodule/__init__.py
from application.modules.inventory import register_inventory_provider
def _build_provider():
return YourCatalogProvider() # any object with the two methods
register_inventory_provider('yourmodule', _build_provider)
That's it — the new provider is now reachable via:
cmdbsyncer ansible inventory yourmodule --list
curl https://syncer/api/v1/inventory/ansible/yourmodule
…and selectable from the playbook manifest inventory: field, the cmdbsyncer-inventory plugin's provider: option, or the CMDBSYNCER_INVENTORY_PROVIDER env var.
Why a registry instead of one endpoint per module?
Earlier the per-module shell wrappers (ansible/inventory, ansible/cmk_server_inventory, ansible/rest_inventory, …) each shelled cmdbsyncer ansible source or its sibling commands. The wrappers are still on disk as thin proxies for backward-compat — they now exec the new cmdbsyncer ansible inventory <provider> command — but the recommended path forward is the YAML plugin spec at ansible/syncer.inventory.yml driven by cmdbsyncer-inventory. The previous shape worked, but:
- It coupled "what data to serve" with "what filename to point Ansible at", so adding a new module meant adding a new shell file in the repo.
- The wrappers required the Syncer checkout to be the working directory.
- Per-format additions (a Checkmk-DCD endpoint, etc.) would have meant duplicating the dispatch in every module.
With the registry, modules expose data; format adapters expose endpoints. Adding a module touches one plugin; adding a format touches one adapter. The two axes don't multiply.