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:
parent
5d5f1c1835
commit
1dfe979e9e
@ -426,7 +426,9 @@ This resource doesn't expose any additional attributes.
|
|||||||
* `vm_id` - (Optional) The ID
|
* `vm_id` - (Optional) The ID
|
||||||
|
|
||||||
###### Attributes
|
###### 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
|
## 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`.
|
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`.
|
||||||
|
@ -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"
|
content_type = "snippets"
|
||||||
datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local"))}"
|
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}"
|
node_name = "${data.proxmox_virtual_environment_datastores.example.node_name}"
|
||||||
@ -19,7 +19,7 @@ resource "proxmox_virtual_environment_file" "cloud_init_config" {
|
|||||||
chpasswd:
|
chpasswd:
|
||||||
list: |
|
list: |
|
||||||
ubuntu:example
|
ubuntu:example
|
||||||
expire: False
|
expire: false
|
||||||
hostname: terraform-provider-proxmox-example
|
hostname: terraform-provider-proxmox-example
|
||||||
packages:
|
packages:
|
||||||
- qemu-guest-agent
|
- qemu-guest-agent
|
||||||
@ -33,7 +33,7 @@ users:
|
|||||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
file_name = "terraform-provider-proxmox-example-cloud-init.yaml"
|
file_name = "terraform-provider-proxmox-example-cloud-config.yaml"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,11 +16,11 @@ resource "proxmox_virtual_environment_vm" "example" {
|
|||||||
|
|
||||||
user_account {
|
user_account {
|
||||||
keys = ["${trimspace(tls_private_key.example.public_key_openssh)}"]
|
keys = ["${trimspace(tls_private_key.example.public_key_openssh)}"]
|
||||||
password = "proxmoxtf"
|
password = "example"
|
||||||
username = "ubuntu"
|
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 {
|
disk {
|
||||||
@ -34,6 +34,20 @@ resource "proxmox_virtual_environment_vm" "example" {
|
|||||||
os_type = "l26"
|
os_type = "l26"
|
||||||
pool_id = "${proxmox_virtual_environment_pool.example.id}"
|
pool_id = "${proxmox_virtual_environment_pool.example.id}"
|
||||||
vm_id = 2038
|
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" {
|
resource "local_file" "example_ssh_private_key" {
|
||||||
@ -54,3 +68,15 @@ resource "tls_private_key" "example" {
|
|||||||
output "resource_proxmox_virtual_environment_vm_example_id" {
|
output "resource_proxmox_virtual_environment_vm_example_id" {
|
||||||
value = "${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}"
|
||||||
|
}
|
||||||
|
@ -66,6 +66,22 @@ VMID:
|
|||||||
return nil, errors.New("Unable to retrieve the next available VM identifier")
|
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.
|
// GetVMStatus retrieves the status for a virtual machine.
|
||||||
func (c *VirtualEnvironmentClient) GetVMStatus(nodeName string, vmID int) (*VirtualEnvironmentVMGetStatusResponseData, error) {
|
func (c *VirtualEnvironmentClient) GetVMStatus(nodeName string, vmID int) (*VirtualEnvironmentVMGetStatusResponseData, error) {
|
||||||
resBody := &VirtualEnvironmentVMGetStatusResponseBody{}
|
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)
|
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.
|
// 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 {
|
func (c *VirtualEnvironmentClient) WaitForState(nodeName string, vmID int, state string, timeout int, delay int) error {
|
||||||
state = strings.ToLower(state)
|
state = strings.ToLower(state)
|
||||||
@ -141,5 +183,5 @@ func (c *VirtualEnvironmentClient) WaitForState(nodeName string, vmID int, state
|
|||||||
timeElapsed = time.Now().Sub(timeStart)
|
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)
|
||||||
}
|
}
|
||||||
|
@ -258,6 +258,43 @@ type VirtualEnvironmentVMCreateRequestBody struct {
|
|||||||
WatchdogDevice *CustomWatchdogDevice `json:"watchdog,omitempty" url:"watchdog,omitempty"`
|
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.
|
// VirtualEnvironmentVMGetResponseBody contains the body from an virtual machine get response.
|
||||||
type VirtualEnvironmentVMGetResponseBody struct {
|
type VirtualEnvironmentVMGetResponseBody struct {
|
||||||
Data *VirtualEnvironmentVMGetResponseData `json:"data,omitempty"`
|
Data *VirtualEnvironmentVMGetResponseData `json:"data,omitempty"`
|
||||||
|
@ -89,6 +89,8 @@ const (
|
|||||||
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable"
|
mkResourceVirtualEnvironmentVMDiskSpeedReadBurstable = "read_burstable"
|
||||||
mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write"
|
mkResourceVirtualEnvironmentVMDiskSpeedWrite = "write"
|
||||||
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable"
|
mkResourceVirtualEnvironmentVMDiskSpeedWriteBurstable = "write_burstable"
|
||||||
|
mkResourceVirtualEnvironmentVMIPv4Addresses = "ipv4_addresses"
|
||||||
|
mkResourceVirtualEnvironmentVMIPv6Addresses = "ipv6_addresses"
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout = "keyboard_layout"
|
mkResourceVirtualEnvironmentVMKeyboardLayout = "keyboard_layout"
|
||||||
mkResourceVirtualEnvironmentVMMemory = "memory"
|
mkResourceVirtualEnvironmentVMMemory = "memory"
|
||||||
mkResourceVirtualEnvironmentVMMemoryDedicated = "dedicated"
|
mkResourceVirtualEnvironmentVMMemoryDedicated = "dedicated"
|
||||||
@ -101,6 +103,7 @@ const (
|
|||||||
mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress = "mac_address"
|
mkResourceVirtualEnvironmentVMNetworkDeviceMACAddress = "mac_address"
|
||||||
mkResourceVirtualEnvironmentVMNetworkDeviceModel = "model"
|
mkResourceVirtualEnvironmentVMNetworkDeviceModel = "model"
|
||||||
mkResourceVirtualEnvironmentVMNetworkDeviceVLANIDs = "vlan_ids"
|
mkResourceVirtualEnvironmentVMNetworkDeviceVLANIDs = "vlan_ids"
|
||||||
|
mkResourceVirtualEnvironmentVMNetworkInterfaceNames = "network_interface_names"
|
||||||
mkResourceVirtualEnvironmentVMNodeName = "node_name"
|
mkResourceVirtualEnvironmentVMNodeName = "node_name"
|
||||||
mkResourceVirtualEnvironmentVMOSType = "os_type"
|
mkResourceVirtualEnvironmentVMOSType = "os_type"
|
||||||
mkResourceVirtualEnvironmentVMPoolID = "pool_id"
|
mkResourceVirtualEnvironmentVMPoolID = "pool_id"
|
||||||
@ -193,7 +196,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
Description: "The cloud-init configuration",
|
Description: "The cloud-init configuration",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return make([]interface{}, 0), nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
@ -202,7 +205,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
Description: "The DNS configuration",
|
Description: "The DNS configuration",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return make([]interface{}, 0), nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
@ -228,7 +231,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
Description: "The IP configuration",
|
Description: "The IP configuration",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return make([]interface{}, 0), nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
@ -237,7 +240,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
Description: "The IPv4 configuration",
|
Description: "The IPv4 configuration",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return make([]interface{}, 0), nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
@ -263,7 +266,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
Description: "The IPv6 configuration",
|
Description: "The IPv6 configuration",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return make([]interface{}, 0), nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
@ -294,7 +297,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
Description: "The user account configuration",
|
Description: "The user account configuration",
|
||||||
Required: true,
|
Required: true,
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return make([]interface{}, 0), nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
Elem: &schema.Resource{
|
Elem: &schema.Resource{
|
||||||
Schema: map[string]*schema.Schema{
|
Schema: map[string]*schema.Schema{
|
||||||
@ -487,6 +490,24 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
MaxItems: 14,
|
MaxItems: 14,
|
||||||
MinItems: 0,
|
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: {
|
mkResourceVirtualEnvironmentVMKeyboardLayout: {
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
@ -591,7 +612,7 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
Optional: true,
|
Optional: true,
|
||||||
Description: "The VLAN identifiers",
|
Description: "The VLAN identifiers",
|
||||||
DefaultFunc: func() (interface{}, error) {
|
DefaultFunc: func() (interface{}, error) {
|
||||||
return make([]interface{}, 0), nil
|
return []interface{}{}, nil
|
||||||
},
|
},
|
||||||
Elem: &schema.Schema{Type: schema.TypeInt},
|
Elem: &schema.Schema{Type: schema.TypeInt},
|
||||||
},
|
},
|
||||||
@ -600,6 +621,12 @@ func resourceVirtualEnvironmentVM() *schema.Resource {
|
|||||||
MaxItems: 8,
|
MaxItems: 8,
|
||||||
MinItems: 0,
|
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{
|
mkResourceVirtualEnvironmentVMNodeName: &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Description: "The node name",
|
Description: "The node name",
|
||||||
@ -649,37 +676,24 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceSchema := resourceVirtualEnvironmentVM().Schema
|
resource := resourceVirtualEnvironmentVM()
|
||||||
agent := d.Get(mkResourceVirtualEnvironmentVMAgent).([]interface{})
|
|
||||||
|
|
||||||
if len(agent) == 0 {
|
agentBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMAgent}, 0, true)
|
||||||
agentDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMAgent].DefaultValue()
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
agent = agentDefault.([]interface{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
agentBlock := agent[0].(map[string]interface{})
|
|
||||||
agentEnabled := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool))
|
agentEnabled := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentEnabled].(bool))
|
||||||
agentTrim := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool))
|
agentTrim := proxmox.CustomBool(agentBlock[mkResourceVirtualEnvironmentVMAgentTrim].(bool))
|
||||||
agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string)
|
agentType := agentBlock[mkResourceVirtualEnvironmentVMAgentType].(string)
|
||||||
|
|
||||||
cdrom := d.Get(mkResourceVirtualEnvironmentVMCDROM).([]interface{})
|
cdromBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMCDROM}, 0, true)
|
||||||
|
|
||||||
if len(cdrom) == 0 {
|
if err != nil {
|
||||||
cdromDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMCDROM].DefaultValue()
|
return err
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom = cdromDefault.([]interface{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cdromBlock := cdrom[0].(map[string]interface{})
|
|
||||||
cdromEnabled := cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled].(bool)
|
cdromEnabled := cdromBlock[mkResourceVirtualEnvironmentVMCDROMEnabled].(bool)
|
||||||
cdromFileID := cdromBlock[mkResourceVirtualEnvironmentVMCDROMFileID].(string)
|
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 {
|
if err != nil {
|
||||||
cpuDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMCPU].DefaultValue()
|
return err
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu = cpuDefault.([]interface{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cpuBlock := cpu[0].(map[string]interface{})
|
|
||||||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
||||||
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
cpuHotplugged := cpuBlock[mkResourceVirtualEnvironmentVMCPUHotplugged].(int)
|
||||||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||||||
@ -811,52 +818,20 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||||||
scsiDevices := make(proxmox.CustomStorageDevices, len(disk))
|
scsiDevices := make(proxmox.CustomStorageDevices, len(disk))
|
||||||
|
|
||||||
diskSchemaElem := resourceSchema[mkResourceVirtualEnvironmentVMDisk].Elem
|
for i, diskEntry := range disk {
|
||||||
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)
|
|
||||||
|
|
||||||
diskDevice := proxmox.CustomStorageDevice{
|
diskDevice := proxmox.CustomStorageDevice{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
if speedLimitRead > 0 {
|
block := diskEntry.(map[string]interface{})
|
||||||
diskDevice.MaxReadSpeedMbps = &speedLimitRead
|
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
||||||
}
|
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||||||
|
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
||||||
|
|
||||||
if speedLimitReadBurstable > 0 {
|
speedBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMDisk, mkResourceVirtualEnvironmentVMDiskSpeed}, 0, false)
|
||||||
diskDevice.BurstableReadSpeedMbps = &speedLimitReadBurstable
|
|
||||||
}
|
|
||||||
|
|
||||||
if speedLimitWrite > 0 {
|
if err != nil {
|
||||||
diskDevice.MaxWriteSpeedMbps = &speedLimitWrite
|
return err
|
||||||
}
|
|
||||||
|
|
||||||
if speedLimitWriteBurstable > 0 {
|
|
||||||
diskDevice.BurstableWriteSpeedMbps = &speedLimitWriteBurstable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileID != "" {
|
if fileID != "" {
|
||||||
@ -865,23 +840,39 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
diskDevice.FileVolume = fmt.Sprintf("%s:%d", datastoreID, size)
|
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
|
scsiDevices[i] = diskDevice
|
||||||
}
|
}
|
||||||
|
|
||||||
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||||||
memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
|
memoryBlock, err := getSchemaBlock(resource, d, m, []string{mkResourceVirtualEnvironmentVMMemory}, 0, true)
|
||||||
|
|
||||||
if len(memory) == 0 {
|
if err != nil {
|
||||||
memoryDefault, err := resourceSchema[mkResourceVirtualEnvironmentVMMemory].DefaultValue()
|
return err
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
memory = memoryDefault.([]interface{})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memoryBlock := memory[0].(map[string]interface{})
|
|
||||||
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentVMMemoryDedicated].(int)
|
memoryDedicated := memoryBlock[mkResourceVirtualEnvironmentVMMemoryDedicated].(int)
|
||||||
memoryFloating := memoryBlock[mkResourceVirtualEnvironmentVMMemoryFloating].(int)
|
memoryFloating := memoryBlock[mkResourceVirtualEnvironmentVMMemoryFloating].(int)
|
||||||
memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int)
|
memoryShared := memoryBlock[mkResourceVirtualEnvironmentVMMemoryShared].(int)
|
||||||
@ -891,8 +882,8 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
networkDevice := d.Get(mkResourceVirtualEnvironmentVMNetworkDevice).([]interface{})
|
||||||
networkDeviceObjects := make(proxmox.CustomNetworkDevices, len(networkDevice))
|
networkDeviceObjects := make(proxmox.CustomNetworkDevices, len(networkDevice))
|
||||||
|
|
||||||
for i, d := range networkDevice {
|
for i, networkDeviceEntry := range networkDevice {
|
||||||
block := d.(map[string]interface{})
|
block := networkDeviceEntry.(map[string]interface{})
|
||||||
|
|
||||||
bridge, _ := block[mkResourceVirtualEnvironmentVMNetworkDeviceBridge].(string)
|
bridge, _ := block[mkResourceVirtualEnvironmentVMNetworkDeviceBridge].(string)
|
||||||
enabled, _ := block[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled].(bool)
|
enabled, _ := block[mkResourceVirtualEnvironmentVMNetworkDeviceEnabled].(bool)
|
||||||
@ -1540,6 +1531,43 @@ func resourceVirtualEnvironmentVMRead(d *schema.ResourceData, m interface{}) err
|
|||||||
|
|
||||||
d.Set(mkResourceVirtualEnvironmentVMStarted, status.Status == "running")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,16 +43,25 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
mkResourceVirtualEnvironmentVMVMID,
|
mkResourceVirtualEnvironmentVMVMID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
testComputedAttributes(t, s, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMIPv4Addresses,
|
||||||
|
mkResourceVirtualEnvironmentVMIPv6Addresses,
|
||||||
|
mkResourceVirtualEnvironmentVMNetworkInterfaceNames,
|
||||||
|
})
|
||||||
|
|
||||||
testSchemaValueTypes(t, s, []string{
|
testSchemaValueTypes(t, s, []string{
|
||||||
mkResourceVirtualEnvironmentVMCDROM,
|
mkResourceVirtualEnvironmentVMCDROM,
|
||||||
mkResourceVirtualEnvironmentVMCloudInit,
|
mkResourceVirtualEnvironmentVMCloudInit,
|
||||||
mkResourceVirtualEnvironmentVMCPU,
|
mkResourceVirtualEnvironmentVMCPU,
|
||||||
mkResourceVirtualEnvironmentVMDescription,
|
mkResourceVirtualEnvironmentVMDescription,
|
||||||
mkResourceVirtualEnvironmentVMDisk,
|
mkResourceVirtualEnvironmentVMDisk,
|
||||||
|
mkResourceVirtualEnvironmentVMIPv4Addresses,
|
||||||
|
mkResourceVirtualEnvironmentVMIPv6Addresses,
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
||||||
mkResourceVirtualEnvironmentVMMemory,
|
mkResourceVirtualEnvironmentVMMemory,
|
||||||
mkResourceVirtualEnvironmentVMName,
|
mkResourceVirtualEnvironmentVMName,
|
||||||
mkResourceVirtualEnvironmentVMNetworkDevice,
|
mkResourceVirtualEnvironmentVMNetworkDevice,
|
||||||
|
mkResourceVirtualEnvironmentVMNetworkInterfaceNames,
|
||||||
mkResourceVirtualEnvironmentVMOSType,
|
mkResourceVirtualEnvironmentVMOSType,
|
||||||
mkResourceVirtualEnvironmentVMPoolID,
|
mkResourceVirtualEnvironmentVMPoolID,
|
||||||
mkResourceVirtualEnvironmentVMStarted,
|
mkResourceVirtualEnvironmentVMStarted,
|
||||||
@ -63,10 +72,13 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeBool,
|
schema.TypeBool,
|
||||||
|
@ -131,6 +131,50 @@ func getQEMUAgentTypeValidator() schema.SchemaValidateFunc {
|
|||||||
return validation.StringInSlice([]string{"isa", "virtio"}, false)
|
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 {
|
func getVLANIDsValidator() schema.SchemaValidateFunc {
|
||||||
return func(i interface{}, k string) (ws []string, es []error) {
|
return func(i interface{}, k string) (ws []string, es []error) {
|
||||||
min := 1
|
min := 1
|
||||||
|
Loading…
Reference in New Issue
Block a user