Skip to content

Commit

Permalink
Merge pull request #492 from sygibson/add-pxe-vm-capability
Browse files Browse the repository at this point in the history
enhance(pxe): Add Network PXE boot option, doc, and examples
  • Loading branch information
mleone87 committed Feb 4, 2022
2 parents a44e076 + a645e0b commit b2d39bb
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 15 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ Follow this [install guide](docs/guides/installation.md) to install the plugin.
* `proxmox_vm_qemu` does not (yet) validate vm names, be sure to only use alphanumeric and dashes otherwise you may get
an opaque 400 Parameter Verification failed (indicating a bad value was sent to proxmox).
* When using the `proxmox_lxc` resource, the provider will crash unless `rootfs` is defined.
* When using the Network Boot mode (PXE), a valid NIC must be defined for the VM, and the boot order must specify network first.

## Contributing

Expand Down
54 changes: 47 additions & 7 deletions docs/resources/vm_qemu.md
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,30 @@ This resource manages a Proxmox VM Qemu container.

## Create a Qemu VM resource

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

When creating a VM Qemu resource, you create a `proxmox_vm_qemu` resource block. The name and target node of the VM are
the only required parameters.
When creating a VM Qemu resource, you create a `proxmox_vm_qemu` resource block. For ISO and clone
modes, the name and target node of the VM are the only required parameters.

For the PXE mode, the `boot` directive must contain a *Network* boot order first. Generally, PXE
boot VMs should NOT contain the Agent config (`agent = 1`). PXE boot mode also requires external
infrastructure to support the Network PXE boot request by the VM.

```hcl
resource "proxmox_vm_qemu" "resource-name" {
name = "VM-name"
target_node = "Node to create the VM on"
iso = "ISO file name"
# or
### or for a Clone VM operation
# clone = "template to clone"
### or for a PXE boot VM operation
# pxe = true
# boot = "net0;scsi0"
# agent = 0
}
```

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

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

## Provision through PXE Network Boot

Specifying the `pxe = true` option will enable the Virtual Machine to perform a Network Boot (PXE).
In addition to enabling the PXE mode, a few other options should be specified to ensure successful
boot of the VM. A minimal Resource stanza for a PXE boot VM might look like this:

```
resource "proxmox_vm_qemu" "pxe-minimal-example" {
name = "pxe-minimal-example"
agent = 0
boot = "order=net0;scsi0"
pxe = true
target_node = "test"
network {
bridge = "vmbr0"
firewall = false
link_down = false
model = "e1000"
}
}
```

The primary options that effect the correct operation of Network PXE boot mode are:

* `boot`: a valid boot order must be specified with Network type first (eg `net0;scsi0` or `ncd`)
* a valid NIC attached to a network with a PXE boot server must be added to the VM
* generally speaking, disable the Agent (`agent = 0`) unless the installed OS contains the Agent in OS install configurations

## Argument reference

**Note: Except where explicitly stated in the description, all arguments are assumed to be optional.**
Expand All @@ -104,8 +143,9 @@ The following arguments are supported in the top level resource block.
| `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`). |
| `bootdisk` | `str` | | Enable booting from specified disk. You shouldn't need to change it under most circumstances. |
| `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. |
| `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. |
| `clone` | `str` | | The base VM from which to clone to create the new VM. |
| `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. |
| `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. |
| `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. |
| `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. |
| `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. |
| `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. |
Expand Down
Empty file modified examples/cloudinit_example.tf
100755 → 100644
Empty file.
73 changes: 73 additions & 0 deletions examples/pxe_example.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
terraform {
required_version = ">= 1.1.0"
required_providers {
proxmox = {
source = "telmate/proxmox"
version = ">= 2.9.5"
}
}
}

provider "proxmox" {
pm_tls_insecure = true
pm_api_url = "https://proxmox01.example.com:8006/api2/json"
pm_password = "password"
pm_user = "root@pam"
pm_otp = ""
}

resource "proxmox_vm_qemu" "pxe-example" {
name = "pxe-example"
desc = "A test VM for PXE boot mode."
# PXE option enables the network boot feature
pxe = true
# unless your PXE installed system includes the Agent in the installed
# OS, do not use this, especially for PXE boot VMs
agent = 0
automatic_reboot = true
balloon = 0
bios = "seabios"
# boot order MUST include network first, this is enforced in the Provider
boot = "order=net0;scsi0"
cores = 2
cpu = "host"
define_connection_info = true
force_create = false
hotplug = "network,disk,usb"
kvm = true
memory = 2048
numa = false
onboot = false
oncreate = true
os_type = "Linux 5.x - 2.6 Kernel"
qemu_os = "l26"
scsihw = "virtio-scsi-pci"
sockets = 1
tablet = true
target_node = "test"
vcpus = 0

disk {
backup = 0
cache = "none"
discard = "on"
iothread = 1
mbps = 0
mbps_rd = 0
mbps_rd_max = 0
mbps_wr = 0
mbps_wr_max = 0
replicate = 0
size = "32G"
ssd = 1
storage = "local-lvm"
type = "scsi"
}

network {
bridge = "vmbr0"
firewall = false
link_down = false
model = "e1000"
}
}
51 changes: 43 additions & 8 deletions proxmox/resource_vm_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"log"
"path"
"regexp"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -102,15 +103,23 @@ func resourceVmQemu() *schema.Resource {
Optional: true,
Default: 0,
},
"pxe": {
Type: schema.TypeBool,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"clone"},
},
"iso": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"clone"},
},
"clone": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"iso", "pxe"},
},
"cloudinit_cdrom_storage": {
Type: schema.TypeString,
Expand Down Expand Up @@ -811,7 +820,7 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
vmr.SetPool(pool)
}

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

} else if d.Get("iso").(string) != "" {
config.QemuIso = d.Get("iso").(string)
err := config.CreateVm(vmr, client)
if err != nil {
return err
}
} else if d.Get("pxe").(bool) {
var found bool
bs := d.Get("boot").(string)
regs := [...]string{"^n.*$", "^order=net.*$"}

for _, reg := range regs {
re, err := regexp.Compile(reg)
if err != nil {
return err
}

found = re.MatchString(bs)

if found {
break
}
}

if !found {
return fmt.Errorf("no network boot option matched in 'boot' config")
}

err := config.CreateVm(vmr, client)
if err != nil {
return err
}
} else {
return fmt.Errorf("either clone or iso must be set")
return fmt.Errorf("either 'clone', 'iso', or 'pxe' must be set")
}
} else {
log.Printf("[DEBUG][QemuVmCreate] recycling VM vmId: %d", vmr.VmId())
Expand Down
42 changes: 42 additions & 0 deletions proxmox/resource_vm_qemu_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,27 @@ resource "proxmox_vm_qemu" "%s" {
`, name, name, targetNode)
}

// testAccExampleQemuPxe generates the most simplistic PXE boot VM
// we're able to make this confirms we can spin up a PXE boot VM
// using just default values, a valid Network must be specified
// for the VM to be able to Network boot
func testAccExampleQemuPxe(name string, targetNode string) string {
return fmt.Sprintf(`
resource "proxmox_vm_qemu" "%s" {
name = "%s"
target_node = "%s"
pxe = true
boot = "order=net0;scsi0"
network {
bridge = "vmbr0"
firewall = false
link_down = false
model = "e1000"
}
}
`, name, name, targetNode)
}

// testAccExampleResource generates a virtual machine and uses the disk
// slot setting to assign a non-standard disk position (scsi5 vs scsi0)
// func testAccExampleQemuWithDiskSlot(name string, diskSlot int, targetNode string) string {
Expand Down Expand Up @@ -300,6 +321,27 @@ func TestAccProxmoxVmQemu_CreateCloneWithTwoDisks(t *testing.T) {
})
}

// TestAccProxmoxVmQemu_PxeCreate tests a simple creation and destruction of the smallest, but
// but still viable, configuration for a PXE Network boot VM we can create.
func TestAccProxmoxVmQemu_PxeCreate(t *testing.T) {
resourceName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)
resourcePath := fmt.Sprintf("proxmox_vm_qemu.%s", resourceName)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProxmoxProviderFactory(),
//CheckDestroy: testAccCheckExampleResourceDestroy,
Steps: []resource.TestStep{
{
Config: testAccExampleQemuPxe(resourceName, testAccProxmoxTargetNode),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourcePath, "name", resourceName),
),
},
},
})
}

// TestAccProxmoxVmQemu_StandardUpdateNoReboot tests a simple update of a vm_qemu resource,
// and the modified parameters can be applied without reboot.
func TestAccProxmoxVmQemu_UpdateNoReboot(t *testing.T) {
Expand Down

0 comments on commit b2d39bb

Please sign in to comment.