VirtualMachine¶
VirtualMachine is the user-facing CRD. Everything in banlieue's design is
oriented around keeping its shape small, uniform, and provider-agnostic.
The authoritative definition lives in
crates/banlieue-api/src/banlieue/virtualmachine.rs.
Generated CRD YAML lives in deploy/crds/ and is produced by the crdgen
binary — see Architecture.
Shape (illustrative)¶
apiVersion: banlieue.io/v1alpha1
kind: VirtualMachine
metadata:
name: db-prod-01
spec:
classRef:
name: db-prod-large # name of a VMClass (CPU/memory/disk shape)
imageRef:
name: ubuntu-22-04 # name of a VMImage (boot image)
placement:
providerSelector:
matchLabels: { dc: dc1, env: prod } # which Provider(s) may schedule this VM
userData: # optional cloud-init / ignition / sysprep
secretRef: # or configMapRef for non-sensitive data
name: db-prod-01-cloudinit
key: user-data # defaults to "user-data" when omitted
desiredPowerState: PoweredOn
Note what is not there: no vsphere: block, no proxmox: block, no
backend-specific knobs. That's deliberate; see
Abstraction principle.
Status, uniformly¶
VirtualMachine.status follows the K8s conventions: a conditions[] array
plus a small set of well-known fields. Every provider produces the same
condition vocabulary:
Condition type |
Meaning |
|---|---|
Ready |
The VM exists, is provisioned, and is reachable. |
Scheduled |
A Provider matching placement has been selected. |
PlacementValid |
The requested VMClass/VMImage/placement combination is resolvable. |
InfrastructureReady |
The backend infrastructure CR reports Ready. |
Migrating |
(optional) A recreate-based migration to a new Provider/failure domain is in progress. |
status.initialization.provisioned (a boolean, not a condition) tracks
whether the backend has ever accepted the spec.
Status is mirrored from the underlying infrastructure CR; the main
controller never sets provisioned=true on its own. See
Architecture → Provision a VM.
Lifecycle¶
Spec field reference¶
The list below is illustrative for Phase 0–1A. Use
kubectl explain virtualmachine.spec(or the API reference) for the authoritative shape.
classRef.name(string, required) — references aVMClass(CPU / memory / disks).imageRef.name(string, required) — references aVMImage(boot image).placement.providerSelector/placement.failureDomainSelector(label selectors, optional) — whichProvider(s)/failure domains may schedule this VM. There is no direct-by-nameProviderreference; the scheduler matches labels. A failure domain that publishes capacity (status.failureDomains[].attributes.capacity, today the Cloud Hypervisor provider) qualifies only if the VM fits beside the VMs already placed there: its class's memory plus the per-VM overhead strictly, and its vCPUs within the published CPUs times the Provider'sspec.scheduling.cpuOvercommitRatio(default 4). A domain without capacity is not checked (ADR-0107). A VM that fits nowhere reportsScheduled=FalsewithNoFailureDomainMatchedand aninsufficient capacityreason per domain. Among the failure domains that qualify, it picks the one with the fewest VMs already scheduled: the fewest of the same pool for a pool member, the fewest overall otherwise. Equal loads are broken per VM, by hashing, so VMs created together spread rather than queue on one domain (ADR-0098).desiredPowerState(string, optional) —PoweredOn(default) |PoweredOff|Suspended. The provider keeps the VM in this state on every pass, on every backend: a VM that powers itself off (for example after an install whose cloud-config setsinstall.poweroff: true) is started again, and one stopped in the hypervisor's own console is too. To hold a VM off, setPoweredOff.userData(object, optional) — references a Secret (secretRef) or ConfigMap (configMapRef) carrying the cloud-init / ignition / sysprep payload (exactly one must be set). See ADR-0025 and ADR-0038.
Per-VM overrides (deltas, not primary definitions)¶
A VirtualMachine inherits its hardware shape and network topology from the
VMClass it references. Two optional fields let you override specific values
for a single VM without creating a new class:
| Field | What it overrides | Type |
|---|---|---|
hardwareOverride.cpus |
CPUs from VMClass.spec.hardware.cpus |
u32? |
hardwareOverride.memoryMiB |
Memory from VMClass.spec.hardware.memoryMiB |
u32? |
hardwareOverride.diskOverrides |
Disk sizes from VMClass.spec.hardware.disks |
list, keyed by name |
networkOverrides |
IPAM on a named interface from VMClass.spec.network.interfaces |
list, keyed by name |
These are deltas, not primary definitions. The VMClass remains the
authoritative source for the VM's shape. Only the fields you set in an
override replace the class value; everything else is inherited unchanged.
Use overrides sparingly. Their purpose is to accommodate the rare VM that
genuinely needs a different budget than its class defines — for example, a
database primary bumped to 16 CPUs while all other replicas use the 4-CPU
class shape, one VM that needs a larger data disk, or one VM that needs a
static IP while the rest use DHCP. If you find yourself setting the same
override on every VM of a given class, create a new VMClass instead.
spec:
classRef:
name: db-prod-large
# Delta: bump memory + data disk for this one VM; CPUs and OS disk stay as
# the class defines.
hardwareOverride:
memoryMiB: 65536
diskOverrides:
- name: data
sizeGiB: 2000
# Delta: pin eth0 to a static address; other interfaces use the class IPAM.
networkOverrides:
- name: eth0
static:
address: 192.0.2.90
prefix: 24
gateway: 192.0.2.1
nameservers: [192.0.2.53]
Static addresses are checked against other banlieue VMs only¶
banlieue checks that no other banlieue VirtualMachine claims a static
address. It does not check the network itself. The check runs in
banlieue-controller against the Kubernetes API and never asks the hypervisor,
an IPAM or the network. A VM created directly in vCenter, a machine managed by
another tool, or a physical host on the same subnet is invisible to it. A
provider-side lookup that would catch those is deferred; see Not decided
here in
ADR-0083.
A VM whose networkOverrides address is already claimed by another
VirtualMachine on the same networkClass reports:
Ready=False reason=DuplicateAddress
message: address 192.0.2.90 on interface eth0 (networkClass prod) is already
claimed by VirtualMachine apps/db-01; choose a free address or
delete the holder (ADR-0083)
- Who counts as a claimant: any VM the controller watches, in any
namespace, that declares the address in
networkOverrides, or that holds it. A VM holds the addresses recorded in itsstatus.heldAddresses, which the controller writes when it applies the VM's infrastructure CR. Guest-reportedstatus.addressesplay no part, so a DHCP VM's lease is not checked: keep static addresses outside DHCP scopes. - Who wins: a VM that holds the address keeps it, even while its guest is rebooting. Otherwise the older VM wins. Editing a running VM onto an address another VM holds blocks the edited VM.
- Behaviour is the same on every provider. Holding comes from what the controller applied, not from what a provider reports, so it works on vSphere (which publishes no guest addresses today) exactly as on libvirt and Cloud Hypervisor (whose guest-reported addresses disappear while a guest reboots).
- A blocked VM that was never provisioned gets no infrastructure CR, so the address never reaches a provider.
- A blocked VM that is already running is not frozen. Its guest keeps its
current address and its status keeps updating.
desiredPowerStateis still applied, so you can power it off. Other spec changes are held back until the conflict is resolved, and the condition message says so. - Different networks: the same address on two different
networkClasses is allowed, because isolated networks may reuse a subnet. - Other namespaces: a holder in another namespace blocks too, but the message says "a VirtualMachine in another namespace" instead of naming it.
- Unblocking: delete the holder or choose a free address. The blocked VM rechecks every 30 seconds and proceeds on the first check after the conflict is gone.
Related CRDs¶
- VMClass — flavour / size shape (CPU, memory, disk).
- VMImage — boot image source.
- Provider — which backend serves this VM.
The infrastructure CRDs (VSphereMachine, future ProxmoxMachine,
LibvirtMachine) are documented under Provider Model and
Infrastructure CRDs & CAPI. The user normally doesn't
touch them.