diff --git a/nutanix/resource_nutanix_virtual_machine.go b/nutanix/resource_nutanix_virtual_machine.go index e9f143e2b..308014eae 100644 --- a/nutanix/resource_nutanix_virtual_machine.go +++ b/nutanix/resource_nutanix_virtual_machine.go @@ -45,6 +45,11 @@ func resourceNutanixVirtualMachine() *schema.Resource { }, }, Schema: map[string]*schema.Schema{ + "cloud_init_cdrom_uuid": { + Type: schema.TypeString, + Computed: true, + Optional: true, + }, "metadata": { Type: schema.TypeMap, Computed: true, @@ -922,6 +927,8 @@ func resourceNutanixVirtualMachineRead(d *schema.ResourceData, meta interface{}) // Get client connection conn := meta.(*Client).API setVMTimeout(meta) + + var err error // Make request to the API resp, err := conn.V3.GetVM(d.Id()) @@ -940,58 +947,61 @@ func resourceNutanixVirtualMachineRead(d *schema.ResourceData, meta interface{}) return nil } - if err := flattenClusterReference(resp.Status.ClusterReference, d); err != nil { + if err = flattenClusterReference(resp.Status.ClusterReference, d); err != nil { return fmt.Errorf("error setting cluster information for Virtual Machine %s: %s", d.Id(), err) } m, c := setRSEntityMetadata(resp.Metadata) - if err := d.Set("metadata", m); err != nil { + if err = d.Set("metadata", m); err != nil { return fmt.Errorf("error setting metadata for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("categories", c); err != nil { + if err = d.Set("categories", c); err != nil { return fmt.Errorf("error setting categories for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("project_reference", flattenReferenceValues(resp.Metadata.ProjectReference)); err != nil { + if err = d.Set("project_reference", flattenReferenceValues(resp.Metadata.ProjectReference)); err != nil { return fmt.Errorf("error setting project_reference for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("owner_reference", flattenReferenceValues(resp.Metadata.OwnerReference)); err != nil { + if err = d.Set("owner_reference", flattenReferenceValues(resp.Metadata.OwnerReference)); err != nil { return fmt.Errorf("error setting owner_reference for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("availability_zone_reference", flattenReferenceValues(resp.Status.AvailabilityZoneReference)); err != nil { + if err = d.Set("availability_zone_reference", flattenReferenceValues(resp.Status.AvailabilityZoneReference)); err != nil { return fmt.Errorf("error setting availability_zone_reference for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("nic_list", flattenNicList(resp.Spec.Resources.NicList)); err != nil { + if err = d.Set("nic_list", flattenNicList(resp.Spec.Resources.NicList)); err != nil { return fmt.Errorf("error setting nic_list for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("nic_list_status", flattenNicListStatus(resp.Status.Resources.NicList)); err != nil { + if err = d.Set("nic_list_status", flattenNicListStatus(resp.Status.Resources.NicList)); err != nil { return fmt.Errorf("error setting nic_list_status for Virtual Machine %s: %s", d.Id(), err) } - - if err := d.Set("disk_list", flattenDiskList(resp.Spec.Resources.DiskList)); err != nil { + flatDiskList, err := flattenDiskListFilterCloudInit(d, resp.Spec.Resources.DiskList) + if err != nil { + return fmt.Errorf("error flattening disk list for vm %s: %s", d.Id(), err) + } + if err = d.Set("disk_list", flatDiskList); err != nil { return fmt.Errorf("error setting disk_list for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("serial_port_list", flattenSerialPortList(resp.Status.Resources.SerialPortList)); err != nil { + if err = d.Set("serial_port_list", flattenSerialPortList(resp.Status.Resources.SerialPortList)); err != nil { return fmt.Errorf("error setting serial_port_list for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("host_reference", flattenReferenceValues(resp.Status.Resources.HostReference)); err != nil { + if err = d.Set("host_reference", flattenReferenceValues(resp.Status.Resources.HostReference)); err != nil { return fmt.Errorf("error setting host_reference for Virtual Machine %s: %s", d.Id(), err) } - if err := flattenNutanixGuestTools(d, resp.Status.Resources.GuestTools); err != nil { + if err = flattenNutanixGuestTools(d, resp.Status.Resources.GuestTools); err != nil { return fmt.Errorf("error setting nutanix_guest_tools for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("gpu_list", flattenGPUList(resp.Status.Resources.GpuList)); err != nil { + if err = d.Set("gpu_list", flattenGPUList(resp.Status.Resources.GpuList)); err != nil { return fmt.Errorf("error setting gpu_list for Virtual Machine %s: %s", d.Id(), err) } - if err := d.Set("parent_reference", flattenReferenceValues(resp.Status.Resources.ParentReference)); err != nil { + if err = d.Set("parent_reference", flattenReferenceValues(resp.Status.Resources.ParentReference)); err != nil { return fmt.Errorf("error setting parent_reference for Virtual Machine %s: %s", d.Id(), err) } if uha, ok := d.GetOkExists("use_hot_add"); ok { useHotAdd = uha.(bool) } - if err := d.Set("use_hot_add", useHotAdd); err != nil { + if err = d.Set("use_hot_add", useHotAdd); err != nil { return fmt.Errorf("error setting use_hot_add for Virtual Machine %s: %s", d.Id(), err) } @@ -1019,7 +1029,7 @@ func resourceNutanixVirtualMachineRead(d *schema.ResourceData, meta interface{}) } } - if err := d.Set("boot_device_order_list", b); err != nil { + if err = d.Set("boot_device_order_list", b); err != nil { return fmt.Errorf("error setting boot_device_order_list %s", err) } @@ -1292,7 +1302,6 @@ func resourceNutanixVirtualMachineUpdate(d *schema.ResourceData, meta interface{ if err != nil { return err } - res.DiskList = expandDiskListUpdate(d, response) postCdromCount, err := CountDiskListCdrom(res.DiskList) @@ -1798,13 +1807,11 @@ func expandIPAddressList(ipl []interface{}) []*v3.IPAddress { func expandDiskListUpdate(d *schema.ResourceData, vm *v3.VMIntentResponse) []*v3.VMDisk { eDiskList := expandDiskList(d) - - if vm.Spec != nil && vm.Spec.Resources != nil { - for _, disk := range vm.Spec.Resources.DiskList { - if disk.DeviceProperties != nil && disk.DeviceProperties.DiskAddress != nil { - index := disk.DeviceProperties.DiskAddress.DeviceIndex - adapterType := disk.DeviceProperties.DiskAddress.AdapterType - if *index == 3 && *adapterType == IDE { + if cloudInitCdromUUIDInt, ok := d.GetOk("cloud_init_cdrom_uuid"); ok { + cloudInitCdromUUID := cloudInitCdromUUIDInt.(string) + if cloudInitCdromUUID != "" && vm.Spec != nil && vm.Spec.Resources != nil { + for _, disk := range vm.Spec.Resources.DiskList { + if disk.UUID != nil && *disk.UUID == cloudInitCdromUUID { eDiskList = append(eDiskList, disk) } } @@ -2120,13 +2127,25 @@ func waitForIPRefreshFunc(client *v3.Client, vmUUID string) resource.StateRefres } } -func CountDiskListCdrom(dl []*v3.VMDisk) (int, error) { - counter := 0 +func GetCdromDiskList(dl []*v3.VMDisk) []*v3.VMDisk { + cdList := make([]*v3.VMDisk, 0) for _, v := range dl { - if v.DeviceProperties != nil && *v.DeviceProperties.DeviceType == "CDROM" { - counter++ + if isCdromDisk(v) { + cdList = append(cdList, v) } } + return cdList +} + +func isCdromDisk(d *v3.VMDisk) bool { + if d.DeviceProperties != nil && *d.DeviceProperties.DeviceType == "CDROM" { + return true + } + return false +} + +func CountDiskListCdrom(dl []*v3.VMDisk) (int, error) { + counter := len(GetCdromDiskList(dl)) return counter, nil } diff --git a/nutanix/structure.go b/nutanix/structure.go index 5fa221390..2014ce25b 100644 --- a/nutanix/structure.go +++ b/nutanix/structure.go @@ -103,7 +103,99 @@ func flattenNicList(nics []*v3.VMNic) []map[string]interface{} { return nicLists } +func usesGuestCustomization(d *schema.ResourceData) bool { + keys := []string{ + "guest_customization_cloud_init_user_data", + "guest_customization_cloud_init_meta_data", + "guest_customization_cloud_init_custom_key_values", + "guest_customization_is_overridable", + "guest_customization_sysprep", + "guest_customization_sysprep_custom_key_values"} + for _, k := range keys { + if _, ok := d.GetOk(k); ok { + return true + } + } + return false +} + +func getDeviceIndexForDisk(disk *v3.VMDisk) (*int64, error) { + if disk.DeviceProperties == nil { + return nil, fmt.Errorf("deviceproperties was nil for disk") + } + if disk.DeviceProperties.DiskAddress == nil { + return nil, fmt.Errorf("disk address was nil for disk") + } + if disk.DeviceProperties.DiskAddress.DeviceIndex == nil { + return nil, fmt.Errorf("device index was nil for disk") + } + diskIndex := *disk.DeviceProperties.DiskAddress.DeviceIndex + return &diskIndex, nil +} + +func flattenDiskListFilterCloudInit(d *schema.ResourceData, disks []*v3.VMDisk) ([]map[string]interface{}, error) { + //todo check if guestcust is passed -> if it is not passed, just continue without searching for cloud-init uuid + // reason: no device_index or disk id will result in crash + cloudInitCdromUUID := "" + if cloudInitCdromUUIDInput, cliOk := d.GetOk("cloud_init_cdrom_uuid"); cliOk { + cloudInitCdromUUID = cloudInitCdromUUIDInput.(string) + } + filteredDiskList := disks + potentialCloudInitIDs := make([]string, 0) + if cloudInitCdromUUID == "" && usesGuestCustomization(d) { + filteredDiskList = make([]*v3.VMDisk, 0) + //expand the user inputted list of disks + expandedOrgDiskList := expandDiskList(d) + //extract the CD-rom drives + userCdromDiskList := GetCdromDiskList(expandedOrgDiskList) + for _, eDisk := range disks { + //if existing disk is not CD-rom, append it to the list and continue + if !isCdromDisk(eDisk) { + filteredDiskList = append(filteredDiskList, eDisk) + continue + } else { + //Get existing CDrom device Index + eDiskIndexP, err := getDeviceIndexForDisk(eDisk) //*eDisk.DeviceProperties.DiskAddress.DeviceIndex + if err != nil { + return nil, err + } + eDiskIndex := *eDiskIndexP + match := false + // Loop over the user defined cdrom drives + for _, uDisk := range userCdromDiskList { + //extract the device index of the user defined cdrom + uDiskIndexP, err := getDeviceIndexForDisk(uDisk) + if err != nil { + return nil, err + } + uDiskIndex := *uDiskIndexP + // if there is a matching device index for a userdefined and an existing cdrom, it is not the cloud-init one + if eDiskIndex == uDiskIndex { + filteredDiskList = append(filteredDiskList, eDisk) + match = true + break + } + } + if !match { + potentialCloudInitIDs = append(potentialCloudInitIDs, *eDisk.UUID) + } + } + } + if len(potentialCloudInitIDs) == 1 { + cloudInitCdromUUID = potentialCloudInitIDs[0] + d.Set("cloud_init_cdrom_uuid", cloudInitCdromUUID) + } + if len(potentialCloudInitIDs) > 1 { + return nil, fmt.Errorf("more than 1 unknown cd-rom device: %v", potentialCloudInitIDs) + } + } + fDiskList := flattenDiskListHelper(filteredDiskList, cloudInitCdromUUID) + return fDiskList, nil +} func flattenDiskList(disks []*v3.VMDisk) []map[string]interface{} { + return flattenDiskListHelper(disks, "") +} +func flattenDiskListHelper(disks []*v3.VMDisk, cloudInitCdromUUID string) []map[string]interface{} { diskList := make([]map[string]interface{}, 0) for _, v := range disks { var deviceProps []map[string]interface{} @@ -114,10 +206,6 @@ func flattenDiskList(disks []*v3.VMDisk) []map[string]interface{} { index := fmt.Sprintf("%d", utils.Int64Value(v.DeviceProperties.DiskAddress.DeviceIndex)) adapter := v.DeviceProperties.DiskAddress.AdapterType - if index == "3" && *adapter == IDE { - continue - } - deviceProps[0] = map[string]interface{}{ "device_type": v.DeviceProperties.DeviceType, "disk_address": map[string]interface{}{ @@ -141,8 +229,12 @@ func flattenDiskList(disks []*v3.VMDisk) []map[string]interface{} { }) } + diskUUID := utils.StringValue(v.UUID) + if cloudInitCdromUUID == diskUUID { + continue + } diskList = append(diskList, map[string]interface{}{ - "uuid": utils.StringValue(v.UUID), + "uuid": diskUUID, "disk_size_bytes": utils.Int64Value(v.DiskSizeBytes), "disk_size_mib": utils.Int64Value(v.DiskSizeMib), "device_properties": deviceProps,