|
| 1 | +# Odorobo VM manifest contract |
| 2 | + |
| 3 | +The Odorobo VM manifest is the provider-neutral description of VM intent. It is |
| 4 | +not a Cloud Hypervisor `VmConfig`; the Cloud Hypervisor driver owns conversion, |
| 5 | +node-local paths, and runtime details. The current contract is version `1` and |
| 6 | +is represented by `odorobo::manifest::VmManifest`. |
| 7 | + |
| 8 | +## Existing field inventory |
| 9 | + |
| 10 | +The legacy `VirtualMachine` model in `odorobo/src/types.rs` currently combines |
| 11 | +intent and runtime data: `VMData` contains identity, name, vCPU limits, memory, |
| 12 | +an image, volumes, and network IDs, while `VirtualMachine` adds node, status, |
| 13 | +metadata, and affinity. The manifest separates those concerns so the control |
| 14 | +plane can provide stable intent without depending on the legacy shape. |
| 15 | + |
| 16 | +The current Cloud Hypervisor conversion in `odorobo/src/ch_driver/actor.rs` |
| 17 | +consumes vCPUs, maximum vCPUs, memory, and the image as a disk. Firmware and |
| 18 | +serial/platform defaults are currently driver-owned. Network, volume-to-disk, |
| 19 | +cloud-init, and vsock conversion remain provider integration work; their |
| 20 | +manifest fields are defined here so those later conversions have a stable |
| 21 | +contract and explicit ownership boundary. |
| 22 | + |
| 23 | +## State ownership |
| 24 | + |
| 25 | +`desired` is supplied by the control plane and is the source of truth for what |
| 26 | +Odorobo should provision. It contains: |
| 27 | + |
| 28 | +- `metadata`: stable name, labels, and annotations. |
| 29 | +- `compute`: boot vCPUs, optional scaling ceiling, and memory in bytes. |
| 30 | +- `storage`: ordered storage attachments. An attachment references either a storage URI or a |
| 31 | + provisioned volume ID; `boot` identifies the boot attachment. The control plane owns |
| 32 | + attachment identity and ordering; the provider driver/storage transform resolves the |
| 33 | + URI or volume ID to a node-local device path. |
| 34 | +- `networks`: stable network IDs and optional guest MAC addresses. The control plane owns |
| 35 | + the attachment identity and requested MAC; the provider networking transform resolves |
| 36 | + the network to a host interface or tap device. |
| 37 | +- `placement`: scheduling hints, including an optional node, required node labels, |
| 38 | + and affinity rules. Affinity rules support required or weighted-preferred |
| 39 | + VM/agent matching; `inverse` selects anti-affinity, with OR-ed |
| 40 | + label/annotation requirements. |
| 41 | +- `boot`: whether to start after provisioning and optional firmware/kernel/ |
| 42 | + command-line intent. |
| 43 | +- `cloud_init`: paired NoCloud user-data and meta-data. |
| 44 | +- `vsock`: guest CID and the desired host-side socket location. |
| 45 | + |
| 46 | +`observed` is reported by Odorobo and is never used as desired input. It records |
| 47 | +status, the node currently running the VM, the provider's runtime state, and an |
| 48 | +error message when applicable. Cloud Hypervisor configuration and generated |
| 49 | +paths are observed/driver-owned implementation details, not manifest fields. |
| 50 | + |
| 51 | +Providers may reject a valid manifest field when they cannot implement it, but |
| 52 | +must report that explicitly. They must not silently discard storage, network, |
| 53 | +boot, cloud-init, or vsock intent. |
| 54 | + |
| 55 | +## Validation and evolution |
| 56 | + |
| 57 | +A manifest must use a supported `api_version`, have a non-empty metadata name, |
| 58 | +non-zero vCPUs and memory, and satisfy these relationships: |
| 59 | + |
| 60 | +- `max_vcpus` must be at least `vcpus`. |
| 61 | +- Every storage attachment must have a non-empty ID and exactly one usable source (URI or volume reference), and |
| 62 | + a boot storage attachment cannot be read-only. At most one storage attachment may be marked as boot. |
| 63 | +- Affinity requirements within a rule are OR-ed; rules are combined according to |
| 64 | + their strictness, and `inverse` negates a rule's result. `lt` and `gt` comparisons require exactly one |
| 65 | + finite numeric value. |
| 66 | +- Every network must have a non-empty, non-whitespace ID. |
| 67 | +- Cloud-init must provide non-empty configuration with user-data and meta-data |
| 68 | + supplied together. |
| 69 | +- A vsock guest CID must be non-zero and its socket must be an absolute path. |
| 70 | + |
| 71 | +Invalid field combinations are rejected during deserialization, as are unknown |
| 72 | +fields, rather than silently interpreted. New fields should be added in a future manifest version when they |
| 73 | +change semantics; unreleased formats do not require Proxmox compatibility |
| 74 | +layers. Providers may reject a valid manifest field they cannot implement, with |
| 75 | +a clear unsupported-field error, rather than dropping it. This contract is |
| 76 | +therefore intentionally forward-evolving, not a compatibility layer for |
| 77 | +Proxmox or unreleased Odorobo formats. |
| 78 | + |
| 79 | +## Examples |
| 80 | + |
| 81 | +Representative JSON fixtures are in [`fixtures/manifest`](fixtures/manifest): |
| 82 | + |
| 83 | +- [`minimal.json`](fixtures/manifest/minimal.json) |
| 84 | +- [`storage-backed.json`](fixtures/manifest/storage-backed.json) |
| 85 | +- [`networked.json`](fixtures/manifest/networked.json) |
| 86 | +- [`cloud-init.json`](fixtures/manifest/cloud-init.json) |
| 87 | +- [`vsock.json`](fixtures/manifest/vsock.json) |
| 88 | + |
| 89 | +For example: |
| 90 | + |
| 91 | +```json |
| 92 | +{ |
| 93 | + "api_version": 1, |
| 94 | + "id": "01J00000000000000000000005", |
| 95 | + "desired": { |
| 96 | + "metadata": { "name": "vm", "labels": {}, "annotations": {} }, |
| 97 | + "compute": { "vcpus": 2, "memory_bytes": 2147483648 }, |
| 98 | + "storage": [], |
| 99 | + "networks": [], |
| 100 | + "placement": {}, |
| 101 | + "boot": { "start": true }, |
| 102 | + "vsock": { "guest_cid": 42, "socket": "/run/odorobo/vms/vm/vsock.sock" } |
| 103 | + } |
| 104 | +} |
| 105 | +``` |
0 commit comments