Skip to content

v0.1.7: block device boot sources, migration priority, port security, scheduler hints, and resmgr absence detection - #45

Merged
PF9-pushkar merged 6 commits into
mainfrom
pushkar/instance-block-device
Aug 18, 2026
Merged

v0.1.7: block device boot sources, migration priority, port security, scheduler hints, and resmgr absence detection#45
PF9-pushkar merged 6 commits into
mainfrom
pushkar/instance-block-device

Conversation

@PF9-pushkar

Copy link
Copy Markdown
Collaborator

Everything a VM can be created from in the UI, the placement controls that need, and one
resmgr read that was quietly wrong.

Added

  • block_device on pcd_compute_instance — the four boot sources the create-VM wizard offers
    and the provider could not express: boot from a new volume, from an existing volume, from a volume
    snapshot, and install from an ISO. Mirrors openstack_compute_instance_v2 so configurations port
    across unchanged. volume_type in a block-device mapping needs compute microversion 2.67, so that
    is negotiated for the create call alone — read paths keep the 2.1 response shapes they were
    written against.
  • migration_priority on pcd_compute_instance — how DRR treats a VM when balancing hosts,
    stored as the migration-priority metadata key exactly as the UI's dialog does. Updatable in
    place, coexists with user metadata, and the reserved key is rejected at plan time.
  • port_security_enabled on pcd_networking_network (and its data source) — with it, a Layer 2
    / "Simple" network is expressible: a provider segment, no subnet, port security off, tagged
    simple_network. Note that Nova refuses to boot on a subnet-less network by uuid, so those VMs
    attach through a pcd_networking_port; the example says so.
  • scheduler_hints on pcd_compute_instancegroup, different_host, same_host,
    additional_properties. pcd_compute_servergroup existed but nothing could join one.

Fixed

  • availability_zone = "<az>:<host>" no longer fails with "inconsistent result after apply".
    Nova reports only the zone, so an admin's host pin was being dropped on read.

  • resmgr absence detection. GET /resmgr/v2/clusters/<name> answers 200 with a body of null
    for a cluster that is gone, rather than a 404. That decoded into a zero-value struct with no
    error, so Read reported the resource as present-but-blank and never called RemoveResource
    leaving Terraform convinced a destroyed cluster still existed, and a torn-down region impossible
    to rebuild. Fixed in the shared getJSON helper, so the blueprint, host-config and host reads get
    it too.

    Before and after, same state, same out-of-band deletion:

    pre-fix   No changes. Your infrastructure matches the configuration.
    fixed     # pcd_cluster.proof will be created
              Plan: 1 to add, 0 to change, 0 to destroy.
    

Verification

Static: go build, go vet, gofmt, golangci-lint (0 issues), unit tests across all 11
packages, make generate clean, terraform fmt -check examples/.

Live, against a two-hypervisor PCD 2026.4 CE lab: all four block-device boot sources reach ACTIVE
with the ISO landing as an IDE cdrom (confirmed in libvirt); migration priority round-trips
including an out-of-band UI change showing as drift; port security false → true → false → omitted;
the L2 network classified as Layer 2 by the UI's own rule; and the full zero-to-hero path — empty
region to blueprint, cluster, host configs, four converged roles, image, flavour, VM, volume and a
ping — in 8m54s with a clean plan afterwards.

The absence-detection fix and the import gap it sits next to were both found by the new pre-PR
regression suite on its first live run. The import gap is tracked as #44 and is not addressed here.

PF9-pushkar and others added 6 commits August 17, 2026 12:48
…abled

Close the gaps between what the PCD UI's Deploy VM wizard and Simple
Networking can do and what the provider could express. Every change is
verified live against a 2026.4 CE lab, with the API as the judge and a
"No changes" plan after each step.

pcd_compute_instance gains block_device (Nova block_device_mapping_v2),
mirroring openstack_compute_instance_v2 so configurations port
unchanged. This unlocks the four wizard boot sources the provider could
not express: a NEW volume from an image (the wizard's default), an
EXISTING volume, a VOLUME SNAPSHOT, and INSTALL FROM ISO (blank root at
boot_index 0 + the ISO as a cdrom device at boot_index 1). image_id /
image_name become optional when a block_device supplies the root disk.
Nova only accepts volume_type in a block-device mapping at compute API
microversion >= 2.67, so the create call negotiates 2.67 when block
devices are present — on a copy of the client, so read paths keep the
2.1 response shapes they were written against. Live: all four sources
reach ACTIVE; libvirt shows the ISO on hda as device='cdrom' (IDE) with
the blank target on vda (virtio), exactly what the wizard produces.

pcd_compute_instance gains migration_priority (normal | low | high |
never), a first-class handle on the DRR service's per-VM setting. It is
stored as the "migration-priority" server-metadata key — precisely what
the UI's Set Migration Priority dialog writes — and kept out of the
user-facing metadata map on read, so the two attributes never drift
against each other; configuring the key in metadata is rejected at plan
time, as is an invalid value. Updatable in place, "" clears (Nova's
metadata update merges, so clearing issues an explicit delete). Live:
create/update/clear round-trip; user metadata keys survive; an
out-of-band UI write shows up as a plan diff back to the configured
value.

pcd_networking_network gains port_security_enabled (default true), on
the resource and data source. false is the defining property of a
Layer 2 / "Simple" network — no subnet, no DHCP, no security groups —
and with segments plus the simple_network tag the UI keys on, an L2
network is now one resource. Nova refuses to boot on a subnet-less
network by id, so the instance attaches through a pcd_networking_port on
it (the L2 model anyway); the example shows both halves. Live: create
false → true → false round-trips; a VM boots on the L2 network via a
port; the UI classifies the network as Layer 2.
resmgr does not 404 for an object that is gone. `GET /resmgr/v2/clusters/<name>`
answers 200 with a body of `null` for a cluster that was deleted or never
existed — reproduced against 2026.4:

    $ curl .../resmgr/v2/clusters/does-not-exist   ->  200, body: null

Decoded straight into a struct that is a nil unmarshal: no error, every field
left at zero. So Read reported the resource as present-but-blank, never reached
isNotFound, and never called RemoveResource — leaving Terraform convinced a
destroyed cluster still existed. The next apply then plans no create for it, so
a region that has been torn down can never be rebuilt, which is the whole point
of the resource.

Fixed in the shared getJSON helper rather than in one Read, so the cluster,
blueprint, host-config and host reads behind host_config_assignment and
host_role all get it: read the body first, and report a 200 that carries no
object as absence. Every getJSON caller fetches a single object by name or id,
so no list response passes through here.

Found by the new pre-PR regression suite on its first live run.
@PF9-pushkar
PF9-pushkar merged commit 9ff0b64 into main Aug 18, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant