From f335b1072cff30f0de38cac4a637918e49469cb3 Mon Sep 17 00:00:00 2001 From: Dan Petersen Date: Sat, 28 Dec 2019 04:12:39 +0100 Subject: [PATCH] Continued work on VM resource --- README.md | 3 +- proxmox/virtual_environment_vm_types.go | 54 ++++- proxmoxtf/resource_virtual_environment_vm.go | 222 ++++++++++++++++-- .../resource_virtual_environment_vm_test.go | 9 +- 4 files changed, 262 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 7f15efe9..57d0aeb1 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,6 @@ This resource doesn't expose any additional attributes. * `description` - (Optional) The description * `disk` - (Optional) The disk configuration (multiple blocks supported) * `datastore_id` - (Optional) The ID of the datastore to create the disk in (defaults to `local-lvm`) - * `enabled` - (Optional) Whether to enable the disk (defaults to `true`) * `file_format` - (Optional) The file format (defaults to `qcow2`) * `qcow2` - QEMU Disk Image v2 * `raw` - Raw Disk Image @@ -354,7 +353,9 @@ This resource doesn't expose any additional attributes. * `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 + * `read_burstable` - (Optional) The maximum burstable read speed in megabytes per second * `write` - (Optional) The maximum write speed in megabytes per second + * `write_burstable` - (Optional) The maximum burstable write speed in megabytes per second * `keyboard_layout` - (Optional) The keyboard layout (defaults to `en-us`) * `da` - Danish * `de` - German diff --git a/proxmox/virtual_environment_vm_types.go b/proxmox/virtual_environment_vm_types.go index 90f7d5a2..9507d20a 100644 --- a/proxmox/virtual_environment_vm_types.go +++ b/proxmox/virtual_environment_vm_types.go @@ -139,13 +139,16 @@ 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"` - 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"` + AIO *string `json:"aio,omitempty" url:"aio,omitempty"` + BackupEnabled *CustomBool `json:"backup,omitempty" url:"backup,omitempty,int"` + BurstableReadSpeedMbps *int `json:"mbps_rd_max,omitempty" url:"mbps_rd_max,omitempty"` + BurstableWriteSpeedMbps *int `json:"mbps_wr_max,omitempty" url:"mbps_wr_max,omitempty"` + 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"` + Size *string `json:"size,omitempty" url:"size,omitempty"` } // CustomStorageDevices handles QEMU SATA device parameters. @@ -337,7 +340,10 @@ type VirtualEnvironmentVMGetResponseData struct { SCSIDevice12 *CustomStorageDevice `json:"scsi12,omitempty"` SCSIDevice13 *CustomStorageDevice `json:"scsi13,omitempty"` SCSIHardware *string `json:"scsihw,omitempty"` - SerialDevices *CustomSerialDevices `json:"serial,omitempty"` + SerialDevice0 *string `json:"serial0,omitempty"` + SerialDevice1 *string `json:"serial1,omitempty"` + SerialDevice2 *string `json:"serial2,omitempty"` + SerialDevice3 *string `json:"serial3,omitempty"` SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty"` SkipLock *CustomBool `json:"skiplock,omitempty"` SMBIOS *CustomSMBIOS `json:"smbios1,omitempty"` @@ -818,6 +824,14 @@ func (r CustomStorageDevice) EncodeValues(key string, v *url.Values) error { } } + if r.BurstableReadSpeedMbps != nil { + values = append(values, fmt.Sprintf("mbps_rd_max=%d", *r.BurstableReadSpeedMbps)) + } + + if r.BurstableWriteSpeedMbps != nil { + values = append(values, fmt.Sprintf("mbps_wr_max=%d", *r.BurstableWriteSpeedMbps)) + } + if r.MaxReadSpeedMbps != nil { values = append(values, fmt.Sprintf("mbps_rd=%d", *r.MaxReadSpeedMbps)) } @@ -830,6 +844,10 @@ func (r CustomStorageDevice) EncodeValues(key string, v *url.Values) error { values = append(values, fmt.Sprintf("media=%s", *r.Media)) } + if r.Size != nil { + values = append(values, fmt.Sprintf("size=%s", *r.Size)) + } + v.Add(key, strings.Join(values, ",")) return nil @@ -1039,6 +1057,8 @@ func (r *CustomNetworkDevice) UnmarshalJSON(b []byte) error { r.Trunks[i] = iv } + default: + r.Model = v[0] } } } @@ -1155,6 +1175,14 @@ func (r *CustomStorageDevice) UnmarshalJSON(b []byte) error { } r.MaxReadSpeedMbps = &iv + case "mbps_rd_max": + iv, err := strconv.Atoi(v[1]) + + if err != nil { + return err + } + + r.BurstableReadSpeedMbps = &iv case "mbps_wr": iv, err := strconv.Atoi(v[1]) @@ -1163,8 +1191,18 @@ func (r *CustomStorageDevice) UnmarshalJSON(b []byte) error { } r.MaxWriteSpeedMbps = &iv + case "mbps_wr_max": + iv, err := strconv.Atoi(v[1]) + + if err != nil { + return err + } + + r.BurstableWriteSpeedMbps = &iv case "media": r.Media = &v[1] + case "size": + r.Size = &v[1] } } } diff --git a/proxmoxtf/resource_virtual_environment_vm.go b/proxmoxtf/resource_virtual_environment_vm.go index 31ccfa9e..b90994f1 100644 --- a/proxmoxtf/resource_virtual_environment_vm.go +++ b/proxmoxtf/resource_virtual_environment_vm.go @@ -6,6 +6,7 @@ package proxmoxtf import ( "fmt" + "math" "strconv" "strings" @@ -28,12 +29,13 @@ const ( dvResourceVirtualEnvironmentVMCPUSockets = 1 dvResourceVirtualEnvironmentVMDescription = "" dvResourceVirtualEnvironmentVMDiskDatastoreID = "local-lvm" - dvResourceVirtualEnvironmentVMDiskEnabled = true dvResourceVirtualEnvironmentVMDiskFileFormat = "qcow2" dvResourceVirtualEnvironmentVMDiskFileID = "" dvResourceVirtualEnvironmentVMDiskSize = 8 dvResourceVirtualEnvironmentVMDiskSpeedRead = 0 + dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable = 0 dvResourceVirtualEnvironmentVMDiskSpeedWrite = 0 + dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = 0 dvResourceVirtualEnvironmentVMKeyboardLayout = "en-us" dvResourceVirtualEnvironmentVMMemoryDedicated = 512 dvResourceVirtualEnvironmentVMMemoryFloating = 0 @@ -77,13 +79,14 @@ const ( mkResourceVirtualEnvironmentVMDescription = "description" mkResourceVirtualEnvironmentVMDisk = "disk" mkResourceVirtualEnvironmentVMDiskDatastoreID = "datastore_id" - mkResourceVirtualEnvironmentVMDiskEnabled = "enabled" mkResourceVirtualEnvironmentVMDiskFileFormat = "file_format" mkResourceVirtualEnvironmentVMDiskFileID = "file_id" mkResourceVirtualEnvironmentVMDiskSize = "size" mkResourceVirtualEnvironmentVMDiskSpeed = "speed" mkResourceVirtualEnvironmentVMDiskSpeedRead = "read" + mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable" mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write" + mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable" mkResourceVirtualEnvironmentVMKeyboardLayout = "keyboard_layout" mkResourceVirtualEnvironmentVMMemory = "memory" mkResourceVirtualEnvironmentVMMemoryDedicated = "dedicated" @@ -397,13 +400,6 @@ func resourceVirtualEnvironmentVM() *schema.Resource { Description: "The datastore id", Default: dvResourceVirtualEnvironmentVMDiskDatastoreID, }, - mkResourceVirtualEnvironmentVMDiskEnabled: { - Type: schema.TypeBool, - Optional: true, - ForceNew: true, - Description: "Whether to enable the disk", - Default: dvResourceVirtualEnvironmentVMDiskEnabled, - }, mkResourceVirtualEnvironmentVMDiskFileFormat: { Type: schema.TypeString, Optional: true, @@ -437,7 +433,9 @@ func resourceVirtualEnvironmentVM() *schema.Resource { defaultMap := make(map[string]interface{}) defaultMap[mkResourceVirtualEnvironmentVMDiskSpeedRead] = dvResourceVirtualEnvironmentVMDiskSpeedRead + defaultMap[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable] = dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable defaultMap[mkResourceVirtualEnvironmentVMDiskSpeedWrite] = dvResourceVirtualEnvironmentVMDiskSpeedWrite + defaultMap[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable] = dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable defaultList[0] = defaultMap @@ -451,11 +449,23 @@ func resourceVirtualEnvironmentVM() *schema.Resource { Description: "The maximum read speed in megabytes per second", Default: dvResourceVirtualEnvironmentVMDiskSpeedRead, }, + mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable: { + Type: schema.TypeInt, + Optional: true, + Description: "The maximum burstable read speed in megabytes per second", + Default: dvResourceVirtualEnvironmentVMDiskSpeedReadBurstable, + }, mkResourceVirtualEnvironmentVMDiskSpeedWrite: { Type: schema.TypeInt, Optional: true, Description: "The maximum write speed in megabytes per second", - Default: dvResourceVirtualEnvironmentVMDiskSpeedRead, + Default: dvResourceVirtualEnvironmentVMDiskSpeedWrite, + }, + mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable: { + Type: schema.TypeInt, + Optional: true, + Description: "The maximum burstable write speed in megabytes per second", + Default: dvResourceVirtualEnvironmentVMDiskSpeedWriteBurstable, }, }, }, @@ -791,7 +801,6 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e block := d.(map[string]interface{}) datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string) - enabled, _ := block[mkResourceVirtualEnvironmentVMDiskEnabled].(bool) fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string) size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int) speed := block[mkResourceVirtualEnvironmentVMDiskSpeed].([]interface{}) @@ -808,20 +817,30 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e speedBlock := speed[0].(map[string]interface{}) speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int) + speedLimitReadBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable].(int) speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int) + speedLimitWriteBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable].(int) diskDevice := proxmox.CustomStorageDevice{ - Enabled: enabled, + Enabled: true, } if speedLimitRead > 0 { diskDevice.MaxReadSpeedMbps = &speedLimitRead } + if speedLimitReadBurstable > 0 { + diskDevice.BurstableReadSpeedMbps = &speedLimitReadBurstable + } + if speedLimitWrite > 0 { diskDevice.MaxWriteSpeedMbps = &speedLimitWrite } + if speedLimitWriteBurstable > 0 { + diskDevice.BurstableWriteSpeedMbps = &speedLimitWriteBurstable + } + if fileID != "" { diskDevice.Enabled = false } else { @@ -1033,10 +1052,9 @@ func resourceVirtualEnvironmentVMCreateImportedDisks(d *schema.ResourceData, m i for i, d := range disk { block := d.(map[string]interface{}) - enabled, _ := block[mkResourceVirtualEnvironmentVMDiskEnabled].(bool) fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string) - if !enabled || fileID == "" { + if fileID == "" { continue } @@ -1163,6 +1181,7 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err return err } + // Compare the agent configuration to the one stored in the state. if vmConfig.Agent != nil { agent := make(map[string]interface{}) @@ -1196,6 +1215,34 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err d.Set(mkResourceVirtualEnvironmentVMAgent, make([]interface{}, 0)) } + // Compare the IDE devices to the CDROM and cloud-init configurations stored in the state. + if vmConfig.IDEDevice2 != nil { + if *vmConfig.IDEDevice2.Media == "cdrom" { + if strings.Contains(vmConfig.IDEDevice2.FileVolume, fmt.Sprintf("vm-%d-cloudinit", vmID)) { + d.Set(mkResourceVirtualEnvironmentVMCDROM, make([]interface{}, 0)) + } else { + d.Set(mkResourceVirtualEnvironmentVMCloudInit, make([]interface{}, 0)) + + cdrom := make([]interface{}, 1) + cdromBlock := make(map[string]interface{}) + + cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled] = true + cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID] = vmConfig.IDEDevice2.FileVolume + + cdrom[0] = cdromBlock + + d.Set(mkResourceVirtualEnvironmentVMCDROM, cdrom) + } + } else { + d.Set(mkResourceVirtualEnvironmentVMCDROM, make([]interface{}, 0)) + d.Set(mkResourceVirtualEnvironmentVMCloudInit, make([]interface{}, 0)) + } + } else { + d.Set(mkResourceVirtualEnvironmentVMCDROM, make([]interface{}, 0)) + d.Set(mkResourceVirtualEnvironmentVMCloudInit, make([]interface{}, 0)) + } + + // Compare the CPU configuration to the one stored in the state. cpu := make(map[string]interface{}) if vmConfig.CPUCores != nil { @@ -1225,6 +1272,7 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err d.Set(mkResourceVirtualEnvironmentVMCPU, []interface{}{cpu}) } + // Compare the description and keyboard layout to the values stored in the state. if vmConfig.Description != nil { d.Set(mkResourceVirtualEnvironmentVMDescription, *vmConfig.Description) } else { @@ -1237,6 +1285,101 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err d.Set(mkResourceVirtualEnvironmentVMKeyboardLayout, "") } + // Compare the disks to those stored in the state. + currentDiskList := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{}) + + diskList := make([]interface{}, 0) + diskObjects := []*proxmox.CustomStorageDevice{ + vmConfig.SCSIDevice0, + vmConfig.SCSIDevice1, + vmConfig.SCSIDevice2, + vmConfig.SCSIDevice3, + vmConfig.SCSIDevice4, + vmConfig.SCSIDevice5, + vmConfig.SCSIDevice6, + vmConfig.SCSIDevice7, + vmConfig.SCSIDevice8, + vmConfig.SCSIDevice9, + vmConfig.SCSIDevice10, + vmConfig.SCSIDevice11, + vmConfig.SCSIDevice12, + vmConfig.SCSIDevice13, + } + + for di, dd := range diskObjects { + disk := make(map[string]interface{}) + + if dd == nil { + continue + } + + fileIDParts := strings.Split(dd.FileVolume, ":") + + disk[mkResourceVirtualEnvironmentVMDiskDatastoreID] = fileIDParts[0] + + if len(currentDiskList) > di { + currentDisk := currentDiskList[di].(map[string]interface{}) + + disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = currentDisk[mkResourceVirtualEnvironmentVMDiskFileFormat] + disk[mkResourceVirtualEnvironmentVMDiskFileID] = currentDisk[mkResourceVirtualEnvironmentVMDiskFileID] + } + + diskSize := 0 + + if dd.Size != nil { + if strings.HasSuffix(*dd.Size, "T") { + diskSize, err = strconv.Atoi(strings.TrimSuffix(*dd.Size, "T")) + + if err != nil { + return err + } + + diskSize = int(math.Ceil(float64(diskSize) * 1024)) + } else if strings.HasSuffix(*dd.Size, "G") { + diskSize, err = strconv.Atoi(strings.TrimSuffix(*dd.Size, "G")) + + if err != nil { + return err + } + } else if strings.HasSuffix(*dd.Size, "M") { + diskSize, err = strconv.Atoi(strings.TrimSuffix(*dd.Size, "M")) + + if err != nil { + return err + } + + diskSize = int(math.Ceil(float64(diskSize) / 1024)) + } else { + return fmt.Errorf("Cannot parse storage size \"%s\"", *dd.Size) + } + } + + disk[mkResourceVirtualEnvironmentVMDiskSize] = diskSize + + if dd.BurstableReadSpeedMbps != nil || + dd.BurstableWriteSpeedMbps != nil || + dd.MaxReadSpeedMbps != nil || + dd.MaxWriteSpeedMbps != nil { + speed := map[string]interface{}{} + + speed[mkResourceVirtualEnvironmentVMDiskSpeedRead] = *dd.MaxReadSpeedMbps + speed[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable] = *dd.BurstableReadSpeedMbps + speed[mkResourceVirtualEnvironmentVMDiskSpeedWrite] = *dd.MaxWriteSpeedMbps + speed[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable] = *dd.BurstableWriteSpeedMbps + + disk[mkResourceVirtualEnvironmentVMDiskSpeed] = []interface{}{speed} + } else { + disk[mkResourceVirtualEnvironmentVMDiskSpeed] = []interface{}{} + } + + diskList = append(diskList, disk) + } + + if len(currentDiskList) > 0 || len(diskList) > 0 { + d.Set(mkResourceVirtualEnvironmentVMDisk, diskList) + } + + // Compare the memory configuration to the one stored in the state. memory := make(map[string]interface{}) if vmConfig.DedicatedMemory != nil { @@ -1266,12 +1409,63 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err d.Set(mkResourceVirtualEnvironmentVMMemory, []interface{}{memory}) } + // Compare the name to the value stored in the state. if vmConfig.Name != nil { d.Set(mkResourceVirtualEnvironmentVMName, *vmConfig.Name) } else { d.Set(mkResourceVirtualEnvironmentVMName, "") } + // Compare the network devices to those stored in the state. + currentNetworkDeviceList := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{}) + + networkDeviceLast := -1 + networkDeviceList := make([]interface{}, 8) + networkDeviceObjects := []*proxmox.CustomNetworkDevice{ + vmConfig.NetworkDevice0, + vmConfig.NetworkDevice1, + vmConfig.NetworkDevice2, + vmConfig.NetworkDevice3, + vmConfig.NetworkDevice4, + vmConfig.NetworkDevice5, + vmConfig.NetworkDevice6, + vmConfig.NetworkDevice7, + } + + for ni, nd := range networkDeviceObjects { + networkDevice := make(map[string]interface{}) + + if nd != nil { + networkDeviceLast = ni + + if nd.Bridge != nil { + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceBridge] = *nd.Bridge + } else { + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceBridge] = "" + } + + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled] = nd.Enabled + + if nd.MACAddress != nil { + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress] = *nd.MACAddress + } else { + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress] = "" + } + + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceModel] = nd.Model + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceVLANIDs] = nd.Trunks + } else { + networkDevice[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled] = false + } + + networkDeviceList[ni] = networkDevice + } + + if len(currentNetworkDeviceList) > 0 || networkDeviceLast > -1 { + d.Set(mkResourceVirtualEnvironmentVMNetworkDevice, networkDeviceList[0:networkDeviceLast+1]) + } + + // Compare the OS type and pool ID to the values stored in the state. if vmConfig.OSType != nil { d.Set(mkResourceVirtualEnvironmentVMOSType, *vmConfig.OSType) } else { diff --git a/proxmoxtf/resource_virtual_environment_vm_test.go b/proxmoxtf/resource_virtual_environment_vm_test.go index 994abb9c..655a62b5 100644 --- a/proxmoxtf/resource_virtual_environment_vm_test.go +++ b/proxmoxtf/resource_virtual_environment_vm_test.go @@ -230,7 +230,6 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) { testOptionalArguments(t, diskSchema, []string{ mkResourceVirtualEnvironmentVMDiskDatastoreID, - mkResourceVirtualEnvironmentVMDiskEnabled, mkResourceVirtualEnvironmentVMDiskFileFormat, mkResourceVirtualEnvironmentVMDiskFileID, mkResourceVirtualEnvironmentVMDiskSize, @@ -238,13 +237,11 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) { testSchemaValueTypes(t, diskSchema, []string{ mkResourceVirtualEnvironmentVMDiskDatastoreID, - mkResourceVirtualEnvironmentVMDiskEnabled, mkResourceVirtualEnvironmentVMDiskFileFormat, mkResourceVirtualEnvironmentVMDiskFileID, mkResourceVirtualEnvironmentVMDiskSize, }, []schema.ValueType{ schema.TypeString, - schema.TypeBool, schema.TypeString, schema.TypeString, schema.TypeInt, @@ -254,15 +251,21 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) { testOptionalArguments(t, diskSpeedSchema, []string{ mkResourceVirtualEnvironmentVMDiskSpeedRead, + mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable, mkResourceVirtualEnvironmentVMDiskSpeedWrite, + mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable, }) testSchemaValueTypes(t, diskSpeedSchema, []string{ mkResourceVirtualEnvironmentVMDiskSpeedRead, + mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable, mkResourceVirtualEnvironmentVMDiskSpeedWrite, + mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable, }, []schema.ValueType{ schema.TypeInt, schema.TypeInt, + schema.TypeInt, + schema.TypeInt, }) memorySchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMMemory)