diff --git a/vsphere/data_source_vsphere_vmfs_disks.go b/vsphere/data_source_vsphere_vmfs_disks.go index 79b816042..97370204f 100644 --- a/vsphere/data_source_vsphere_vmfs_disks.go +++ b/vsphere/data_source_vsphere_vmfs_disks.go @@ -7,6 +7,8 @@ import ( "sort" "time" + "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/structure" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/vmware/govmomi/vim25/mo" @@ -36,10 +38,32 @@ func dataSourceVSphereVmfsDisks() *schema.Resource { }, "disks": { Type: schema.TypeList, - Description: "The names of the disks discovered by the search.", + Description: "The canonical names of the disks discovered by the search.", Computed: true, Elem: &schema.Schema{Type: schema.TypeString}, }, + "disk_details": { + Type: schema.TypeList, + Description: "The details of the disks discovered by the search.", + Computed: true, + Elem: &schema.Resource{Schema: map[string]*schema.Schema{ + "display_name": { + Type: schema.TypeString, + Computed: true, + Description: "Display name of the disk.", + }, + "device_path": { + Type: schema.TypeString, + Computed: true, + Description: "Path of the physical volume of the disk.", + }, + "capacity_gb": { + Type: schema.TypeInt, + Computed: true, + Description: "Capacity of the disk in GiB.", + }, + }}, + }, }, } } @@ -70,19 +94,35 @@ func dataSourceVSphereVmfsDisksRead(d *schema.ResourceData, meta interface{}) er d.SetId(time.Now().UTC().String()) var disks []string + diskDetailsMap := make(map[string]map[string]interface{}) for _, sl := range hss.StorageDeviceInfo.ScsiLun { if hsd, ok := sl.(*types.HostScsiDisk); ok { if matched, _ := regexp.MatchString(d.Get("filter").(string), hsd.CanonicalName); matched { + disk := make(map[string]interface{}) + disk["display_name"] = hsd.DisplayName + disk["device_path"] = hsd.DevicePath + block := hsd.Capacity.Block + blockSize := int64(hsd.Capacity.BlockSize) + disk["capacity_gb"] = structure.ByteToGiB(block * blockSize) disks = append(disks, hsd.CanonicalName) + diskDetailsMap[hsd.CanonicalName] = disk } } } - sort.Strings(disks) + // use the now sorted name list to create a matching order details list + diskDetails := make([]map[string]interface{}, len(disks)) + for i, name := range disks { + diskDetails[i] = diskDetailsMap[name] + } if err := d.Set("disks", disks); err != nil { return fmt.Errorf("error saving results to state: %s", err) } + if err := d.Set("disk_details", diskDetails); err != nil { + return fmt.Errorf("error saving results to state: %s", err) + } + return nil } diff --git a/vsphere/data_source_vsphere_vmfs_disks_test.go b/vsphere/data_source_vsphere_vmfs_disks_test.go index 51f39d936..9167f8f21 100644 --- a/vsphere/data_source_vsphere_vmfs_disks_test.go +++ b/vsphere/data_source_vsphere_vmfs_disks_test.go @@ -26,6 +26,12 @@ func TestAccDataSourceVSphereVmfsDisks_basic(t *testing.T) { testCheckOutputBool("found", "true"), ), }, + { + Config: testAccDataSourceVSphereVmfsDisksInfoConfig(), + Check: resource.ComposeTestCheckFunc( + testCheckOutputBool("found", "true"), + ), + }, }, }) } @@ -79,3 +85,26 @@ output "found" { os.Getenv("TF_VAR_VSPHERE_ESXI1"), ) } + +func testAccDataSourceVSphereVmfsDisksInfoConfig() string { + return fmt.Sprintf(` +%s + +data "vsphere_host" "esxi_host" { + name = "%s" + datacenter_id = "${data.vsphere_datacenter.rootdc1.id}" +} + +data "vsphere_vmfs_disks" "available" { + host_system_id = "${data.vsphere_host.esxi_host.id}" + rescan = true +} + +output "found" { + value = "${length(data.vsphere_vmfs_disks.available.disk_details) >= 1 ? "true" : "false" }" +} +`, + testhelper.CombineConfigs(testhelper.ConfigDataRootDC1(), testhelper.ConfigDataRootPortGroup1()), + os.Getenv("TF_VAR_VSPHERE_ESXI1"), + ) +} diff --git a/vsphere/helper_test.go b/vsphere/helper_test.go index a7b280992..1b6a0ac45 100644 --- a/vsphere/helper_test.go +++ b/vsphere/helper_test.go @@ -384,9 +384,10 @@ func testRenameVMFirstDisk(s *terraform.State, resourceName string, new string) var dcSpec []types.BaseVirtualDeviceConfigSpec for _, d := range vprops.Config.Hardware.Device { if oldDisk, ok := d.(*types.VirtualDisk); ok { + backing, _ := virtualdevice.GetBackingForDisk(oldDisk) newFileName, err := virtualdisk.Move( tVars.client, - oldDisk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).FileName, + backing.GetFileName(), dc, new, nil, @@ -400,9 +401,9 @@ func testRenameVMFirstDisk(s *terraform.State, resourceName string, new string) VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{ FileName: newFileName, }, - ThinProvisioned: oldDisk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).ThinProvisioned, - EagerlyScrub: oldDisk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).EagerlyScrub, - DiskMode: oldDisk.Backing.(*types.VirtualDiskFlatVer2BackingInfo).DiskMode, + ThinProvisioned: backing.GetThinProvisioned(), + EagerlyScrub: backing.GetEagerlyScrub(), + DiskMode: backing.GetDiskMode(), }, }, } diff --git a/vsphere/internal/virtualdevice/virtual_machine_disk_backing.go b/vsphere/internal/virtualdevice/virtual_machine_disk_backing.go new file mode 100644 index 000000000..ae2fe1562 --- /dev/null +++ b/vsphere/internal/virtualdevice/virtual_machine_disk_backing.go @@ -0,0 +1,432 @@ +package virtualdevice + +import ( + "github.com/vmware/govmomi/vim25/types" +) + +type TerraformVirtualDiskFlatVer2BackingInfo struct { + *types.VirtualDiskFlatVer2BackingInfo +} + +type TerraformVirtualDiskRawDiskMappingVer1BackingInfo struct { + *types.VirtualDiskRawDiskMappingVer1BackingInfo +} + +type TerraformVirtualMachineDiskBackingInfo interface { + types.BaseVirtualDeviceBackingInfo + GetDiskMode() string + SetDiskMode(string) + GetSplit() *bool + SetSplit(*bool) + GetWriteThrough() *bool + SetWriteThrough(*bool) + GetThinProvisioned() *bool + SetThinProvisioned(*bool) + GetEagerlyScrub() *bool + SetEagerlyScrub(*bool) + GetUuid() string + GetContentId() string + SetContentId(string) + GetChangeId() string + SetChangeId(string) + GetParent() interface{} + SetParent(interface{}) + GetDeltaDiskFormat() string + SetDeltaDiskFormat(string) + GetDigestEnabled() *bool + SetDigestEnabled(*bool) + GetDeltaDiskFormatVariant() string + SetDeltaDiskFormatVariant(string) + GetDeltaGrainSize() int32 + SetDeltaGrainSize(int32) + GetSharing() string + SetSharing(string) + GetKeyId() *types.CryptoKeyId + SetKeyId(*types.CryptoKeyId) + GetLunUuid() string + SetLunUuid(string) + GetDeviceName() string + SetDeviceName(string) + GetCompatibilityMode() string + SetCompatibilityMode(string) + GetDatastore() *types.ManagedObjectReference + SetDatastore(*types.ManagedObjectReference) + GetFileName() string + SetFileName(string) + GetBackingObjectId() string + SetBackingObjectId(string) +} + +func GetBackingForDisk(disk *types.VirtualDisk) (TerraformVirtualMachineDiskBackingInfo, bool) { + return GetBacking(disk.Backing) +} + +func GetBacking(backingInfo types.BaseVirtualDeviceBackingInfo) (TerraformVirtualMachineDiskBackingInfo, bool) { + switch backingInfo.(type) { + case *types.VirtualDiskFlatVer2BackingInfo: + return TerraformVirtualDiskFlatVer2BackingInfo{backingInfo.(*types.VirtualDiskFlatVer2BackingInfo)}, true + case *types.VirtualDiskRawDiskMappingVer1BackingInfo: + return TerraformVirtualDiskRawDiskMappingVer1BackingInfo{backingInfo.(*types.VirtualDiskRawDiskMappingVer1BackingInfo)}, true + default: + return nil, false + } +} + +func ToVirtualDiskFlatVer2BackingInfo(backing TerraformVirtualMachineDiskBackingInfo) types.VirtualDiskFlatVer2BackingInfo { + flatBacking := types.VirtualDiskFlatVer2BackingInfo{} + + flatBacking.FileName = backing.GetFileName() + flatBacking.Datastore = backing.GetDatastore() + flatBacking.BackingObjectId = backing.GetBackingObjectId() + + flatBacking.DiskMode = backing.GetDiskMode() + flatBacking.Split = backing.GetSplit() + flatBacking.WriteThrough = backing.GetWriteThrough() + flatBacking.ThinProvisioned = backing.GetThinProvisioned() + flatBacking.EagerlyScrub = backing.GetEagerlyScrub() + flatBacking.Uuid = backing.GetUuid() + flatBacking.ContentId = backing.GetContentId() + flatBacking.ChangeId = backing.GetChangeId() + if parentBacking, ok := backing.GetParent().(*types.VirtualDiskFlatVer2BackingInfo); ok { + flatBacking.Parent = parentBacking + } + flatBacking.DeltaDiskFormat = backing.GetDeltaDiskFormat() + flatBacking.DigestEnabled = backing.GetDigestEnabled() + flatBacking.DeltaGrainSize = backing.GetDeltaGrainSize() + flatBacking.DeltaDiskFormatVariant = backing.GetDeltaDiskFormatVariant() + flatBacking.Sharing = backing.GetSharing() + flatBacking.KeyId = backing.GetKeyId() + + return flatBacking +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetDiskMode() string { + return backing.DiskMode +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetDiskMode(diskMode string) { + backing.DiskMode = diskMode +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetSplit() *bool { + return backing.Split +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetSplit(split *bool) { + backing.Split = split +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetWriteThrough() *bool { + return backing.WriteThrough +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetWriteThrough(writeThrough *bool) { + backing.WriteThrough = writeThrough +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetThinProvisioned() *bool { + return backing.ThinProvisioned +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetThinProvisioned(thinProvisioned *bool) { + backing.ThinProvisioned = thinProvisioned +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetEagerlyScrub() *bool { + return backing.EagerlyScrub +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetEagerlyScrub(eagerlyScrub *bool) { + backing.EagerlyScrub = eagerlyScrub +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetUuid() string { + return backing.Uuid +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetContentId() string { + return backing.ContentId +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetContentId(contentId string) { + backing.ContentId = contentId +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetChangeId() string { + return backing.ChangeId +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetChangeId(changeId string) { + backing.ChangeId = changeId + +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetParent() interface{} { + return backing.Parent +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetParent(i interface{}) { + backing.Parent = i.(*types.VirtualDiskFlatVer2BackingInfo) +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetDeltaDiskFormat() string { + return backing.DeltaDiskFormat +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetDeltaDiskFormat(deltaDiskFormat string) { + backing.DeltaDiskFormat = deltaDiskFormat +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetDigestEnabled() *bool { + return backing.DigestEnabled +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetDigestEnabled(digestEnabled *bool) { + backing.DigestEnabled = digestEnabled +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetDeltaDiskFormatVariant() string { + return backing.DeltaDiskFormatVariant + +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetDeltaDiskFormatVariant(deltaDiskFormatVariant string) { + backing.DeltaDiskFormatVariant = deltaDiskFormatVariant +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetDeltaGrainSize() int32 { + return backing.DeltaGrainSize +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetDeltaGrainSize(size int32) { + backing.DeltaGrainSize = size +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetSharing() string { + return backing.Sharing +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetSharing(sharing string) { + backing.Sharing = sharing +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetKeyId() *types.CryptoKeyId { + return backing.KeyId +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetKeyId(keyId *types.CryptoKeyId) { + backing.KeyId = keyId +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetLunUuid() string { + return "" +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetLunUuid(string) { + //no op +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetDeviceName() string { + return "" +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetDeviceName(string) { + //no op +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetCompatibilityMode() string { + return "" +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetCompatibilityMode(string) { + //no op +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetDatastore() *types.ManagedObjectReference { + return backing.Datastore +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetDatastore(mor *types.ManagedObjectReference) { + backing.Datastore = mor +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetFileName() string { + return backing.FileName +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetFileName(fileName string) { + backing.FileName = fileName +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) GetBackingObjectId() string { + return backing.BackingObjectId +} + +func (backing TerraformVirtualDiskFlatVer2BackingInfo) SetBackingObjectId(id string) { + backing.BackingObjectId = id +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetDiskMode() string { + return backing.DiskMode +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetDiskMode(diskMode string) { + backing.DiskMode = diskMode +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetSplit() *bool { + return nil +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetSplit(split *bool) { + //no op +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetWriteThrough() *bool { + return nil +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetWriteThrough(writeThrough *bool) { + //no op +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetThinProvisioned() *bool { + return nil +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetThinProvisioned(thinProvisioned *bool) { + //no op +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetEagerlyScrub() *bool { + return nil +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetEagerlyScrub(eagerlyScrub *bool) { + //no op +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetUuid() string { + return backing.LunUuid +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetContentId() string { + return backing.ContentId +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetContentId(id string) { + backing.ContentId = id +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetChangeId() string { + return backing.ChangeId +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetChangeId(id string) { + backing.ChangeId = id +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetParent() interface{} { + return backing.Parent +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetParent(i interface{}) { + backing.Parent = i.(*types.VirtualDiskRawDiskMappingVer1BackingInfo) +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetDeltaDiskFormat() string { + return backing.DeltaDiskFormat +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetDeltaDiskFormat(format string) { + backing.DeltaDiskFormat = format +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetDigestEnabled() *bool { + return nil +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetDigestEnabled(*bool) { + //no op +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetDeltaDiskFormatVariant() string { + return "" + +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetDeltaDiskFormatVariant(string) { + //no op +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetDeltaGrainSize() int32 { + return backing.DeltaGrainSize +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetDeltaGrainSize(size int32) { + backing.DeltaGrainSize = size +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetSharing() string { + return backing.Sharing +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetSharing(sharing string) { + backing.Sharing = sharing +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetKeyId() *types.CryptoKeyId { + return nil +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetKeyId(*types.CryptoKeyId) { + //no op +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetLunUuid() string { + return backing.LunUuid +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetLunUuid(lunUuid string) { + backing.LunUuid = lunUuid +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetDeviceName() string { + return backing.DeviceName +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetDeviceName(deviceName string) { + backing.DeviceName = deviceName +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetCompatibilityMode() string { + return backing.CompatibilityMode +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetCompatibilityMode(compatibilityMode string) { + backing.CompatibilityMode = compatibilityMode +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetDatastore() *types.ManagedObjectReference { + return backing.Datastore +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetDatastore(mor *types.ManagedObjectReference) { + backing.Datastore = mor +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetFileName() string { + return backing.FileName +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetFileName(fileName string) { + backing.FileName = fileName +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) GetBackingObjectId() string { + return backing.BackingObjectId +} + +func (backing TerraformVirtualDiskRawDiskMappingVer1BackingInfo) SetBackingObjectId(id string) { + backing.BackingObjectId = id +} diff --git a/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go b/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go index 460454696..a618e3632 100644 --- a/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go +++ b/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go @@ -56,6 +56,11 @@ var diskSubresourceSharingAllowedValues = []string{ string(types.VirtualDiskSharingSharingMultiWriter), } +var diskSubresourceCompatibilityModeAllowedValues = []string{ + string(types.VirtualDiskCompatibilityModeVirtualMode), + string(types.VirtualDiskCompatibilityModePhysicalMode), +} + // DiskSubresourceSchema represents the schema for the disk sub-resource. func DiskSubresourceSchema() map[string]*schema.Schema { s := map[string]*schema.Schema{ @@ -200,6 +205,19 @@ func DiskSubresourceSchema() map[string]*schema.Schema { Optional: true, Description: "The type of controller the disk should be connected to. Must be 'scsi', 'sata', or 'ide'.", }, + "rdm_lun_path": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"compatibility_mode"}, + Description: "The path to the LUN to be used for RDM disk.", + }, + "compatibility_mode": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"rdm_lun_path"}, + ValidateFunc: validation.StringInSlice(diskSubresourceCompatibilityModeAllowedValues, false), + Description: "Compatibility mode for RDM disk.", + }, } structure.MergeSchema(s, subresourceSchema()) return s @@ -1097,9 +1115,9 @@ func DiskImportOperation(d *schema.ResourceData, l object.VirtualDeviceList) err // this is a VMDK-backed virtual disk to make sure we aren't importing RDM // disks or what not. The device should have already been validated as a // virtual disk via SelectDisks. - if _, ok := device.(*types.VirtualDisk).Backing.(*types.VirtualDiskFlatVer2BackingInfo); !ok { + if _, ok := GetBackingForDisk(device.(*types.VirtualDisk)); !ok { return fmt.Errorf( - "disk.%d: unsupported disk type at %s (expected flat VMDK version 2, got %T)", + "disk.%d: unsupported disk type at %s (expected flat VMDK version 2 or RDM version 1, got %T)", i, addr, device.(*types.VirtualDisk).Backing, @@ -1150,17 +1168,17 @@ func ReadDiskAttrsForDataSource(l object.VirtualDeviceList, d *schema.ResourceDa var out []map[string]interface{} for i, device := range devices { disk := device.(*types.VirtualDisk) - backing, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) + backing, ok := GetBackingForDisk(disk) if !ok { - return nil, fmt.Errorf("disk number %d has an unsupported backing type (expected flat VMDK version 2, got %T)", i, disk.Backing) + return nil, fmt.Errorf("disk number %d has an unsupported backing type (expected flat VMDK version 2 or RDM version 1, got %T)", i, disk.Backing) } m := make(map[string]interface{}) var eager, thin bool - if backing.EagerlyScrub != nil { - eager = *backing.EagerlyScrub + if backing.GetEagerlyScrub() != nil { + eager = *backing.GetEagerlyScrub() } - if backing.ThinProvisioned != nil { - thin = *backing.ThinProvisioned + if backing.GetThinProvisioned() != nil { + thin = *backing.GetThinProvisioned() } if di, ok := disk.DeviceInfo.(*types.Description); ok { m["label"] = di.Label @@ -1257,13 +1275,14 @@ func (r *DiskSubresource) Read(l object.VirtualDeviceList) error { attach = r.Get("attach").(bool) } // Save disk backing settings - b, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) + b, ok := GetBackingForDisk(disk) if !ok { return fmt.Errorf("disk backing at %s is of an unsupported type (type %T)", r.Get("device_address").(string), disk.Backing) } - r.Set("uuid", b.Uuid) - r.Set("disk_mode", b.DiskMode) - r.Set("write_through", b.WriteThrough) + r.Set("uuid", b.GetUuid()) + r.Set("disk_mode", b.GetDiskMode()) + r.Set("write_through", b.GetWriteThrough()) + r.Set("compatibility_mode", b.GetCompatibilityMode()) // Only use disk_sharing if we are on vSphere 6.0 and higher. In addition, // skip if the value is unset - this prevents spurious diffs during upgrade @@ -1271,21 +1290,21 @@ func (r *DiskSubresource) Read(l object.VirtualDeviceList) error { // sharing. In this situation, the value will be blank, and setting it will // actually result in an error. version := viapi.ParseVersionFromClient(r.client) - if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) && b.Sharing != "" { - r.Set("disk_sharing", b.Sharing) + if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) && b.GetSharing() != "" { + r.Set("disk_sharing", b.GetSharing()) } if !attach { - r.Set("thin_provisioned", b.ThinProvisioned) - r.Set("eagerly_scrub", b.EagerlyScrub) + r.Set("thin_provisioned", b.GetThinProvisioned()) + r.Set("eagerly_scrub", b.GetEagerlyScrub()) } - r.Set("datastore_id", b.Datastore.Value) + r.Set("datastore_id", b.GetDatastore().Value) // Disk settings if !attach { dp := &object.DatastorePath{} - if ok := dp.FromString(b.FileName); !ok { - return fmt.Errorf("could not parse path from filename: %s", b.FileName) + if ok := dp.FromString(b.GetFileName()); !ok { + return fmt.Errorf("could not parse path from filename: %s", b.GetFileName()) } r.Set("path", dp.Path) r.Set("size", diskCapacityInGiB(disk)) @@ -1545,6 +1564,11 @@ func (r *DiskSubresource) DiffGeneral() error { if r.Get("eagerly_scrub").(bool) && r.Get("thin_provisioned").(bool) { return fmt.Errorf("%s: eagerly_scrub and thin_provisioned cannot both be set to true", name) } + + if r.Get("compatibility_mode").(string) == string(types.VirtualDiskCompatibilityModePhysicalMode) && r.Get("disk_mode").(string) != string(types.VirtualDiskModeIndependent_persistent) { + return fmt.Errorf("%s: To add RDM Disks in physical compatibility mode, only independent persistent disk mode is supported", name) + } + log.Printf("[DEBUG] %s: Diff validation complete", r) return nil } @@ -1671,10 +1695,14 @@ func (r *DiskSubresource) Relocate(l object.VirtualDeviceList, clone bool) (type if r.rdd.Id() == "" { log.Printf("[DEBUG] %s: Adding additional options to relocator for cloning", r) - backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) - backing.FileName = ds.Path("") - backing.Datastore = &dsref - relocate.DiskBackingInfo = backing + backing, ok := GetBackingForDisk(disk) + if !ok { + return relocate, fmt.Errorf("disk has an unsupported backing type (expected flat VMDK version 2 or RDM version 1, got %T)", disk.Backing) + } + targetBacking := ToVirtualDiskFlatVer2BackingInfo(backing) + targetBacking.FileName = ds.Path("") + targetBacking.Datastore = &dsref + relocate.DiskBackingInfo = &targetBacking } // Attach the SPBM storage policy if specified @@ -1704,14 +1732,19 @@ func (r *DiskSubresource) String() string { // configuration. func (r *DiskSubresource) expandDiskSettings(disk *types.VirtualDisk) error { // Backing settings - b := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) - b.DiskMode = r.GetWithRestart("disk_mode").(string) - b.WriteThrough = structure.BoolPtr(r.GetWithRestart("write_through").(bool)) + b, ok := GetBackingForDisk(disk) + if !ok { + return fmt.Errorf("disk has an unsupported backing type (expected flat VMDK version 2 or RDM version 1, got %T)", disk.Backing) + } + b.SetDiskMode(r.GetWithRestart("disk_mode").(string)) + b.SetWriteThrough(structure.BoolPtr(r.GetWithRestart("write_through").(bool))) + b.SetDeviceName(r.Get("rdm_lun_path").(string)) + b.SetCompatibilityMode(r.Get("compatibility_mode").(string)) // Only use disk_sharing if we are on vSphere 6.0 and higher version := viapi.ParseVersionFromClient(r.client) if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) { - b.Sharing = r.GetWithRestart("disk_sharing").(string) + b.SetSharing(r.GetWithRestart("disk_sharing").(string)) } // This settings are only set for internal disks @@ -1721,12 +1754,12 @@ func (r *DiskSubresource) expandDiskSettings(disk *types.VirtualDisk) error { if v, err = r.GetWithVeto("thin_provisioned"); err != nil { return err } - b.ThinProvisioned = structure.BoolPtr(v.(bool)) + b.SetThinProvisioned(structure.BoolPtr(v.(bool))) if v, err = r.GetWithVeto("eagerly_scrub"); err != nil { return err } - b.EagerlyScrub = structure.BoolPtr(v.(bool)) + b.SetEagerlyScrub(structure.BoolPtr(v.(bool))) // Disk settings os, ns := r.GetChange("size") @@ -1753,7 +1786,11 @@ func (r *DiskSubresource) expandDiskSettings(disk *types.VirtualDisk) error { // createDisk performs all of the logic for a base virtual disk creation. func (r *DiskSubresource) createDisk(l object.VirtualDeviceList) (*types.VirtualDisk, error) { disk := new(types.VirtualDisk) - disk.Backing = new(types.VirtualDiskFlatVer2BackingInfo) + if r.Get("rdm_lun_path").(string) == "" { + disk.Backing = new(types.VirtualDiskFlatVer2BackingInfo) + } else { + disk.Backing = new(types.VirtualDiskRawDiskMappingVer1BackingInfo) + } // Only assign backing info if a datastore cluster is not specified. If one // is, skip this step. @@ -1804,9 +1841,12 @@ func (r *DiskSubresource) assignBackingInfo(disk *types.VirtualDisk) error { diskName = getDiskPath(r.data) } - backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) - backing.FileName = ds.Path(diskName) - backing.Datastore = &dsref + backing, ok := GetBackingForDisk(disk) + if !ok { + return fmt.Errorf("disk has an unsupported backing type (expected flat VMDK version 2 or RDM version 1, got %T)", disk.Backing) + } + backing.SetFileName(ds.Path(diskName)) + backing.SetDatastore(&dsref) return nil } @@ -1979,8 +2019,8 @@ func diskRelocateListString(relocators []types.VirtualMachineRelocateSpecDiskLoc func diskRelocateString(relocate types.VirtualMachineRelocateSpecDiskLocator) string { key := relocate.DiskId var locstring string - if backing, ok := relocate.DiskBackingInfo.(*types.VirtualDiskFlatVer2BackingInfo); ok && backing != nil { - locstring = backing.FileName + if backing, ok := GetBacking(relocate.DiskBackingInfo); ok && backing != nil { + locstring = backing.GetFileName() } else { locstring = relocate.Datastore.Value } @@ -2145,11 +2185,11 @@ func diskUUIDMatch(device types.BaseVirtualDevice, uuid string) bool { if !ok { return false } - backing, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) + backing, ok := GetBackingForDisk(disk) if !ok { return false } - if backing.Uuid != uuid { + if backing.GetUuid() != uuid { return false } return true diff --git a/vsphere/resource_vsphere_virtual_machine_test.go b/vsphere/resource_vsphere_virtual_machine_test.go index 55578730a..636a20000 100644 --- a/vsphere/resource_vsphere_virtual_machine_test.go +++ b/vsphere/resource_vsphere_virtual_machine_test.go @@ -540,6 +540,32 @@ func TestAccResourceVSphereVirtualMachine_highDiskUnitsToRegularSingleController }) } +func TestAccResourceVSphereVirtualMachine_RDMDisk(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { + RunSweepers() + testAccPreCheck(t) + testAccResourceVSphereVirtualMachinePreCheck(t) + if os.Getenv("TF_VAR_VSPHERE_RDM_DISK_LUN_PATH") == "" { + t.Skip("set TF_VAR_VSPHERE_RDM_DISK_LUN_PATH to run vsphere_virtual_machine RDM tests") + } + }, + Providers: testAccProviders, + CheckDestroy: testAccResourceVSphereVirtualMachineCheckExists(false), + Steps: []resource.TestStep{ + { + Config: testAccResourceVSphereVirtualMachineConfigRDMDisk(), + Check: resource.ComposeTestCheckFunc( + testAccResourceVSphereVirtualMachineCheckExists(true), + testAccResourceVSphereVirtualMachineCheckDiskBus("testacc-test.vmdk", 0, 0), + testAccResourceVSphereVirtualMachineCheckDiskBus("testacc-test_1.vmdk", 0, 1), + testAccResourceVSphereVirtualMachineCheckRDMDiskAtNumber(1), + ), + }, + }, + }) +} + func TestAccResourceVSphereVirtualMachine_scsiBusSharing(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { @@ -2607,10 +2633,10 @@ func testAccResourceVSphereVirtualMachineCheckDiskBus(name string, expectedBus, for _, dev := range props.Config.Hardware.Device { if disk, ok := dev.(*types.VirtualDisk); ok { - if info, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok { + if info, ok := virtualdevice.GetBackingForDisk(disk); ok { dp := new(object.DatastorePath) - if ok := dp.FromString(info.FileName); !ok { - return fmt.Errorf("could not parse datastore path %q", info.FileName) + if ok := dp.FromString(info.GetFileName()); !ok { + return fmt.Errorf("could not parse datastore path %q", info.GetFileName()) } if path.Base(dp.Path) != name { continue @@ -2642,6 +2668,32 @@ func testAccResourceVSphereVirtualMachineCheckDiskBus(name string, expectedBus, } } +func testAccResourceVSphereVirtualMachineCheckRDMDiskAtNumber(diskNumber int) resource.TestCheckFunc { + return func(s *terraform.State) error { + props, err := testGetVirtualMachineProperties(s, "vm") + if err != nil { + return err + } + + currIndex := 0 + for _, dev := range props.Config.Hardware.Device { + if disk, ok := dev.(*types.VirtualDisk); ok { + if currIndex == diskNumber { + switch disk.Backing.(type) { + case *types.VirtualDiskRawDiskMappingVer1BackingInfo: + return nil + default: + return fmt.Errorf("incorrect disk backing found, expected RDM disk, found %s", disk.Backing) + } + } + currIndex++ + } + } + + return fmt.Errorf("could not find RDM disk") + } +} + // testAccResourceVSphereVirtualMachineCheckFolder checks to make sure a // virtual machine's folder matches the folder supplied with expected. func testAccResourceVSphereVirtualMachineCheckFolder(expected string) resource.TestCheckFunc { @@ -3696,6 +3748,51 @@ resource "vsphere_virtual_machine" "vm" { ) } +func testAccResourceVSphereVirtualMachineConfigRDMDisk() string { + return fmt.Sprintf(` + + +%s // Mix and match config + +resource "vsphere_virtual_machine" "vm" { + name = "testacc-test" + resource_pool_id = data.vsphere_compute_cluster.rootcompute_cluster1.resource_pool_id + datastore_id = data.vsphere_datastore.rootds1.id + + scsi_controller_count = 3 + + num_cpus = 2 + memory = 2048 + guest_id = "other3xLinux64Guest" + wait_for_guest_ip_timeout = 0 + wait_for_guest_net_timeout = 0 + + network_interface { + network_id = data.vsphere_network.network1.id + bandwidth_share_level = "normal" + } + + disk { + label = "disk0" + size = 20 + } + + disk { + label = "disk1" + unit_number = 1 + size = 2 + disk_mode = "independent_persistent" + compatibility_mode = "physicalMode" + rdm_lun_path = "%s" + } +} +`, + + testAccResourceVSphereVirtualMachineConfigBase(), + os.Getenv("TF_VAR_VSPHERE_RDM_DISK_LUN_PATH"), + ) +} + func testAccResourceVSphereVirtualMachineConfigMultiDevice() string { return fmt.Sprintf(` diff --git a/website/docs/d/vmfs_disks.html.markdown b/website/docs/d/vmfs_disks.html.markdown index 1c2e9e56e..eaa595c48 100644 --- a/website/docs/d/vmfs_disks.html.markdown +++ b/website/docs/d/vmfs_disks.html.markdown @@ -57,3 +57,8 @@ the output `disks` attribute below, which is lexicographically sorted. * `disks` - A lexicographically sorted list of devices discovered by the operation, matching the supplied `filter`, if provided. + +* `disk_details` - List of disks discovered by the operation with more details about them. The order matches that of `disks` + * `display_name` - Display name of the disk + * `device_path` - Path of the physical volume of the disk. + * `capacity_gb` - Capacity of the disk in GiB. diff --git a/website/docs/r/virtual_machine.html.markdown b/website/docs/r/virtual_machine.html.markdown index 42110b026..c15b4f626 100644 --- a/website/docs/r/virtual_machine.html.markdown +++ b/website/docs/r/virtual_machine.html.markdown @@ -33,8 +33,7 @@ of this resource. ### Disks The `vsphere_virtual_machine` resource currently only supports standard -VMDK-backed virtual disks - it does not support other special kinds of disk -devices like RDM disks. +VMDK-backed virtual disks and RDM disks. Disks are managed by an arbitrary label supplied to the [`label`](#label) attribute of a [`disk` block](#disk-options). This is separate from the @@ -887,6 +886,14 @@ resource "vsphere_virtual_machine" "vm" { size = "100" unit_number = 1 } + + disk { + label = "disk2" + size = "10" + unit_number = 2 + rdm_lun_path = "/path/to/lun" + compatibility_mode = "physicalMode" + } # ... other configuration ... } @@ -969,6 +976,11 @@ an error if you try to label a disk with this prefix. * `controller_type` - (Optional) The type of storage controller to attach the disk to. Can be `scsi`, `sata`, or `ide`. You must have the appropriate number of controllers enabled for the selected type. Default `scsi`. +* `rdm_lun_path` - (Optional) Target LUN path when adding a RDM disk. Datasource `vsphere_vmfs_disks` can be used + to discover LUNs attached to a host. +* `compatibility_mode` - (Optional) The compatibility mode for the RDM disk. The allowed values + are `virtualMode` and `physicalMode`. When compatibility is in `physicalMode`, the disk + mode has to be `independent_persistent`. #### Computed disk attributes @@ -1002,6 +1014,9 @@ until the settings are corrected. ~> **NOTE:** The disk type cannot be changed once set. +~> **NOTE:** When adding a RDM disk, some options like `eagerly_scrub` and `thin_provisioned` are +not available and size of the disk should be the capacity of the LUN. + ### Network interface options Network interfaces are managed by adding an instance of the `network_interface`