Interactions — magnum — branch stable/2026.2
Branch
stable/2026.2@1910928b7d9f— repository https://opendev.org/openstack/magnum — analysed on 2026-11-09
Magnum core talks to six kinds of systems: the client (REST), RabbitMQ (RPC between magnum-api and magnum-conductor, plus notifications), MariaDB, Keystone (token validation and the service catalogue), the OpenStack services used to validate requests and store certificates (Glance, Nova, Neutron, Barbican, Cinder, Octavia), and the Kubernetes API of each cluster (health). Everything a driver does (for the Cluster API driver: the Kubernetes API of the management cluster and the OpenStack APIs through CAPO) is described in 08.
flowchart LR
client[Client / Skyline / openstack CLI] -->|HTTPS REST :9511| api[magnum-api]
api -->|GET /v3/auth/tokens| ks[Keystone]
api -->|AMQP cast / call topic magnum-conductor| mq{{RabbitMQ}}
mq --> cond[magnum-conductor]
api -->|SQL - uWSGI mode| db[(MariaDB)]
cond -->|SQL| db
api -->|REST validation| glance[Glance] & nova[Nova] & neutron[Neutron]
cond -->|REST secrets + containers| barbican[Barbican]
cond -->|AMQP notifications magnum.cluster.*| mq
cond -->|HTTPS mTLS /healthz /api/v1/nodes| k8s[Cluster Kubernetes API]
cond -->|Python driver API| drv[Driver e.g. magnum-cluster-api]
drv -->|HTTPS| mgmt[Management cluster API: CAPI objects]RabbitMQ
Topology
| Exchange | Type | Routing key / queue | Producer | Consumer |
|---|---|---|---|---|
[DEFAULT]/control_exchange (default openstack, magnum in most deployments [inferred]) | topic | magnum-conductor (shared queue) | magnum-api (magnum/conductor/api.py:28) | every conductor worker (magnum/common/rpc_service.py:38) |
| same | topic | magnum-conductor.<server> (per-worker queue; server = random 12-character id, magnum/cmd/conductor.py:57) | nobody in core (only ListenerAPI, magnum/conductor/api.py:190) | the worker |
reply_<uuid> | direct | reply queue of each API worker | conductor | magnum-api (call replies) |
| control exchange | topic | notifications.info, notifications.error ([oslo_messaging_notifications]/topics) | magnum-conductor (magnum/common/rpc.py:59) | Ceilometer, CloudKitty, auditors [external] |
The transport is created from [DEFAULT]/transport_url (magnum/common/rpc.py:56). Clients are cached per (topic, server, timeout) for the life of the process (magnum/common/rpc_service.py:78).
RPC methods
| Method | Direction | Mode | Topic / server | Version | Arguments | Response | Defined at |
|---|---|---|---|---|---|---|---|
cluster_create | api → conductor | cast | magnum-conductor | none | cluster (Cluster object, not yet saved), master_count, node_count, create_timeout | — | magnum/conductor/api.py:39 |
cluster_update | api → conductor | cast | idem | none | cluster, node_count, health_status, health_status_reason, rollback | — | magnum/conductor/api.py:58 |
cluster_delete | api → conductor | cast | idem | none | uuid | — | magnum/conductor/api.py:48 |
cluster_resize | api → conductor | cast | idem | none | cluster, node_count, nodes_to_remove, nodegroup (NodeGroup object) | — | magnum/conductor/api.py:76 |
cluster_upgrade | api → conductor | call | idem | none | cluster, cluster_template, max_batch_size, nodegroup | Cluster object | magnum/conductor/api.py:84 |
nodegroup_create / nodegroup_update / nodegroup_delete | api → conductor | cast | idem | none | cluster, nodegroup | — | magnum/conductor/api.py:163, magnum/conductor/api.py:177, magnum/conductor/api.py:170 |
sign_certificate | api → conductor | call | idem | none | cluster, certificate (Certificate object with csr) | Certificate with pem | magnum/conductor/api.py:125 |
get_ca_certificate | api → conductor | call | idem | none | cluster, ca_cert_type | Certificate with pem | magnum/conductor/api.py:129 |
rotate_ca_certificate | api → conductor | call | idem | none | cluster | Cluster | magnum/conductor/api.py:133 |
credential_rotate | api → conductor | call | idem | none | cluster | — | magnum/conductor/api.py:182 |
federation_create / federation_update / federation_delete | api → conductor | cast | idem | none | federation, create_timeout / rollback / uuid | — (raises) | magnum/conductor/api.py:106, magnum/conductor/api.py:119, magnum/conductor/api.py:113 |
object_class_action_versions | api → conductor | call | idem | none | objname, objmethod, object_versions, args, kwargs | object primitive or value | magnum/conductor/api.py:138 |
object_action | api → conductor | call | idem | none | objinst, objmethod, args, kwargs | (updates, result) | magnum/conductor/api.py:146 |
object_backport_versions | api → conductor | call | idem | none | objinst, object_versions | object primitive | magnum/conductor/api.py:151 |
ping_conductor | conductor → conductor | call | <topic>.<server> | none | — | true | magnum/conductor/api.py:195 |
Object versions on the wire (magnum_object.version): Cluster 1.24 (magnum/objects/cluster.py:63), ClusterTemplate 1.21 (magnum/objects/cluster_template.py:47), NodeGroup 1.2 (magnum/objects/nodegroup.py:32), Certificate 1.2, X509KeyPair 1.2, Federation 1.0, Quota 1.0, MagnumService 1.0, Stats 1.0.
Notifications
Only cluster operations are notified; templates, nodegroups, quotas and certificates are not.
| event_type | Priority | Emitted when | Payload | Emitted at |
|---|---|---|---|---|
magnum.cluster.create | info (pending, success) / error (failure) | conductor starts a create; the poller sees CREATE_COMPLETE / CREATE_FAILED; the conductor catches an exception | CADF event (below) | magnum/conductor/handlers/cluster_conductor.py:64, magnum/service/periodic.py:78 |
magnum.cluster.update | info / error | update, resize, upgrade, CA rotation start, refusal or failure; poller sees UPDATE_* or ROLLBACK_* final states | CADF event | magnum/conductor/handlers/cluster_conductor.py:143, magnum/conductor/handlers/cluster_conductor.py:242, magnum/conductor/handlers/ca_conductor.py:104 |
magnum.cluster.delete | info / error | delete start or failure; poller sees DELETE_COMPLETE / DELETE_FAILED | CADF event | magnum/conductor/handlers/cluster_conductor.py:175, magnum/service/periodic.py:78 |
publisher_id is container-infra.<[DEFAULT]/host or FQDN> (magnum/common/rpc.py:168). The priority is error when the CADF outcome is failure, info otherwise (magnum/conductor/utils.py:149). The event is built with pycadf (magnum/conductor/utils.py:128): eventType activity, action (create, update, delete), outcome (pending, success, failure), initiator (user, project, domain of the request; empty for the periodic tasks, which run as admin), observer and target of type service/magnum/cluster, and a cluster_data attachment with the cluster fields (magnum/conductor/utils.py:93). One attachment key is misspelt with a trailing colon: docker_volume_size: (magnum/conductor/utils.py:108).
Sample frame (cast cluster_create, AMQP body)
{"oslo.version": "2.0",
"oslo.message": "{\"method\": \"cluster_create\", \"namespace\": null, \"version\": null,
\"args\": {\"cluster\": {\"magnum_object.name\": \"Cluster\", \"magnum_object.namespace\": \"magnum\",
\"magnum_object.version\": \"1.24\",
\"magnum_object.data\": {\"uuid\": \"5d12f6fd-…\", \"name\": \"k8s-prod\",
\"cluster_template_id\": \"0b1c…\", \"keypair\": \"ops\", \"labels\": {\"kube_tag\": \"v1.31.4\"},
\"flavor_id\": \"m1.large\", \"master_flavor_id\": \"m1.medium\", \"project_id\": \"…\", \"user_id\": \"…\",
\"master_lb_enabled\": true, \"floating_ip_enabled\": true, \"coe_version\": null, \"container_version\": null},
\"magnum_object.changes\": [\"uuid\", \"name\", \"…\"]},
\"master_count\": 3, \"node_count\": 4, \"create_timeout\": 60},
\"_unique_id\": \"…\", \"_context_auth_token\": \"gAAAA…\", \"_context_user_id\": \"…\",
\"_context_project_id\": \"…\", \"_context_roles\": [\"member\", \"reader\"], \"_context_is_admin\": false,
\"_context_request_id\": \"req-…\", \"_context_auth_token_info\": {…}}"}
The user's Keystone token and the full token body travel in the context of every message (magnum/common/context.py:66, magnum/common/context.py:79; the context also carries all_tenants, magnum/common/context.py:80): the conductor reuses them to call OpenStack services on the user's behalf. A cast that waits in the queue longer than the token lifetime fails with AuthorizationFailure in the conductor.
Database
oslo.db / SQLAlchemy, [database]/connection. The schema has no foreign keys: integrity is enforced by code (magnum/db/sqlalchemy/models.py). Alembic head: b3e1cf4a2d5e (magnum/db/sqlalchemy/alembic/versions/b3e1cf4a2d5e_add_node_labels_and_node_taints.py).
Tables
| Table | Role | Written by | Read by |
|---|---|---|---|
cluster | one row per cluster: template, flavors, labels, status, health, API address, certificate references, stack_id (driver's id of the cluster) | conductor (create, update, poller, delete); API only through objects in uWSGI mode | API (all routes), poller, health poller |
nodegroup | sizes, flavors, images, labels, node labels and taints, status per role (master, worker); unique (cluster_id, name) | conductor, drivers | API (cluster sizes and addresses are sums over this table) |
cluster_template | templates; public / hidden flags | API (create, patch, delete) | API, conductor (driver selection) |
x509keypair | certificates and encrypted private keys with their passphrases, when [certificates]/cert_manager_type = x509keypair | conductor | conductor |
quotas | (project_id, resource) → hard_limit | API (admin) | API (cluster create) |
magnum_service | conductor heartbeats | conductor | API (/v1/mservices) |
federation | federations (never written by the API, see 01) | nobody | API |
Notable transactions
- Template update locks the row (
SELECT … FOR UPDATE), then checks whether a cluster uses it before allowing anything other than rename or (un)publish (magnum/db/sqlalchemy/api.py:436):sql SELECT * FROM cluster_template WHERE uuid = :id FOR UPDATE; SELECT 1 FROM cluster WHERE cluster_template_id = :uuid LIMIT 1; UPDATE cluster_template SET … WHERE id = :id; - Cluster update (
save()) also usesSELECT … FOR UPDATEthenUPDATE(magnum/db/sqlalchemy/api.py:267), wrapped inretry_on_deadlock. There is no optimistic version column: two conductors saving the same cluster overwrite each other's fields. - Template visibility is a
UNIONof the project's templates, the public non-hidden ones and, for admins, the public hidden ones (magnum/db/sqlalchemy/api.py:294). - Cluster list by size: filtering on
node_countsumsnodegroup.node_countgrouped bycluster_id(magnum/db/sqlalchemy/api.py:139). - Stats:
SELECT COUNT(*) FROM cluster [WHERE project_id = :p]andSELECT SUM(node_count) FROM nodegroup [WHERE project_id = :p](magnum/db/sqlalchemy/api.py:221). - Status poller:
SELECT * FROM cluster WHERE status IN (…4 states…)every 10 s (magnum/service/periodic.py:166), then one nodegroup list per cluster.
Outbound REST calls
All OpenStack clients are openstacksdk connections built from a keystoneauth session (magnum/common/clients.py). In the API and the conductor the session reuses the user's token (AccessInfoPlugin built from keystone.token_info, magnum/common/keystone.py:66). Without a token, i.e. in the periodic tasks and for the Barbican admin client, it uses the service credentials of [keystone_auth] (magnum/common/keystone.py:73). The endpoint comes from the catalogue, with [<service>]/region_name and endpoint_type (publicURL by default) (magnum/common/clients.py:44).
| Target service | Method + path | When | Authentication | Timeout / retry | Errors handled | Defined at |
|---|---|---|---|---|---|---|
| Keystone | GET /v3/auth/tokens | every authenticated request | service token (keystonemiddleware [keystone_authtoken]) | keystonemiddleware defaults | 401 to the client | etc/magnum/api-paste.ini:7, magnum/api/middleware/auth_token.py:60 |
| Keystone | GET /v3/projects/{id} | POST /v1/quotas | user token | sdk defaults | 404 → ProjectNotFound | magnum/api/validation.py:148 |
| Keystone | GET /v3/regions | region name validation (Cinder helpers) | user token | — | → RegionsListFailed | magnum/common/keystone.py:100 |
| Glance | GET /v2/images/{id} or GET /v2/images?name= (find_image) | template create (twice: driver resolution and validation), template patch | user token | sdk defaults | 404 → ImageNotFound, 403 → ImageNotAuthorized, duplicate → 409 | magnum/api/validation.py:76, magnum/api/attr_validator.py:36 |
| Nova | GET /flavors/detail (all flavors) | every flavor check: template create/patch, cluster create/patch, nodegroup create/patch — up to 4 full listings per request | user token | — | → FlavorNotFound | magnum/api/attr_validator.py:59, magnum/api/attr_validator.py:191 |
| Nova | GET /os-keypairs/{name} | cluster create with a keypair | user token | — | 404 → KeyPairNotFound | magnum/api/attr_validator.py:74 |
| Neutron | GET /v2.0/networks?router:external=True, GET /v2.0/networks, GET /v2.0/subnets (all, filtered in Python) | template and cluster validation | user token | — | → *NotFound, 409 on duplicates | magnum/api/attr_validator.py:83, magnum/api/attr_validator.py:104, magnum/api/attr_validator.py:126 |
| Barbican | POST /v1/secrets (×2–4), POST /v1/containers | each certificate stored (4 per cluster create, 4 per CA rotation) | Magnum service credentials (admin client, magnum/common/cert_manager/barbican_cert_manager.py:73) | — | rollback of created secrets, then CertificatesToClusterFailed | magnum/common/cert_manager/barbican_cert_manager.py:95 |
| Barbican | GET /v1/containers/{id}, GET /v1/secrets/{id}/payload | CA download, CSR signing, every health poll (unless a cache dir exists) | service credentials | — | exception logged and re-raised | magnum/common/cert_manager/barbican_cert_manager.py:170 |
| Barbican | DELETE /v1/containers/{id} and secrets | cluster deleted (Kubernetes CA and client certificate only) | service credentials | — | warning, continue | magnum/common/cert_manager/barbican_cert_manager.py:195 |
| Cluster Kubernetes API | GET {api_address}/healthz, GET {api_address}/api/v1/nodes | health poller, every 60 s per cluster | mTLS with the Magnum client certificate (CN=admin, O=system:masters) and the cluster CA | no timeout, no retry | exception → health unchanged | magnum/conductor/k8s_api.py:44 |
Because the certificates are created with the service credentials, the Barbican secrets belong to the Magnum service project, not to the user's project. A cluster deleted while Barbican is unreachable leaves its secrets behind, and the etcd and front-proxy CAs are never deleted (magnum/conductor/handlers/common/cert_manager.py:240).
Certificate storage back ends
[certificates]/cert_manager_type | Storage | Private key protection | Defined at |
|---|---|---|---|
barbican (default) | one Barbican certificate container per certificate | key encrypted with a random passphrase, stored as a separate secret in the same container | magnum/common/cert_manager/barbican_cert_manager.py:95 |
x509keypair | x509keypair table | the encrypted key and its passphrase are in the same row | magnum/common/cert_manager/x509keypair_cert_manager.py:47 |
local | files under [certificates]/storage_path (/var/lib/magnum/certificates/) on the conductor host | same | magnum/common/cert_manager/local_cert_manager.py |
Certificate formats (magnum/common/x509/operations.py): RSA keys of [x509]/rsa_key_size bits (2048), SHA-256 signatures, CA with BasicConstraints(ca=True, path_length=0) and key usage keyCertSign (magnum/common/x509/operations.py:83), client certificates with clientAuth (magnum/common/x509/operations.py:68), validity [x509]/term_of_validity days (1825).
Other
- Files: when
[cluster]/temp_cache_dir(/var/lib/magnum/certificate-cache) exists, the conductor caches each cluster's CA, client key and certificate in clear text under<dir>/<cluster_uuid>/, mode 0600 (magnum/conductor/handlers/common/cert_manager.py:178). The cache is never refreshed after a CA rotation, so the health poller keeps using the old client certificate [inferred: the cache directory is only written when absent,magnum/conductor/handlers/common/cert_manager.py:184]. Otherwise temporary files are created for each poll. - Drivers: loaded with stevedore from the
magnum.driversentry point (magnum/drivers/common/driver.py:36);[drivers]/disabled_drivershides some. - No subprocess, rootwrap, privsep, cache (Memcached/Redis) or coordination back end is used by Magnum core.
Source: reverse-spec-magnum/03-interactions_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.