Skip to content

Commit 9ff0b64

Browse files
authored
Merge pull request #45 from platform9/pushkar/instance-block-device
v0.1.7: block device boot sources, migration priority, port security, scheduler hints, and resmgr absence detection
2 parents fa5f6f4 + af31bfa commit 9ff0b64

12 files changed

Lines changed: 1141 additions & 57 deletions

File tree

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,52 @@ All notable changes to this project are documented here. The format is based on
66

77
## [Unreleased]
88

9+
## [0.1.7] - 2026-08-18
10+
11+
### Fixed
12+
13+
- `pcd_cluster`, `pcd_cluster_blueprint`, `pcd_host_config` and the host reads behind
14+
`pcd_host_config_assignment` / `pcd_host_role`: an object resmgr no longer has is now removed from
15+
state. resmgr answers `GET` for a deleted cluster with `200` and a body of `null` rather than a
16+
`404`; that decoded into a zero-value struct with no error, so Read reported the resource as
17+
present-but-blank and never called `RemoveResource` — leaving Terraform convinced a destroyed
18+
region still existed and unable to rebuild it. A 200 that describes no object is now absence.
19+
- `pcd_compute_instance`: `availability_zone = "<az>:<host>"` (admin host pin) no longer fails with "inconsistent result after apply" — the pin is preserved on read, as upstream does.
20+
21+
### Added
22+
- `pcd_compute_instance`: `scheduler_hints` block (`group`, `different_host`, `same_host`,
23+
`additional_properties`) — upstream parity; makes `pcd_compute_servergroup` usable for
24+
affinity / anti-affinity placement.
25+
- **`pcd_compute_instance` gains `block_device`** — Nova `block_device_mapping_v2`, mirroring
26+
`openstack_compute_instance_v2` so configurations port unchanged. This unlocks every boot
27+
source the PCD UI's Deploy VM wizard offers beyond "Image": boot from a **new volume**
28+
(`source_type = "image"`, `destination_type = "volume"`, `volume_size`, `volume_type`,
29+
`delete_on_termination` — the wizard's default), an **existing volume**, a **volume
30+
snapshot**, and **install from ISO** (a blank root volume at `boot_index = 0` plus the ISO
31+
as a `device_type = "cdrom"` volume at `boot_index = 1`). `image_id`/`image_name` become
32+
optional when a `block_device` with `boot_index = 0` supplies the root disk. Extra data disks
33+
at boot are `boot_index = -1`. Create-only, like upstream; use `pcd_compute_volume_attach`
34+
for day-2 attach/detach. When block devices are present the create call negotiates compute
35+
API microversion 2.67 (required for `volume_type` in a block-device mapping; PCD 2026.4
36+
serves up to 2.100); every other call keeps the provider's default so read paths are
37+
unchanged. Verified live against a 2026.4 CE: all four boot sources reach ACTIVE, the ISO
38+
path presents the installer as an IDE `cdrom` device with the blank target on virtio.
39+
- **`pcd_compute_instance` gains `migration_priority`** — how PCD's Dynamic Resource
40+
Rebalancing (DRR) service treats the VM: `normal`, `low`, `high`, or `never` (excluded).
41+
Stored as the `migration-priority` server-metadata key, exactly as the PCD UI's "Set
42+
Migration Priority" dialog does; updatable in place, `""` clears. The key is reserved and
43+
kept out of `metadata` on read, so the two attributes never drift against each other, and
44+
configuring it directly in `metadata` is rejected.
45+
- **`pcd_networking_network` gains `port_security_enabled`** (default `true`), mirroring
46+
`openstack_networking_network_v2`. Setting it `false` is what makes a Layer 2 / "Simple"
47+
network (the PCD UI's Simple Networking option: no subnet, no DHCP, no security groups —
48+
VMs manage their own addressing). Together with `segments` and the `simple_network` tag the
49+
UI keys on, an L2 network is now fully expressible in one resource. Note that Nova refuses
50+
to boot on a subnet-less network by network id ("requires a subnet in order to boot
51+
instances on"): attach the instance through a `pcd_networking_port` on the network instead —
52+
the L2 model anyway. The example shows both halves. Also read back by the
53+
`pcd_networking_network` data source.
54+
955
## [0.1.6] - 2026-08-15
1056

1157
### Fixed

docs/data-sources/networking_network.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ data "pcd_networking_network" "example" {
3434
- `description` (String) The network description.
3535
- `external` (Boolean) Whether the network is external.
3636
- `id` (String) The network ID.
37+
- `port_security_enabled` (Boolean) Whether port security is enforced on ports of this network. `false` on a Layer 2 / "Simple" network.
3738
- `shared` (Boolean) Whether the network is shared.

docs/resources/compute_instance.md

Lines changed: 176 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@ resource "pcd_networking_network" "example" {
1717
name = "tf-example-network"
1818
}
1919
20-
resource "pcd_compute_instance" "example" {
20+
# Boot from an image (ephemeral root disk on the hypervisor).
21+
resource "pcd_compute_instance" "from_image" {
2122
name = "tf-example-instance"
2223
image_name = "Ubuntu-22.04"
2324
flavor_name = "m1.small"
2425
key_pair = "tf-example-key"
2526
2627
security_groups = ["default"]
2728
29+
# How PCD's Dynamic Resource Rebalancing (DRR) treats this VM when balancing
30+
# hosts: normal | low | high | never (excluded). Same as the UI's
31+
# "Set Migration Priority" action. Updatable in place.
32+
migration_priority = "high"
33+
2834
metadata = {
2935
environment = "dev"
3036
}
@@ -33,6 +39,137 @@ resource "pcd_compute_instance" "example" {
3339
uuid = pcd_networking_network.example.id
3440
}
3541
}
42+
43+
# A VM on a Layer 2 / "Simple" network. A subnet-less network cannot be booted
44+
# on by network uuid (Nova requires a subnet for that), so the VM attaches
45+
# through a port on it — which is the L2 model anyway: a port on the segment,
46+
# and the guest owns its IP. No DHCP, so addressing comes from cloud-init via
47+
# config drive; no security groups apply (port security is off on the network).
48+
resource "pcd_networking_port" "l2" {
49+
name = "tf-example-l2-port"
50+
network_id = "L2-NETWORK-UUID" # e.g. pcd_networking_network.l2.id
51+
}
52+
53+
resource "pcd_compute_instance" "on_l2" {
54+
name = "tf-example-l2"
55+
image_name = "Ubuntu-22.04"
56+
flavor_name = "m1.small"
57+
config_drive = true
58+
user_data = file("cloud-init-static-ip.yaml")
59+
60+
network {
61+
port = pcd_networking_port.l2.id
62+
}
63+
}
64+
65+
# Boot from a NEW volume created from an image (persistent root disk) — the
66+
# PCD UI's default "New Volume" option. No image_name needed: the block_device
67+
# with boot_index = 0 is the root disk.
68+
resource "pcd_compute_instance" "from_new_volume" {
69+
name = "tf-example-bfv"
70+
flavor_name = "m1.small"
71+
72+
block_device {
73+
source_type = "image"
74+
uuid = "IMAGE-UUID"
75+
destination_type = "volume"
76+
volume_size = 20
77+
volume_type = "ssd"
78+
boot_index = 0
79+
delete_on_termination = true
80+
}
81+
82+
network {
83+
uuid = pcd_networking_network.example.id
84+
}
85+
}
86+
87+
# Boot from an EXISTING volume (e.g. one restored from a backup).
88+
resource "pcd_compute_instance" "from_existing_volume" {
89+
name = "tf-example-existing"
90+
flavor_name = "m1.small"
91+
92+
block_device {
93+
source_type = "volume"
94+
uuid = "VOLUME-UUID"
95+
destination_type = "volume"
96+
boot_index = 0
97+
}
98+
99+
network {
100+
uuid = pcd_networking_network.example.id
101+
}
102+
}
103+
104+
# Boot from a VOLUME SNAPSHOT (a new volume is cloned from it).
105+
resource "pcd_compute_instance" "from_volume_snapshot" {
106+
name = "tf-example-snap"
107+
flavor_name = "m1.small"
108+
109+
block_device {
110+
source_type = "snapshot"
111+
uuid = "SNAPSHOT-UUID"
112+
destination_type = "volume"
113+
boot_index = 0
114+
delete_on_termination = true
115+
}
116+
117+
network {
118+
uuid = pcd_networking_network.example.id
119+
}
120+
}
121+
122+
# Install from an ISO: a blank target volume to install onto (boot_index 0)
123+
# plus the installer ISO attached as a CD-ROM (boot_index 1) — the PCD UI's
124+
# "Install from ISO" option.
125+
resource "pcd_compute_instance" "from_iso" {
126+
name = "tf-example-iso"
127+
flavor_name = "m1.small"
128+
129+
block_device {
130+
source_type = "blank"
131+
destination_type = "volume"
132+
volume_size = 40
133+
boot_index = 0
134+
delete_on_termination = false
135+
}
136+
137+
block_device {
138+
source_type = "image"
139+
uuid = "ISO-IMAGE-UUID"
140+
destination_type = "volume"
141+
volume_size = 1
142+
boot_index = 1
143+
device_type = "cdrom"
144+
delete_on_termination = true
145+
}
146+
147+
network {
148+
uuid = pcd_networking_network.example.id
149+
}
150+
}
151+
152+
# --- Placement: server groups and scheduler hints ---------------------------
153+
resource "pcd_compute_servergroup" "web" {
154+
name = "web-anti-affinity"
155+
policies = ["anti-affinity"]
156+
}
157+
158+
resource "pcd_compute_instance" "web" {
159+
count = 2
160+
name = "web-${count.index}"
161+
image_name = "cirros-0.6.2"
162+
flavor_name = "m1.tiny"
163+
164+
network {
165+
uuid = pcd_networking_network.example.id
166+
}
167+
168+
# Each member lands on a different hypervisor.
169+
scheduler_hints {
170+
group = pcd_compute_servergroup.web.id
171+
}
172+
}
36173
```
37174

38175
<!-- schema generated by tfplugindocs -->
@@ -44,16 +181,19 @@ resource "pcd_compute_instance" "example" {
44181

45182
### Optional
46183

47-
- `availability_zone` (String) Availability zone to launch in. Changing this forces a new resource.
184+
- `availability_zone` (String) Availability zone to launch in. Admins may pin a host with `<az>:<host>` (e.g. `nova:hyp2`); the pin is preserved on refresh (Nova only reports the zone); `terraform import` recovers only the zone, so re-set the pin in state before applying. Changing this forces a new resource.
185+
- `block_device` (Block List) Block devices to create the instance with (Nova `block_device_mapping_v2`), one block per device. Mirrors `openstack_compute_instance_v2`. Use it to boot from a new volume, an existing volume, or a volume snapshot, to install from an ISO, or to attach extra disks at boot. The device with `boot_index = 0` is the root disk; when one is present `image_id`/`image_name` may be omitted. Create-only: changing this forces a new resource. Attach/detach volumes on a running instance with `pcd_compute_volume_attach` instead. (see [below for nested schema](#nestedblock--block_device))
48186
- `config_drive` (Boolean) Whether to use a config drive. Changing this forces a new resource.
49187
- `flavor_id` (String) The flavor ID (alternative to flavor_name). Changing this triggers an in-place resize.
50188
- `flavor_name` (String) The flavor name (alternative to flavor_id). Changing this triggers an in-place resize.
51-
- `image_id` (String) The image ID to boot from (alternative to image_name). Changing this forces a new resource.
52-
- `image_name` (String) The image name to boot from, resolved via Glance (alternative to image_id). Exactly one of image_id/image_name is required. Changing this forces a new resource.
189+
- `image_id` (String) The image ID to boot from (alternative to image_name). Required unless a `block_device` with `boot_index = 0` supplies the boot disk. Changing this forces a new resource.
190+
- `image_name` (String) The image name to boot from, resolved via Glance (alternative to image_id). Required unless a `block_device` with `boot_index = 0` supplies the boot disk. Changing this forces a new resource.
53191
- `key_pair` (String) The name of a keypair to inject. Changing this forces a new resource.
54-
- `metadata` (Map of String) Key-value metadata attached to the instance.
192+
- `metadata` (Map of String) Key-value metadata attached to the instance. The key `migration-priority` is reserved — set it through `migration_priority` instead.
193+
- `migration_priority` (String) How PCD's Dynamic Resource Rebalancing (DRR) service treats this VM when balancing hosts: `normal`, `low`, `high`, or `never` (excluded from automatic migration). Unset means DRR's default. Stored as the `migration-priority` server metadata key, exactly as the PCD UI's Set Migration Priority dialog does. Updatable in place; set `""` to clear.
55194
- `network` (Block List) Networks to attach. Changing this forces a new resource. (see [below for nested schema](#nestedblock--network))
56195
- `region` (String) The region. Defaults to the provider's region.
196+
- `scheduler_hints` (Block List) Scheduler hints passed to Nova at boot (`os:scheduler_hints`). Mirrors `openstack_compute_instance_v2`. Use `group` with a `pcd_compute_servergroup` for affinity / anti-affinity placement, `different_host` / `same_host` to place relative to existing instances. At most one block. Create-only: changing this forces a new resource. Nova does not report hints back, so the block round-trips from configuration. (see [below for nested schema](#nestedblock--scheduler_hints))
57197
- `security_groups` (Set of String) Names of security groups to associate. Changing this forces a new resource.
58198
- `user_data` (String) User data (cloud-init) for the instance. Changing this forces a new resource.
59199

@@ -63,6 +203,26 @@ resource "pcd_compute_instance" "example" {
63203
- `id` (String) The instance ID.
64204
- `status` (String) The Nova status (e.g. ACTIVE).
65205

206+
<a id="nestedblock--block_device"></a>
207+
### Nested Schema for `block_device`
208+
209+
Required:
210+
211+
- `source_type` (String) The source of the device: `image`, `volume`, `snapshot`, or `blank`.
212+
213+
Optional:
214+
215+
- `boot_index` (Number) Boot order. `0` is the root disk, `1` the next device (e.g. an installer ISO), `-1` for a non-bootable data disk. Defaults to `-1`.
216+
- `delete_on_termination` (Boolean) Delete the created volume when the instance is deleted. Defaults to `false`.
217+
- `destination_type` (String) Where the device lives: `volume` (Cinder-backed, persistent) or `local` (ephemeral on the hypervisor). Defaults to `volume`.
218+
- `device_type` (String) The device type: `disk` (default) or `cdrom` (for an installer ISO).
219+
- `disk_bus` (String) The bus to attach the device on (e.g. `virtio`, `scsi`, `ide`).
220+
- `guest_format` (String) Filesystem format for a `blank` device (e.g. `ext4`, `swap`).
221+
- `uuid` (String) The ID of the source image, volume, or snapshot. Not used with `source_type = "blank"`.
222+
- `volume_size` (Number) Size in GiB of the volume to create. Required for `blank`; for `image`/`snapshot` sources it must be at least the source size.
223+
- `volume_type` (String) The Cinder volume type for a created volume.
224+
225+
66226
<a id="nestedblock--network"></a>
67227
### Nested Schema for `network`
68228

@@ -72,6 +232,17 @@ Optional:
72232
- `port` (String) Existing port to attach (required unless uuid is set).
73233
- `uuid` (String) Network UUID to attach to (required unless port is set).
74234

235+
236+
<a id="nestedblock--scheduler_hints"></a>
237+
### Nested Schema for `scheduler_hints`
238+
239+
Optional:
240+
241+
- `additional_properties` (Map of String) Arbitrary extra hints (key → value) passed through unvalidated, e.g. `query`. Merged last, so a key here overrides the typed attributes (`group`, `different_host`, `same_host`); `query` must be a JSON-encoded string.
242+
- `different_host` (List of String) Instance IDs whose hosts this instance must NOT be scheduled on.
243+
- `group` (String) A server group ID to place the instance in (see `pcd_compute_servergroup`).
244+
- `same_host` (List of String) Instance IDs whose host this instance MUST be scheduled on.
245+
75246
## Import
76247

77248
Import is supported using the following syntax:

docs/resources/networking_network.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Manages a Neutron network in PCD.
1313
## Example Usage
1414

1515
```terraform
16+
# A tenant (self-service) network.
1617
resource "pcd_networking_network" "example" {
1718
name = "tf-example-network"
1819
description = "Managed by Terraform"
@@ -32,6 +33,26 @@ resource "pcd_networking_network" "provider" {
3233
physical_network = "physnet1"
3334
}]
3435
}
36+
37+
# Layer 2 / "Simple" network — the PCD UI's Simple Networking option, akin to
38+
# a VMware port group: no subnet, no DHCP, no IP management. VMs on it manage
39+
# their own addressing. Three things define it, all set here:
40+
# 1. no pcd_networking_subnet is created for it
41+
# 2. port_security_enabled = false (security groups do not apply)
42+
# 3. the "simple_network" tag — what the PCD UI keys on to list it under
43+
# "Layer 2 Networks" in the Deploy VM wizard
44+
resource "pcd_networking_network" "l2" {
45+
name = "vlan-100-l2"
46+
shared = true
47+
port_security_enabled = false
48+
tags = ["simple_network"]
49+
50+
segments = [{
51+
network_type = "vlan"
52+
physical_network = "physnet1"
53+
segmentation_id = 100
54+
}]
55+
}
3556
```
3657

3758
<!-- schema generated by tfplugindocs -->
@@ -43,6 +64,7 @@ resource "pcd_networking_network" "provider" {
4364
- `description` (String) A description of the network.
4465
- `external` (Boolean) Whether the network has an external routing facility.
4566
- `name` (String) The name of the network.
67+
- `port_security_enabled` (Boolean) Whether port security (security groups and anti-spoofing) is enforced on ports of this network. Defaults to `true`. Set `false` for a Layer 2 / "Simple" network, where the VM manages its own addressing and security groups do not apply — mirrors the PCD UI's Simple Network option.
4668
- `region` (String) The region. Defaults to the provider's region.
4769
- `segments` (Attributes List) Provider-network segments (admin only). One segment creates a physical network (e.g. `network_type = "flat"` / `"vlan"` on a `physical_network` label from the host config); multiple segments create a multi-provider network. Create-only: segments are not refreshed from the API and cannot be imported. Changing this forces a new resource. (see [below for nested schema](#nestedatt--segments))
4870
- `shared` (Boolean) Whether the network is shared across projects.

0 commit comments

Comments
 (0)