Newer
Older
openstack-caracal-dc-dc / opentofu / modules / cloudinit-vm / main.tf
# cloudinit-vm: one cloud-init-provisioned VM booting from a shared base
# image via copy-on-write. For Office1's own service VMs (MAAS-region,
# NetBox, GitBucket -- D-103) -- genuinely different from
# modules/node-vm's blank-disk PXE pattern, which is for MAAS-managed
# OpenStack nodes that MAAS itself images after enlistment. Call
# modules/base-image once per distinct image, this module once per VM
# instance.
#
# NOT CONFIRMED for OPNsense: this whole pattern (cloud-init/NoCloud via
# libvirt_cloudinit_disk) is standard for Linux cloud images; OPNsense is
# FreeBSD-based and its cloud-init/NoCloud support is not the same guarantee.
# Do not assume this module covers OPNsense without checking that
# specifically first -- see opentofu/README.md.
#
# Every resource/attribute shape below is used exactly as shown in
# examples/alpine_cloudinit.tf (fetched and read directly this session, tag
# v0.9.8) -- attribute-style nested objects throughout, same convention as
# modules/node-vm and the corrected modules/dc-planes etc. No `graphics`
# block: this is a headless server VM pattern (the real example included VNC
# graphics; omitted here as unnecessary for a service VM, not because it's
# confirmed invalid to include -- add it back if console-debug access is
# ever wanted).

resource "libvirt_volume" "disk" {
  name     = "${var.vm_name}-disk.qcow2"
  pool     = var.pool_name
  capacity = var.disk_size_bytes

  target = {
    format = {
      type = "qcow2"
    }
  }

  backing_store = {
    path = var.base_volume_path # the caller passes module.<base_image>.path
                                 # straight through -- a real attribute
                                 # reference, matching
                                 # examples/alpine_cloudinit.tf's
                                 # `libvirt_volume.alpine_base.path` exactly,
                                 # just threaded across a module boundary
                                 # instead of within one file.
    format = {
      type = "qcow2"
    }
  }
}

resource "libvirt_cloudinit_disk" "seed" {
  name = "${var.vm_name}-cloudinit"

  user_data      = var.user_data
  meta_data      = var.meta_data
  network_config = var.network_config
}

resource "libvirt_volume" "seed" {
  name = "${var.vm_name}-cloudinit.iso"
  pool = var.pool_name

  create = {
    content = {
      url = libvirt_cloudinit_disk.seed.path
    }
  }
}

resource "libvirt_domain" "vm" {
  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"
        }
      },
      {
        device = "cdrom"
        source = {
          volume = {
            pool   = libvirt_volume.seed.pool
            volume = libvirt_volume.seed.name
          }
        }
        target = {
          dev = "sda"
          bus = "sata"
        }
      }
    ]

    interfaces = [
      for net_name in var.network_names : {
        model = {
          type = "virtio"
        }
        source = {
          network = {
            network = net_name
          }
        }
      }
    ]
  }
}