Cluster API driver (magnum-cluster-api) — magnum — branch stable/2026.2
Driver repository https://github.com/vexxhost/magnum-cluster-api @ v0.39.0 (066fdc2) — analysed on 2026-11-09 — paired with Magnum stable/2026.2 @ 1910928
Reference convention: magnum-cluster-api:<path>:<line> points into the driver clone at tag v0.39.0 (full SHA 066fdc23d45f2d0f9a0633bc7c9b61e2f64e4268); magnum:<path>:<line> points into the Magnum clone at 1910928. Statements marked [inferred] were not traced to the end.
Tag choice: v0.39.0 is the latest tag and the first release whose changelog says it runs under Magnum 2026.2 ("run under Magnum 2026.2, which dropped eventlet and novaclient", magnum-cluster-api:CHANGELOG.md:17). The driver supports both eventlet (≤ 2026.1) and native threads (2026.2) by detecting the eventlet patcher (magnum-cluster-api:magnum_cluster_api/utils.py:63-91).
1. Overview
magnum-cluster-api is an out-of-tree Magnum driver that turns a Magnum cluster into a **Cluster API (CAPI) topology-managed Cluster** reconciled by the CAPI core, kubeadm bootstrap / control-plane providers and CAPO (Cluster API Provider OpenStack). The driver itself never creates Nova servers: it writes Kubernetes objects into a management cluster (namespace magnum-system) and reads their status back.
Code layout:
| Part | Language | Role |
|---|---|---|
magnum_cluster_api/driver.py | Python | Magnum Driver subclass: entry points called by the Magnum conductor (magnum-cluster-api:magnum_cluster_api/driver.py:46-737). |
magnum_cluster_api/resources.py | Python | Builds the CAPI Cluster (topology + ~40 variables), secrets, autoscaler release (magnum-cluster-api:magnum_cluster_api/resources.py:871-1294). |
magnum_cluster_api/utils.py | Python | Label parsing, validation, server groups, Octavia cleanup, cloud.conf generation (magnum-cluster-api:magnum_cluster_api/utils.py:144-772). |
src/ (crate magnum-cluster-api 0.39.0, pyo3 0.29, kube-rs 4) | Rust (pyo3 extension module magnum_cluster_api.magnum_cluster_api) | Driver (ClusterClass + addon ClusterResourceSets), KubeClient (generic create/replace/delete), Monitor (health) (magnum-cluster-api:src/lib.rs:26-35, magnum-cluster-api:Cargo.toml:3, magnum-cluster-api:Cargo.toml:48-49). |
magnum_cluster_api/charts/* | Helm charts | cilium, openstack-cloud-controller-manager, openstack-cinder-csi, openstack-manila-csi, k8s-keystone-auth, cluster-autoscaler, rendered with helm template (Python, external helm binary) or an in-process Rust templater. |
magnum_cluster_api/manifests/calico/*.yaml, manifests/nfs-csi | YAML | Static Calico and NFS CSI manifests (magnum-cluster-api:magnum_cluster_api/resources.py:436-462). |
magnum_cluster_api/proxy/* | Python | Optional magnum-cluster-api-proxy daemon: HAProxy in Neutron network namespaces for clusters without API floating IP (magnum-cluster-api:magnum_cluster_api/proxy/manager.py:333-353). |
Key design facts:
- One shared ClusterClass per driver release, named
magnum-<git describe>(e.g.magnum-v0.39.0) (magnum-cluster-api:src/lib.rs:17-18), re-applied on every create/upgrade (magnum-cluster-api:src/driver.rs:302-319). All per-cluster differences are topology variables consumed by ClusterClass JSON patches (magnum-cluster-api:src/resources.rs:144-153). - CAPI name ≠ Magnum UUID: the CAPI
Clusteris namedkube-<5 random [a-z0-9]>and stored in Magnum'scluster.stack_id(magnum-cluster-api:magnum_cluster_api/utils.py:113-123, magnum-cluster-api:magnum_cluster_api/driver.py:81-82). - Status is polled, not watched: Magnum's periodic
sync_cluster_statuscallsupdate_cluster_status(magnum:magnum/service/periodic.py:66-71, magnum:magnum/service/periodic.py:161-169). - No Heat,
needs_trust = False(magnum-cluster-api:magnum_cluster_api/driver.py:47); CAPO/CCM/CSI authenticate with a per-cluster Keystone application credential (magnum-cluster-api:magnum_cluster_api/driver.py:98-102). - A distributed lock (sherlock
KubernetesLock, namecluster-<uuid>, namespacemagnum-system, expiry 60 s) serialises every mutating call and the status sync across conductors (magnum-cluster-api:magnum_cluster_api/sync.py:18-36, magnum-cluster-api:magnum_cluster_api/driver.py:37-43).
2. Driver interface implemented
Registration: five magnum.drivers entry points (magnum-cluster-api:pyproject.toml:68-73), one per OS; all share BaseDriver and differ only in provides (server_type is always vm, coe always kubernetes). Config options are exported with oslo.config.opts (magnum-cluster-api:pyproject.toml:75).
| Entry point | Class | provides (server_type / os / coe) | Source |
|---|---|---|---|
k8s_cluster_api_ubuntu | UbuntuDriver | vm / ubuntu / kubernetes | magnum-cluster-api:magnum_cluster_api/driver.py:740-745 |
k8s_cluster_api_ubuntu_focal | UbuntuFocalDriver | vm / ubuntu-focal / kubernetes | magnum-cluster-api:magnum_cluster_api/driver.py:748-753 |
k8s_cluster_api_debian | DebianDriver | vm / debian / kubernetes | magnum-cluster-api:magnum_cluster_api/driver.py:756-761 |
k8s_cluster_api_flatcar | FlatcarDriver | vm / flatcar / kubernetes | magnum-cluster-api:magnum_cluster_api/driver.py:764-769 |
k8s_cluster_api_rockylinux | RockyLinuxDriver | vm / rockylinux / kubernetes | magnum-cluster-api:magnum_cluster_api/driver.py:772-777 |
The Magnum cluster template's cluster_distro (image os_distro) selects the driver; get_operating_system then maps it by prefix to ubuntu|debian|flatcar|rockylinux for the operatingSystem variable (magnum-cluster-api:magnum_cluster_api/utils.py:46, magnum-cluster-api:magnum_cluster_api/utils.py:545-550).
Methods (base contract in magnum:magnum/drivers/common/driver.py:199-308):
| Method | Locked | Sync/async for Magnum API | What it does | Source |
|---|---|---|---|---|
__init__ | — | — | Builds a pykube client from KUBECONFIG/in-cluster and the Rust Driver("magnum-system") | magnum-cluster-api:magnum_cluster_api/driver.py:49-51 |
create_cluster(ctx, cluster, timeout) | inner _create_cluster locked | async (conductor) | Generates stack_id, saves it; Rust create_cluster (ClusterClass + legacy CRS + cloud-provider CRS); validates; then app credential, cloud-config secret, 4 CA secrets, server groups, addon secrets, CAPI Cluster. cluster_create_timeout is ignored. | magnum-cluster-api:magnum_cluster_api/driver.py:70-138 |
update_cluster_status(ctx, cluster) | yes | periodic | Derives KCP / nodegroup / cluster status, fills api_address, coe_version, installs autoscaler, finishes deletion. See §5. | magnum-cluster-api:magnum_cluster_api/driver.py:200-313 |
update_cluster(...) | yes | — | raise NotImplementedError() (cluster template updates are not supported). | magnum-cluster-api:magnum_cluster_api/driver.py:316-329 |
resize_cluster(ctx, cluster, mgr, node_count, nodes_to_remove, nodegroup) | yes | async | Validates; annotates the Machines whose OpenStack instance UUID is in nodes_to_remove with cluster.x-k8s.io/delete-machine: "yes"; then _update_nodegroup. | magnum-cluster-api:magnum_cluster_api/driver.py:332-372 |
upgrade_cluster(ctx, cluster, template, max_batch_size, nodegroup, ...) | yes | sync | Copies kube_tag and image_id of the new template into cluster and all nodegroups (ignores nodegroup, max_batch_size), re-applies ClusterClass/addons, replaces the CAPI Cluster topology. | magnum-cluster-api:magnum_cluster_api/driver.py:375-436 |
delete_cluster(ctx, cluster) | yes | async | No-op if stack_id is None; deletes Octavia LBs created by the in-cluster CCM; Rust deletes addon CRS/secrets; deletes CAPI Cluster; helm delete of the autoscaler. | magnum-cluster-api:magnum_cluster_api/driver.py:442-465 |
validate_master_resize(node_count) | no | sync (API) | Allows control-plane resize; rejects even or < 1 counts with MasterNGSizeInvalid (base driver raises MasterNGResizeNotSupported). | magnum-cluster-api:magnum_cluster_api/driver.py:468-470, magnum:magnum/drivers/common/driver.py:258-260 |
validate_master_size | — | — | Inherited (odd and ≥ 1). | magnum:magnum/drivers/common/driver.py:253-256 |
create_nodegroup(ctx, cluster, ng) | yes | async | Validates name (RFC 1123), ensures worker server group, appends a machineDeployments[] entry to the topology (server-side apply). | magnum-cluster-api:magnum_cluster_api/driver.py:473-499 |
update_nodegroup(ctx, cluster, ng) | yes | async | _update_nodegroup (control-plane replicas, or MD replicas/autoscaler annotations). | magnum-cluster-api:magnum_cluster_api/driver.py:613-679 |
delete_nodegroup(ctx, cluster, ng) | yes | async | Sets ng DELETE_IN_PROGRESS, removes the MD from the topology; raises heatclient HTTPNotFound if absent. | magnum-cluster-api:magnum_cluster_api/driver.py:682-720 |
get_monitor(ctx, cluster) | yes | periodic | Returns monitor.Monitor (Rust health + autoscaler replica sync). | magnum-cluster-api:magnum_cluster_api/driver.py:722-724 |
rotate_ca_certificate | — | — | Not overridden (commented out) → base raises NotSupported. | magnum-cluster-api:magnum_cluster_api/driver.py:726-728, magnum:magnum/drivers/common/driver.py:306-308 |
rotate_credential, pre_delete_cluster, get_scale_manager | — | — | Not overridden (base behaviour). | magnum:magnum/drivers/common/driver.py:228-233, magnum:magnum/drivers/common/driver.py:292-304 |
create/update/delete_federation | — | — | NotImplementedError. | magnum-cluster-api:magnum_cluster_api/driver.py:730-737 |
3. Objects created on the management cluster
3.1 Management cluster access
- Python side:
pykube.HTTPClient(pykube.KubeConfig.from_env())—KUBECONFIGenv or in-cluster service account (magnum-cluster-api:magnum_cluster_api/clients.py:173-174). - Rust side: one process-wide
kube::Client::try_default()(same resolution rules), cached in aOnceCellto avoid FD leaks (magnum-cluster-api:src/clients/kubernetes.rs:37-60). - Workload cluster access: kubeconfig read from the CAPI-generated secret
<stack_id>-kubeconfig, keyvalue, inmagnum-system(magnum-cluster-api:src/magnum.rs:247-287). Used only during upgrade to delete legacy addon workloads (magnum-cluster-api:src/driver.rs:82-105) and by the autoscaler (clusterAPIKubeconfigSecret) (magnum-cluster-api:magnum_cluster_api/resources.py:82). - There is no driver option for the management kubeconfig: everything is in the hard-coded namespace
magnum-system(magnum-cluster-api:magnum_cluster_api/driver.py:51). - Write semantics: Rust
create_or_update= GET →replacewith resourceVersion, retried with exponential backoff on HTTP 409; 404 →create(magnum-cluster-api:src/clients/kubernetes.rs:153-176). Topology edits from Python use server-side apply withfieldManager=atmosphere-operator,force=true(magnum-cluster-api:magnum_cluster_api/utils.py:589-607). Upgrade uses a GET/replace ofmetadata.labels,spec.clusterNetwork,spec.topologywith 409 retry (magnum-cluster-api:src/client.rs:73-107).
3.2 Object catalogue
| Object (apiVersion / kind) | Name | Created by / when | Content | Labels / annotations | Source |
|---|---|---|---|---|---|
v1 Namespace | magnum-system | Rust apply_cluster_class (create, upgrade) | — | — | magnum-cluster-api:src/driver.rs:210-212, magnum-cluster-api:src/driver.rs:381-391 |
infrastructure.cluster.x-k8s.io/v1beta1 OpenStackClusterTemplate | magnum-<version> | idem | identityRef {name: PLACEHOLDER, cloudName: default}, managedSecurityGroups.allowAllInClusterTraffic: true + NodePort 30000-32767 TCP/UDP from 0.0.0.0/0 | — | magnum-cluster-api:src/features/mod.rs:378-421 |
infrastructure.cluster.x-k8s.io/v1beta1 OpenStackMachineTemplate | magnum-<version> | idem | placeholder flavor / image id / identityRef | — | magnum-cluster-api:src/features/mod.rs:354-374 |
controlplane.cluster.x-k8s.io/v1beta1 KubeadmControlPlaneTemplate | magnum-<version> | idem | rolloutBefore.certificatesExpiryDays: 21; apiserver profiling=false, webhooks hostPath volume; etcd metrics 0.0.0.0:2381; controller-manager/scheduler bind-address 0.0.0.0, cloud-provider external; files: audit policy, keystone webhook kubeconfig, kube-proxy script; format cloud-config; kubelet cloud-provider=external; node name {{ local_hostname }} | — | magnum-cluster-api:src/features/mod.rs:187-313 |
bootstrap.cluster.x-k8s.io/v1beta1 KubeadmConfigTemplate | magnum-<version> | idem | join config, kubelet cloud-provider=external | — | magnum-cluster-api:src/features/mod.rs:317-350 |
cluster.x-k8s.io/v1beta1 ClusterClass | magnum-<version> | idem | control plane + one worker class default-worker; MHC (maxUnhealthy 80 %, Ready False/Unknown 5m); nodeVolumeDetachTimeout 5m0s; variables + patches from every registered feature | — | magnum-cluster-api:src/resources.rs:41-153 |
v1 Secret type cluster.x-k8s.io/secret | <stack_id>-ca, -etcd, -proxy, -sa | Python _create_cluster | tls.crt/tls.key of the Magnum-generated CAs (decrypted key) | cluster.x-k8s.io/cluster-name=<stack_id> | magnum-cluster-api:magnum_cluster_api/resources.py:513-632, magnum-cluster-api:magnum_cluster_api/driver.py:112-123 |
v1 Secret | <stack_id>-cloud-config | Python _create_cluster | clouds.yaml (cloud default, app credential), cacert | cluster-uuid=<uuid> | magnum-cluster-api:magnum_cluster_api/resources.py:635-699 |
v1 Secret type addons.cluster.x-k8s.io/resource-set | <uuid> ("legacy") | Python (data) + Rust (cilium.yaml) | cilium.yaml (if cilium) or calico.yml, cloud-config-secret.yaml, NFS CSI manifests (if Manila), keystone-auth.yaml (if enabled) | — | magnum-cluster-api:magnum_cluster_api/resources.py:379-500, magnum-cluster-api:src/magnum.rs:350-374 |
addons.cluster.x-k8s.io/v1beta1 ClusterResourceSet | <uuid> | Rust create_cluster only | selects clusters with label cluster-uuid=<uuid>, resource Secret <uuid>, default strategy | — | magnum-cluster-api:src/magnum.rs:328-348, magnum-cluster-api:src/driver.rs:44-63 |
v1 Secret type addons.cluster.x-k8s.io/resource-set | <stack_id>-cloud-provider | Python (data) | cloud-controller-manager.yaml (Rust-rendered OCCM chart), cloud-config-secret.yaml, cinder-csi.yaml + one StorageClass block-<type> per Cinder volume type (default type annotated default), manila-csi.yaml + csi-manila-secrets + StorageClass share-<type> (only if manila_csi_share_network_id) | — | magnum-cluster-api:magnum_cluster_api/resources.py:210-366 |
addons.cluster.x-k8s.io/v1beta1 ClusterResourceSet | <stack_id>-cloud-provider | Rust (create + upgrade) | selector cluster-uuid=<uuid>, strategy Reconcile | — | magnum-cluster-api:src/magnum.rs:289-313, magnum-cluster-api:src/driver.rs:65-119 |
cluster.x-k8s.io/v1beta1 Cluster | <stack_id> | Python resources.Cluster.apply | clusterNetwork, topology (class, version, controlPlane, workers.machineDeployments, variables) — see §4 | cluster-uuid=<uuid>, cni=calico-<tag> (Calico only) | magnum-cluster-api:magnum_cluster_api/resources.py:871-1289 |
Helm release (in magnum-system) | <stack_id> (fullnameOverride <stack_id>-autoscaler) | update_cluster_status once API is up (create) or apply_cluster_from_magnum_cluster (upgrade) | cluster-autoscaler, cloudProvider: clusterapi, clusterAPIMode: kubeconfig-incluster, nodeSelector openstack-control-plane=enabled, enforce-node-group-min-size | — | magnum-cluster-api:magnum_cluster_api/resources.py:60-108, magnum-cluster-api:magnum_cluster_api/driver.py:254-258 |
| Lock object | cluster-<uuid> in magnum-system | sherlock KubernetesLock | expiry 60 s, retry 1 s | — | magnum-cluster-api:magnum_cluster_api/sync.py:24-36 (object kind [inferred]: sherlock's Kubernetes backend) |
Annotation on Machine | — | resize_cluster | cluster.x-k8s.io/delete-machine: "yes" | — | magnum-cluster-api:magnum_cluster_api/driver.py:363-370 |
v1 Service / discovery.k8s.io/v1 EndpointSlice | <stack_id> | magnum-cluster-api-proxy (optional daemon) | port 6443 → HAProxy in the Neutron netns; rewrites the kubeconfig secret to https://<stack_id>.magnum-system:6443 | — | magnum-cluster-api:magnum_cluster_api/proxy/manager.py:279-313, magnum-cluster-api:magnum_cluster_api/proxy/structs.py:44-78 |
Objects created by CAPI/CAPO from the topology (not by the driver) but read by the driver: KubeadmControlPlane, MachineDeployment (label topology.cluster.x-k8s.io/deployment-name=<ng.name>), Machine, OpenStackMachine, OpenStackCluster, <stack_id>-kubeconfig secret (magnum-cluster-api:magnum_cluster_api/objects.py:72-98, magnum-cluster-api:magnum_cluster_api/driver.py:562-569).
Addons delivered to the workload cluster (via CRS, namespace kube-system):
| Addon | Condition | Version source | Source |
|---|---|---|---|
Cilium (Helm chart 1.15.3, kube-proxy replacement serviceProxyName: cilium, pool cilium_ipv4pool) | network_driver == cilium | label cilium_tag (default v1.15.3) | magnum-cluster-api:src/addons/cilium.rs:130-140, magnum-cluster-api:src/addons/cilium.rs:319-339, magnum-cluster-api:src/magnum.rs:30-37 |
| Calico (static manifest) | network_driver == calico | label calico_tag (default v3.32.1) | magnum-cluster-api:magnum_cluster_api/resources.py:50, magnum-cluster-api:magnum_cluster_api/resources.py:436-447 |
| OpenStack CCM (chart 2.35.0) | always | label cloud_provider_tag, else per Kubernetes minor (1.22→v1.22.2 … 1.35→v1.35.0, default v1.35.0) | magnum-cluster-api:src/addons/cloud_controller_manager.rs:74-133, magnum-cluster-api:src/magnum.rs:113-150 |
| Cinder CSI (chart 2.32.0) | label cinder_csi_enabled (default true) and Keystone catalog has volumev3 or block-storage | cinder_csi_plugin_tag v1.32.0, sidecar tags csi_*_tag | magnum-cluster-api:magnum_cluster_api/integrations/cinder.py:25-28, magnum-cluster-api:src/magnum.rs:46-106 |
| Manila CSI + NFS CSI | label manila_csi_enabled (default true) and service sharev2/share | manila_csi_plugin_tag v1.32.0 | magnum-cluster-api:magnum_cluster_api/integrations/manila.py:20-23, magnum-cluster-api:magnum_cluster_api/resources.py:449-462 |
| k8s-keystone-auth (DaemonSet, hostNetwork, 127.0.0.1:8443) | label keystone_auth_enabled (default true) | chart appVersion v1.29.0 | magnum-cluster-api:magnum_cluster_api/resources.py:465-495, magnum-cluster-api:magnum_cluster_api/charts/k8s-keystone-auth/templates/daemonset.yaml:54, magnum-cluster-api:magnum_cluster_api/charts/k8s-keystone-auth/templates/daemonset.yaml:75 |
Image mirroring: when container_infra_prefix is set, every image is rewritten to the private registry with fixed rules (calico, cilium, k8scloudprovider, sig-storage, provider-os, coredns, autoscaling, etcd/kube-*/pause) (magnum-cluster-api:magnum_cluster_api/image_utils.py:42-77).
flowchart LR
subgraph Magnum
MC[Magnum cluster uuid + stack_id]
end
subgraph mgmt["Management cluster, ns magnum-system"]
CC[ClusterClass magnum-vX.Y.Z]
OCT[OpenStackClusterTemplate]
OMT[OpenStackMachineTemplate]
KCPT[KubeadmControlPlaneTemplate]
KCT[KubeadmConfigTemplate]
CL[Cluster stack_id\nlabel cluster-uuid]
CCS[Secret stack_id-cloud-config\nclouds.yaml app cred]
CAS[Secrets stack_id-ca/-etcd/-proxy/-sa]
LS[Secret uuid legacy addons]
CPS[Secret stack_id-cloud-provider]
CRS1[CRS uuid]
CRS2[CRS stack_id-cloud-provider Reconcile]
AS[Helm release stack_id autoscaler]
KCP[KubeadmControlPlane]
MD[MachineDeployment per nodegroup]
M[Machine / OpenStackMachine]
OSC[OpenStackCluster]
KC[Secret stack_id-kubeconfig]
end
subgraph OpenStack
NOVA[Nova servers + server groups]
NEU[Neutron net/subnet/router/SG/FIP]
OCTA[Octavia API LB]
CIN[Cinder boot/etcd/docker volumes]
end
subgraph Workload["Workload cluster kube-system"]
CNI[Cilium or Calico]
CCM[OpenStack CCM]
CSI[Cinder / Manila CSI]
KA[k8s-keystone-auth]
end
MC --> CL
CC --> OCT & OMT & KCPT & KCT
CL -- topology.class --> CC
CL --> KCP & MD & OSC
KCP --> M
MD --> M
CCS -. identityRef .-> OSC & M
CAS -. kubeadm CA .-> KCP
OSC --> NEU & OCTA
M --> NOVA & CIN
CL --> KC
LS --> CRS1 --> CNI & KA
CPS --> CRS2 --> CCM & CSI
KC -.-> AS4. Field / label mapping (Magnum → CAPI / CAPO)
Unless stated, the target is a **CAPI Cluster.spec.topology.variables[] entry**, then a ClusterClass JSON patch writes it into the concrete template. Label values are strings; booleans are parsed with strutils.bool_from_string(strict=True) and integers with validate_integer (magnum-cluster-api:magnum_cluster_api/utils.py:327-356). Cluster labels are Magnum's merged template+cluster labels; nodegroup labels are used for per-MD overrides.
4.1 Cluster / template fields
| Magnum field | Default | Variable / CAPI field | Final target (patch) | Source |
|---|---|---|---|---|
cluster.stack_id (generated kube-xxxxx) | — | Cluster.metadata.name; all CAPI child names | — | magnum-cluster-api:magnum_cluster_api/resources.py:897-898 |
cluster.uuid | — | label cluster-uuid on Cluster; CRS selector; legacy secret/CRS name; CCM cluster.name | — | magnum-cluster-api:magnum_cluster_api/resources.py:204-207, magnum-cluster-api:src/addons/cloud_controller_manager.rs:125-129 |
labels.kube_tag | v1.25.3 (Python) | topology.version | KCP/MD Kubernetes version; also selects autoscaler + CCM image | magnum-cluster-api:magnum_cluster_api/utils.py:228-229, magnum-cluster-api:magnum_cluster_api/resources.py:977 |
cluster.master_count | — | topology.controlPlane.replicas | KCP replicas (must be odd) | magnum-cluster-api:magnum_cluster_api/resources.py:984, magnum-cluster-api:magnum_cluster_api/utils.py:504-505 |
cluster.master_flavor_id (name or id, resolved via Nova) | — | controlPlaneFlavor = flavor name | control-plane OpenStackMachineTemplate.spec.flavor | magnum-cluster-api:magnum_cluster_api/resources.py:944, magnum-cluster-api:magnum_cluster_api/resources.py:1098-1100, magnum-cluster-api:src/features/flavors.rs:52-54 |
cluster.flavor_id | — | flavor (cluster level) | worker OSMT spec.flavor | magnum-cluster-api:magnum_cluster_api/resources.py:1159-1162, magnum-cluster-api:src/features/flavors.rs:75-77 |
default_ng_master.image_id (validated via Glance) | — | imageUUID; hardwareDiskBus from image property hw_disk_bus | OSMT spec.image.id (CP + workers); device names /dev/sdX vs /dev/vdX | magnum-cluster-api:magnum_cluster_api/resources.py:946, magnum-cluster-api:magnum_cluster_api/resources.py:1169-1172, magnum-cluster-api:magnum_cluster_api/resources.py:1199-1202, magnum-cluster-api:src/features/images.rs:52-54 |
cluster.master_lb_enabled | — | apiServerLoadBalancer.enabled | OpenStackClusterTemplate.spec.apiServerLoadBalancer | magnum-cluster-api:magnum_cluster_api/resources.py:948-950, magnum-cluster-api:src/features/api_server_load_balancer.rs:66 |
cluster.keypair | "" | sshKeyName | OSMT spec.sshKeyName | magnum-cluster-api:magnum_cluster_api/resources.py:1191-1194, magnum-cluster-api:src/features/ssh_key.rs:52-54 |
template.external_network_id (name/id resolved) | — | externalNetworkId | OpenStackCluster.spec.externalNetwork.id | magnum-cluster-api:magnum_cluster_api/resources.py:1138-1144, magnum-cluster-api:src/features/external_network.rs:48-53 |
cluster.fixed_network (name/id) | "" | fixedNetworkId | if set: spec.network.id; if empty: CAPO creates managedSubnets | magnum-cluster-api:magnum_cluster_api/resources.py:1145-1151, magnum-cluster-api:src/features/networks.rs:48-49, magnum-cluster-api:src/features/networks.rs:84-96 |
cluster.fixed_subnet | "" | fixedSubnetId | spec.subnets[0].id | magnum-cluster-api:magnum_cluster_api/resources.py:1152-1158, magnum-cluster-api:src/features/networks.rs:116-128 |
template.dns_nameserver (comma list) | — | dnsNameservers | managedSubnets[0].dnsNameservers | magnum-cluster-api:magnum_cluster_api/resources.py:1133-1137, magnum-cluster-api:src/features/networks.rs:61-70 |
template.network_driver | must be cilium or calico | pod CIDR choice, CNI addon, Cluster label cni | — | magnum-cluster-api:magnum_cluster_api/utils.py:500-501, magnum-cluster-api:magnum_cluster_api/resources.py:903-908 |
template.cluster_distro | — | operatingSystem | Flatcar: Ignition format patches; Ubuntu/Debian: apt proxy | magnum-cluster-api:magnum_cluster_api/resources.py:1195-1198, magnum-cluster-api:src/features/operating_system.rs:65, magnum-cluster-api:src/features/operating_system.rs:137-138 |
template.http_proxy / https_proxy / no_proxy | none | systemdProxyConfig, aptProxyConfig (base64) | /etc/systemd/system/containerd.service.d/proxy.conf, apt proxy file | magnum-cluster-api:magnum_cluster_api/utils.py:273-312, magnum-cluster-api:magnum_cluster_api/resources.py:1079-1090, magnum-cluster-api:src/features/containerd_config.rs:131-150 |
cluster.docker_volume_size | None | enableDockerVolume, dockerVolumeSize | extra Cinder block device mounted at /var/lib/containerd (CP + workers) | magnum-cluster-api:magnum_cluster_api/resources.py:1203-1210, magnum-cluster-api:src/features/volumes.rs:249-305 |
cluster.keypair, cluster.project_id | — | keystone-auth default policy (role member + project) | — | magnum-cluster-api:magnum_cluster_api/utils.py:566-586 |
cluster.labels → admission_control_list | NodeRestriction (always prepended) | admissionControlList | apiserver --enable-admission-plugins | magnum-cluster-api:magnum_cluster_api/resources.py:916-926, magnum-cluster-api:src/features/admission_plugins.rs:47 |
cluster.floating_ip_enabled | — | not used (only master_lb_floating_ip_enabled label) | — | [inferred] from absence in magnum-cluster-api:magnum_cluster_api/resources.py:955-1289 |
cluster_create_timeout | — | ignored | — | magnum-cluster-api:magnum_cluster_api/driver.py:70-88 |
4.2 Cluster labels
| Label | Default | Variable | Target | Source |
|---|---|---|---|---|
calico_ipv4pool / cilium_ipv4pool | 10.100.0.0/16 | — | Cluster.spec.clusterNetwork.pods.cidrBlocks[0] (+ Cilium IPAM pool) | magnum-cluster-api:magnum_cluster_api/resources.py:57, magnum-cluster-api:magnum_cluster_api/resources.py:931-941 |
service_cluster_ip_range | 10.254.0.0/16 | — | clusterNetwork.services.cidrBlocks[0] | magnum-cluster-api:magnum_cluster_api/resources.py:967-972 |
dns_cluster_domain | cluster.local | — | clusterNetwork.serviceDomain | magnum-cluster-api:magnum_cluster_api/resources.py:962-964 |
fixed_subnet_cidr | 10.0.0.0/24 | nodeCidr | OpenStackCluster.spec.managedSubnets[0].cidr | magnum-cluster-api:magnum_cluster_api/resources.py:1185-1190 |
master_lb_floating_ip_enabled | true | disableAPIServerFloatingIP (+ ...Managed gate) | OpenStackCluster.spec.disableAPIServerFloatingIP | magnum-cluster-api:magnum_cluster_api/utils.py:244-245, magnum-cluster-api:magnum_cluster_api/resources.py:1101-1131, magnum-cluster-api:src/features/disable_api_server_floating_ip.rs:61 |
api_server_floating_ip | "" | apiServerFloatingIP | OpenStackCluster.spec.apiServerFloatingIP | magnum-cluster-api:magnum_cluster_api/resources.py:1006-1011, magnum-cluster-api:src/features/api_server_floating_ip.rs:47 |
octavia_provider | CCM: amphorav2; API LB: unset (CAPO default) | apiServerLoadBalancer.provider; CCM lb-provider | OpenStackCluster.spec.apiServerLoadBalancer.provider; cloud.conf | magnum-cluster-api:src/immutable_fields.rs:162-167, magnum-cluster-api:magnum_cluster_api/utils.py:161 |
api_server_lb_flavor | unset | apiServerLoadBalancer.flavor | ...apiServerLoadBalancer.flavor | magnum-cluster-api:src/immutable_fields.rs:168-173 |
api_server_lb_availability_zone | unset | apiServerLoadBalancer.availabilityZone | ...apiServerLoadBalancer.availabilityZone | magnum-cluster-api:src/immutable_fields.rs:174-179 |
octavia_lb_algorithm | ROUND_ROBIN (amphora*), SOURCE_IP_PORT (ovn; anything else rejected) | — | CCM cloud.conf lb-method | magnum-cluster-api:magnum_cluster_api/utils.py:162-172 |
octavia_lb_healthcheck | True | — | CCM create-monitor | magnum-cluster-api:magnum_cluster_api/utils.py:163, magnum-cluster-api:magnum_cluster_api/utils.py:186 |
api_server_tls_cipher_suites | 6 ECDHE suites | apiServerTLSCipherSuites | apiserver --tls-cipher-suites | magnum-cluster-api:magnum_cluster_api/resources.py:999-1005, magnum-cluster-api:src/features/tls.rs:57-59 |
kubelet_tls_cipher_suites | 3 ECDSA suites | kubeletTLSCipherSuites | kubelet tls-cipher-suites (init/join, CP + workers) | magnum-cluster-api:magnum_cluster_api/resources.py:1173-1179, magnum-cluster-api:src/features/tls.rs:66-77 |
api_server_cert_sans (comma list) | "" | apiServerSANs (YAML list text) | clusterConfiguration.apiServer.certSANs | magnum-cluster-api:magnum_cluster_api/utils.py:610-615, magnum-cluster-api:src/features/tls.rs:86-93 |
oidc_issuer_url, oidc_client_id, oidc_username_claim (sub), oidc_username_prefix (-), oidc_groups_claim, oidc_groups_prefix | as shown | openidConnect.* (patch only if issuer set) | apiserver --oidc-* | magnum-cluster-api:magnum_cluster_api/resources.py:1012-1034, magnum-cluster-api:src/features/openid_connect.rs:59-120 |
audit_log_enabled (false), audit_log_max_age (30), audit_log_max_backup (10), audit_log_max_size (100) | as shown | auditLog.* | apiserver --audit-log-*, policy file, hostPath volumes | magnum-cluster-api:magnum_cluster_api/resources.py:1035-1051, magnum-cluster-api:src/features/audit_log.rs:55-103 |
boot_volume_size | [cinder] default_boot_volume_size (0 = no boot volume) | bootVolume.size | OSMT spec.rootVolume.sizeGiB (patch only if > 0) | magnum-cluster-api:magnum_cluster_api/resources.py:1052-1065, magnum-cluster-api:src/features/boot_volume.rs:48-76 |
boot_volume_type | [cinder] default_boot_volume_type, else Cinder default type | bootVolume.type | rootVolume.type | magnum-cluster-api:magnum_cluster_api/integrations/cinder.py:31-50 |
boot_volume_availability_zone | availability_zone label | bootVolumeAvailabilityZone | rootVolume.availabilityZone.name | magnum-cluster-api:magnum_cluster_api/resources.py:1066-1072 |
etcd_volume_size (0), etcd_volume_type (Cinder default) | as shown | enableEtcdVolume, etcdVolumeSize, etcdVolumeType | CP additionalBlockDevices + partition/fs label etcd_disk mounted /var/lib/etcd | magnum-cluster-api:magnum_cluster_api/resources.py:1218-1241, magnum-cluster-api:src/features/volumes.rs:152-240 |
docker_volume_type | Cinder default type | dockerVolumeType | block device type | magnum-cluster-api:magnum_cluster_api/resources.py:1211-1217 |
availability_zone | "" | availabilityZone | AZ of the extra etcd/docker volumes | magnum-cluster-api:magnum_cluster_api/resources.py:1242-1245, magnum-cluster-api:src/features/volumes.rs:166-179 |
control_plane_availability_zones (comma list) | "" | controlPlaneAvailabilityZones | OpenStackCluster.spec.controlPlaneAvailabilityZones (only if first item non-empty) | magnum-cluster-api:magnum_cluster_api/resources.py:1260-1265, magnum-cluster-api:src/features/control_plane_availability_zones.rs:34-49 |
server_group_policies | soft-anti-affinity | creates Nova server group <stack_id> → serverGroupId | OSMT spec.serverGroup.id | magnum-cluster-api:magnum_cluster_api/utils.py:47-53, magnum-cluster-api:magnum_cluster_api/utils.py:656-692, magnum-cluster-api:src/features/server_groups.rs:58-63 |
different_failure_domain | false | isServerGroupDiffFailureDomain | OSMT schedulerHintAdditionalProperties[different_failure_domain] | magnum-cluster-api:magnum_cluster_api/utils.py:677-680, magnum-cluster-api:src/features/server_groups.rs:75-81 |
container_infra_prefix | "" | imageRepository; addon image rewrite | KCP clusterConfiguration.imageRepository | magnum-cluster-api:magnum_cluster_api/resources.py:1163-1168, magnum-cluster-api:src/features/image_repository.rs:39-53 |
| (computed) | pause registry.k8s.io/pause:3.9 | containerdConfig (base64 TOML, SystemdCgroup=true) | /etc/containerd/config.toml | magnum-cluster-api:magnum_cluster_api/utils.py:248-270, magnum-cluster-api:magnum_cluster_api/images.py:21, magnum-cluster-api:src/features/containerd_config.rs:65-71 |
| (computed) | <stack_id>-cloud-config | clusterIdentityRefName | identityRef.name in OSCT and OSMT | magnum-cluster-api:magnum_cluster_api/resources.py:1073-1078, magnum-cluster-api:src/features/cluster_identity.rs:53, magnum-cluster-api:src/features/cluster_identity.rs:74 |
keystone_auth_enabled | true | enableKeystoneAuth | kube-apiserver static pod kustomized with --authentication-token-webhook-config-file, --authorization-webhook-config-file, --authorization-mode=Webhook | magnum-cluster-api:magnum_cluster_api/resources.py:1246-1251, magnum-cluster-api:src/features/keystone_auth.rs:58-123 |
kubelet_serving_cert_verification_enabled | false | enableKubeletServingCertificateVerification | kubelet serverTLSBootstrap: true, apiserver --kubelet-certificate-authority | magnum-cluster-api:magnum_cluster_api/resources.py:1252-1259, magnum-cluster-api:src/features/kubelet_serving_certificate_verification.rs:49-77 |
auto_healing_enabled | true | — | topology.controlPlane.machineHealthCheck.enable and per-MD machineHealthCheck.enable | magnum-cluster-api:magnum_cluster_api/utils.py:236-237, magnum-cluster-api:magnum_cluster_api/resources.py:985-987, magnum-cluster-api:magnum_cluster_api/resources.py:798 |
auto_scaling_enabled | false | — | MD annotations + autoscaler Helm release | magnum-cluster-api:magnum_cluster_api/utils.py:232-233 |
calico_tag | v3.32.1 | — | Calico manifest file, Cluster label cni | magnum-cluster-api:magnum_cluster_api/resources.py:437 |
cilium_tag (v1.15.3), cilium_hubble_ui_enabled (false) | as shown | — | Cilium chart values | magnum-cluster-api:src/magnum.rs:30-43 |
cloud_provider_tag | per K8s minor | — | OCCM image tag | magnum-cluster-api:src/magnum.rs:68-70, magnum-cluster-api:src/magnum.rs:122-150 |
cinder_csi_enabled, cinder_csi_plugin_tag, manila_csi_enabled, manila_csi_plugin_tag, manila_csi_share_network_id, csi_attacher_tag (v4.7.0), csi_liveness_probe_tag (v2.14.0), csi_node_driver_registrar_tag (v2.12.0), csi_provisioner_tag (v5.1.0), csi_resizer_tag (v1.12.0), csi_snapshotter_tag (v8.1.0) | as shown | — | CSI chart values / StorageClasses | magnum-cluster-api:src/magnum.rs:46-106, magnum-cluster-api:magnum_cluster_api/resources.py:304 |
Variables are first passed through resolve_immutable_fields: for each mapped path, the value **already present on the live OpenStackCluster wins over the label**, so LB provider/flavor/AZ and disableAPIServerFloatingIP never change after creation (CAPO's webhook rejects changes) (magnum-cluster-api:src/immutable_fields.rs:66-100, magnum-cluster-api:src/immutable_fields.rs:159-196, magnum-cluster-api:magnum_cluster_api/resources.py:951-953).
4.3 Nodegroup → topology.workers.machineDeployments[]
| Magnum nodegroup field / label | Default | Target | Source |
|---|---|---|---|
name (must match RFC 1123) | — | machineDeployments[].name; MD label topology.cluster.x-k8s.io/deployment-name | magnum-cluster-api:magnum_cluster_api/utils.py:534-538, magnum-cluster-api:magnum_cluster_api/resources.py:797 |
| — | default-worker | class | magnum-cluster-api:magnum_cluster_api/resources.py:796 |
role | — | MD metadata.labels: node-role.kubernetes.io/<role>: "", node.cluster.x-k8s.io/nodegroup: <name> | magnum-cluster-api:magnum_cluster_api/resources.py:730-733 |
node_count (autoscaling off) | — | replicas | magnum-cluster-api:magnum_cluster_api/resources.py:768-770 |
min_node_count / max_node_count (or label max_node_count, else min+1) (autoscaling on) | — | replicas removed; annotations cluster.x-k8s.io/cluster-api-autoscaler-node-group-min-size / -max-size, capacity.cluster-autoscaler.kubernetes.io/{memory,cpu,ephemeral-disk,labels} from the flavor | magnum-cluster-api:magnum_cluster_api/resources.py:741-767, magnum-cluster-api:magnum_cluster_api/utils.py:315-324 |
flavor_id | — | variable override flavor (name) | magnum-cluster-api:magnum_cluster_api/resources.py:815-818 |
image_id | — | override imageUUID, hardwareDiskBus | magnum-cluster-api:magnum_cluster_api/resources.py:826-833 |
labels boot_volume_size, boot_volume_type | conf / Cinder default | override bootVolume | magnum-cluster-api:magnum_cluster_api/resources.py:802-814 |
label container_infra_prefix | "" | override imageRepository | magnum-cluster-api:magnum_cluster_api/resources.py:819-825 |
labels server_group_policies, different_failure_domain | cluster values | Nova server group <stack_id>-<ng.name> → overrides serverGroupId, isServerGroupDiffFailureDomain | magnum-cluster-api:magnum_cluster_api/resources.py:834-846, magnum-cluster-api:magnum_cluster_api/utils.py:644-705 |
label availability_zone | none | failureDomain | magnum-cluster-api:magnum_cluster_api/resources.py:851-853 |
auto_healing_enabled (cluster label) | true | machineHealthCheck.enable | magnum-cluster-api:magnum_cluster_api/resources.py:798 |
| — | 300s | nodeVolumeDetachTimeout | magnum-cluster-api:magnum_cluster_api/resources.py:52, magnum-cluster-api:magnum_cluster_api/resources.py:773-775 |
On existing MDs only replicas/annotations/labels/nodeVolumeDetachTimeout (and an empty→set failureDomain) are changed; variables are frozen to avoid rolling updates (magnum-cluster-api:magnum_cluster_api/resources.py:776-789). Nodegroups with role master or status DELETE* are excluded (magnum-cluster-api:magnum_cluster_api/resources.py:857-868). The master nodegroup is represented only by topology.controlPlane; its image_id is the cluster imageUUID (magnum-cluster-api:magnum_cluster_api/resources.py:946).
5. Status reconciliation
Triggers (Magnum core): sync_cluster_status periodic task selects clusters in CREATE/UPDATE/DELETE/ROLLBACK_IN_PROGRESS and calls driver.update_cluster_status; on DELETE_COMPLETE Magnum destroys certificates, nodegroups and the cluster row (magnum:magnum/service/periodic.py:66-91, magnum:magnum/service/periodic.py:161-169). sync_cluster_health_status calls get_monitor().poll_health_status() and stores health_status/health_status_reason (magnum:magnum/service/periodic.py:104-125, magnum:magnum/conductor/monitors.py:58-66).
5.1 Control-plane nodegroup (KubeadmControlPlane)
update_cluster_control_plane_status (magnum-cluster-api:magnum_cluster_api/driver.py:166-198):
action= prefix of the current master nodegroup status (CREATE,UPDATE, …); if KCPstatus.observedGeneration > 1the action becomesUPDATE(lines 172-180).updatedReplicas != replicas→<action>_IN_PROGRESS(lines 188-189).updatedReplicas == replicas == ng.node_countandstatus.ready→<action>_COMPLETE(lines 190-193).status_reason= KCPstatus.failureMessage(line 194). No_FAILEDis ever derived.
5.2 Worker nodegroups (MachineDeployment)
update_nodegroups_status (magnum-cluster-api:magnum_cluster_api/driver.py:501-610):
| Current ng status | Condition on management cluster | New ng status | Lines |
|---|---|---|---|
DELETE_COMPLETE (non-default, or cluster deleting) | — | row destroyed | 517-524 |
DELETE_IN_PROGRESS | MD not found | Nova server group deleted, DELETE_COMPLETE | 535-544 |
CREATE_IN_PROGRESS | MD status.phase == Running | CREATE_COMPLETE | 546-559 |
UPDATE_IN_PROGRESS | MD Running and equals_spec (annotations equal when autoscaling, else spec.replicas equal) and every OpenStackMachine.spec.image.id == imageUUID override and every spec.flavor == flavor override | UPDATE_COMPLETE | 562-608, magnum-cluster-api:magnum_cluster_api/objects.py:112-126 |
| anything else | — | unchanged | — |
5.3 Cluster
update_cluster_status (magnum-cluster-api:magnum_cluster_api/driver.py:200-313), under the cluster lock, after cluster.refresh():
CREATE_IN_PROGRESS/UPDATE_IN_PROGRESS: if no CAPIClusteryet → return. Builds a map of CAPICluster.status.conditions; if any ofControlPlaneReady,InfrastructureReady,Readyis not"True"(missing counts as not true) →status_reason = "CAPI Cluster status: <reason>: <message>. CAPI OpenstackCluster status reason: <reason>: <message>"from the latest Kubernetes Event of theClusterand of theOpenStackCluster, save, return (lines 222-244, 140-164). Otherwise:api_address = "https://<controlPlaneEndpoint.host>:<port>",coe_version = spec.topology.version(lines 246-250);- on create with autoscaling,
helm upgrade --installthe autoscaler (lines 254-258); - if any nodegroup (incl. master) is not
*_COMPLETE→ return (lines 260-270); CREATE_IN_PROGRESS → CREATE_COMPLETE,UPDATE_IN_PROGRESS → UPDATE_COMPLETE,status_reason = None(lines 272-279).
DELETE_IN_PROGRESS: while the CAPIClusterexists → refreshstatus_reasonfrom events, return. When gone → delete application credential<uuid>(Forbidden/NotFound ignored), cloud-config secret, 4 CA secrets, all Nova server groups →DELETE_COMPLETE(lines 281-313, magnum-cluster-api:magnum_cluster_api/clients.py:52-73).- **No
*_FAILEDstate is produced by the driver.**_FAILEDcomes only from Magnum core when a driver call raises (magnum:magnum/conductor/handlers/cluster_conductor.py:77-79, magnum:magnum/conductor/handlers/cluster_conductor.py:181-185, magnum:magnum/conductor/handlers/nodegroup_conductor.py:85-89). A CAPI cluster that never becomes Ready stays*_IN_PROGRESSforever (cluster_create_timeoutis ignored). - Not filled:
node_addresses,master_addresses,discovery_url(no reference in the driver; [inferred] by grep absence).
5.4 Health monitor
Rust Monitor.poll_health_status (magnum-cluster-api:src/monitor.rs:131-185): lists Machines and KubeadmControlPlane labelled cluster.x-k8s.io/cluster-name=<stack_id>; HEALTHY iff KCP condition Available=True and every Machine NodeHealthy=True; health_status_reason = {"<infraRef name>.Ready": "True|False", "api": "ok|nok"}; UNKNOWN if no stack_id (magnum-cluster-api:src/monitor.rs:18-49, magnum-cluster-api:src/monitor.rs:75-89). With autoscaling, the Python monitor also copies each MD's spec.replicas into nodegroup.node_count (magnum-cluster-api:magnum_cluster_api/monitor.py:31-51).
stateDiagram-v2 [*] --> CREATE_IN_PROGRESS: conductor create_cluster OK [*] --> CREATE_FAILED: driver raised (Magnum core) CREATE_IN_PROGRESS --> CREATE_IN_PROGRESS: CAPI Cluster absent or ControlPlaneReady/InfrastructureReady/Ready != True (status_reason = last events) CREATE_IN_PROGRESS --> CREATE_IN_PROGRESS: conditions True but a nodegroup not *_COMPLETE CREATE_IN_PROGRESS --> CREATE_COMPLETE: conditions True and all nodegroups *_COMPLETE (api_address, coe_version set) CREATE_COMPLETE --> UPDATE_IN_PROGRESS: resize / nodegroup create-update-delete / upgrade UPDATE_COMPLETE --> UPDATE_IN_PROGRESS: idem UPDATE_IN_PROGRESS --> UPDATE_COMPLETE: conditions True and all nodegroups *_COMPLETE UPDATE_IN_PROGRESS --> UPDATE_FAILED: driver raised (Magnum core) CREATE_COMPLETE --> DELETE_IN_PROGRESS: delete_cluster OK UPDATE_COMPLETE --> DELETE_IN_PROGRESS: delete_cluster OK DELETE_IN_PROGRESS --> DELETE_IN_PROGRESS: CAPI Cluster still exists DELETE_IN_PROGRESS --> DELETE_COMPLETE: CAPI Cluster gone, app cred + secrets + server groups removed [*] --> DELETE_FAILED: delete_cluster raised (Magnum core) DELETE_COMPLETE --> [*]: Magnum destroys rows
| Magnum status | Set by | Derived from | Source |
|---|---|---|---|
| CREATE_IN_PROGRESS | Magnum API/conductor | — | magnum:magnum/conductor/handlers/cluster_conductor.py:60-79 |
| CREATE_COMPLETE | driver | CAPI Cluster conditions ControlPlaneReady, InfrastructureReady, Ready all True + all ngs _COMPLETE | magnum-cluster-api:magnum_cluster_api/driver.py:238-274 |
| UPDATE_IN_PROGRESS | driver (_update_nodegroup) or Magnum | — | magnum-cluster-api:magnum_cluster_api/driver.py:675-679 |
| UPDATE_COMPLETE | driver | same as CREATE_COMPLETE | magnum-cluster-api:magnum_cluster_api/driver.py:275-277 |
| DELETE_IN_PROGRESS | Magnum after delete_cluster returns | — | magnum:magnum/conductor/handlers/cluster_conductor.py:178-180 |
| DELETE_COMPLETE | driver | CAPI Cluster no longer exists | magnum-cluster-api:magnum_cluster_api/driver.py:281-313 |
| *_FAILED | Magnum core only | exception from a driver call | magnum:magnum/conductor/handlers/cluster_conductor.py:77-79 |
ng <action>_IN_PROGRESS/_COMPLETE (master) | driver | KCP updatedReplicas, replicas, ready, observedGeneration | magnum-cluster-api:magnum_cluster_api/driver.py:166-198 |
| ng CREATE/UPDATE/DELETE_COMPLETE (workers) | driver | MD status.phase, spec equality, OpenStackMachine image/flavor | magnum-cluster-api:magnum_cluster_api/driver.py:501-610 |
| health_status HEALTHY/UNHEALTHY/UNKNOWN | driver monitor | KCP Available, Machine NodeHealthy | magnum-cluster-api:src/monitor.rs:131-185 |
6. Credentials and certificates
- Application credential per cluster, created in the context of the requesting user: name = cluster UUID, description
Magnum cluster (<uuid>),user = cluster.user_id, unrestricted roles [inferred: no roles/access_rules passed] (magnum-cluster-api:magnum_cluster_api/driver.py:98-102, magnum-cluster-api:magnum_cluster_api/clients.py:37-50). Deleted only once the CAPIClusteris gone, so CAPO keeps access during teardown (magnum-cluster-api:magnum_cluster_api/driver.py:287-294). - CAPO identity: secret
<stack_id>-cloud-configwithclouds.yaml(clouddefault,region_name= Cinder region,endpoint_typefrom[capi_client] endpoint_typeminusURL,verify = not [capi_client] insecure,auth_urlfrom the catalog) andcacert=[capi_client] ca_filecontent or the certifi bundle (magnum-cluster-api:magnum_cluster_api/resources.py:635-699). Referenced byidentityRef.nameof OpenStackCluster and OpenStackMachines (magnum-cluster-api:src/features/cluster_identity.rs:53-74). - CCM / Cinder CSI: workload secret
kube-system/cloud-configwithcloud.confbuilt by re-reading the app credential from<stack_id>-cloud-config:[Global] auth-url(public identity endpoint),region,application-credential-id/secret,tls-insecurefrom Magnum[drivers] verify_ca,ca-file=/etc/config/ca.crtwhen Magnum has an OpenStack CA;[LoadBalancer] lb-provider,lb-method,create-monitor(magnum-cluster-api:magnum_cluster_api/utils.py:144-188, magnum-cluster-api:magnum_cluster_api/resources.py:243-260). The CSI chart mounts that secret (secret.name: cloud-config) (magnum-cluster-api:src/addons/cinder_csi.rs:182-187). - Manila CSI: workload secret
kube-system/csi-manila-secretswithos-authURL,os-region,os-applicationCredentialID/Secret,os-TLSInsecure, optionalos-certAuthorityPath(magnum-cluster-api:magnum_cluster_api/utils.py:191-225). - Keystone webhook: k8s-keystone-auth gets
auth_url(public,/v3appended), Magnum's OpenStack CA and a policy from[kubernetes] keystone_auth_default_policy(file,$PROJECT_IDsubstituted) or a built-in policy (rolememberin the cluster's project maylistpods/services/deployments/pvc indefault) (magnum-cluster-api:magnum_cluster_api/resources.py:465-495, magnum-cluster-api:magnum_cluster_api/utils.py:566-586). The apiserver calls it athttps://127.0.0.1:8443/webhookwithinsecure-skip-tls-verify: true(magnum-cluster-api:data/keystone-auth/webhook.yaml:7-8). - Certificates — Magnum CA is the kubeadm CA: Magnum core generates the cluster CAs before calling the driver (magnum:magnum/conductor/handlers/cluster_conductor.py:62-63); the driver uploads them as CAPI-format secrets
<stack_id>-ca(Kubernetes CA,ca_cert_ref),-etcd(etcd_ca_cert_ref),-proxy(front_proxy_ca_cert_ref),-sa(service-account key pair frommagnum_cert_ref) with decrypted private keys (magnum-cluster-api:magnum_cluster_api/resources.py:558-632). kubeadm then signs component certs from these CAs, somagnum ca-show/ca-signstay consistent. KCP renews leaf certs 21 days before expiry (magnum-cluster-api:src/features/mod.rs:194-196). CA rotation is not supported (magnum-cluster-api:magnum_cluster_api/driver.py:726-728). - Kubelet serving certificates (optional):
serverTLSBootstrap+ externalcluster-api-csr-approverin the management cluster (magnum-cluster-api:docs/admin/kubelet-serving-certificates.md:14-15).
7. Operations
7.1 Create
sequenceDiagram
participant MC as Magnum conductor
participant D as Driver (Py + Rust)
participant K as Mgmt K8s API (magnum-system)
participant C as CAPI / KCP / CAPO controllers
participant OS as Nova / Neutron / Octavia / Cinder / Keystone
MC->>MC: generate cluster CAs (cert_manager)
MC->>D: create_cluster(ctx, cluster, timeout)
D->>K: GET Cluster kube-xxxxx (name uniqueness, retry on conflict)
D->>MC: cluster.stack_id = kube-xxxxx; save
D->>K: apply Namespace, 4 templates, ClusterClass magnum-vX
D->>K: apply CRS <uuid>, CRS <stack_id>-cloud-provider
D->>OS: validate network driver / master_count / fixed net+subnet (Neutron)
D->>K: acquire lock cluster-<uuid>
D->>OS: Keystone create application credential <uuid>
D->>K: Secret <stack_id>-cloud-config, Secrets -ca -etcd -proxy -sa
D->>OS: Nova ensure server groups <stack_id>, <stack_id>-<ng>
D->>OS: Cinder volume types, Glance image, Nova flavors, Keystone catalog
D->>K: Secret <uuid> (CNI, keystone-auth), Secret <stack_id>-cloud-provider (CCM, CSI, StorageClasses)
D->>K: create Cluster <stack_id> (topology + variables)
D-->>MC: return (status CREATE_IN_PROGRESS)
C->>K: watch Cluster, render OpenStackCluster/KCP/MD from ClusterClass patches
C->>OS: Neutron network/subnet/router/SGs, Octavia API LB (+FIP)
C->>OS: Nova servers (+Cinder boot/etcd/docker volumes, server group hints)
C->>K: Secret <stack_id>-kubeconfig, conditions Ready
C->>K: CRS applies CNI/CCM/CSI/keystone-auth into the workload cluster
loop periodic sync_cluster_status
MC->>D: update_cluster_status
D->>K: read KCP, MDs, OpenStackMachines, Cluster conditions, Events
D->>K: helm upgrade --install autoscaler (if enabled, once API up)
D-->>MC: CREATE_COMPLETE, api_address, coe_version
endSources: magnum-cluster-api:magnum_cluster_api/driver.py:70-138, magnum-cluster-api:src/driver.rs:302-310, magnum-cluster-api:magnum_cluster_api/resources.py:1297-1313.
Note the order: the Rust create_cluster (ClusterClass and CRSs) runs before validate_cluster, so a rejected cluster still leaves two CRS objects (deleted later by delete_cluster) (magnum-cluster-api:magnum_cluster_api/driver.py:84-86).
7.2 Resize / nodegroup operations
sequenceDiagram
participant MC as Magnum conductor
participant D as Driver
participant K as Mgmt K8s API
participant C as CAPI / CAPO
participant OS as Nova / Cinder
MC->>D: resize_cluster(node_count, nodes_to_remove, ng)
D->>K: lock cluster-<uuid>; strip empty failureDomain (JSON patch)
opt nodes_to_remove
D->>K: list Machines (cluster-name, deployment-name)
D->>K: annotate matching Machine cluster.x-k8s.io/delete-machine=yes
end
alt ng.role == master
D->>K: SSA Cluster topology.controlPlane.replicas = node_count
else worker
D->>OS: ensure Nova server group <stack_id>-<ng>
D->>K: SSA Cluster machineDeployments[ng].replicas (or autoscaler annotations)
end
D->>MC: ng + cluster UPDATE_IN_PROGRESS
C->>K: MachineSet scale (annotated machines deleted first)
C->>OS: create / delete Nova servers and volumes
MC->>D: update_cluster_status (periodic) -> UPDATE_COMPLETECreate nodegroup appends a new MD entry (magnum-cluster-api:magnum_cluster_api/driver.py:473-499); delete nodegroup removes it, and the periodic sync later deletes the server group and marks DELETE_COMPLETE (magnum-cluster-api:magnum_cluster_api/driver.py:682-720, magnum-cluster-api:magnum_cluster_api/driver.py:535-544). No-op detection: identical replicas / MD spec → return without status change (magnum-cluster-api:magnum_cluster_api/driver.py:646-668).
Autoscaler: cluster-autoscaler runs in the management cluster (one Helm release per cluster, image [auto_scaling] image_repository/cluster-autoscaler:v<per-minor>) and scales MDs through the CAPI provider; replicas are left unset in the topology (magnum-cluster-api:magnum_cluster_api/images.py:23-49, magnum-cluster-api:magnum_cluster_api/resources.py:750-753).
Auto-healing: CAPI MachineHealthCheck generated by the ClusterClass (Ready False/Unknown for 5 min, maxUnhealthy 80 %), enabled per topology flag auto_healing_enabled (magnum-cluster-api:src/resources.rs:46-61, magnum-cluster-api:src/resources.rs:96-111).
7.3 Upgrade
sequenceDiagram participant MC as Magnum conductor participant D as Driver participant K as Mgmt K8s API participant W as Workload K8s API participant C as CAPI / KCP / CAPO participant OS as Nova / Glance MC->>D: upgrade_cluster(cluster, new_template, ...) D->>D: cluster.template_id, labels.kube_tag; every ng: image_id, kube_tag, UPDATE_IN_PROGRESS D->>K: strip empty failureDomain D->>K: apply ClusterClass magnum-<new driver version> D->>W: delete legacy CCM/CSI workloads (via <stack_id>-kubeconfig) D->>K: replace CRS <stack_id>-cloud-provider D->>K: re-apply server groups, Secret <uuid>, Secret <stack_id>-cloud-provider, autoscaler release D->>K: GET+replace Cluster (labels, clusterNetwork, topology incl. new class/version/imageUUID) C->>K: KCP rolling replace, then MD rolling replace C->>OS: new Nova servers from new image, old ones deleted MC->>D: update_cluster_status -> UPDATE_COMPLETE when KCP + MDs + OpenStackMachine image ids match
Sources: magnum-cluster-api:magnum_cluster_api/driver.py:375-436, magnum-cluster-api:src/driver.rs:65-119, magnum-cluster-api:src/driver.rs:312-319, magnum-cluster-api:src/client.rs:73-107. Only kube_tag and image_id are taken from the new template; other label changes are not propagated (magnum-cluster-api:magnum_cluster_api/driver.py:388-396). Because existing MD variables are frozen (§4.3) the per-MD imageUUID override is kept [inferred: worker image change depends on mutate_machine_deployment returning the existing spec unchanged; whether workers get the new image needs verification against CAPI topology semantics].
7.4 Delete
sequenceDiagram
participant MC as Magnum conductor
participant D as Driver
participant K as Mgmt K8s API
participant C as CAPI / CAPO
participant OS as Octavia / Neutron / Nova / Keystone
MC->>D: delete_cluster(cluster)
D->>OS: Octavia list LBs "Kubernetes .+ from cluster <uuid>", cascade delete, delete VIP FIPs, wait (pre_delete_lb_timeout)
D->>K: delete CRS+Secret <stack_id>-cloud-provider, CRS+Secret <uuid>
D->>K: delete Cluster <stack_id>
D->>K: helm delete <stack_id> (autoscaler)
D-->>MC: return -> DELETE_IN_PROGRESS
C->>OS: delete servers, volumes, API LB, network, router, SGs
loop periodic
MC->>D: update_cluster_status
D->>K: Cluster still exists? -> status_reason from events
end
D->>OS: Keystone delete app cred <uuid>; Nova delete server groups
D->>K: delete Secrets cloud-config, ca, etcd, proxy, sa
D-->>MC: DELETE_COMPLETE (Magnum destroys DB rows)Sources: magnum-cluster-api:magnum_cluster_api/driver.py:442-465, magnum-cluster-api:magnum_cluster_api/utils.py:359-465, magnum-cluster-api:magnum_cluster_api/driver.py:281-313. Octavia LBs created by the in-cluster CCM (Services of type LoadBalancer) are removed by the driver because CAPO does not know them (magnum-cluster-api:magnum_cluster_api/driver.py:451-455).
8. Errors and logs
| Error | Raised when | Effect | Source |
|---|---|---|---|
UnsupportedCNI (plain Exception) | network_driver ∉ {cilium, calico} | create → CREATE_FAILED | magnum-cluster-api:magnum_cluster_api/utils.py:500-501, magnum-cluster-api:magnum_cluster_api/exceptions.py:60-61 |
ClusterMasterCountEven | master_count even | CREATE_FAILED | magnum-cluster-api:magnum_cluster_api/utils.py:504-505 |
MasterNGSizeInvalid (Magnum) | control-plane resize to even or < 1 | API 400 [inferred] | magnum-cluster-api:magnum_cluster_api/driver.py:468-470 |
MachineInvalidName (InvalidName) | nodegroup name not RFC 1123 | "Expected a lowercase RFC 1123 subdomain name, got %(name)s." | magnum-cluster-api:magnum_cluster_api/utils.py:534-538, magnum-cluster-api:magnum_cluster_api/exceptions.py:64-65 |
MachineDeploymentNotFound (ObjectNotFound) | MD lookup fails | "MachineDeployment %(name)s not found." | magnum-cluster-api:magnum_cluster_api/exceptions.py:68-69, magnum-cluster-api:magnum_cluster_api/objects.py:96-97 |
heatclient HTTPNotFound | delete_nodegroup and MD absent | intended to let Magnum destroy the ng; in Magnum 2026.2 the handler catches any Exception → DELETE_FAILED (confirmed by the history: Magnum commit 31b84327db88 removed the HTTPNotFound special case, see 06 § 21) | magnum-cluster-api:magnum_cluster_api/driver.py:708-714, magnum:magnum/conductor/handlers/nodegroup_conductor.py:121-131 |
InvalidOctaviaLoadBalancerAlgorithm (Invalid) | octavia_provider=ovn with algorithm ≠ SOURCE_IP_PORT | "Invalid value for octavia_lb_algorithm: …" | magnum-cluster-api:magnum_cluster_api/utils.py:169-172, magnum-cluster-api:magnum_cluster_api/exceptions.py:72-73 |
FlavorNotFound | flavor name/id not in Nova list | — | magnum-cluster-api:magnum_cluster_api/utils.py:475-486 |
VolumeTypeNotFound | no default Cinder type and no conf | — | magnum-cluster-api:magnum_cluster_api/integrations/cinder.py:47-48 |
ServicesListFailed | Keystone service list fails (log Failed to list services) | — | magnum-cluster-api:magnum_cluster_api/integrations/common.py:40-44 |
Conflict | generated name exists (retried by tenacity) / >1 server group with same name | — | magnum-cluster-api:magnum_cluster_api/utils.py:113-123, magnum-cluster-api:magnum_cluster_api/utils.py:638-639 |
PreDeletionFailed | any Octavia cleanup error, incl. "Timeout waiting for the load balancers %s to be deleted." | DELETE_FAILED | magnum-cluster-api:magnum_cluster_api/utils.py:395-396, magnum-cluster-api:magnum_cluster_api/utils.py:460-463 |
Exception("Certificate for %s doesn't exist.") | Magnum cert ref missing | CREATE_FAILED | magnum-cluster-api:magnum_cluster_api/resources.py:559-563 |
OpenStackClusterNotCreated | openstack_cluster property with no OpenStackCluster (also reachable from _get_cluster_status_reason) | status sync raises [inferred] | magnum-cluster-api:magnum_cluster_api/objects.py:247-258 |
HelmReleaseNotFound | helm delete without skip_missing | — | magnum-cluster-api:magnum_cluster_api/helm.py:92-109 |
NotImplementedError | update_cluster, federation | UPDATE_FAILED | magnum-cluster-api:magnum_cluster_api/driver.py:329 |
Rust ImmutableFieldError | ambiguous / unpatchable OpenStackCluster fields ("expected at most 1 {kind} for cluster …") | — | magnum-cluster-api:src/driver.rs:22-28, magnum-cluster-api:src/immutable_fields.rs:16-38 |
Rust KubeError / RuntimeError | any Kubernetes API failure ("API request failed: …", "missing stack id for cluster", "kubeconfig secret not found for cluster") | — | magnum-cluster-api:src/client.rs:16-42, magnum-cluster-api:src/magnum.rs:153-181 |
Rust PyMonitorError | "Failed to get machines", "Failed to get find KubeadmControlPlane" | Magnum logs a warning and skips health | magnum-cluster-api:src/monitor.rs:92-113, magnum:magnum/service/periodic.py:109-118 |
Logs: very few. Python: Helm release %s is already in progress / already exists (INFO, swallowed), Helm template %s failed, There is no %s service enabled in the cloud. (magnum-cluster-api:magnum_cluster_api/helm.py:84-87, magnum-cluster-api:magnum_cluster_api/helm.py:152, magnum-cluster-api:magnum_cluster_api/integrations/common.py:49). Rust logs go to stderr via env_logger, default level warn, controlled by RUST_LOG (not oslo.log) (magnum-cluster-api:src/logging.rs:37-45); one debug message on upgrade (magnum-cluster-api:src/driver.rs:83). The user-visible diagnostic channel is status_reason built from Kubernetes Events.
Rust / pyo3 summary: the extension provides (a) Driver: ClusterClass + templates generation from a feature registry (inventory), addon CRS/secret rendering with an embedded Helm templater (crates/helm), immutable-field resolution, failure-domain cleanup; (b) KubeClient: generic dict→DynamicObject create/replace/delete; (c) Monitor: health. Blocking calls release the GIL (Python::detach) and run on a Tokio runtime (magnum-cluster-api:src/driver.rs:187-241, magnum-cluster-api:src/client.rs:52-70).
9. Configuration options
Driver-specific (registered via oslo.config.opts) (magnum-cluster-api:magnum_cluster_api/conf.py:99-126):
| Group | Option | Default | Use | Source |
|---|---|---|---|---|
[auto_scaling] | image_repository | registry.k8s.io/autoscaling | autoscaler image prefix | magnum-cluster-api:magnum_cluster_api/conf.py:31-37 |
[capi_client] | endpoint_type | publicURL | interface written in clouds.yaml / auth_url lookup | magnum-cluster-api:magnum_cluster_api/conf.py:40-49 |
[capi_client] | ca_file, cert_file, key_file, insecure (False) | — | cacert and verify of clouds.yaml (cert/key unused [inferred]) | magnum-cluster-api:magnum_cluster_api/conf.py:85-97 |
[manila_client] | region_name, endpoint_type (publicURL), api_version (3), ca_file, cert_file, key_file, insecure | — | manilaclient for share types | magnum-cluster-api:magnum_cluster_api/conf.py:52-73, magnum-cluster-api:magnum_cluster_api/clients.py:151-170 |
[proxy] | haproxy_pid_path | /var/run/haproxy.pid | proxy daemon | magnum-cluster-api:magnum_cluster_api/conf.py:76-82 |
Magnum core options consumed: [cinder] default_boot_volume_size (0) and default_boot_volume_type (''), [drivers] verify_ca (True), [kubernetes] keystone_auth_default_policy (/etc/magnum/keystone_auth_default_policy.json), [cluster] pre_delete_lb_timeout (60), [nova_client] endpoint/region (magnum:magnum/conf/cinder.py:40-50, magnum:magnum/conf/drivers.py:19, magnum:magnum/conf/kubernetes.py:19-20, magnum:magnum/conf/cluster.py:35-38, magnum-cluster-api:magnum_cluster_api/clients.py:121-125).
Environment: KUBECONFIG (management cluster), RUST_LOG; proxy: PROXY_PORT, PROXY_BIND, PROXY_ALWAYS (magnum-cluster-api:magnum_cluster_api/proxy/manager.py:44-47, magnum-cluster-api:magnum_cluster_api/proxy/structs.py:51-55). Hard-coded: namespace magnum-system, SSA field manager atmosphere-operator, lock expiry 60 s, server-group cache TTL 300 s on local disk (diskcache) (magnum-cluster-api:magnum_cluster_api/cache.py:12-20).
External requirements:
| Requirement | Value | Source |
|---|---|---|
| CAPI core / kubeadm bootstrap / kubeadm control plane | v1.13.2 in CI (API v1beta1 used everywhere) | magnum-cluster-api:devstack/plugin.sh:1, magnum-cluster-api:src/cluster_api/clusters.rs:19-24 |
| CAPO | v0.14.4 in CI (infrastructure.cluster.x-k8s.io/v1beta1) | magnum-cluster-api:devstack/plugin.sh:2, magnum-cluster-api:src/cluster_api/openstackclusters.rs:17 |
| CAPI feature gates | EXP_CLUSTER_RESOURCE_SET=true, EXP_KUBEADM_BOOTSTRAP_FORMAT_IGNITION=true (Flatcar), CLUSTER_TOPOLOGY=true | magnum-cluster-api:devstack/plugin.sh:65-67 |
helm binary on every conductor | for autoscaler release and keystone-auth templating | magnum-cluster-api:magnum_cluster_api/helm.py:26-28 |
Management nodes labelled openstack-control-plane=enabled | autoscaler pods | magnum-cluster-api:magnum_cluster_api/resources.py:90-92 |
Optional cluster-api-csr-approver | kubelet serving cert verification | magnum-cluster-api:docs/admin/kubelet-serving-certificates.md:14-15 |
| Node images | vexxhost/capo-image-elements qcow2 with os_distro and kubeadm binaries baked in | magnum-cluster-api:docs/user/images.md:13-18, magnum-cluster-api:devstack/local.conf.sample:32 |
| Nova API ≥ 2.15 | soft-(anti-)affinity server groups | magnum-cluster-api:magnum_cluster_api/utils.py:747-748 |
| Management cluster ≥ 1.30 recommended | CRD validation ratcheting for legacy failureDomain: "" | magnum-cluster-api:magnum_cluster_api/driver.py:59-68 |
| Network reachability | CAPI controllers must reach each workload API (FIP / LB VIP), or run the proxy daemon on Neutron network nodes for clusters without FIP | magnum-cluster-api:magnum_cluster_api/proxy/structs.py:49-78 |
10. What an OpenKeel Go rewrite must reproduce vs can simplify
Context: OpenKeel already runs on Kubernetes; Nova is backed by KubeVirt, Neutron by OVN/Kube-OVN, Octavia API in neutron-api, Cinder on CSI block storage.
Must reproduce (user-visible contract)
- Magnum API semantics of the driver:
providestuples for the OS values users already put in templates (ubuntu,ubuntu-focal,debian,flatcar,rockylinux);update_clusterunsupported; control-plane resize allowed only for odd counts;upgradechanges onlykube_tag+ image of the target template (§2). - The status machine of §5 exactly: CAPI
ClusterconditionsControlPlaneReady+InfrastructureReady+Ready→*_COMPLETE; nodegroup rules on KCP replicas and MD phase/spec;status_reasonbuilt from the latest Events;api_address = https://host:portofcontrolPlaneEndpoint;coe_version = topology.version; healthHEALTHY/UNHEALTHY/UNKNOWNwith<machine>.Readyandapikeys (tools and Skyline read these). - The label catalogue of §4 with the same defaults (pod CIDR 10.100.0.0/16, service CIDR 10.254.0.0/16,
auto_healing_enabled=true,keystone_auth_enabled=true,master_lb_floating_ip_enabled=true,boot_volume_*,octavia_*, OIDC, audit, TLS suites, CSI/CCM tags). Unknown labels are silently ignored. - Magnum-generated CAs used as kubeadm CAs (secrets
<name>-ca/-etcd/-proxy/-sa) soca-show/ca-signkeep working. - Per-cluster application credential lifecycle (create at cluster create, delete after infrastructure is gone) and the cloud.conf for CCM/Cinder CSI/Manila CSI.
- Pre-delete cleanup of CCM-created Octavia LBs matching
Kubernetes .+ from cluster <uuid>. - Immutable OpenStackCluster fields (API LB provider/flavor/AZ,
disableAPIServerFloatingIP) must not change on later updates. - Autoscaler min/max annotations and MHC behaviour (
auto_healing_enabled).
Can simplify
- Drop pykube + Python/Rust split: one Go controller using controller-runtime and the upstream CAPI/CAPO Go types; no GIL/eventlet workarounds (magnum-cluster-api:magnum_cluster_api/utils.py:63-91, magnum-cluster-api:src/logging.rs:1-45), no
tpool. - Watch instead of poll: an informer on
Cluster,KubeadmControlPlane,MachineDeployment,Machinecan update Magnum status immediately, replacingsync_cluster_statuspolling and the sherlock lock (use the Magnum DB row lock or a Lease per cluster). - Management cluster = OpenKeel's own cluster: no separate kubeconfig, no
magnum-cluster-api-proxy/HAProxy-in-netns (OpenKeel pods can reach tenant networks through OVN/Kube-OVN or a Service) [inferred: depends on OpenKeel tenant-network reachability design]. - Infrastructure provider choice: CAPO against OpenKeel's Nova/Neutron/Octavia/Cinder APIs (zero-change compatibility, CAPO only needs the OpenStack API) vs. CAPK (Cluster API Provider KubeVirt) directly on KubeVirt (fewer hops, but loses Nova server groups, Octavia API LB, Cinder root volumes and the
OpenStackMachineimage/flavor checks in §5.2 — status logic would need re-mapping). Recommendation: keep CAPO first for behavioural parity [inferred]. - No Helm binary: render addon manifests in-process (Go
helm.sh/helm/v3engine or pre-rendered embedded YAML) and deploy the autoscaler as a plain Deployment. - Drop legacy paths: the legacy CRS
<uuid>vs<stack_id>-cloud-providersplit, Calico manifest zoo (18 versions), legacy CCM/CSI cleanup on upgrade,failureDomain: ""cleanup,disableAPIServerFloatingIPManaged,ubuntu-focal— all exist only for upgrades from older driver releases. - Single ClusterClass per OpenKeel release is a good pattern to keep; versioned name makes upgrades a
topology.classswitch. - No Memcached/Redis: the driver's only cache is a local diskcache for server-group ids (magnum-cluster-api:magnum_cluster_api/cache.py:4-20) — replace with an in-process map or none.
11. Blind spots / [inferred]
- Magnum 2026.2 + heatclient: the driver still imports
heatclient.exc(magnum-cluster-api:magnum_cluster_api/driver.py:18) and relies on its ownpython-heatclientdependency (magnum-cluster-api:pyproject.toml:44); Magnum 2026.2'snodegroup_deleteno longer special-casesHTTPNotFound(magnum:magnum/conductor/handlers/nodegroup_conductor.py:121-131), so deleting a nodegroup whose MD is already gone ends inDELETE_FAILEDinstead of row deletion: Magnum commit31b84327db88removed theHTTPNotFoundspecial case (06 § 21). Not run. needs_trust = False(magnum-cluster-api:magnum_cluster_api/driver.py:47) is not read anywhere in Magnum 2026.2 core (grep found no consumer); whether 2026.2 still creates trusts for this driver was not traced.kube_tagdefault mismatch: Python defaults tov1.25.3(magnum-cluster-api:magnum_cluster_api/utils.py:228-229) while the RustClusterLabels.kube_taghas no pyo3 default (magnum-cluster-api:src/magnum.rs:108-110); a cluster without the label likely fails Rust extraction — not executed._get_cluster_status_reasonmay raiseOpenStackClusterNotCreatedbefore CAPO creates theOpenStackCluster(magnum-cluster-api:magnum_cluster_api/driver.py:153, magnum-cluster-api:magnum_cluster_api/objects.py:255-256); impact on the periodic sync not verified.- Sherlock's Kubernetes lock object kind (Lease vs. custom) not checked (library not installed locally).
- Upgrade of worker images: depends on CAPI topology propagation of the top-level
imageUUIDvs. per-MD overrides frozen bymutate_machine_deployment(§7.3); not traced in CAPI. - Exact rendered contents of Cilium/OCCM/CSI charts (values beyond those cited) were not reviewed line by line; Flatcar Ignition patches and volume partition layouts summarised from patch paths only.
docs/user/configs.mddocuments[auto_scaling] v1_22_image…v1_27_imageoptions that no longer exist inconf.py(magnum-cluster-api:docs/user/configs.md:15-49 vs magnum-cluster-api:magnum_cluster_api/conf.py:31-37): doc drift.- CI versions (CAPI v1.13.2, CAPO v0.14.4) are what the project tests with; no hard version constraint is enforced at runtime.
- The
magnum-cluster-api-image-builderCLI (magnum-cluster-api:pyproject.toml:65) was not analysed.
Source: reverse-spec-magnum/08-driver-magnum-cluster-api_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.