Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #78: Cannot specify target container when adding disks to a virtual machine resource #138

Merged
merged 4 commits into from
Jun 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions client/v3/v3_structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,20 @@ type VMDiskDeviceProperties struct {
DiskAddress *DiskAddress `json:"disk_address,omitempty" mapstructure:"disk_address,omitempty"`
}

// StorageContainerReference references to a kind. Either one of (kind, uuid) or url needs to be specified.
type StorageContainerReference struct {
URL string `json:"url,omitempty"`
Kind string `json:"kind,omitempty"`
UUID string `json:"uuid,omitempty"`
Name string `json:"name,omitempty"`
}

// VMStorageConfig specifies the storage configuration parameters for VM disks.
type VMStorageConfig struct {
FlashMode string `json:"flash_mode,omitempty"`
StorageContainerReference *StorageContainerReference `json:"storage_container_reference,omitempty"`
}

// VMDisk VirtualMachine Disk (VM Disk).
type VMDisk struct {
DataSourceReference *Reference `json:"data_source_reference,omitempty" mapstructure:"data_source_reference,omitempty"`
Expand All @@ -212,6 +226,9 @@ type VMDisk struct {
UUID *string `json:"uuid,omitempty" mapstructure:"uuid,omitempty"`

VolumeGroupReference *Reference `json:"volume_group_reference,omitempty" mapstructure:"volume_group_reference,omitempty"`

// This preference specifies the storage configuration parameters for VM disks.
StorageConfig *VMStorageConfig `json:"storage_config,omitempty" mapstructure:"storage_config,omitempty"`
}

// VMResources VM Resources Definition.
Expand Down
43 changes: 41 additions & 2 deletions nutanix/data_source_nutanix_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,43 @@ func dataSourceNutanixVirtualMachine() *schema.Resource {
Type: schema.TypeInt,
Computed: true,
},
"storage_config": {
Type: schema.TypeList,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"flash_mode": {
Type: schema.TypeString,
Computed: true,
},
"storage_container_reference": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"url": {
Type: schema.TypeString,
Computed: true,
},
"kind": {
Type: schema.TypeString,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"uuid": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
},
},
"device_properties": {
Type: schema.TypeList,
Computed: true,
Expand Down Expand Up @@ -570,7 +607,6 @@ func dataSourceNutanixVirtualMachine() *schema.Resource {
},
},
},

"volume_group_reference": {
Type: schema.TypeMap,
Computed: true,
Expand Down Expand Up @@ -665,6 +701,9 @@ func dataSourceNutanixVirtualMachineRead(d *schema.ResourceData, meta interface{
if err := d.Set("parent_reference", flattenReferenceValues(resp.Status.Resources.ParentReference)); err != nil {
return err
}
if err := d.Set("disk_list", flattenDiskList(resp.Status.Resources.DiskList)); err != nil {
return err
}

diskAddress := make(map[string]interface{})
mac := ""
Expand Down Expand Up @@ -753,7 +792,7 @@ func dataSourceNutanixVirtualMachineRead(d *schema.ResourceData, meta interface{
d.Set("vga_console_enabled", utils.BoolValue(resp.Status.Resources.VgaConsoleEnabled))
d.SetId(utils.StringValue(resp.Metadata.UUID))

return d.Set("disk_list", setDiskList(resp.Status.Resources.DiskList, resp.Status.Resources.GuestCustomization))
return nil
}

func resourceDatasourceVirtualMachineInstanceStateUpgradeV0(is map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
Expand Down
72 changes: 68 additions & 4 deletions nutanix/data_source_nutanix_virtual_machine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,70 @@ func TestAccNutanixVirtualMachineDataSource_WithDisk(t *testing.T) {
})
}

func TestAccNutanixVirtualMachineDataSource_withDiskContainer(t *testing.T) {
datasourceName := "data.nutanix_virtual_machine.nutanix_virtual_machine"
vmName := acctest.RandomWithPrefix("test-dou-vm")

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
Config: testAccVMDataSourceWithDiskContainer(vmName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet(datasourceName, "vm_id"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.#"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.0.disk_size_bytes"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.0.disk_size_mib"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.0.storage_config.#"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.0.storage_config.0.storage_container_reference.#"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.0.storage_config.0.storage_container_reference.0.kind"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.0.storage_config.0.storage_container_reference.0.uuid"),
resource.TestCheckResourceAttrSet(datasourceName, "disk_list.0.storage_config.0.storage_container_reference.0.name"),
),
},
},
})
}

func testAccVMDataSourceWithDiskContainer(vmName string) string {
return fmt.Sprintf(`
data "nutanix_clusters" "clusters" {}

locals {
cluster1 = [
for cluster in data.nutanix_clusters.clusters.entities :
cluster.metadata.uuid if cluster.service_list[0] != "PRISM_CENTRAL"
][0]
}

resource "nutanix_virtual_machine" "vm-disk" {
name = "%s"
cluster_uuid = local.cluster1
num_vcpus_per_socket = 1
num_sockets = 1
memory_size_mib = 186

disk_list {
# disk_size_mib = 300
disk_size_bytes = 68157440
disk_size_mib = 65

storage_config {
storage_container_reference {
kind = "storage_container"
uuid = "2bbe77bc-fd14-4697-8de1-6369757f9219"
}
}
}
}

data "nutanix_virtual_machine" "nutanix_virtual_machine" {
vm_id = nutanix_virtual_machine.vm-disk.id
}
`, vmName)
}

func testAccVMDataSourceConfig(r int) string {
return fmt.Sprintf(`
data "nutanix_clusters" "clusters" {}
Expand Down Expand Up @@ -72,25 +136,25 @@ data "nutanix_virtual_machine" "nutanix_virtual_machine" {
func testAccVMDataSourceConfigWithDisk(r int) string {
return fmt.Sprintf(`
data "nutanix_clusters" "clusters" {}

locals {
cluster1 = "${data.nutanix_clusters.clusters.entities.0.service_list.0 == "PRISM_CENTRAL"
? data.nutanix_clusters.clusters.entities.1.metadata.uuid : data.nutanix_clusters.clusters.entities.0.metadata.uuid}"
}

resource "nutanix_image" "cirros-034-disk" {
name = "test-image-dou-vm-create-%[1]d"
source_uri = "http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img"
description = "heres a tiny linux image, not an iso, but a real disk!"
}

resource "nutanix_virtual_machine" "vm1" {
name = "test-dou-vm-%[1]d"
cluster_uuid = "${local.cluster1}"
num_vcpus_per_socket = 1
num_sockets = 1
memory_size_mib = 186

disk_list {
data_source_reference = {
kind = "image"
Expand Down
119 changes: 96 additions & 23 deletions nutanix/resource_nutanix_virtual_machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"strings"
"time"

"github.com/spf13/cast"
v3 "github.com/terraform-providers/terraform-provider-nutanix/client/v3"
"github.com/terraform-providers/terraform-provider-nutanix/utils"

Expand Down Expand Up @@ -662,6 +663,46 @@ func resourceNutanixVirtualMachine() *schema.Resource {
Optional: true,
Computed: true,
},
"storage_config": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"flash_mode": {
Type: schema.TypeString,
Optional: true,
},
"storage_container_reference": {
Type: schema.TypeList,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"url": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},
"kind": {
Type: schema.TypeString,
Optional: true,
Default: "storage_container",
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"uuid": {
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
},
},
"device_properties": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -1694,59 +1735,49 @@ func expandDiskList(d *schema.ResourceData, isCreation bool) ([]*v3.VMDisk, erro
dsk := v.([]interface{})
if len(dsk) > 0 {
dls := make([]*v3.VMDisk, len(dsk))

for k, val := range dsk {
hasDSRef := false
v := val.(map[string]interface{})
dl := &v3.VMDisk{}

// uuid
if v1, ok1 := v["uuid"]; ok1 && v1.(string) != "" {
dl.UUID = utils.StringPtr(v1.(string))
}
// storage_config
if v, ok1 := v["storage_config"]; ok1 {
dl.StorageConfig = expandStorageConfig(v.([]interface{}))
}
// device_properties
if v1, ok1 := v["device_properties"]; ok1 {
dvp := v1.([]interface{})
if len(dvp) > 0 {
d := dvp[0].(map[string]interface{})
dp := &v3.VMDiskDeviceProperties{}
if v1, ok := d["device_type"]; ok {
dp.DeviceType = utils.StringPtr(v1.(string))
}
if v2, ok := d["disk_address"]; ok && len(v2.(map[string]interface{})) > 0 {
da := v2.(map[string]interface{})
v3disk := &v3.DiskAddress{}
if di, diok := da["device_index"]; diok {
index, _ := strconv.Atoi(di.(string))
v3disk.DeviceIndex = utils.Int64Ptr(int64(index))
}
if di, diok := da["adapter_type"]; diok {
v3disk.AdapterType = utils.StringPtr(di.(string))
}
dp.DiskAddress = v3disk
}
dl.DeviceProperties = dp
}
dl.DeviceProperties = expandDeviceProperties(v1.([]interface{}))
}
// data_source_reference
if v1, ok := v["data_source_reference"]; ok && len(v1.(map[string]interface{})) != 0 {
hasDSRef = true
dsref := v1.(map[string]interface{})
dl.DataSourceReference = validateShortRef(dsref)
}
// volume_group_reference
if v1, ok := v["volume_group_reference"]; ok {
volgr := v1.(map[string]interface{})
dl.VolumeGroupReference = validateRef(volgr)
}

// disk_size_bytes
if v1, ok1 := v["disk_size_bytes"]; ok1 && v1.(int) != 0 {
if hasDSRef && isCreation {
return nil, fmt.Errorf(`"disk_list.%[1]d.disk_size_bytes": conflicts with disk_list.%[1]d.data_source_reference`, k)
}
dl.DiskSizeBytes = utils.Int64Ptr(int64(v1.(int)))
}
// disk_size_mib
if v1, ok := v["disk_size_mib"]; ok && v1.(int) != 0 {
if hasDSRef && isCreation {
return nil, fmt.Errorf(`"disk_list.%[1]d.disk_size_mib": conflicts with disk_list.%[1]d.data_source_reference`, k)
}
dl.DiskSizeMib = utils.Int64Ptr(int64(v1.(int)))
}

dls[k] = dl
}
return dls, nil
Expand All @@ -1755,6 +1786,48 @@ func expandDiskList(d *schema.ResourceData, isCreation bool) ([]*v3.VMDisk, erro
return nil, nil
}

func expandStorageConfig(storageConfig []interface{}) *v3.VMStorageConfig {
if len(storageConfig) > 0 {
v := storageConfig[0].(map[string]interface{})
scr := v["storage_container_reference"].([]interface{})[0].(map[string]interface{})

return &v3.VMStorageConfig{
FlashMode: cast.ToString(v["flash_mode"]),
StorageContainerReference: &v3.StorageContainerReference{
URL: cast.ToString(scr["url"]),
Kind: cast.ToString(scr["kind"]),
UUID: cast.ToString(scr["uuid"]),
},
}
}
return nil
}

func expandDeviceProperties(deviceProperties []interface{}) *v3.VMDiskDeviceProperties {
if len(deviceProperties) > 0 {
dp := &v3.VMDiskDeviceProperties{}
d := deviceProperties[0].(map[string]interface{})

if v, ok := d["device_type"]; ok {
dp.DeviceType = utils.StringPtr(v.(string))
}
if v, ok := d["disk_address"]; ok && len(v.(map[string]interface{})) > 0 {
da := v.(map[string]interface{})
v3disk := &v3.DiskAddress{}

if di, diOk := da["device_index"]; diOk {
v3disk.DeviceIndex = utils.Int64Ptr(cast.ToInt64(di))
}
if at, atOk := da["adapter_type"]; atOk {
v3disk.AdapterType = utils.StringPtr(at.(string))
}
dp.DiskAddress = v3disk
}
return dp
}
return nil
}

func expandSerialPortList(d *schema.ResourceData) []*v3.VMSerialPort {
if v, ok := d.GetOk("serial_port_list"); ok {
spl := v.([]interface{})
Expand Down
Loading