0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-10 15:55:01 +00:00

Initial support for remote-exec provisioners

This commit is contained in:
Dan Petersen 2019-12-29 21:21:48 +01:00
parent 5d5f1c1835
commit 1dfe979e9e
8 changed files with 287 additions and 96 deletions

View File

@ -426,7 +426,9 @@ This resource doesn't expose any additional attributes.
* `vm_id` - (Optional) The ID
###### Attributes
This resource doesn't expose any additional attributes.
* `ipv4_addresses` - The IPv4 addresses per network interface published by the QEMU agent (empty list when `agent.enabled` is `false`)
* `ipv6_addresses` - The IPv6 addresses per network interface published by the QEMU agent (empty list when `agent.enabled` is `false`)
* `network_interface_names` - The network interface names published by the QEMU agent (empty list when `agent.enabled` is `false`)
## Developing the Provider
If you wish to work on the provider, you'll first need [Go](http://www.golang.org) installed on your machine (version 1.13+ is *required*). You'll also need to correctly setup a [GOPATH](http://golang.org/doc/code.html#GOPATH), as well as adding `$GOPATH/bin` to your `$PATH`.

View File

@ -8,7 +8,7 @@ resource "proxmox_virtual_environment_file" "ubuntu_cloud_image" {
}
}
resource "proxmox_virtual_environment_file" "cloud_init_config" {
resource "proxmox_virtual_environment_file" "cloud_config" {
content_type = "snippets"
datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}"
node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}"
@ -19,7 +19,7 @@ resource "proxmox_virtual_environment_file" "cloud_init_config" {
chpasswd:
list: |
ubuntu:example
expire: False
expire: false
hostname: terraform-provider-proxmox-example
packages:
- qemu-guest-agent
@ -33,7 +33,7 @@ users:
sudo: ALL=(ALL) NOPASSWD:ALL
EOF
file_name = "terraform-provider-proxmox-example-cloud-init.yaml"
file_name = "terraform-provider-proxmox-example-cloud-config.yaml"
}
}

View File

@ -16,11 +16,11 @@ resource "proxmox_virtual_environment_vm" "example" {
user_account {
keys = ["${trimspace(tls_private_key.example.public_key_openssh)}"]
password = "proxmoxtf"
password = "example"
username = "ubuntu"
}
user_data_file_id = "${proxmox_virtual_environment_file.cloud_init_config.id}"
user_data_file_id = "${proxmox_virtual_environment_file.cloud_config.id}"
}
disk {
@ -34,6 +34,20 @@ resource "proxmox_virtual_environment_vm" "example" {
os_type = "l26"
pool_id = "${proxmox_virtual_environment_pool.example.id}"
vm_id = 2038
connection {
type = "ssh"
agent = false
host = "${element(element(self.ipv4_addresses, index(self.network_interface_names, "eth0")), 0)}"
private_key = "${tls_private_key.example.private_key_pem}"
user = "ubuntu"
}
provisioner "remote-exec" {
inline = [
"echo Welcome to $(hostname)!",
]
}
}
resource "local_file" "example_ssh_private_key" {
@ -54,3 +68,15 @@ resource "tls_private_key" "example" {
output "resource_proxmox_virtual_environment_vm_example_id" {
value = "${proxmox_virtual_environment_vm.example.id}"
}
output "resource_proxmox_virtual_environment_vm_example_ipv4_addresses" {
value = "${proxmox_virtual_environment_vm.example.ipv4_addresses}"
}
output "resource_proxmox_virtual_environment_vm_example_ipv6_addresses" {
value = "${proxmox_virtual_environment_vm.example.ipv6_addresses}"
}
output "resource_proxmox_virtual_environment_vm_example_network_interface_names" {
value = "${proxmox_virtual_environment_vm.example.network_interface_names}"
}

View File

@ -66,6 +66,22 @@ VMID:
return nil, errors.New("Unable to retrieve the next available VM identifier")
}
// GetVMNetworkInterfacesFromAgent retrieves the network interfaces reported by the QEMU agent.
func (c *VirtualEnvironmentClient) GetVMNetworkInterfacesFromAgent(nodeName string, vmID int) (*VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseData, error) {
resBody := &VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseBody{}
err := c.DoRequest(hmGET, fmt.Sprintf("nodes/%s/qemu/%d/agent/network-get-interfaces", url.PathEscape(nodeName), vmID), nil, resBody)
if err != nil {
return nil, err
}
if resBody.Data == nil {
return nil, errors.New("The server did not include a data object in the response")
}
return resBody.Data, nil
}
// GetVMStatus retrieves the status for a virtual machine.
func (c *VirtualEnvironmentClient) GetVMStatus(nodeName string, vmID int) (*VirtualEnvironmentVMGetStatusResponseData, error) {
resBody := &VirtualEnvironmentVMGetStatusResponseBody{}
@ -112,6 +128,32 @@ func (c *VirtualEnvironmentClient) UpdateVMAsync(nodeName string, vmID int, d *V
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/config", url.PathEscape(nodeName), vmID), d, nil)
}
// WaitForNetworkInterfacesFromAgent waits for a virtual machine's QEMU agent to publish the network interfaces.
func (c *VirtualEnvironmentClient) WaitForNetworkInterfacesFromAgent(nodeName string, vmID int, timeout int, delay int) (*VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseData, error) {
timeDelay := int64(delay)
timeMax := float64(timeout)
timeStart := time.Now()
timeElapsed := timeStart.Sub(timeStart)
for timeElapsed.Seconds() < timeMax {
if int64(timeElapsed.Seconds())%timeDelay == 0 {
data, err := c.GetVMNetworkInterfacesFromAgent(nodeName, vmID)
if err == nil && data != nil {
return data, err
}
time.Sleep(1 * time.Second)
}
time.Sleep(200 * time.Millisecond)
timeElapsed = time.Now().Sub(timeStart)
}
return nil, fmt.Errorf("Timeout while waiting for the QEMU agent on VM \"%d\" to publish the network interfaces", vmID)
}
// WaitForState waits for a virtual machine to reach a specific state.
func (c *VirtualEnvironmentClient) WaitForState(nodeName string, vmID int, state string, timeout int, delay int) error {
state = strings.ToLower(state)
@ -141,5 +183,5 @@ func (c *VirtualEnvironmentClient) WaitForState(nodeName string, vmID int, state
timeElapsed = time.Now().Sub(timeStart)
}
return fmt.Errorf("Failed to wait for VM \"%d\" to enter the state \"%s\"", vmID, state)
return fmt.Errorf("Timeout while waiting for VM \"%d\" to enter the state \"%s\"", vmID, state)
}

View File

@ -258,6 +258,43 @@ type VirtualEnvironmentVMCreateRequestBody struct {
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"`
}
// VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseBody contains the body from a QEMU get network interfaces response.
type VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseBody struct {
Data *VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseData `json:"data,omitempty"`
}
// VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseData contains the data from a QEMU get network interfaces response.
type VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseData struct {
Result *[]VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResult `json:"result,omitempty"`
}
// VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResult contains the result from a QEMU get network interfaces response.
type VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResult struct {
MACAddress string `json:"hardware-address"`
Name string `json:"name"`
Statistics VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResultStatistics `json:"statistics"`
IPAddresses *[]VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResultIPAddress `json:"ip-addresses,omitempty"`
}
// VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResultIPAddress contains the IP address from a QEMU get network interfaces response.
type VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResultIPAddress struct {
Address string `json:"ip-address"`
Prefix int `json:"prefix"`
Type string `json:"ip-address-type"`
}
// VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResultStatistics contains the statistics from a QEMU get network interfaces response.
type VirtualEnvironmentVMGetQEMUNetworkInterfacesResponseResultStatistics struct {
RXBytes int `json:"rx-bytes"`
RXDropped int `json:"rx-dropped"`
RXErrors int `json:"rx-errs"`
RXPackets int `json:"rx-packets"`
TXBytes int `json:"tx-bytes"`
TXDropped int `json:"tx-dropped"`
TXErrors int `json:"tx-errs"`
TXPackets int `json:"tx-packets"`
}
// VirtualEnvironmentVMGetResponseBody contains the body from an virtual machine get response.
type VirtualEnvironmentVMGetResponseBody struct {
Data *VirtualEnvironmentVMGetResponseData `json:"data,omitempty"`

View File

@ -89,6 +89,8 @@ const (
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable"
mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write"
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable"
mkResourceVirtualEnvironmentVMIPv4Addresses = "ipv4_addresses"
mkResourceVirtualEnvironmentVMIPv6Addresses = "ipv6_addresses"
mkResourceVirtualEnvironmentVMKeyboardLayout = "keyboard_layout"
mkResourceVirtualEnvironmentVMMemory = "memory"
mkResourceVirtualEnvironmentVMMemoryDedicated = "dedicated"
@ -101,6 +103,7 @@ const (
mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress = "mac_address"
mkResourceVirtualEnvironmentVMNetworkDeviceModel = "model"
mkResourceVirtualEnvironmentVMNetworkDeviceVLANIDs = "vlan_ids"
mkResourceVirtualEnvironmentVMNetworkInterfaceNames = "network_interface_names"
mkResourceVirtualEnvironmentVMNodeName = "node_name"
mkResourceVirtualEnvironmentVMOSType = "os_type"
mkResourceVirtualEnvironmentVMPoolID = "pool_id"
@ -193,7 +196,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Description: "The cloud-init configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
return make([]interface{}, 0), nil
return []interface{}{}, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@ -202,7 +205,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Description: "The DNS configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
return make([]interface{}, 0), nil
return []interface{}{}, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@ -228,7 +231,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Description: "The IP configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
return make([]interface{}, 0), nil
return []interface{}{}, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@ -237,7 +240,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Description: "The IPv4 configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
return make([]interface{}, 0), nil
return []interface{}{}, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@ -263,7 +266,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Description: "The IPv6 configuration",
Optional: true,
DefaultFunc: func() (interface{}, error) {
return make([]interface{}, 0), nil
return []interface{}{}, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@ -294,7 +297,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Description: "The user account configuration",
Required: true,
DefaultFunc: func() (interface{}, error) {
return make([]interface{}, 0), nil
return []interface{}{}, nil
},
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
@ -487,6 +490,24 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
MaxItems: 14,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMIPv4Addresses: {
Type: schema.TypeList,
Computed: true,
Description: "The IPv4 addresses published by the QEMU agent",
Elem: &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
mkResourceVirtualEnvironmentVMIPv6Addresses: {
Type: schema.TypeList,
Computed: true,
Description: "The IPv6 addresses published by the QEMU agent",
Elem: &schema.Schema{
Type: schema.TypeList,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
mkResourceVirtualEnvironmentVMKeyboardLayout: {
Type: schema.TypeString,
Optional: true,
@ -591,7 +612,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
Optional: true,
Description: "The VLAN identifiers",
DefaultFunc: func() (interface{}, error) {
return make([]interface{}, 0), nil
return []interface{}{}, nil
},
Elem: &schema.Schema{Type: schema.TypeInt},
},
@ -600,6 +621,12 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
MaxItems: 8,
MinItems: 0,
},
mkResourceVirtualEnvironmentVMNetworkInterfaceNames: {
Type: schema.TypeList,
Computed: true,
Description: "The network interface names published by the QEMU agent",
Elem: &schema.Schema{Type: schema.TypeString},
},
mkResourceVirtualEnvironmentVMNodeName: &schema.Schema{
Type: schema.TypeString,
Description: "The node name",
@ -649,37 +676,24 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
return err
}
resourceSchema := resourceVirtualEnvironmentVM().Schema
agent := d.Get(mkResourceVirtualEnvironmentVMAgent).([]interface{})
resource := resourceVirtualEnvironmentVM()
if len(agent) == 0 {
agentDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMAgent].DefaultValue()
agentBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMAgent}, 0, true)
if err != nil {
return err
}
agent = agentDefault.([]interface{})
if err != nil {
return err
}
agentBlock := agent[0].(map[string]interface{})
agentEnabled := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool))
agentTrim := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool))
agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string)
cdrom := d.Get(mkResourceVirtualEnvironmentVMCDROM).([]interface{})
cdromBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMCDROM}, 0, true)
if len(cdrom) == 0 {
cdromDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMCDROM].DefaultValue()
if err != nil {
return err
}
cdrom = cdromDefault.([]interface{})
if err != nil {
return err
}
cdromBlock := cdrom[0].(map[string]interface{})
cdromEnabled := cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled].(bool)
cdromFileID := cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID].(string)
@ -790,19 +804,12 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
}
}
cpu := d.Get(mkResourceVirtualEnvironmentVMCPU).([]interface{})
cpuBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMCPU}, 0, true)
if len(cpu) == 0 {
cpuDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMCPU].DefaultValue()
if err != nil {
return err
}
cpu = cpuDefault.([]interface{})
if err != nil {
return err
}
cpuBlock := cpu[0].(map[string]interface{})
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
@ -811,52 +818,20 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
scsiDevices := make(proxmox.CustomStorageDevices, len(disk))
diskSchemaElem := resourceSchema[mkResourceVirtualEnvironmentVMDisk].Elem
diskSchemaResource := diskSchemaElem.(*schema.Resource)
diskSpeedResource := diskSchemaResource.Schema[mkResourceVirtualEnvironmentVMDiskSpeed]
for i, d := range disk {
block := d.(map[string]interface{})
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
speed := block[mkResourceVirtualEnvironmentVMDiskSpeed].([]interface{})
if len(speed) == 0 {
diskSpeedDefault, err := diskSpeedResource.DefaultValue()
if err != nil {
return err
}
speed = diskSpeedDefault.([]interface{})
}
speedBlock := speed[0].(map[string]interface{})
speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int)
speedLimitReadBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable].(int)
speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int)
speedLimitWriteBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable].(int)
for i, diskEntry := range disk {
diskDevice := proxmox.CustomStorageDevice{
Enabled: true,
}
if speedLimitRead > 0 {
diskDevice.MaxReadSpeedMbps = &speedLimitRead
}
block := diskEntry.(map[string]interface{})
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
if speedLimitReadBurstable > 0 {
diskDevice.BurstableReadSpeedMbps = &speedLimitReadBurstable
}
speedBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMDisk, mkResourceVirtualEnvironmentVMDiskSpeed}, 0, false)
if speedLimitWrite > 0 {
diskDevice.MaxWriteSpeedMbps = &speedLimitWrite
}
if speedLimitWriteBurstable > 0 {
diskDevice.BurstableWriteSpeedMbps = &speedLimitWriteBurstable
if err != nil {
return err
}
if fileID != "" {
@ -865,23 +840,39 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
diskDevice.FileVolume = fmt.Sprintf("%s:%d", datastoreID, size)
}
if len(speedBlock) > 0 {
speedLimitRead := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedRead].(int)
speedLimitReadBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable].(int)
speedLimitWrite := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWrite].(int)
speedLimitWriteBurstable := speedBlock[mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable].(int)
if speedLimitRead > 0 {
diskDevice.MaxReadSpeedMbps = &speedLimitRead
}
if speedLimitReadBurstable > 0 {
diskDevice.BurstableReadSpeedMbps = &speedLimitReadBurstable
}
if speedLimitWrite > 0 {
diskDevice.MaxWriteSpeedMbps = &speedLimitWrite
}
if speedLimitWriteBurstable > 0 {
diskDevice.BurstableWriteSpeedMbps = &speedLimitWriteBurstable
}
}
scsiDevices[i] = diskDevice
}
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
memoryBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMMemory}, 0, true)
if len(memory) == 0 {
memoryDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMMemory].DefaultValue()
if err != nil {
return err
}
memory = memoryDefault.([]interface{})
if err != nil {
return err
}
memoryBlock := memory[0].(map[string]interface{})
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentVMMemoryDedicated].(int)
memoryFloating := memoryBlock[mkResourceVirtualEnvironmentVMMemoryFloating].(int)
memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int)
@ -891,8 +882,8 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
networkDeviceObjects := make(proxmox.CustomNetworkDevices, len(networkDevice))
for i, d := range networkDevice {
block := d.(map[string]interface{})
for i, networkDeviceEntry := range networkDevice {
block := networkDeviceEntry.(map[string]interface{})
bridge, _ := block[mkResourceVirtualEnvironmentVMNetworkDeviceBridge].(string)
enabled, _ := block[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled].(bool)
@ -1540,6 +1531,43 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err
d.Set(mkResourceVirtualEnvironmentVMStarted, status.Status == "running")
// Populate the attributes that rely on the QEMU agent.
ipv4Addresses := []interface{}{}
ipv6Addresses := []interface{}{}
networkInterfaceNames := []interface{}{}
if vmConfig.Agent != nil && vmConfig.Agent.Enabled != nil && *vmConfig.Agent.Enabled {
networkInterfaces, err := veClient.WaitForNetworkInterfacesFromAgent(nodeName, vmID, 600, 5)
if err == nil && networkInterfaces.Result != nil {
ipv4Addresses = make([]interface{}, len(*networkInterfaces.Result))
ipv6Addresses = make([]interface{}, len(*networkInterfaces.Result))
networkInterfaceNames = make([]interface{}, len(*networkInterfaces.Result))
for ri, rv := range *networkInterfaces.Result {
rvIPv4Addresses := []interface{}{}
rvIPv6Addresses := []interface{}{}
for _, ip := range *rv.IPAddresses {
switch ip.Type {
case "ipv4":
rvIPv4Addresses = append(rvIPv4Addresses, ip.Address)
case "ipv6":
rvIPv6Addresses = append(rvIPv6Addresses, ip.Address)
}
}
ipv4Addresses[ri] = rvIPv4Addresses
ipv6Addresses[ri] = rvIPv6Addresses
networkInterfaceNames[ri] = rv.Name
}
}
}
d.Set(mkResourceVirtualEnvironmentVMIPv4Addresses, ipv4Addresses)
d.Set(mkResourceVirtualEnvironmentVMIPv6Addresses, ipv6Addresses)
d.Set(mkResourceVirtualEnvironmentVMNetworkInterfaceNames, networkInterfaceNames)
return nil
}

View File

@ -43,16 +43,25 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
mkResourceVirtualEnvironmentVMVMID,
})
testComputedAttributes(t, s, []string{
mkResourceVirtualEnvironmentVMIPv4Addresses,
mkResourceVirtualEnvironmentVMIPv6Addresses,
mkResourceVirtualEnvironmentVMNetworkInterfaceNames,
})
testSchemaValueTypes(t, s, []string{
mkResourceVirtualEnvironmentVMCDROM,
mkResourceVirtualEnvironmentVMCloudInit,
mkResourceVirtualEnvironmentVMCPU,
mkResourceVirtualEnvironmentVMDescription,
mkResourceVirtualEnvironmentVMDisk,
mkResourceVirtualEnvironmentVMIPv4Addresses,
mkResourceVirtualEnvironmentVMIPv6Addresses,
mkResourceVirtualEnvironmentVMKeyboardLayout,
mkResourceVirtualEnvironmentVMMemory,
mkResourceVirtualEnvironmentVMName,
mkResourceVirtualEnvironmentVMNetworkDevice,
mkResourceVirtualEnvironmentVMNetworkInterfaceNames,
mkResourceVirtualEnvironmentVMOSType,
mkResourceVirtualEnvironmentVMPoolID,
mkResourceVirtualEnvironmentVMStarted,
@ -63,10 +72,13 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeList,
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeString,
schema.TypeList,
schema.TypeList,
schema.TypeString,
schema.TypeString,
schema.TypeBool,

View File

@ -131,6 +131,50 @@ func getQEMUAgentTypeValidator() schema.SchemaValidateFunc {
return validation.StringInSlice([]string{"isa", "virtio"}, false)
}
func getSchemaBlock(r *schema.Resource, d *schema.ResourceData, m interface{}, k []string, i int, allowDefault bool) (map[string]interface{}, error) {
var resourceBlock map[string]interface{}
var resourceData interface{}
var resourceSchema *schema.Schema
for ki, kv := range k {
if ki == 0 {
resourceData = d.Get(kv)
resourceSchema = r.Schema[kv]
} else {
mapValues := resourceData.([]interface{})
if len(mapValues) <= i {
return resourceBlock, fmt.Errorf("Index out of bounds %d", i)
}
mapValue := mapValues[i].(map[string]interface{})
resourceData = mapValue[kv]
resourceSchema = resourceSchema.Elem.(*schema.Resource).Schema[kv]
}
}
list := resourceData.([]interface{})
if len(list) == 0 {
if allowDefault {
listDefault, err := resourceSchema.DefaultValue()
if err != nil {
return nil, err
}
list = listDefault.([]interface{})
}
}
if len(list) > i {
resourceBlock = list[i].(map[string]interface{})
}
return resourceBlock, nil
}
func getVLANIDsValidator() schema.SchemaValidateFunc {
return func(i interface{}, k string) (ws []string, es []error) {
min := 1