mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-06-30 18:42:58 +00:00
feat(vm): add 'path_in_datastore' disk argument (#606)
* feat(vm): add 'path_in_datastore' disk argument Provide access to actual in-datastore path to disk image, and experimental support for attaching other VM's disks or host devices. Signed-off-by: Oto Petřík <oto.petrik@gmail.com> * chore: added to `/example` for acceptance testing Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> --------- Signed-off-by: Oto Petřík <oto.petrik@gmail.com> Signed-off-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com> Co-authored-by: Pavel Boldyrev <627562+bpg@users.noreply.github.com>
This commit is contained in:
parent
29894bda23
commit
aeb5e88bc9
@ -240,6 +240,10 @@ output "ubuntu_vm_public_key" {
|
|||||||
- `unsafe` - Write directly to the disk bypassing the host cache.
|
- `unsafe` - Write directly to the disk bypassing the host cache.
|
||||||
- `datastore_id` - (Optional) The identifier for the datastore to create
|
- `datastore_id` - (Optional) The identifier for the datastore to create
|
||||||
the disk in (defaults to `local-lvm`).
|
the disk in (defaults to `local-lvm`).
|
||||||
|
- `path_in_datastore` - (Optional) The in-datastore path to the disk image.
|
||||||
|
***Experimental.***Use to attach another VM's disks,
|
||||||
|
or (as root only) host's filesystem paths (`datastore_id` empty string).
|
||||||
|
See "*Example: Attached disks*".
|
||||||
- `discard` - (Optional) Whether to pass discard/trim requests to the
|
- `discard` - (Optional) Whether to pass discard/trim requests to the
|
||||||
underlying storage. Supported values are `on`/`ignore` (defaults
|
underlying storage. Supported values are `on`/`ignore` (defaults
|
||||||
to `ignore`).
|
to `ignore`).
|
||||||
@ -514,6 +518,74 @@ target node. If you need certain disks to be on specific datastores, set
|
|||||||
the `datastore_id` argument of the disks in the `disks` block to move the disks
|
the `datastore_id` argument of the disks in the `disks` block to move the disks
|
||||||
to the correct datastore after the cloning and migrating succeeded.
|
to the correct datastore after the cloning and migrating succeeded.
|
||||||
|
|
||||||
|
## Example: Attached disks
|
||||||
|
|
||||||
|
In this example VM `data_vm` holds two data disks, and is not used as an actual VM,
|
||||||
|
but only as a container for the disks.
|
||||||
|
It does not have any OS installation, it is never started.
|
||||||
|
|
||||||
|
VM `data_user_vm` attaches those disks as `scsi1` and `scsi2`.
|
||||||
|
**VM `data_user_vm` can be *re-created/replaced* without losing data stored on disks
|
||||||
|
owned by `data_vm`.**
|
||||||
|
|
||||||
|
This functionality is **experimental**.
|
||||||
|
|
||||||
|
Do *not* simultaneously run more than one VM using same disk. For most filesystems,
|
||||||
|
attaching one disk to multiple VM will cause errors or even data corruption.
|
||||||
|
|
||||||
|
Do *not* move or resize `data_vm` disks.
|
||||||
|
(Resource `data_user_vm` should reject attempts to move or resize non-owned disks.)
|
||||||
|
|
||||||
|
|
||||||
|
```terraform
|
||||||
|
resource "proxmox_virtual_environment_vm" "data_vm" {
|
||||||
|
node_name = "first-node"
|
||||||
|
started = false
|
||||||
|
on_boot = false
|
||||||
|
|
||||||
|
disk {
|
||||||
|
datastore_id = "local-zfs"
|
||||||
|
file_format = "raw"
|
||||||
|
interface = "scsi0"
|
||||||
|
size = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
disk {
|
||||||
|
datastore_id = "local-zfs"
|
||||||
|
file_format = "raw"
|
||||||
|
interface = "scsi1"
|
||||||
|
size = 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "proxmox_virtual_environment_vm" "data_user_vm" {
|
||||||
|
# boot disk
|
||||||
|
disk {
|
||||||
|
datastore_id = "local-zfs"
|
||||||
|
file_format = "raw"
|
||||||
|
interface = "scsi0"
|
||||||
|
size = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
# attached disks from data_vm
|
||||||
|
dynamic "disk" {
|
||||||
|
for_each = { for idx, val in proxmox_virtual_environment_vm.data_vm.disk : idx => val }
|
||||||
|
iterator = data_disk
|
||||||
|
content {
|
||||||
|
datastore_id = data_disk.value["datastore_id"]
|
||||||
|
path_in_datastore = data_disk.value["path_in_datastore"]
|
||||||
|
file_format = data_disk.value["file_format"]
|
||||||
|
size = data_disk.value["size"]
|
||||||
|
# assign from scsi1 and up
|
||||||
|
interface = "scsi${data_disk.key + 1}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# remainder of VM configuration
|
||||||
|
...
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
## Import
|
## Import
|
||||||
|
|
||||||
Instances can be imported using the `node_name` and the `vm_id`, e.g.,
|
Instances can be imported using the `node_name` and the `vm_id`, e.g.,
|
||||||
|
@ -164,6 +164,40 @@ resource "proxmox_virtual_environment_vm" "example" {
|
|||||||
# mapping = "gpu"
|
# mapping = "gpu"
|
||||||
# pcie = true
|
# pcie = true
|
||||||
#}
|
#}
|
||||||
|
|
||||||
|
# attached disks from data_vm
|
||||||
|
dynamic "disk" {
|
||||||
|
for_each = {for idx, val in proxmox_virtual_environment_vm.data_vm.disk : idx => val}
|
||||||
|
iterator = data_disk
|
||||||
|
content {
|
||||||
|
datastore_id = data_disk.value["datastore_id"]
|
||||||
|
path_in_datastore = data_disk.value["path_in_datastore"]
|
||||||
|
file_format = data_disk.value["file_format"]
|
||||||
|
size = data_disk.value["size"]
|
||||||
|
# assign from scsi1 and up
|
||||||
|
interface = "scsi${data_disk.key + 1}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "proxmox_virtual_environment_vm" "data_vm" {
|
||||||
|
name = "terraform-provider-proxmox-data-vm"
|
||||||
|
node_name = data.proxmox_virtual_environment_nodes.example.names[0]
|
||||||
|
started = false
|
||||||
|
on_boot = false
|
||||||
|
|
||||||
|
disk {
|
||||||
|
datastore_id = local.datastore_id
|
||||||
|
file_format = "raw"
|
||||||
|
interface = "scsi0"
|
||||||
|
size = 1
|
||||||
|
}
|
||||||
|
disk {
|
||||||
|
datastore_id = local.datastore_id
|
||||||
|
file_format = "raw"
|
||||||
|
interface = "scsi1"
|
||||||
|
size = 4
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output "resource_proxmox_virtual_environment_vm_example_id" {
|
output "resource_proxmox_virtual_environment_vm_example_id" {
|
||||||
|
@ -187,6 +187,46 @@ type CustomStorageDevice struct {
|
|||||||
SizeInt *int
|
SizeInt *int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PathInDatastore returns path part of FileVolume or nil if it is not yet allocated.
|
||||||
|
func (r CustomStorageDevice) PathInDatastore() *string {
|
||||||
|
probablyDatastoreID, pathInDatastore, hasDatastoreID := strings.Cut(r.FileVolume, ":")
|
||||||
|
if !hasDatastoreID {
|
||||||
|
// when no ':' separator is found, 'Cut' places the whole string to 'probablyDatastoreID',
|
||||||
|
// we want it in 'pathInDatastore' (as it is absolute filesystem path)
|
||||||
|
pathInDatastore = probablyDatastoreID
|
||||||
|
|
||||||
|
return &pathInDatastore
|
||||||
|
}
|
||||||
|
|
||||||
|
pathInDatastoreWithoutDigits := strings.Map(
|
||||||
|
func(c rune) rune {
|
||||||
|
if c < '0' || c > '9' {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
},
|
||||||
|
pathInDatastore)
|
||||||
|
|
||||||
|
if pathInDatastoreWithoutDigits == "" {
|
||||||
|
// FileVolume is not yet allocated, it is in the "STORAGE_ID:SIZE_IN_GiB" format
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &pathInDatastore
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOwnedBy returns true, if CustomStorageDevice is owned by given VM. Not yet allocated volumes are not owned by any VM.
|
||||||
|
func (r CustomStorageDevice) IsOwnedBy(vmID int) bool {
|
||||||
|
pathInDatastore := r.PathInDatastore()
|
||||||
|
if pathInDatastore == nil {
|
||||||
|
// not yet allocated volume, consider disk not owned by any VM
|
||||||
|
// NOTE: if needed, create IsOwnedByOtherThan(vmId) instead of changing this return value.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.HasPrefix(*pathInDatastore, fmt.Sprintf("vm-%d-", vmID))
|
||||||
|
}
|
||||||
|
|
||||||
// CustomStorageDevices handles QEMU SATA device parameters.
|
// CustomStorageDevices handles QEMU SATA device parameters.
|
||||||
type CustomStorageDevices map[string]CustomStorageDevice
|
type CustomStorageDevices map[string]CustomStorageDevice
|
||||||
|
|
||||||
|
@ -177,6 +177,7 @@ const (
|
|||||||
mkResourceVirtualEnvironmentVMDisk = "disk"
|
mkResourceVirtualEnvironmentVMDisk = "disk"
|
||||||
mkResourceVirtualEnvironmentVMDiskInterface = "interface"
|
mkResourceVirtualEnvironmentVMDiskInterface = "interface"
|
||||||
mkResourceVirtualEnvironmentVMDiskDatastoreID = "datastore_id"
|
mkResourceVirtualEnvironmentVMDiskDatastoreID = "datastore_id"
|
||||||
|
mkResourceVirtualEnvironmentVMDiskPathInDatastore = "path_in_datastore"
|
||||||
mkResourceVirtualEnvironmentVMDiskFileFormat = "file_format"
|
mkResourceVirtualEnvironmentVMDiskFileFormat = "file_format"
|
||||||
mkResourceVirtualEnvironmentVMDiskFileID = "file_id"
|
mkResourceVirtualEnvironmentVMDiskFileID = "file_id"
|
||||||
mkResourceVirtualEnvironmentVMDiskSize = "size"
|
mkResourceVirtualEnvironmentVMDiskSize = "size"
|
||||||
@ -599,14 +600,15 @@ func VM() *schema.Resource {
|
|||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return []interface{}{
|
return []interface{}{
|
||||||
map[string]interface{}{
|
map[string]interface{}{
|
||||||
mkResourceVirtualEnvironmentVMDiskDatastoreID: dvResourceVirtualEnvironmentVMDiskDatastoreID,
|
mkResourceVirtualEnvironmentVMDiskDatastoreID: dvResourceVirtualEnvironmentVMDiskDatastoreID,
|
||||||
mkResourceVirtualEnvironmentVMDiskFileID: dvResourceVirtualEnvironmentVMDiskFileID,
|
mkResourceVirtualEnvironmentVMDiskPathInDatastore: nil,
|
||||||
mkResourceVirtualEnvironmentVMDiskInterface: dvResourceVirtualEnvironmentVMDiskInterface,
|
mkResourceVirtualEnvironmentVMDiskFileID: dvResourceVirtualEnvironmentVMDiskFileID,
|
||||||
mkResourceVirtualEnvironmentVMDiskSize: dvResourceVirtualEnvironmentVMDiskSize,
|
mkResourceVirtualEnvironmentVMDiskInterface: dvResourceVirtualEnvironmentVMDiskInterface,
|
||||||
mkResourceVirtualEnvironmentVMDiskIOThread: dvResourceVirtualEnvironmentVMDiskIOThread,
|
mkResourceVirtualEnvironmentVMDiskSize: dvResourceVirtualEnvironmentVMDiskSize,
|
||||||
mkResourceVirtualEnvironmentVMDiskSSD: dvResourceVirtualEnvironmentVMDiskSSD,
|
mkResourceVirtualEnvironmentVMDiskIOThread: dvResourceVirtualEnvironmentVMDiskIOThread,
|
||||||
mkResourceVirtualEnvironmentVMDiskDiscard: dvResourceVirtualEnvironmentVMDiskDiscard,
|
mkResourceVirtualEnvironmentVMDiskSSD: dvResourceVirtualEnvironmentVMDiskSSD,
|
||||||
mkResourceVirtualEnvironmentVMDiskCache: dvResourceVirtualEnvironmentVMDiskCache,
|
mkResourceVirtualEnvironmentVMDiskDiscard: dvResourceVirtualEnvironmentVMDiskDiscard,
|
||||||
|
mkResourceVirtualEnvironmentVMDiskCache: dvResourceVirtualEnvironmentVMDiskCache,
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
},
|
},
|
||||||
@ -623,6 +625,13 @@ func VM() *schema.Resource {
|
|||||||
Optional: true,
|
Optional: true,
|
||||||
Default: dvResourceVirtualEnvironmentVMDiskDatastoreID,
|
Default: dvResourceVirtualEnvironmentVMDiskDatastoreID,
|
||||||
},
|
},
|
||||||
|
mkResourceVirtualEnvironmentVMDiskPathInDatastore: {
|
||||||
|
Type: schema.TypeString,
|
||||||
|
Description: "The in-datastore path to disk image",
|
||||||
|
Computed: true,
|
||||||
|
Optional: true,
|
||||||
|
Default: nil,
|
||||||
|
},
|
||||||
mkResourceVirtualEnvironmentVMDiskFileFormat: {
|
mkResourceVirtualEnvironmentVMDiskFileFormat: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Description: "The file format",
|
Description: "The file format",
|
||||||
@ -3013,6 +3022,12 @@ func vmGetDiskDeviceObjects(
|
|||||||
|
|
||||||
block := diskEntry.(map[string]interface{})
|
block := diskEntry.(map[string]interface{})
|
||||||
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
||||||
|
pathInDatastore := ""
|
||||||
|
|
||||||
|
if untyped, hasPathInDatastore := block[mkResourceVirtualEnvironmentVMDiskPathInDatastore]; hasPathInDatastore {
|
||||||
|
pathInDatastore = untyped.(string)
|
||||||
|
}
|
||||||
|
|
||||||
fileFormat, _ := block[mkResourceVirtualEnvironmentVMDiskFileFormat].(string)
|
fileFormat, _ := block[mkResourceVirtualEnvironmentVMDiskFileFormat].(string)
|
||||||
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||||||
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
||||||
@ -3039,7 +3054,16 @@ func vmGetDiskDeviceObjects(
|
|||||||
if fileID != "" {
|
if fileID != "" {
|
||||||
diskDevice.Enabled = false
|
diskDevice.Enabled = false
|
||||||
} else {
|
} else {
|
||||||
diskDevice.FileVolume = fmt.Sprintf("%s:%d", datastoreID, size)
|
if pathInDatastore != "" {
|
||||||
|
if datastoreID != "" {
|
||||||
|
diskDevice.FileVolume = fmt.Sprintf("%s:%s", datastoreID, pathInDatastore)
|
||||||
|
} else {
|
||||||
|
// FileVolume is absolute path in the host filesystem
|
||||||
|
diskDevice.FileVolume = pathInDatastore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diskDevice.FileVolume = fmt.Sprintf("%s:%d", datastoreID, size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diskDevice.ID = &datastoreID
|
diskDevice.ID = &datastoreID
|
||||||
@ -3813,24 +3837,34 @@ func vmReadCustom(
|
|||||||
|
|
||||||
disk := map[string]interface{}{}
|
disk := map[string]interface{}{}
|
||||||
|
|
||||||
fileIDParts := strings.Split(dd.FileVolume, ":")
|
datastoreID, pathInDatastore, hasDatastoreID := strings.Cut(dd.FileVolume, ":")
|
||||||
|
if !hasDatastoreID {
|
||||||
|
// when no ':' separator is found, 'Cut' places the whole string to 'datastoreID',
|
||||||
|
// we want it in 'pathInDatastore' (it is absolute filesystem path)
|
||||||
|
pathInDatastore = datastoreID
|
||||||
|
datastoreID = ""
|
||||||
|
}
|
||||||
|
|
||||||
disk[mkResourceVirtualEnvironmentVMDiskDatastoreID] = fileIDParts[0]
|
disk[mkResourceVirtualEnvironmentVMDiskDatastoreID] = datastoreID
|
||||||
|
disk[mkResourceVirtualEnvironmentVMDiskPathInDatastore] = pathInDatastore
|
||||||
|
|
||||||
if dd.Format == nil {
|
if dd.Format == nil {
|
||||||
disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = dvResourceVirtualEnvironmentVMDiskFileFormat
|
disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = dvResourceVirtualEnvironmentVMDiskFileFormat
|
||||||
// disk format may not be returned by config API if it is default for the storage, and that may be different
|
|
||||||
// from the default qcow2, so we need to read it from the storage API to make sure we have the correct value
|
|
||||||
files, err := api.Node(nodeName).ListDatastoreFiles(ctx, fileIDParts[0])
|
|
||||||
if err != nil {
|
|
||||||
diags = append(diags, diag.FromErr(err)...)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range files {
|
if datastoreID != "" {
|
||||||
if v.VolumeID == dd.FileVolume {
|
// disk format may not be returned by config API if it is default for the storage, and that may be different
|
||||||
disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = v.FileFormat
|
// from the default qcow2, so we need to read it from the storage API to make sure we have the correct value
|
||||||
break
|
files, err := api.Node(nodeName).ListDatastoreFiles(ctx, datastoreID)
|
||||||
|
if err != nil {
|
||||||
|
diags = append(diags, diag.FromErr(err)...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range files {
|
||||||
|
if v.VolumeID == dd.FileVolume {
|
||||||
|
disk[mkResourceVirtualEnvironmentVMDiskFileFormat] = v.FileFormat
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -5602,29 +5636,48 @@ func vmUpdateDiskLocationAndSize(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *oldDisk.ID != *diskNewEntries[prefix][oldKey].ID {
|
if *oldDisk.ID != *diskNewEntries[prefix][oldKey].ID {
|
||||||
deleteOriginalDisk := types.CustomBool(true)
|
if oldDisk.IsOwnedBy(vmID) {
|
||||||
|
deleteOriginalDisk := types.CustomBool(true)
|
||||||
|
|
||||||
diskMoveBodies = append(
|
diskMoveBodies = append(
|
||||||
diskMoveBodies,
|
diskMoveBodies,
|
||||||
&vms.MoveDiskRequestBody{
|
&vms.MoveDiskRequestBody{
|
||||||
DeleteOriginalDisk: &deleteOriginalDisk,
|
DeleteOriginalDisk: &deleteOriginalDisk,
|
||||||
Disk: *oldDisk.Interface,
|
Disk: *oldDisk.Interface,
|
||||||
TargetStorage: *diskNewEntries[prefix][oldKey].ID,
|
TargetStorage: *diskNewEntries[prefix][oldKey].ID,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// Cannot be done while VM is running.
|
// Cannot be done while VM is running.
|
||||||
shutdownForDisksRequired = true
|
shutdownForDisksRequired = true
|
||||||
|
} else {
|
||||||
|
return diag.Errorf(
|
||||||
|
"Cannot move %s:%s to datastore %s in VM %d configuration, it is not owned by this VM!",
|
||||||
|
*oldDisk.ID,
|
||||||
|
*oldDisk.PathInDatastore(),
|
||||||
|
*diskNewEntries[prefix][oldKey].ID,
|
||||||
|
vmID,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *oldDisk.SizeInt < *diskNewEntries[prefix][oldKey].SizeInt {
|
if *oldDisk.SizeInt < *diskNewEntries[prefix][oldKey].SizeInt {
|
||||||
diskResizeBodies = append(
|
if oldDisk.IsOwnedBy(vmID) {
|
||||||
diskResizeBodies,
|
diskResizeBodies = append(
|
||||||
&vms.ResizeDiskRequestBody{
|
diskResizeBodies,
|
||||||
Disk: *oldDisk.Interface,
|
&vms.ResizeDiskRequestBody{
|
||||||
Size: *diskNewEntries[prefix][oldKey].Size,
|
Disk: *oldDisk.Interface,
|
||||||
},
|
Size: *diskNewEntries[prefix][oldKey].Size,
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return diag.Errorf(
|
||||||
|
"Cannot resize %s:%s in VM %d configuration, it is not owned by this VM!",
|
||||||
|
*oldDisk.ID,
|
||||||
|
*oldDisk.PathInDatastore(),
|
||||||
|
vmID,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,16 +189,18 @@ func TestVMSchema(t *testing.T) {
|
|||||||
|
|
||||||
test.AssertOptionalArguments(t, diskSchema, []string{
|
test.AssertOptionalArguments(t, diskSchema, []string{
|
||||||
mkResourceVirtualEnvironmentVMDiskDatastoreID,
|
mkResourceVirtualEnvironmentVMDiskDatastoreID,
|
||||||
|
mkResourceVirtualEnvironmentVMDiskPathInDatastore,
|
||||||
mkResourceVirtualEnvironmentVMDiskFileFormat,
|
mkResourceVirtualEnvironmentVMDiskFileFormat,
|
||||||
mkResourceVirtualEnvironmentVMDiskFileID,
|
mkResourceVirtualEnvironmentVMDiskFileID,
|
||||||
mkResourceVirtualEnvironmentVMDiskSize,
|
mkResourceVirtualEnvironmentVMDiskSize,
|
||||||
})
|
})
|
||||||
|
|
||||||
test.AssertValueTypes(t, diskSchema, map[string]schema.ValueType{
|
test.AssertValueTypes(t, diskSchema, map[string]schema.ValueType{
|
||||||
mkResourceVirtualEnvironmentVMDiskDatastoreID: schema.TypeString,
|
mkResourceVirtualEnvironmentVMDiskDatastoreID: schema.TypeString,
|
||||||
mkResourceVirtualEnvironmentVMDiskFileFormat: schema.TypeString,
|
mkResourceVirtualEnvironmentVMDiskPathInDatastore: schema.TypeString,
|
||||||
mkResourceVirtualEnvironmentVMDiskFileID: schema.TypeString,
|
mkResourceVirtualEnvironmentVMDiskFileFormat: schema.TypeString,
|
||||||
mkResourceVirtualEnvironmentVMDiskSize: schema.TypeInt,
|
mkResourceVirtualEnvironmentVMDiskFileID: schema.TypeString,
|
||||||
|
mkResourceVirtualEnvironmentVMDiskSize: schema.TypeInt,
|
||||||
})
|
})
|
||||||
|
|
||||||
diskSpeedSchema := test.AssertNestedSchemaExistence(
|
diskSpeedSchema := test.AssertNestedSchemaExistence(
|
||||||
|
Loading…
Reference in New Issue
Block a user