mirror of
https://github.com/bpg/terraform-provider-proxmox.git
synced 2025-08-22 19:38:35 +00:00
Continued work on VM resource
This commit is contained in:
parent
96d139fcb4
commit
c69cabc57a
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@
|
|||||||
/website/node_modules
|
/website/node_modules
|
||||||
/website/vendor
|
/website/vendor
|
||||||
|
|
||||||
|
autogenerated/
|
||||||
bin/
|
bin/
|
||||||
dist/
|
dist/
|
||||||
modules-dev/
|
modules-dev/
|
||||||
|
@ -3,7 +3,6 @@ resource "proxmox_virtual_environment_file" "ubuntu_cloud_image" {
|
|||||||
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}"
|
||||||
source = "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img"
|
source = "https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img"
|
||||||
source_checksum = "6afb97af96b671572389935d6579557357ad7bbf2c2dd2cb52879c957c85dbee"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
output "resource_proxmox_virtual_environment_file_ubuntu_cloud_image_content_type" {
|
output "resource_proxmox_virtual_environment_file_ubuntu_cloud_image_content_type" {
|
||||||
|
@ -16,9 +16,25 @@ resource "proxmox_virtual_environment_vm" "example" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
disk {
|
||||||
|
datastore_id = "${element(data.proxmox_virtual_environment_datastores.example.datastore_ids, index(data.proxmox_virtual_environment_datastores.example.datastore_ids, "local-lvm"))}"
|
||||||
|
file_id = "${proxmox_virtual_environment_file.ubuntu_cloud_image.id}"
|
||||||
|
}
|
||||||
|
|
||||||
network_device {}
|
network_device {}
|
||||||
|
|
||||||
node_name = "${data.proxmox_virtual_environment_nodes.example.names[0]}"
|
node_name = "${data.proxmox_virtual_environment_nodes.example.names[0]}"
|
||||||
|
os_type = "l26"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "local_file" "example_ssh_private_key" {
|
||||||
|
filename = "${path.module}/autogenerated/id_rsa"
|
||||||
|
sensitive_content = "${tls_private_key.example.private_key_pem}"
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "local_file" "example_ssh_public_key" {
|
||||||
|
filename = "${path.module}/autogenerated/id_rsa.pub"
|
||||||
|
sensitive_content = "${tls_private_key.example.public_key_openssh}"
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "tls_private_key" "example" {
|
resource "tls_private_key" "example" {
|
||||||
|
1
go.mod
1
go.mod
@ -5,4 +5,5 @@ go 1.13
|
|||||||
require (
|
require (
|
||||||
github.com/google/go-querystring v1.0.0
|
github.com/google/go-querystring v1.0.0
|
||||||
github.com/hashicorp/terraform v0.12.18
|
github.com/hashicorp/terraform v0.12.18
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||||
)
|
)
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -150,6 +151,9 @@ func (c *VirtualEnvironmentClient) DoRequest(method, path string, requestBody in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Failed to decode HTTP %s response (path: %s) - Reason: %s", method, modifiedPath, err.Error())
|
return fmt.Errorf("Failed to decode HTTP %s response (path: %s) - Reason: %s", method, modifiedPath, err.Error())
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
data, _ := ioutil.ReadAll(res.Body)
|
||||||
|
log.Printf("[DEBUG] Unhandled HTTP response body: %s", string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -6,9 +6,96 @@ package proxmox
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ExecuteNodeCommands executes commands on a given node.
|
||||||
|
func (c *VirtualEnvironmentClient) ExecuteNodeCommands(nodeName string, commands []string) error {
|
||||||
|
// We must first retrieve the IP address of the node as we need to bypass the API and use SSH instead.
|
||||||
|
networkDevices, err := c.ListNodeNetworkDevices(nodeName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeAddress := ""
|
||||||
|
|
||||||
|
for _, d := range networkDevices {
|
||||||
|
if d.Address != nil {
|
||||||
|
nodeAddress = *d.Address
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if nodeAddress == "" {
|
||||||
|
return fmt.Errorf("Failed to determine the IP address of node \"%s\"", nodeName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can now go ahead and execute the commands using SSH.
|
||||||
|
// Hopefully, the developers will add this feature to the REST API at some point.
|
||||||
|
ur := strings.Split(c.Username, "@")
|
||||||
|
|
||||||
|
sshConfig := &ssh.ClientConfig{
|
||||||
|
User: ur[0],
|
||||||
|
Auth: []ssh.AuthMethod{ssh.Password(c.Password)},
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
}
|
||||||
|
|
||||||
|
sshClient, err := ssh.Dial("tcp", nodeAddress+":22", sshConfig)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer sshClient.Close()
|
||||||
|
|
||||||
|
sshSession, err := sshClient.NewSession()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer sshSession.Close()
|
||||||
|
|
||||||
|
_, err = sshSession.CombinedOutput(
|
||||||
|
fmt.Sprintf(
|
||||||
|
"/bin/bash -c '%s'",
|
||||||
|
strings.ReplaceAll(strings.Join(commands, " && "), "'", "'\"'\"'"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListNodeNetworkDevices retrieves a list of network devices for a specific nodes.
|
||||||
|
func (c *VirtualEnvironmentClient) ListNodeNetworkDevices(nodeName string) ([]*VirtualEnvironmentNodeNetworkDeviceListResponseData, error) {
|
||||||
|
resBody := &VirtualEnvironmentNodeNetworkDeviceListResponseBody{}
|
||||||
|
err := c.DoRequest(hmGET, fmt.Sprintf("nodes/%s/network", url.PathEscape(nodeName)), 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")
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(resBody.Data, func(i, j int) bool {
|
||||||
|
return resBody.Data[i].Priority < resBody.Data[j].Priority
|
||||||
|
})
|
||||||
|
|
||||||
|
return resBody.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ListNodes retrieves a list of nodes.
|
// ListNodes retrieves a list of nodes.
|
||||||
func (c *VirtualEnvironmentClient) ListNodes() ([]*VirtualEnvironmentNodeListResponseData, error) {
|
func (c *VirtualEnvironmentClient) ListNodes() ([]*VirtualEnvironmentNodeListResponseData, error) {
|
||||||
resBody := &VirtualEnvironmentNodeListResponseBody{}
|
resBody := &VirtualEnvironmentNodeListResponseBody{}
|
||||||
|
@ -4,6 +4,19 @@
|
|||||||
|
|
||||||
package proxmox
|
package proxmox
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CustomNodeCommands contains an array of commands to execute.
|
||||||
|
type CustomNodeCommands []string
|
||||||
|
|
||||||
|
// VirtualEnvironmentNodeExecuteRequestBody contains the data for a node execute request.
|
||||||
|
type VirtualEnvironmentNodeExecuteRequestBody struct {
|
||||||
|
Commands CustomNodeCommands `json:"commands" url:"commands"`
|
||||||
|
}
|
||||||
|
|
||||||
// VirtualEnvironmentNodeListResponseBody contains the body from a node list response.
|
// VirtualEnvironmentNodeListResponseBody contains the body from a node list response.
|
||||||
type VirtualEnvironmentNodeListResponseBody struct {
|
type VirtualEnvironmentNodeListResponseBody struct {
|
||||||
Data []*VirtualEnvironmentNodeListResponseData `json:"data,omitempty"`
|
Data []*VirtualEnvironmentNodeListResponseData `json:"data,omitempty"`
|
||||||
@ -21,3 +34,41 @@ type VirtualEnvironmentNodeListResponseData struct {
|
|||||||
SupportLevel *string `json:"level,omitempty"`
|
SupportLevel *string `json:"level,omitempty"`
|
||||||
Uptime *int `json:"uptime"`
|
Uptime *int `json:"uptime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VirtualEnvironmentNodeNetworkDeviceListResponseBody contains the body from a node network device list response.
|
||||||
|
type VirtualEnvironmentNodeNetworkDeviceListResponseBody struct {
|
||||||
|
Data []*VirtualEnvironmentNodeNetworkDeviceListResponseData `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtualEnvironmentNodeNetworkDeviceListResponseData contains the data from a node network device list response.
|
||||||
|
type VirtualEnvironmentNodeNetworkDeviceListResponseData struct {
|
||||||
|
Active *CustomBool `json:"active,omitempty"`
|
||||||
|
Address *string `json:"address,omitempty"`
|
||||||
|
Autostart *CustomBool `json:"autostart,omitempty"`
|
||||||
|
BridgeFD *string `json:"bridge_fd,omitempty"`
|
||||||
|
BridgePorts *string `json:"bridge_ports,omitempty"`
|
||||||
|
BridgeSTP *string `json:"bridge_stp,omitempty"`
|
||||||
|
CIDR *string `json:"cidr,omitempty"`
|
||||||
|
Exists *CustomBool `json:"exists,omitempty"`
|
||||||
|
Families *[]string `json:"families,omitempty"`
|
||||||
|
Gateway *string `json:"gateway,omitempty"`
|
||||||
|
Iface string `json:"iface"`
|
||||||
|
MethodIPv4 *string `json:"method,omitempty"`
|
||||||
|
MethodIPv6 *string `json:"method6,omitempty"`
|
||||||
|
Netmask *string `json:"netmask,omitempty"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeValues converts a CustomNodeCommands array to a JSON encoded URL vlaue.
|
||||||
|
func (r CustomNodeCommands) EncodeValues(key string, v *url.Values) error {
|
||||||
|
jsonArrayBytes, err := json.Marshal(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Add(key, string(jsonArrayBytes))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -227,7 +227,7 @@ type VirtualEnvironmentVMCreateRequestBody struct {
|
|||||||
PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"`
|
PCIDevices CustomPCIDevices `json:"hostpci,omitempty" url:"hostpci,omitempty"`
|
||||||
Revert *string `json:"revert,omitempty" url:"revert,omitempty"`
|
Revert *string `json:"revert,omitempty" url:"revert,omitempty"`
|
||||||
SATADevices CustomStorageDevices `json:"sata,omitempty" url:"sata,omitempty"`
|
SATADevices CustomStorageDevices `json:"sata,omitempty" url:"sata,omitempty"`
|
||||||
SCSIDevices CustomStorageDevices `json:"scsi,omitempty" url:"sata,omitempty"`
|
SCSIDevices CustomStorageDevices `json:"scsi,omitempty" url:"scsi,omitempty"`
|
||||||
SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"`
|
SCSIHardware *string `json:"scsihw,omitempty" url:"scsihw,omitempty"`
|
||||||
SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"`
|
SerialDevices CustomSerialDevices `json:"serial,omitempty" url:"serial,omitempty"`
|
||||||
SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"`
|
SharedMemory *CustomSharedMemory `json:"ivshmem,omitempty" url:"ivshmem,omitempty"`
|
||||||
|
@ -6,6 +6,7 @@ package proxmoxtf
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -610,6 +611,30 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
cpuCores := cpuBlock[mkResourceVirtualEnvironmentVMCPUCores].(int)
|
||||||
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
cpuSockets := cpuBlock[mkResourceVirtualEnvironmentVMCPUSockets].(int)
|
||||||
|
|
||||||
|
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||||||
|
scsiDevices := make(proxmox.CustomStorageDevices, len(disk))
|
||||||
|
|
||||||
|
for i, d := range disk {
|
||||||
|
block := d.(map[string]interface{})
|
||||||
|
|
||||||
|
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
||||||
|
enabled, _ := block[mkResourceVirtualEnvironmentVMDiskEnabled].(bool)
|
||||||
|
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||||||
|
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
||||||
|
|
||||||
|
diskDevice := proxmox.CustomStorageDevice{
|
||||||
|
Enabled: enabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileID != "" {
|
||||||
|
diskDevice.Enabled = false
|
||||||
|
} else {
|
||||||
|
diskDevice.FileVolume = fmt.Sprintf("%s:%d", datastoreID, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
scsiDevices[i] = diskDevice
|
||||||
|
}
|
||||||
|
|
||||||
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
keyboardLayout := d.Get(mkResourceVirtualEnvironmentVMKeyboardLayout).(string)
|
||||||
memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
|
memory := d.Get(mkResourceVirtualEnvironmentVMMemory).([]interface{})
|
||||||
|
|
||||||
@ -679,6 +704,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
var memorySharedObject *proxmox.CustomSharedMemory
|
var memorySharedObject *proxmox.CustomSharedMemory
|
||||||
|
|
||||||
agentEnabled := proxmox.CustomBool(true)
|
agentEnabled := proxmox.CustomBool(true)
|
||||||
|
bootDisk := "scsi0"
|
||||||
bootOrder := "c"
|
bootOrder := "c"
|
||||||
|
|
||||||
if cdromEnabled {
|
if cdromEnabled {
|
||||||
@ -713,6 +739,7 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
|
|
||||||
body := &proxmox.VirtualEnvironmentVMCreateRequestBody{
|
body := &proxmox.VirtualEnvironmentVMCreateRequestBody{
|
||||||
Agent: &proxmox.CustomAgent{Enabled: &agentEnabled},
|
Agent: &proxmox.CustomAgent{Enabled: &agentEnabled},
|
||||||
|
BootDisk: &bootDisk,
|
||||||
BootOrder: &bootOrder,
|
BootOrder: &bootOrder,
|
||||||
CloudInitConfig: cloudInitConfig,
|
CloudInitConfig: cloudInitConfig,
|
||||||
CPUCores: &cpuCores,
|
CPUCores: &cpuCores,
|
||||||
@ -723,11 +750,11 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
KeyboardLayout: &keyboardLayout,
|
KeyboardLayout: &keyboardLayout,
|
||||||
NetworkDevices: networkDeviceObjects,
|
NetworkDevices: networkDeviceObjects,
|
||||||
OSType: &osType,
|
OSType: &osType,
|
||||||
|
SCSIDevices: scsiDevices,
|
||||||
SCSIHardware: &scsiHardware,
|
SCSIHardware: &scsiHardware,
|
||||||
SerialDevices: []string{"socket"},
|
SerialDevices: []string{"socket"},
|
||||||
SharedMemory: memorySharedObject,
|
SharedMemory: memorySharedObject,
|
||||||
TabletDeviceEnabled: &tabletDeviceEnabled,
|
TabletDeviceEnabled: &tabletDeviceEnabled,
|
||||||
VGADevice: &proxmox.CustomVGADevice{Type: "serial0"},
|
|
||||||
VMID: &vmID,
|
VMID: &vmID,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,6 +770,85 @@ func resourceVirtualEnvironmentVMCreate(d *schema.ResourceData, m interface{}) e
|
|||||||
|
|
||||||
d.SetId(strconv.Itoa(vmID))
|
d.SetId(strconv.Itoa(vmID))
|
||||||
|
|
||||||
|
return resourceVirtualEnvironmentVMImportDisks(d, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceVirtualEnvironmentVMImportDisks(d *schema.ResourceData, m interface{}) error {
|
||||||
|
config := m.(providerConfiguration)
|
||||||
|
veClient, err := config.GetVEClient()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeName := d.Get(mkResourceVirtualEnvironmentVMNodeName).(string)
|
||||||
|
vmID, err := strconv.Atoi(d.Id())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
commands := []string{}
|
||||||
|
|
||||||
|
// Determine the ID of the next disk.
|
||||||
|
disk := d.Get(mkResourceVirtualEnvironmentVMDisk).([]interface{})
|
||||||
|
diskCount := 0
|
||||||
|
|
||||||
|
for _, d := range disk {
|
||||||
|
block := d.(map[string]interface{})
|
||||||
|
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||||||
|
|
||||||
|
if fileID == "" {
|
||||||
|
diskCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the commands required to import the specified disks.
|
||||||
|
importedDiskCount := 0
|
||||||
|
|
||||||
|
for i, d := range disk {
|
||||||
|
block := d.(map[string]interface{})
|
||||||
|
|
||||||
|
datastoreID, _ := block[mkResourceVirtualEnvironmentVMDiskDatastoreID].(string)
|
||||||
|
enabled, _ := block[mkResourceVirtualEnvironmentVMDiskEnabled].(bool)
|
||||||
|
fileFormat, _ := block[mkResourceVirtualEnvironmentVMDiskFileFormat].(string)
|
||||||
|
fileID, _ := block[mkResourceVirtualEnvironmentVMDiskFileID].(string)
|
||||||
|
size, _ := block[mkResourceVirtualEnvironmentVMDiskSize].(int)
|
||||||
|
|
||||||
|
if !enabled || fileID == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fileIDParts := strings.Split(fileID, ":")
|
||||||
|
filePath := fmt.Sprintf("/var/lib/vz/template/%s", fileIDParts[1])
|
||||||
|
filePathTmp := fmt.Sprintf("/tmp/vm-%d-disk-%d.%s", vmID, diskCount+importedDiskCount, fileFormat)
|
||||||
|
|
||||||
|
commands = append(
|
||||||
|
commands,
|
||||||
|
fmt.Sprintf("cp %s %s", filePath, filePathTmp),
|
||||||
|
fmt.Sprintf("qemu-img resize %s %dG", filePathTmp, size),
|
||||||
|
fmt.Sprintf("qm importdisk %d %s %s -format qcow2", vmID, filePathTmp, datastoreID),
|
||||||
|
fmt.Sprintf("qm set %d --scsi%d %s:vm-%d-disk-%d", vmID, i, datastoreID, vmID, diskCount+importedDiskCount),
|
||||||
|
fmt.Sprintf("rm -f %s", filePathTmp),
|
||||||
|
)
|
||||||
|
|
||||||
|
importedDiskCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the commands on the node and wait for the result.
|
||||||
|
// This is a highly experimental approach to disk imports and is not recommended by Proxmox.
|
||||||
|
if len(commands) > 0 {
|
||||||
|
for _, cmd := range commands {
|
||||||
|
log.Printf("[DEBUG] Node command: %s", cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = veClient.ExecuteNodeCommands(nodeName, commands)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return resourceVirtualEnvironmentVMRead(d, m)
|
return resourceVirtualEnvironmentVMRead(d, m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
|
|
||||||
testOptionalArguments(t, s, []string{
|
testOptionalArguments(t, s, []string{
|
||||||
mkResourceVirtualEnvironmentVMCDROM,
|
mkResourceVirtualEnvironmentVMCDROM,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInit,
|
||||||
mkResourceVirtualEnvironmentVMCPU,
|
mkResourceVirtualEnvironmentVMCPU,
|
||||||
mkResourceVirtualEnvironmentVMDisk,
|
mkResourceVirtualEnvironmentVMDisk,
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
||||||
@ -41,6 +42,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
|
|
||||||
testSchemaValueTypes(t, s, []string{
|
testSchemaValueTypes(t, s, []string{
|
||||||
mkResourceVirtualEnvironmentVMCDROM,
|
mkResourceVirtualEnvironmentVMCDROM,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInit,
|
||||||
mkResourceVirtualEnvironmentVMCPU,
|
mkResourceVirtualEnvironmentVMCPU,
|
||||||
mkResourceVirtualEnvironmentVMDisk,
|
mkResourceVirtualEnvironmentVMDisk,
|
||||||
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
mkResourceVirtualEnvironmentVMKeyboardLayout,
|
||||||
@ -53,6 +55,7 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
schema.TypeList,
|
schema.TypeList,
|
||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
@ -76,6 +79,102 @@ func TestResourceVirtualEnvironmentVMSchema(t *testing.T) {
|
|||||||
schema.TypeString,
|
schema.TypeString,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
cloudInitSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMCloudInit)
|
||||||
|
|
||||||
|
testRequiredArguments(t, cloudInitSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitUserAccount,
|
||||||
|
})
|
||||||
|
|
||||||
|
testOptionalArguments(t, cloudInitSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitDNS,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfig,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSchemaValueTypes(t, cloudInitSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitDNS,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfig,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitUserAccount,
|
||||||
|
}, []schema.ValueType{
|
||||||
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
|
})
|
||||||
|
|
||||||
|
cloudInitDNSSchema := testNestedSchemaExistence(t, cloudInitSchema, mkResourceVirtualEnvironmentVMCloudInitDNS)
|
||||||
|
|
||||||
|
testOptionalArguments(t, cloudInitDNSSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitDNSDomain,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitDNSServer,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSchemaValueTypes(t, cloudInitDNSSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitDNSDomain,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitDNSServer,
|
||||||
|
}, []schema.ValueType{
|
||||||
|
schema.TypeString,
|
||||||
|
schema.TypeString,
|
||||||
|
})
|
||||||
|
|
||||||
|
cloudInitIPConfigSchema := testNestedSchemaExistence(t, cloudInitSchema, mkResourceVirtualEnvironmentVMCloudInitIPConfig)
|
||||||
|
|
||||||
|
testOptionalArguments(t, cloudInitIPConfigSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv4,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv6,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSchemaValueTypes(t, cloudInitIPConfigSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv4,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv6,
|
||||||
|
}, []schema.ValueType{
|
||||||
|
schema.TypeList,
|
||||||
|
schema.TypeList,
|
||||||
|
})
|
||||||
|
|
||||||
|
cloudInitIPConfigIPv4Schema := testNestedSchemaExistence(t, cloudInitIPConfigSchema, mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv4)
|
||||||
|
|
||||||
|
testOptionalArguments(t, cloudInitIPConfigIPv4Schema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv4Address,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv4Gateway,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSchemaValueTypes(t, cloudInitIPConfigIPv4Schema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv4Address,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv4Gateway,
|
||||||
|
}, []schema.ValueType{
|
||||||
|
schema.TypeString,
|
||||||
|
schema.TypeString,
|
||||||
|
})
|
||||||
|
|
||||||
|
cloudInitIPConfigIPv6Schema := testNestedSchemaExistence(t, cloudInitIPConfigSchema, mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv6)
|
||||||
|
|
||||||
|
testOptionalArguments(t, cloudInitIPConfigIPv6Schema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv6Address,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv6Gateway,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSchemaValueTypes(t, cloudInitIPConfigIPv6Schema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv6Address,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitIPConfigIPv6Gateway,
|
||||||
|
}, []schema.ValueType{
|
||||||
|
schema.TypeString,
|
||||||
|
schema.TypeString,
|
||||||
|
})
|
||||||
|
|
||||||
|
cloudInitUserAccountSchema := testNestedSchemaExistence(t, cloudInitSchema, mkResourceVirtualEnvironmentVMCloudInitUserAccount)
|
||||||
|
|
||||||
|
testRequiredArguments(t, cloudInitUserAccountSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitUserAccountKeys,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitUserAccountUsername,
|
||||||
|
})
|
||||||
|
|
||||||
|
testSchemaValueTypes(t, cloudInitUserAccountSchema, []string{
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitUserAccountKeys,
|
||||||
|
mkResourceVirtualEnvironmentVMCloudInitUserAccountUsername,
|
||||||
|
}, []schema.ValueType{
|
||||||
|
schema.TypeList,
|
||||||
|
schema.TypeString,
|
||||||
|
})
|
||||||
|
|
||||||
cpuSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMCPU)
|
cpuSchema := testNestedSchemaExistence(t, s, mkResourceVirtualEnvironmentVMCPU)
|
||||||
|
|
||||||
testOptionalArguments(t, cpuSchema, []string{
|
testOptionalArguments(t, cpuSchema, []string{
|
||||||
|
Loading…
Reference in New Issue
Block a user