diff --git a/vsphere/data_source_vsphere_virtual_machine.go b/vsphere/data_source_vsphere_virtual_machine.go index 04fd9d782..bbc7e3d0f 100644 --- a/vsphere/data_source_vsphere_virtual_machine.go +++ b/vsphere/data_source_vsphere_virtual_machine.go @@ -5,103 +5,99 @@ import ( "log" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/structure" "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/virtualmachine" "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/virtualdevice" "github.com/vmware/govmomi/object" ) func dataSourceVSphereVirtualMachine() *schema.Resource { - return &schema.Resource{ - Read: dataSourceVSphereVirtualMachineRead, - - Schema: map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Description: "The name or path of the virtual machine.", - Required: true, - }, - "datacenter_id": { - Type: schema.TypeString, - Description: "The managed object ID of the datacenter the virtual machine is in. This is not required when using ESXi directly, or if there is only one datacenter in your infrastructure.", - Optional: true, - }, - "scsi_controller_scan_count": { - Type: schema.TypeInt, - Description: "The number of SCSI controllers to scan for disk sizes and controller types on.", - Optional: true, - Default: 1, - }, - "sata_controller_scan_count": { - Type: schema.TypeInt, - Description: "The number of SATA controllers to scan for disk sizes and controller types on.", - Optional: true, - Default: 0, - }, - "ide_controller_scan_count": { - Type: schema.TypeInt, - Description: "The number of IDE controllers to scan for disk sizes and controller types on.", - Optional: true, - Default: 2, - }, - "guest_id": { - Type: schema.TypeString, - Description: "The guest ID of the virtual machine.", - Computed: true, - }, - "firmware": { - Type: schema.TypeString, - Description: "The firmware type for this virtual machine.", - Computed: true, - }, - "alternate_guest_name": { - Type: schema.TypeString, - Description: "The alternate guest name of the virtual machine when guest_id is a non-specific operating system, like otherGuest.", - Computed: true, - }, - "scsi_type": { - Type: schema.TypeString, - Computed: true, - Description: "The common SCSI bus type of all controllers on the virtual machine.", - }, - "scsi_bus_sharing": { - Type: schema.TypeString, - Computed: true, - Description: "Mode for sharing the SCSI bus.", - }, - "disks": { - Type: schema.TypeList, - Description: "Select configuration attributes from the disks on this virtual machine, sorted by bus and unit number.", - Computed: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "size": { - Type: schema.TypeInt, - Computed: true, - }, - "eagerly_scrub": { - Type: schema.TypeBool, - Computed: true, - }, - "thin_provisioned": { - Type: schema.TypeBool, - Computed: true, - }, + s := map[string]*schema.Schema{ + "datacenter_id": { + Type: schema.TypeString, + Description: "The managed object ID of the datacenter the virtual machine is in. This is not required when using ESXi directly, or if there is only one datacenter in your infrastructure.", + Optional: true, + }, + "scsi_controller_scan_count": { + Type: schema.TypeInt, + Description: "The number of SCSI controllers to scan for disk sizes and controller types on.", + Optional: true, + Default: 1, + }, + "sata_controller_scan_count": { + Type: schema.TypeInt, + Description: "The number of SATA controllers to scan for disk sizes and controller types on.", + Optional: true, + Default: 0, + }, + "ide_controller_scan_count": { + Type: schema.TypeInt, + Description: "The number of IDE controllers to scan for disk sizes and controller types on.", + Optional: true, + Default: 2, + }, + "scsi_type": { + Type: schema.TypeString, + Computed: true, + Description: "The common SCSI bus type of all controllers on the virtual machine.", + }, + "scsi_bus_sharing": { + Type: schema.TypeString, + Computed: true, + Description: "Mode for sharing the SCSI bus.", + }, + "disks": { + Type: schema.TypeList, + Description: "Select configuration attributes from the disks on this virtual machine, sorted by bus and unit number.", + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "size": { + Type: schema.TypeInt, + Computed: true, + }, + "eagerly_scrub": { + Type: schema.TypeBool, + Computed: true, + }, + "thin_provisioned": { + Type: schema.TypeBool, + Computed: true, + }, + "label": { + Type: schema.TypeString, + Computed: true, + }, + "unit_number": { + Type: schema.TypeInt, + Computed: true, }, }, }, - "network_interface_types": { - Type: schema.TypeList, - Description: "The types of network interfaces found on the virtual machine, sorted by unit number.", - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, - "guest_ip_addresses": { - Type: schema.TypeList, - Description: "The current list of IP addresses on this virtual machine.", - Computed: true, - Elem: &schema.Schema{Type: schema.TypeString}, - }, }, + "network_interface_types": { + Type: schema.TypeList, + Description: "The types of network interfaces found on the virtual machine, sorted by unit number.", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "guest_ip_addresses": { + Type: schema.TypeList, + Description: "The current list of IP addresses on this virtual machine.", + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + } + + // Merge the VirtualMachineConfig structure so that we can include the number of + // include the number of cpus, memory, firmware, disks, etc. + structure.MergeSchema(s, schemaVirtualMachineConfigSpec()) + + // Now that the schema has been composed and merged, we can attach our reader and + // return the resource back to our host process. + return &schema.Resource{ + Read: dataSourceVSphereVirtualMachineRead, + Schema: s, } } @@ -136,6 +132,11 @@ func dataSourceVSphereVirtualMachineRead(d *schema.ResourceData, meta interface{ return fmt.Errorf("virtual machine %q does not have a UUID", vm.InventoryPath) } + // Read general VM config info + if err := flattenVirtualMachineConfigInfo(d, props.Config); err != nil { + return fmt.Errorf("error reading virtual machine configuration: %s", err) + } + d.SetId(props.Config.Uuid) d.Set("guest_id", props.Config.GuestId) d.Set("alternate_guest_name", props.Config.AlternateGuestName) diff --git a/vsphere/data_source_vsphere_virtual_machine_test.go b/vsphere/data_source_vsphere_virtual_machine_test.go index 526854d8b..50b6f31e5 100644 --- a/vsphere/data_source_vsphere_virtual_machine_test.go +++ b/vsphere/data_source_vsphere_virtual_machine_test.go @@ -26,10 +26,17 @@ func TestAccDataSourceVSphereVirtualMachine_basic(t *testing.T) { regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "guest_id"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "scsi_type"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "memory"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "num_cpus"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "num_cores_per_socket"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "firmware"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "hardware_version"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.#"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.size"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.eagerly_scrub"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.thin_provisioned"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.unit_number"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.label"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "network_interface_types.#"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "firmware"), ), @@ -55,10 +62,17 @@ func TestAccDataSourceVSphereVirtualMachine_noDatacenterAndAbsolutePath(t *testi regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "guest_id"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "scsi_type"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "memory"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "num_cpus"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "num_cores_per_socket"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "firmware"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "hardware_version"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.#"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.size"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.eagerly_scrub"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.thin_provisioned"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.unit_number"), + resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "disks.0.label"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "network_interface_types.#"), resource.TestCheckResourceAttrSet("data.vsphere_virtual_machine.template", "firmware"), ), diff --git a/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go b/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go index b90109a50..115e0a183 100644 --- a/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go +++ b/vsphere/internal/virtualdevice/virtual_machine_disk_subresource.go @@ -1186,9 +1186,17 @@ func ReadDiskAttrsForDataSource(l object.VirtualDeviceList, d *schema.ResourceDa if backing.ThinProvisioned != nil { thin = *backing.ThinProvisioned } + if di, ok := disk.DeviceInfo.(*types.Description); ok { + m["label"] = di.Label + } else { + m["label"] = fmt.Sprintf("Disk %d", i) + } + m["size"] = diskCapacityInGiB(disk) m["eagerly_scrub"] = eager m["thin_provisioned"] = thin + m["unit_number"] = disk.UnitNumber + out = append(out, m) } log.Printf("[DEBUG] ReadDiskAttrsForDataSource: Attributes returned: %+v", out) @@ -1486,6 +1494,12 @@ func (r *DiskSubresource) DiffExisting() error { // Ensure that there is no change in either eagerly_scrub or thin_provisioned // - these values cannot be changed once set. + if _, err = r.GetWithVeto("label"); err != nil { + return fmt.Errorf("virtual disk %q: %s", name, err) + } + if _, err = r.GetWithVeto("unit_number"); err != nil { + return fmt.Errorf("virtual disk %q: %s", name, err) + } if _, err = r.GetWithVeto("eagerly_scrub"); err != nil { return fmt.Errorf("virtual disk %q: %s", name, err) } @@ -1548,6 +1562,8 @@ func (r *DiskSubresource) DiffGeneral() error { switch { case r.Get("datastore_id").(string) == "": return fmt.Errorf("datastore_id for disk %q is required when attach is set", name) + case r.Get("label").(int) > 0: + return fmt.Errorf("label for disk %q cannot be defined when attach is set", name) case r.Get("size").(int) > 0: return fmt.Errorf("size for disk %q cannot be defined when attach is set", name) case r.Get("eagerly_scrub").(bool): diff --git a/vsphere/virtual_machine_config_structure.go b/vsphere/virtual_machine_config_structure.go index c2d3fce2b..00a986429 100644 --- a/vsphere/virtual_machine_config_structure.go +++ b/vsphere/virtual_machine_config_structure.go @@ -220,9 +220,15 @@ func schemaVirtualMachineConfigSpec() map[string]*schema.Schema { Default: 1024, Description: "The size of the virtual machine's memory, in MB.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if len(d.Get("ovf_deploy").([]interface{})) > 0 { + ovf, ok := d.GetOk("ovf_deploy") + if !ok { + return false + } + + if items, ok := ovf.([]interface{}); ok && len(items) > 0 { return true } + return false }, }, @@ -243,9 +249,15 @@ func schemaVirtualMachineConfigSpec() map[string]*schema.Schema { Optional: true, Description: "User-provided description of the virtual machine.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if len(d.Get("ovf_deploy").([]interface{})) > 0 && new == "" { + ovf, ok := d.GetOk("ovf_deploy") + if !ok { + return false + } + + if items, ok := ovf.([]interface{}); ok && len(items) > 0 && new == "" { return true } + return false }, }, @@ -255,9 +267,15 @@ func schemaVirtualMachineConfigSpec() map[string]*schema.Schema { Computed: true, Description: "The guest ID for the operating system.", DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if len(d.Get("ovf_deploy").([]interface{})) > 0 { + ovf, ok := d.GetOk("ovf_deploy") + if !ok { + return false + } + + if items, ok := ovf.([]interface{}); ok && len(items) > 0 && new == "" { return true } + return false }, }, diff --git a/website/docs/d/virtual_machine.html.markdown b/website/docs/d/virtual_machine.html.markdown index cb5059fb0..a83a8c75a 100644 --- a/website/docs/d/virtual_machine.html.markdown +++ b/website/docs/d/virtual_machine.html.markdown @@ -63,6 +63,14 @@ The following attributes are exported: * `guest_id` - The guest ID of the virtual machine or template. * `alternate_guest_name` - The alternate guest name of the virtual machine when guest_id is a non-specific operating system, like `otherGuest`. +* `annotation` - The user-provided description of this virtual machine. +* `memory` - The size of the virtual machine's memory, in MB. +* `num_cpus` - The total number of virtual processor cores assigned to this + virtual machine. +* `num_cores_per_socket` - The number of cores per socket for this virtual machine. +* `firmware` - The firmware interface that is used by this virtual machine. Can be + either `bios` or `EFI`. +* `hardware_version` - The hardware version number on this virtual machine. * `scsi_type` - The common type of all SCSI controllers on this virtual machine. Will be one of `lsilogic` (LSI Logic Parallel), `lsilogic-sas` (LSI Logic SAS), `pvscsi` (VMware Paravirtual), `buslogic` (BusLogic), or `mixed` when @@ -79,9 +87,11 @@ The following attributes are exported: source must be the same on the destination virtual machine as the source. Only the first number of controllers defined by `scsi_controller_scan_count` are scanned for disks. The sub-attributes are: + * `label` - The label for the disk. * `size` - The size of the disk, in GIB. * `eagerly_scrub` - Set to `true` if the disk has been eager zeroed. * `thin_provisioned` - Set to `true` if the disk has been thin provisioned. + * `unit_number` - The disk number on the storage bus. * `network_interface_types` - The network interface types for each network interface found on the virtual machine, in device bus order. Will be one of `e1000`, `e1000e`, `pcnet32`, `sriov`, `vmxnet2`, or `vmxnet3`.