diff --git a/README.md b/README.md index 6c88f6c6..20e03c2e 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,9 @@ This resource doesn't expose any additional attributes. * `file_format` - (Optional) The file format (defaults to `qcow2`) * `file_id` - (Optional) The file ID for a disk image * `size` - (Optional) The disk size in gigabytes (defaults to `8`) + * `speed` - (Optional) The speed limits + * `read` - (Optional) The maximum read speed in megabytes per second + * `write` - (Optional) The maximum write speed in megabytes per second * `keyboard_layout` - (Optional) The keyboard layout (defaults to `en-us`) * `memory` - (Optional) The memory configuration * `dedicated` - (Optional) The dedicated memory in megabytes (defaults to `512`) diff --git a/proxmox/virtual_environment_vm_types.go b/proxmox/virtual_environment_vm_types.go index 7df62f96..108be42c 100644 --- a/proxmox/virtual_environment_vm_types.go +++ b/proxmox/virtual_environment_vm_types.go @@ -138,11 +138,13 @@ type CustomStartupOrder struct { // CustomStorageDevice handles QEMU SATA device parameters. type CustomStorageDevice struct { - AIO *string `json:"aio,omitempty" url:"aio,omitempty"` - BackupEnabled *CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` - Enabled bool `json:"-" url:"-"` - FileVolume string `json:"file" url:"file"` - Media *string `json:"media,omitempty" url:"media,omitempty"` + AIO *string `json:"aio,omitempty" url:"aio,omitempty"` + BackupEnabled *CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` + Enabled bool `json:"-" url:"-"` + FileVolume string `json:"file" url:"file"` + MaxReadSpeedMbps *int `json:"mbps_rd,omitempty" url:"mbps_rd,omitempty"` + MaxWriteSpeedMbps *int `json:"mbps_wr,omitempty" url:"mbps_wr,omitempty"` + Media *string `json:"media,omitempty" url:"media,omitempty"` } // CustomStorageDevices handles QEMU SATA device parameters. @@ -787,6 +789,14 @@ func (r CustomStorageDevice) EncodeValues(key string, v *url.Values) error { } } + if r.MaxReadSpeedMbps != nil { + values = append(values, fmt.Sprintf("mbps_rd=%d", *r.MaxReadSpeedMbps)) + } + + if r.MaxWriteSpeedMbps != nil { + values = append(values, fmt.Sprintf("mbps_wr=%d", *r.MaxWriteSpeedMbps)) + } + if r.Media != nil { values = append(values, fmt.Sprintf("media=%s", *r.Media)) } diff --git a/proxmoxtf/resource_virtual_environment_vm.go b/proxmoxtf/resource_virtual_environment_vm.go index 36ab600c..200f0e30 100644 --- a/proxmoxtf/resource_virtual_environment_vm.go +++ b/proxmoxtf/resource_virtual_environment_vm.go @@ -32,6 +32,8 @@ const ( dvResourceVirtualEnvironmentVMDiskFileFormat = "qcow2" dvResourceVirtualEnvironmentVMDiskFileID = "" dvResourceVirtualEnvironmentVMDiskSize = 8 + dvResourceVirtualEnvironmentVMDiskSpeedRead = 0 + dvResourceVirtualEnvironmentVMDiskSpeedWrite = 0 dvResourceVirtualEnvironmentVMKeyboardLayout = "en-us" dvResourceVirtualEnvironmentVMMemoryDedicated = 512 dvResourceVirtualEnvironmentVMMemoryFloating = 0 @@ -79,6 +81,9 @@ const ( mkResourceVirtualEnvironmentVMDiskFileFormat = "file_format" mkResourceVirtualEnvironmentVMDiskFileID = "file_id" mkResourceVirtualEnvironmentVMDiskSize = "size" + mkResourceVirtualEnvironmentVMDiskSpeed = "speed" + mkResourceVirtualEnvironmentVMDiskSpeedRead = "read" + mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write" mkResourceVirtualEnvironmentVMKeyboardLayout = "keyboard_layout" mkResourceVirtualEnvironmentVMMemory = "memory" mkResourceVirtualEnvironmentVMMemoryDedicated = "dedicated" @@ -416,6 +421,40 @@ func resourceVirtualEnvironmentVM() *schema.Resource { Default: dvResourceVirtualEnvironmentVMDiskSize, ValidateFunc: validation.IntBetween(1, 8192), }, + mkResourceVirtualEnvironmentVMDiskSpeed: { + Type: schema.TypeList, + Description: "The speed limits", + Optional: true, + DefaultFunc: func() (interface{}, error) { + defaultList := make([]interface{}, 1) + defaultMap := make(map[string]interface{}) + + defaultMap[mkResourceVirtualEnvironmentVMDiskSpeedRead] = dvResourceVirtualEnvironmentVMDiskSpeedRead + defaultMap[mkResourceVirtualEnvironmentVMDiskSpeedWrite] = dvResourceVirtualEnvironmentVMDiskSpeedWrite + + defaultList[0] = defaultMap + + return defaultList, nil + }, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + mkResourceVirtualEnvironmentVMDiskSpeedRead: { + Type: schema.TypeInt, + Optional: true, + Description: "The maximum read speed in megabytes per second", + Default: dvResourceVirtualEnvironmentVMDiskSpeedRead, + }, + mkResourceVirtualEnvironmentVMDiskSpeedWrite: { + Type: schema.TypeInt, + Optional: true, + Description: "The maximum write speed in megabytes per second", + Default: dvResourceVirtualEnvironmentVMDiskSpeedRead, + }, + }, + }, + MaxItems: 1, + MinItems: 0, + }, }, }, MaxItems: 14, @@ -567,11 +606,11 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e return err } - schema := resourceVirtualEnvironmentVM().Schema + resourceSchema := resourceVirtualEnvironmentVM().Schema agent := d.Get(mkResourceVirtualEnvironmentVMAgent).([]interface{}) if len(agent) == 0 { - agentDefault, err := schema[mkResourceVirtualEnvironmentVMAgent].DefaultValue() + agentDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMAgent].DefaultValue() if err != nil { return err @@ -588,7 +627,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e cdrom := d.Get(mkResourceVirtualEnvironmentVMCDROM).([]interface{}) if len(cdrom) == 0 { - cdromDefault, err := schema[mkResourceVirtualEnvironmentVMCDROM].DefaultValue() + cdromDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMCDROM].DefaultValue() if err != nil { return err @@ -703,7 +742,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e cpu := d.Get(mkResourceVirtualEnvironmentVMCPU).([]interface{}) if len(cpu) == 0 { - cpuDefault, err := schema[mkResourceVirtualEnvironmentVMCPU].DefaultValue() + cpuDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMCPU].DefaultValue() if err != nil { return err @@ -721,6 +760,10 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{}) scsiDevices := make(proxmox.CustomStorageDevices, len(disk)) + diskSchemaElem := resourceSchema[mkResourceVirtualEnvironmentVMDisk].Elem + diskSchemaResource := diskSchemaElem.(*schema.Resource) + diskSpeedResource := diskSchemaResource.Schema[mkResourceVirtualEnvironmentVMDiskSpeed] + for i, d := range disk { block := d.(map[string]interface{}) @@ -728,11 +771,34 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e enabled, _ := block[mkResourceVirtualEnvironmentVMDiskEnabled].(bool) fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string) size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int) + speed := block[mkResourceVirtualEnvironmentVMDiskSpeed].([]interface{}) + + if len(speed) == 0 { + diskSpeedDefault, err := diskSpeedResource.DefaultValue() + + if err != nil { + return err + } + + speed = diskSpeedDefault.([]interface{}) + } + + speedBlock := speed[0].(map[string]interface{}) + speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int) + speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int) diskDevice := proxmox.CustomStorageDevice{ Enabled: enabled, } + if speedLimitRead > 0 { + diskDevice.MaxReadSpeedMbps = &speedLimitRead + } + + if speedLimitWrite > 0 { + diskDevice.MaxWriteSpeedMbps = &speedLimitWrite + } + if fileID != "" { diskDevice.Enabled = false } else { @@ -746,7 +812,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{}) if len(memory) == 0 { - memoryDefault, err := schema[mkResourceVirtualEnvironmentVMMemory].DefaultValue() + memoryDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMMemory].DefaultValue() if err != nil { return err @@ -842,6 +908,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e } scsiHardware := "virtio-scsi-pci" + startOnBoot := proxmox.CustomBool(true) tabletDeviceEnabled := proxmox.CustomBool(true) body := &proxmox.VirtualEnvironmentVMCreateRequestBody{ @@ -865,6 +932,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e SCSIHardware: &scsiHardware, SerialDevices: []string{"socket"}, SharedMemory: memorySharedObject, + StartOnBoot: &startOnBoot, TabletDeviceEnabled: &tabletDeviceEnabled, VMID: &vmID, } @@ -926,22 +994,54 @@ func resourceVirtualEnvironmentVMCreateImportedDisks(d *schema.ResourceData, m i } } + // Retrieve some information about the disk schema. + resourceSchema := resourceVirtualEnvironmentVM().Schema + diskSchemaElem := resourceSchema[mkResourceVirtualEnvironmentVMDisk].Elem + diskSchemaResource := diskSchemaElem.(*schema.Resource) + diskSpeedResource := diskSchemaResource.Schema[mkResourceVirtualEnvironmentVMDiskSpeed] + // Generate the commands required to import the specified disks. importedDiskCount := 0 for i, d := range disk { block := d.(map[string]interface{}) - datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string) enabled, _ := block[mkResourceVirtualEnvironmentVMDiskEnabled].(bool) - fileFormat, _ := block[mkResourceVirtualEnvironmentVMDiskFileFormat].(string) fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string) - size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int) if !enabled || fileID == "" { continue } + datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string) + fileFormat, _ := block[mkResourceVirtualEnvironmentVMDiskFileFormat].(string) + size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int) + speed := block[mkResourceVirtualEnvironmentVMDiskSpeed].([]interface{}) + + if len(speed) == 0 { + diskSpeedDefault, err := diskSpeedResource.DefaultValue() + + if err != nil { + return err + } + + speed = diskSpeedDefault.([]interface{}) + } + + speedBlock := speed[0].(map[string]interface{}) + speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int) + speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int) + + diskOptions := "" + + if speedLimitRead > 0 { + diskOptions += fmt.Sprintf(",mbps_rd=%d", speedLimitRead) + } + + if speedLimitWrite > 0 { + diskOptions += fmt.Sprintf(",mbps_wr=%d", speedLimitWrite) + } + fileIDParts := strings.Split(fileID, ":") filePath := fmt.Sprintf("/var/lib/vz/template/%s", fileIDParts[1]) filePathTmp := fmt.Sprintf("/tmp/vm-%d-disk-%d.%s", vmID, diskCount+importedDiskCount, fileFormat) @@ -951,7 +1051,7 @@ func resourceVirtualEnvironmentVMCreateImportedDisks(d *schema.ResourceData, m i fmt.Sprintf("cp %s %s", filePath, filePathTmp), fmt.Sprintf("qemu-img resize %s %dG", filePathTmp, size), fmt.Sprintf("qm importdisk %d %s %s -format qcow2", vmID, filePathTmp, datastoreID), - fmt.Sprintf("qm set %d --scsi%d %s:vm-%d-disk-%d", vmID, i, datastoreID, vmID, diskCount+importedDiskCount), + fmt.Sprintf("qm set %d -scsi%d %s:vm-%d-disk-%d%s", vmID, i, datastoreID, vmID, diskCount+importedDiskCount, diskOptions), fmt.Sprintf("rm -f %s", filePathTmp), ) diff --git a/proxmoxtf/resource_virtual_environment_vm_test.go b/proxmoxtf/resource_virtual_environment_vm_test.go index d8bc971e..8176d1cf 100644 --- a/proxmoxtf/resource_virtual_environment_vm_test.go +++ b/proxmoxtf/resource_virtual_environment_vm_test.go @@ -247,6 +247,21 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) { schema.TypeInt, }) + diskSpeedSchema := testNestedSchemaExistence(t, diskSchema, mkResourceVirtualEnvironmentVMDiskSpeed) + + testOptionalArguments(t, diskSpeedSchema, []string{ + mkResourceVirtualEnvironmentVMDiskSpeedRead, + mkResourceVirtualEnvironmentVMDiskSpeedWrite, + }) + + testSchemaValueTypes(t, diskSpeedSchema, []string{ + mkResourceVirtualEnvironmentVMDiskSpeedRead, + mkResourceVirtualEnvironmentVMDiskSpeedWrite, + }, []schema.ValueType{ + schema.TypeInt, + schema.TypeInt, + }) + memorySchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMMemory) testOptionalArguments(t, memorySchema, []string{