0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-10 07:45:02 +00:00

Add vga argument to VM resource

This commit is contained in:
Dan Petersen 2019-12-31 01:00:53 +01:00
parent 87d8652d37
commit 7344300126
6 changed files with 265 additions and 5 deletions

View File

@ -1,3 +1,9 @@
## 0.2.0 (UNRELEASED)
ENHANCEMENTS:
resource/virtual_environment_vm: Add `vga` argument
## 0.1.0
FEATURES:

View File

@ -433,6 +433,22 @@ This resource doesn't expose any additional attributes.
* `wxp` - Windows XP
* `pool_id` - (Optional) The ID of a pool to assign the virtual machine to
* `started` - (Optional) Whether to start the virtual machine (defaults to `true`)
* `vga` - (Optional) The VGA configuration
* `enabled` - (Optional) Whether to enable the VGA device (defaults to `true`)
* `memory` - (Optional) The VGA memory in megabytes (4-512 MB)
* `type` - (Optional) The VGA type
* `cirrus` - Cirrus (deprecated since QEMU 2.2)
* `qxl` - SPICE
* `qxl2` - SPICE Dual Monitor
* `qxl3` - SPICE Triple Monitor
* `qxl4` - SPICE Quad Monitor
* `serial0` - Serial Terminal 0
* `serial1` - Serial Terminal 1
* `serial2` - Serial Terminal 2
* `serial3` - Serial Terminal 3
* `std` - Standard VGA
* `virtio` - VirtIO-GPU
* `vmware` - VMware Compatible
* `vm_id` - (Optional) The ID
###### Attributes

View File

@ -166,7 +166,7 @@ type CustomUSBDevices []CustomUSBDevice
// CustomVGADevice handles QEMU VGA device parameters.
type CustomVGADevice struct {
Memory *int `json:"memory,omitempty" url:"memory,omitempty"`
Type string `json:"type" url:"type"`
Type *string `json:"type,omitempty" url:"type,omitempty"`
}
// CustomVirtualIODevice handles QEMU VirtIO device parameters.
@ -950,14 +950,16 @@ func (r CustomUSBDevices) EncodeValues(key string, v *url.Values) error {
// EncodeValues converts a CustomVGADevice struct to a URL vlaue.
func (r CustomVGADevice) EncodeValues(key string, v *url.Values) error {
values := []string{
fmt.Sprintf("type=%s", r.Type),
}
values := []string{}
if r.Memory != nil {
values = append(values, fmt.Sprintf("memory=%d", *r.Memory))
}
if r.Type != nil {
values = append(values, fmt.Sprintf("type=%s", *r.Type))
}
v.Add(key, strings.Join(values, ","))
return nil
@ -1355,3 +1357,43 @@ func (r *CustomStorageDevice) UnmarshalJSON(b []byte) error {
return nil
}
// UnmarshalJSON converts a CustomVGADevice string to an object.
func (r *CustomVGADevice) UnmarshalJSON(b []byte) error {
var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
if s == "" {
return nil
}
pairs := strings.Split(s, ",")
for _, p := range pairs {
v := strings.Split(strings.TrimSpace(p), "=")
if len(v) == 1 {
r.Type = &v[0]
} else if len(v) == 2 {
switch v[0] {
case "memory":
m, err := strconv.Atoi(v[1])
if err != nil {
return err
}
r.Memory = &m
case "type":
r.Type = &v[1]
}
}
}
return nil
}

View File

@ -51,6 +51,9 @@ const (
dvResourceVirtualEnvironmentVMOSType = "other"
dvResourceVirtualEnvironmentVMPoolID = ""
dvResourceVirtualEnvironmentVMStarted = true
dvResourceVirtualEnvironmentVMVGAEnabled = true
dvResourceVirtualEnvironmentVMVGAMemory = 0
dvResourceVirtualEnvironmentVMVGAType = "std"
dvResourceVirtualEnvironmentVMVMID = -1
mkResourceVirtualEnvironmentVMAgent = "agent"
@ -112,6 +115,10 @@ const (
mkResourceVirtualEnvironmentVMOSType = "os_type"
mkResourceVirtualEnvironmentVMPoolID = "pool_id"
mkResourceVirtualEnvironmentVMStarted = "started"
mkResourceVirtualEnvironmentVMVGA = "vga"
mkResourceVirtualEnvironmentVMVGAEnabled = "enabled"
mkResourceVirtualEnvironmentVMVGAMemory = "memory"
mkResourceVirtualEnvironmentVMVGAType = "type"
mkResourceVirtualEnvironmentVMVMID = "vm_id"
)
@ -669,6 +676,49 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Optional: true,
Default: dvResourceVirtualEnvironmentVMStarted,
},
mkResourceVirtualEnvironmentVMVGA: &schema.Schema{
Type: schema.TypeList,
Description: "The VGA configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
defaultList := make([]interface{}, 1)
defaultMap := map[string]interface{}{}
defaultMap[mkResourceVirtualEnvironmentVMVGAEnabled] = dvResourceVirtualEnvironmentVMVGAEnabled
defaultMap[mkResourceVirtualEnvironmentVMVGAMemory] = dvResourceVirtualEnvironmentVMVGAMemory
defaultMap[mkResourceVirtualEnvironmentVMVGAType] = dvResourceVirtualEnvironmentVMVGAType
defaultList[0] = defaultMap
return defaultList, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
mkResourceVirtualEnvironmentVMVGAEnabled: {
Type: schema.TypeBool,
Description: "Whether to enable the VGA device",
Optional: true,
Default: dvResourceVirtualEnvironmentVMVGAEnabled,
},
mkResourceVirtualEnvironmentVMVGAMemory: {
Type: schema.TypeInt,
Description: "The VGA memory in megabytes (4-512 MB)",
Optional: true,
Default: dvResourceVirtualEnvironmentVMVGAMemory,
ValidateFunc: getVGAMemoryValidator(),
},
mkResourceVirtualEnvironmentVMVGAType: {
Type: schema.TypeString,
Description: "The VGA type",
Optional: true,
Default: dvResourceVirtualEnvironmentVMVGAType,
ValidateFunc: getVGATypeValidator(),
},
},
},
MaxItems: 1,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMVMID: {
Type: schema.TypeInt,
Description: "The VM identifier",
@ -769,6 +819,13 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
osType := d.Get(mkResourceVirtualEnvironmentVMOSType).(string)
poolID := d.Get(mkResourceVirtualEnvironmentVMPoolID).(string)
started := proxmox.CustomBool(d.Get(mkResourceVirtualEnvironmentVMStarted).(bool))
vgaDevice, err := resourceVirtualEnvironmentVMGetVGADeviceObject(d, m)
if err != nil {
return err
}
vmID := d.Get(mkResourceVirtualEnvironmentVMVMID).(int)
if vmID == -1 {
@ -840,6 +897,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
SharedMemory: memorySharedObject,
StartOnBoot: &started,
TabletDeviceEnabled: &tabletDeviceEnabled,
VGADevice: vgaDevice,
VMID: &vmID,
}
@ -1228,6 +1286,38 @@ func resourceVirtualEnvironmentVMGetNetworkDeviceObjects(d *schema.ResourceData,
return networkDeviceObjects, nil
}
func resourceVirtualEnvironmentVMGetVGADeviceObject(d *schema.ResourceData, m interface{}) (*proxmox.CustomVGADevice, error) {
resource := resourceVirtualEnvironmentVM()
vgaBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMVGA}, 0, true)
if err != nil {
return nil, err
}
vgaEnabled := proxmox.CustomBool(vgaBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool))
vgaMemory := vgaBlock[mkResourceVirtualEnvironmentVMVGAMemory].(int)
vgaType := vgaBlock[mkResourceVirtualEnvironmentVMVGAType].(string)
vgaDevice := &proxmox.CustomVGADevice{}
if vgaEnabled {
if vgaMemory > 0 {
vgaDevice.Memory = &vgaMemory
}
vgaDevice.Type = &vgaType
} else {
vgaType = "none"
vgaDevice = &proxmox.CustomVGADevice{
Type: &vgaType,
}
}
return vgaDevice, nil
}
func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) error {
config := m.(providerConfiguration)
veClient, err := config.GetVEClient()
@ -1715,6 +1805,48 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err
d.Set(mkResourceVirtualEnvironmentVMPoolID, *vmConfig.PoolID)
}
// Compare the VGA configuration to the one stored in the state.
vga := map[string]interface{}{}
if vmConfig.VGADevice != nil {
vgaEnabled := true
if vmConfig.VGADevice.Type != nil {
vgaEnabled = *vmConfig.VGADevice.Type != "none"
}
vga[mkResourceVirtualEnvironmentVMVGAEnabled] = vgaEnabled
if vmConfig.VGADevice.Memory != nil {
vga[mkResourceVirtualEnvironmentVMVGAMemory] = *vmConfig.VGADevice.Memory
} else {
vga[mkResourceVirtualEnvironmentVMVGAMemory] = dvResourceVirtualEnvironmentVMVGAMemory
}
if vgaEnabled {
if vmConfig.VGADevice.Type != nil {
vga[mkResourceVirtualEnvironmentVMVGAType] = *vmConfig.VGADevice.Type
} else {
vga[mkResourceVirtualEnvironmentVMVGAType] = dvResourceVirtualEnvironmentVMVGAType
}
}
} else {
vga[mkResourceVirtualEnvironmentVMVGAEnabled] = true
vga[mkResourceVirtualEnvironmentVMVGAMemory] = dvResourceVirtualEnvironmentVMVGAMemory
vga[mkResourceVirtualEnvironmentVMVGAType] = dvResourceVirtualEnvironmentVMVGAType
}
currentVGA := d.Get(mkResourceVirtualEnvironmentVMVGA).([]interface{})
if len(currentVGA) > 0 ||
vga[mkResourceVirtualEnvironmentVMVGAEnabled] != dvResourceVirtualEnvironmentVMVGAEnabled ||
vga[mkResourceVirtualEnvironmentVMVGAMemory] != dvResourceVirtualEnvironmentVMVGAMemory ||
vga[mkResourceVirtualEnvironmentVMVGAType] != dvResourceVirtualEnvironmentVMVGAType {
d.Set(mkResourceVirtualEnvironmentVMVGA, []interface{}{vga})
} else {
d.Set(mkResourceVirtualEnvironmentVMVGA, make([]interface{}, 0))
}
// Determine the state of the virtual machine in order to update the "started" argument.
status, err := veClient.GetVMStatus(nodeName, vmID)
@ -2006,6 +2138,17 @@ func resourceVirtualEnvironmentVMUpdate(d *schema.ResourceData, m interface{}) e
rebootRequired = true
}
// Prepare the new VGA configuration.
if d.HasChange(mkResourceVirtualEnvironmentVMVGA) {
body.VGADevice, err = resourceVirtualEnvironmentVMGetVGADeviceObject(d, m)
if err != nil {
return err
}
rebootRequired = true
}
// Update the configuration now that everything has been prepared.
err = veClient.UpdateVM(nodeName, vmID, body)

View File

@ -327,4 +327,22 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
schema.TypeFloat,
schema.TypeInt,
})
vgaSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMVGA)
testOptionalArguments(t, vgaSchema, []string{
mkResourceVirtualEnvironmentVMVGAEnabled,
mkResourceVirtualEnvironmentVMVGAMemory,
mkResourceVirtualEnvironmentVMVGAType,
})
testSchemaValueTypes(t, vgaSchema, []string{
mkResourceVirtualEnvironmentVMVGAEnabled,
mkResourceVirtualEnvironmentVMVGAMemory,
mkResourceVirtualEnvironmentVMVGAType,
}, []schema.ValueType{
schema.TypeBool,
schema.TypeInt,
schema.TypeString,
})
}

View File

@ -175,6 +175,41 @@ func getSchemaBlock(r *schema.Resource, d *schema.ResourceData, m interface{}, k
return resourceBlock, nil
}
func getVGAMemoryValidator() schema.SchemaValidateFunc {
return func(i interface{}, k string) ([]string, []error) {
v, ok := i.(int)
if !ok {
return []string{}, []error{fmt.Errorf("expected type of %s to be []interface{}", k)}
}
if v == 0 {
return []string{}, []error{}
}
validator := validation.IntBetween(4, 512)
return validator(i, k)
}
}
func getVGATypeValidator() schema.SchemaValidateFunc {
return validation.StringInSlice([]string{
"cirrus",
"qxl",
"qxl2",
"qxl3",
"qxl4",
"serial0",
"serial1",
"serial2",
"serial3",
"std",
"virtio",
"vmware",
}, false)
}
func getVLANIDsValidator() schema.SchemaValidateFunc {
return func(i interface{}, k string) (ws []string, es []error) {
min := 1