Skip to content

Commit b2d39bb

Browse files
authored
Merge pull request #492 from sygibson/add-pxe-vm-capability
enhance(pxe): Add Network PXE boot option, doc, and examples
2 parents a44e076 + a645e0b commit b2d39bb

File tree

6 files changed

+206
-15
lines changed

6 files changed

+206
-15
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Follow this [install guide](docs/guides/installation.md) to install the plugin.
2424
* `proxmox_vm_qemu` does not (yet) validate vm names, be sure to only use alphanumeric and dashes otherwise you may get
2525
an opaque 400 Parameter Verification failed (indicating a bad value was sent to proxmox).
2626
* When using the `proxmox_lxc` resource, the provider will crash unless `rootfs` is defined.
27+
* When using the Network Boot mode (PXE), a valid NIC must be defined for the VM, and the boot order must specify network first.
2728

2829
## Contributing
2930

docs/resources/vm_qemu.md

100755100644
Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,30 @@ This resource manages a Proxmox VM Qemu container.
44

55
## Create a Qemu VM resource
66

7-
You can start from either an ISO or clone an existing VM. Optimally, you could create a VM resource you will use a clone
8-
base with an ISO, and make the rest of the VM resources depend on that base "template" and clone it.
7+
You can start from either an ISO, PXE boot the VM, or clone an existing VM. Optimally, you could
8+
create a VM resource you will use a clone base with an ISO, and make the rest of the VM resources
9+
depend on that base "template" and clone it.
910

10-
When creating a VM Qemu resource, you create a `proxmox_vm_qemu` resource block. The name and target node of the VM are
11-
the only required parameters.
11+
When creating a VM Qemu resource, you create a `proxmox_vm_qemu` resource block. For ISO and clone
12+
modes, the name and target node of the VM are the only required parameters.
13+
14+
For the PXE mode, the `boot` directive must contain a *Network* boot order first. Generally, PXE
15+
boot VMs should NOT contain the Agent config (`agent = 1`). PXE boot mode also requires external
16+
infrastructure to support the Network PXE boot request by the VM.
1217

1318
```hcl
1419
resource "proxmox_vm_qemu" "resource-name" {
1520
name = "VM-name"
1621
target_node = "Node to create the VM on"
1722
iso = "ISO file name"
18-
# or
23+
24+
### or for a Clone VM operation
1925
# clone = "template to clone"
26+
27+
### or for a PXE boot VM operation
28+
# pxe = true
29+
# boot = "net0;scsi0"
30+
# agent = 0
2031
}
2132
```
2233

@@ -80,6 +91,34 @@ variable `ciuser`, `cipassword`, `ipconfig0`, `ipconfig1`, `ipconfig2`, `ipconfi
8091

8192
For more information, see the [Cloud-init guide](../guides/cloud_init.md).
8293

94+
## Provision through PXE Network Boot
95+
96+
Specifying the `pxe = true` option will enable the Virtual Machine to perform a Network Boot (PXE).
97+
In addition to enabling the PXE mode, a few other options should be specified to ensure successful
98+
boot of the VM. A minimal Resource stanza for a PXE boot VM might look like this:
99+
100+
```
101+
resource "proxmox_vm_qemu" "pxe-minimal-example" {
102+
name = "pxe-minimal-example"
103+
agent = 0
104+
boot = "order=net0;scsi0"
105+
pxe = true
106+
target_node = "test"
107+
network {
108+
bridge = "vmbr0"
109+
firewall = false
110+
link_down = false
111+
model = "e1000"
112+
}
113+
}
114+
```
115+
116+
The primary options that effect the correct operation of Network PXE boot mode are:
117+
118+
* `boot`: a valid boot order must be specified with Network type first (eg `net0;scsi0` or `ncd`)
119+
* a valid NIC attached to a network with a PXE boot server must be added to the VM
120+
* generally speaking, disable the Agent (`agent = 0`) unless the installed OS contains the Agent in OS install configurations
121+
83122
## Argument reference
84123

85124
**Note: Except where explicitly stated in the description, all arguments are assumed to be optional.**
@@ -104,8 +143,9 @@ The following arguments are supported in the top level resource block.
104143
| `boot` | `str` | `"cdn"` | The boot order for the VM. Ordered string of characters denoting boot order. Options: floppy (`a`), hard disk (`c`), CD-ROM (`d`), or network (`n`). |
105144
| `bootdisk` | `str` | | Enable booting from specified disk. You shouldn't need to change it under most circumstances. |
106145
| `agent` | `int` | `0` | Set to `1` to enable the QEMU Guest Agent. Note, you must run the [`qemu-guest-agent`](https://pve.proxmox.com/wiki/Qemu-guest-agent) daemon in the quest for this to have any effect. |
107-
| `iso` | `str` | | The name of the ISO image to mount to the VM. Only applies when `clone` is not set. Either `clone` or `iso` needs to be set. |
108-
| `clone` | `str` | | The base VM from which to clone to create the new VM. |
146+
| `iso` | `str` | | The name of the ISO image to mount to the VM. Only applies when `clone` is not set. Either `clone` or `iso` needs to be set. Note that `iso` is mutually exclussive with `clone` and `pxe` modes. |
147+
| `pxe` | `bool` | `false` | If set to `true`, enable PXE boot of the VM. Also requires a `boot` order be set with Network first (eg `boot = "net0;scsi0"`). Note that `pxe` is mutually exclussive with `iso` and `clone` modes. |
148+
| `clone` | `str` | | The base VM from which to clone to create the new VM. Note that `clone` is mutually exclussive with `pxe` and `iso` modes. |
109149
| `full_clone` | `bool` | `true` | Set to `true` to create a full clone, or `false` to create a linked clone. See the [docs about cloning](https://pve.proxmox.com/pve-docs/chapter-qm.html#qm_copy_and_clone) for more info. Only applies when `clone` is set. |
110150
| `hastate` | `str` | | Requested HA state for the resource. One of "started", "stopped", "enabled", "disabled", or "ignored". See the [docs about HA](https://pve.proxmox.com/pve-docs/chapter-ha-manager.html#ha_manager_resource_config) for more info. |
111151
| `hagroup` | `str` | | The HA group identifier the resource belongs to (requires `hastate` to be set!). See the [docs about HA](https://pve.proxmox.com/pve-docs/chapter-ha-manager.html#ha_manager_resource_config) for more info. |

examples/cloudinit_example.tf

100755100644
File mode changed.

examples/pxe_example.tf

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
terraform {
2+
required_version = ">= 1.1.0"
3+
required_providers {
4+
proxmox = {
5+
source = "telmate/proxmox"
6+
version = ">= 2.9.5"
7+
}
8+
}
9+
}
10+
11+
provider "proxmox" {
12+
pm_tls_insecure = true
13+
pm_api_url = "https://proxmox01.example.com:8006/api2/json"
14+
pm_password = "password"
15+
pm_user = "root@pam"
16+
pm_otp = ""
17+
}
18+
19+
resource "proxmox_vm_qemu" "pxe-example" {
20+
name = "pxe-example"
21+
desc = "A test VM for PXE boot mode."
22+
# PXE option enables the network boot feature
23+
pxe = true
24+
# unless your PXE installed system includes the Agent in the installed
25+
# OS, do not use this, especially for PXE boot VMs
26+
agent = 0
27+
automatic_reboot = true
28+
balloon = 0
29+
bios = "seabios"
30+
# boot order MUST include network first, this is enforced in the Provider
31+
boot = "order=net0;scsi0"
32+
cores = 2
33+
cpu = "host"
34+
define_connection_info = true
35+
force_create = false
36+
hotplug = "network,disk,usb"
37+
kvm = true
38+
memory = 2048
39+
numa = false
40+
onboot = false
41+
oncreate = true
42+
os_type = "Linux 5.x - 2.6 Kernel"
43+
qemu_os = "l26"
44+
scsihw = "virtio-scsi-pci"
45+
sockets = 1
46+
tablet = true
47+
target_node = "test"
48+
vcpus = 0
49+
50+
disk {
51+
backup = 0
52+
cache = "none"
53+
discard = "on"
54+
iothread = 1
55+
mbps = 0
56+
mbps_rd = 0
57+
mbps_rd_max = 0
58+
mbps_wr = 0
59+
mbps_wr_max = 0
60+
replicate = 0
61+
size = "32G"
62+
ssd = 1
63+
storage = "local-lvm"
64+
type = "scsi"
65+
}
66+
67+
network {
68+
bridge = "vmbr0"
69+
firewall = false
70+
link_down = false
71+
model = "e1000"
72+
}
73+
}

proxmox/resource_vm_qemu.go

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"log"
88
"path"
9+
"regexp"
910
"strconv"
1011
"strings"
1112
"time"
@@ -102,15 +103,23 @@ func resourceVmQemu() *schema.Resource {
102103
Optional: true,
103104
Default: 0,
104105
},
106+
"pxe": {
107+
Type: schema.TypeBool,
108+
Optional: true,
109+
ForceNew: true,
110+
ConflictsWith: []string{"clone"},
111+
},
105112
"iso": {
106-
Type: schema.TypeString,
107-
Optional: true,
108-
ForceNew: true,
113+
Type: schema.TypeString,
114+
Optional: true,
115+
ForceNew: true,
116+
ConflictsWith: []string{"clone"},
109117
},
110118
"clone": {
111-
Type: schema.TypeString,
112-
Optional: true,
113-
ForceNew: true,
119+
Type: schema.TypeString,
120+
Optional: true,
121+
ForceNew: true,
122+
ConflictsWith: []string{"iso", "pxe"},
114123
},
115124
"cloudinit_cdrom_storage": {
116125
Type: schema.TypeString,
@@ -811,7 +820,7 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
811820
vmr.SetPool(pool)
812821
}
813822

814-
// check if ISO or clone
823+
// check if ISO, clone, or PXE boot
815824
if d.Get("clone").(string) != "" {
816825
fullClone := 1
817826
if !d.Get("full_clone").(bool) {
@@ -880,12 +889,38 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
880889

881890
} else if d.Get("iso").(string) != "" {
882891
config.QemuIso = d.Get("iso").(string)
892+
err := config.CreateVm(vmr, client)
893+
if err != nil {
894+
return err
895+
}
896+
} else if d.Get("pxe").(bool) {
897+
var found bool
898+
bs := d.Get("boot").(string)
899+
regs := [...]string{"^n.*$", "^order=net.*$"}
900+
901+
for _, reg := range regs {
902+
re, err := regexp.Compile(reg)
903+
if err != nil {
904+
return err
905+
}
906+
907+
found = re.MatchString(bs)
908+
909+
if found {
910+
break
911+
}
912+
}
913+
914+
if !found {
915+
return fmt.Errorf("no network boot option matched in 'boot' config")
916+
}
917+
883918
err := config.CreateVm(vmr, client)
884919
if err != nil {
885920
return err
886921
}
887922
} else {
888-
return fmt.Errorf("either clone or iso must be set")
923+
return fmt.Errorf("either 'clone', 'iso', or 'pxe' must be set")
889924
}
890925
} else {
891926
log.Printf("[DEBUG][QemuVmCreate] recycling VM vmId: %d", vmr.VmId())

proxmox/resource_vm_qemu_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,27 @@ resource "proxmox_vm_qemu" "%s" {
8686
`, name, name, targetNode)
8787
}
8888

89+
// testAccExampleQemuPxe generates the most simplistic PXE boot VM
90+
// we're able to make this confirms we can spin up a PXE boot VM
91+
// using just default values, a valid Network must be specified
92+
// for the VM to be able to Network boot
93+
func testAccExampleQemuPxe(name string, targetNode string) string {
94+
return fmt.Sprintf(`
95+
resource "proxmox_vm_qemu" "%s" {
96+
name = "%s"
97+
target_node = "%s"
98+
pxe = true
99+
boot = "order=net0;scsi0"
100+
network {
101+
bridge = "vmbr0"
102+
firewall = false
103+
link_down = false
104+
model = "e1000"
105+
}
106+
}
107+
`, name, name, targetNode)
108+
}
109+
89110
// testAccExampleResource generates a virtual machine and uses the disk
90111
// slot setting to assign a non-standard disk position (scsi5 vs scsi0)
91112
// func testAccExampleQemuWithDiskSlot(name string, diskSlot int, targetNode string) string {
@@ -300,6 +321,27 @@ func TestAccProxmoxVmQemu_CreateCloneWithTwoDisks(t *testing.T) {
300321
})
301322
}
302323

324+
// TestAccProxmoxVmQemu_PxeCreate tests a simple creation and destruction of the smallest, but
325+
// but still viable, configuration for a PXE Network boot VM we can create.
326+
func TestAccProxmoxVmQemu_PxeCreate(t *testing.T) {
327+
resourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
328+
resourcePath := fmt.Sprintf("proxmox_vm_qemu.%s", resourceName)
329+
330+
resource.Test(t, resource.TestCase{
331+
PreCheck: func() { testAccPreCheck(t) },
332+
Providers: testAccProxmoxProviderFactory(),
333+
//CheckDestroy: testAccCheckExampleResourceDestroy,
334+
Steps: []resource.TestStep{
335+
{
336+
Config: testAccExampleQemuPxe(resourceName, testAccProxmoxTargetNode),
337+
Check: resource.ComposeTestCheckFunc(
338+
resource.TestCheckResourceAttr(resourcePath, "name", resourceName),
339+
),
340+
},
341+
},
342+
})
343+
}
344+
303345
// TestAccProxmoxVmQemu_StandardUpdateNoReboot tests a simple update of a vm_qemu resource,
304346
// and the modified parameters can be applied without reboot.
305347
func TestAccProxmoxVmQemu_UpdateNoReboot(t *testing.T) {

0 commit comments

Comments
 (0)