# node-vm: a single MAAS-managed OpenStack node VM (D-103's "OpenTofu creates
# the node-VM libvirt domains (SHIM -- Section 9)" -- Stage 3 / buildout-design
# Phase 2). BLANK disk, PXE-boot -- this is deliberately NOT the cloud-init +
# pre-built-image pattern (that's a separate, still-unbuilt module for Office1
# service VMs / OPNsense; see opentofu/README.md). MAAS enlists, commissions,
# and deploys these VMs after OpenTofu creates them; OpenTofu does not image
# them itself.
#
# Every attribute below is used exactly as shown in the provider's own
# examples/domain_with_network.tf and examples/alpine_cloudinit.tf (fetched
# and read directly this session, tag v0.9.8) -- attribute-style nested
# objects (`os = { ... }`, `devices = { ... }`), NOT classic HCL blocks. The
# ONE inference in this file, the per-device `boot` object's internal shape
# (`order`), is now BETTER-SUPPORTED after a 2026-07-09 audit pass (see
# opentofu/README.md): the provider is confirmed to code-generate its schema
# as a 1:1 mirror of libvirt's own domain XML, and libvirt's own official
# docs confirm the native XML element is exactly `<boot order='N'/>` (one
# attribute, `order`) -- consistent with every other single-attribute
# element already confirmed in this schema (e.g. `mtu = { size = N }` mirrors
# `<mtu size='N'/>`). Reasonably confident, not a blind guess anymore, but
# still not seen in an actual generated schema dump -- VERIFY with
# `tofu providers schema -json` before the first real apply regardless; if
# wrong, `tofu plan` will simply reject the attribute name, not silently
# misconfigure anything.
resource "libvirt_volume" "disk" {
name = "${var.vm_name}-disk.qcow2"
pool = var.pool_name
capacity = var.disk_size_bytes
format = {
type = "qcow2"
}
}
resource "libvirt_domain" "node" {
name = var.vm_name
memory = var.memory_mib
vcpu = var.vcpu
type = "kvm"
running = true
os = {
type = "hvm"
type_arch = "x86_64"
type_machine = "q35"
}
devices = {
disks = [
{
source = {
volume = {
pool = libvirt_volume.disk.pool
volume = libvirt_volume.disk.name
}
}
target = {
dev = "vda"
bus = "virtio"
}
driver = {
type = "qcow2"
}
# boot.order shape UNVERIFIED (see file header) -- disk boots second,
# network boots first (PXE).
boot = {
order = 2
}
}
]
interfaces = [
for i, net_name in var.network_names : {
model = {
type = "virtio"
}
source = {
network = {
network = net_name
}
}
# only the first (PXE) interface gets an explicit boot order;
# see the disk boot-order note above for the same caveat.
boot = i == 0 ? { order = 1 } : null
}
]
}
}