2017-02-09 04:53:24 +00:00
|
|
|
package proxmox
|
|
|
|
|
|
|
|
import (
|
2017-02-10 19:43:21 +00:00
|
|
|
"fmt"
|
2017-02-13 21:09:55 +00:00
|
|
|
"log"
|
2018-07-16 18:57:46 +00:00
|
|
|
"math"
|
2017-02-28 17:30:44 +00:00
|
|
|
"path"
|
2018-07-13 17:25:37 +00:00
|
|
|
"regexp"
|
2017-02-10 19:43:21 +00:00
|
|
|
"strconv"
|
2017-03-06 23:44:46 +00:00
|
|
|
"strings"
|
2017-02-13 19:11:33 +00:00
|
|
|
"time"
|
2018-07-10 17:25:46 +00:00
|
|
|
|
|
|
|
pxapi "github.com/Telmate/proxmox-api-go/proxmox"
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
2017-02-09 04:53:24 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func resourceVmQemu() *schema.Resource {
|
2017-02-09 21:36:31 +00:00
|
|
|
*pxapi.Debug = true
|
2017-02-09 04:53:24 +00:00
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceVmQemuCreate,
|
|
|
|
Read: resourceVmQemuRead,
|
2017-02-10 19:43:21 +00:00
|
|
|
Update: resourceVmQemuUpdate,
|
2017-02-09 04:53:24 +00:00
|
|
|
Delete: resourceVmQemuDelete,
|
2017-05-24 17:18:06 +00:00
|
|
|
Importer: &schema.ResourceImporter{
|
2019-05-23 11:39:39 +00:00
|
|
|
State: schema.ImportStatePassthrough,
|
2017-05-24 17:18:06 +00:00
|
|
|
},
|
2017-02-09 04:53:24 +00:00
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"name": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"desc": {
|
|
|
|
Type: schema.TypeString,
|
2017-02-09 21:36:31 +00:00
|
|
|
Optional: true,
|
2017-05-01 21:13:58 +00:00
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
|
|
|
"target_node": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
2018-07-13 17:25:37 +00:00
|
|
|
ForceNew: true,
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
2018-07-29 01:39:11 +00:00
|
|
|
"onboot": {
|
|
|
|
Type: schema.TypeBool,
|
2018-07-16 16:10:23 +00:00
|
|
|
Optional: true,
|
2018-07-29 01:39:11 +00:00
|
|
|
Default: true,
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
2019-08-05 18:00:29 +00:00
|
|
|
"boot": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "cdn",
|
|
|
|
},
|
|
|
|
"bootdisk": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
2019-04-12 14:57:49 +00:00
|
|
|
"agent": {
|
2019-05-16 23:56:17 +00:00
|
|
|
Type: schema.TypeInt,
|
2019-04-12 14:57:49 +00:00
|
|
|
Optional: true,
|
2019-05-22 02:24:15 +00:00
|
|
|
Default: 0,
|
2019-04-12 14:57:49 +00:00
|
|
|
},
|
2017-02-09 21:36:31 +00:00
|
|
|
"iso": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2018-07-13 17:25:37 +00:00
|
|
|
ForceNew: true,
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
|
|
|
"clone": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2018-07-13 17:25:37 +00:00
|
|
|
ForceNew: true,
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
2019-09-30 11:23:40 +00:00
|
|
|
"full_clone": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Default: true,
|
|
|
|
},
|
2017-02-10 19:43:21 +00:00
|
|
|
"qemu_os": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "l26",
|
2019-05-22 02:24:15 +00:00
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
if new == "l26" {
|
|
|
|
return len(d.Get("clone").(string)) > 0 // the cloned source may have a different os, which we shoud leave alone
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
|
|
|
"memory": {
|
|
|
|
Type: schema.TypeInt,
|
2019-05-23 12:39:18 +00:00
|
|
|
Optional: true,
|
|
|
|
Default: 512,
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
|
|
|
"cores": {
|
|
|
|
Type: schema.TypeInt,
|
2019-05-23 12:39:18 +00:00
|
|
|
Optional: true,
|
|
|
|
Default: 1,
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
|
|
|
"sockets": {
|
|
|
|
Type: schema.TypeInt,
|
2019-05-23 12:39:18 +00:00
|
|
|
Optional: true,
|
|
|
|
Default: 1,
|
2017-02-10 19:43:21 +00:00
|
|
|
},
|
2019-08-06 07:48:51 +00:00
|
|
|
"cpu": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "host",
|
|
|
|
},
|
|
|
|
"numa": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
2019-08-06 10:38:48 +00:00
|
|
|
"hotplug": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "network,disk,usb",
|
|
|
|
},
|
2019-08-05 09:00:26 +00:00
|
|
|
"scsihw": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "",
|
|
|
|
},
|
2018-07-29 01:39:11 +00:00
|
|
|
"network": &schema.Schema{
|
|
|
|
Type: schema.TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
ConflictsWith: []string{"nic", "bridge", "vlan", "mac"},
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"id": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"model": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"macaddr": &schema.Schema{
|
|
|
|
// TODO: Find a way to set MAC address in .tf config.
|
|
|
|
Type: schema.TypeString,
|
2019-03-07 16:04:45 +00:00
|
|
|
Optional: true,
|
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
if new == "" {
|
|
|
|
return true // macaddr auto-generates and its ok
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2018-07-29 01:39:11 +00:00
|
|
|
},
|
|
|
|
"bridge": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "nat",
|
|
|
|
},
|
|
|
|
"tag": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Description: "VLAN tag.",
|
|
|
|
Default: -1,
|
|
|
|
},
|
|
|
|
"firewall": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
"rate": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: -1,
|
|
|
|
},
|
|
|
|
"queues": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: -1,
|
|
|
|
},
|
|
|
|
"link_down": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"disk": &schema.Schema{
|
|
|
|
Type: schema.TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
ConflictsWith: []string{"disk_gb", "storage", "storage_type"},
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"id": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"type": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"storage": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"storage_type": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "dir",
|
|
|
|
Description: "One of PVE types as described: https://pve.proxmox.com/wiki/Storage",
|
|
|
|
},
|
|
|
|
"size": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"format": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "raw",
|
|
|
|
},
|
|
|
|
"cache": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "none",
|
|
|
|
},
|
|
|
|
"backup": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
"iothread": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
"replicate": &schema.Schema{
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Deprecated single disk config.
|
2017-02-10 19:43:21 +00:00
|
|
|
"disk_gb": {
|
2018-07-29 01:39:11 +00:00
|
|
|
Type: schema.TypeFloat,
|
|
|
|
Deprecated: "Use `disk.size` instead",
|
|
|
|
Optional: true,
|
2018-07-16 18:57:46 +00:00
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
// bigger ok
|
|
|
|
oldf, _ := strconv.ParseFloat(old, 64)
|
|
|
|
newf, _ := strconv.ParseFloat(new, 64)
|
|
|
|
return oldf >= newf
|
|
|
|
},
|
2017-02-10 19:43:21 +00:00
|
|
|
},
|
2018-07-29 01:39:11 +00:00
|
|
|
"storage": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Deprecated: "Use `disk.storage` instead",
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"storage_type": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Deprecated: "Use `disk.type` instead",
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: false,
|
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
if new == "" {
|
|
|
|
return true // empty template ok
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Deprecated single nic config.
|
2017-02-10 19:43:21 +00:00
|
|
|
"nic": {
|
2018-07-29 01:39:11 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Deprecated: "Use `network` instead",
|
|
|
|
Optional: true,
|
2017-02-10 19:43:21 +00:00
|
|
|
},
|
|
|
|
"bridge": {
|
2018-07-29 01:39:11 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Deprecated: "Use `network.bridge` instead",
|
|
|
|
Optional: true,
|
2017-02-10 19:43:21 +00:00
|
|
|
},
|
|
|
|
"vlan": {
|
2018-07-29 01:39:11 +00:00
|
|
|
Type: schema.TypeInt,
|
|
|
|
Deprecated: "Use `network.tag` instead",
|
|
|
|
Optional: true,
|
|
|
|
Default: -1,
|
|
|
|
},
|
|
|
|
"mac": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Deprecated: "Use `network.macaddr` to access the auto generated MAC address",
|
|
|
|
Optional: true,
|
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
if new == "" {
|
|
|
|
return true // macaddr auto-generates and its ok
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2017-02-10 19:43:21 +00:00
|
|
|
},
|
2019-08-06 14:05:34 +00:00
|
|
|
"serial": &schema.Schema{
|
2019-09-04 19:45:02 +00:00
|
|
|
Type: schema.TypeSet,
|
|
|
|
Optional: true,
|
2019-08-06 14:05:34 +00:00
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"id": &schema.Schema{
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
"type": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-02-10 19:43:21 +00:00
|
|
|
"os_type": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"os_network_config": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2017-02-09 21:36:31 +00:00
|
|
|
ForceNew: true,
|
2017-05-01 21:13:58 +00:00
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2017-02-09 04:53:24 +00:00
|
|
|
},
|
2018-07-29 01:39:11 +00:00
|
|
|
"ssh_forward_ip": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
2017-02-11 00:06:14 +00:00
|
|
|
"ssh_user": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
"ssh_private_key": {
|
2017-02-15 23:32:37 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Sensitive: true,
|
2017-03-06 23:44:46 +00:00
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2017-02-11 00:06:14 +00:00
|
|
|
},
|
2017-02-28 17:30:44 +00:00
|
|
|
"force_create": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
2018-07-13 17:25:37 +00:00
|
|
|
"ci_wait": { // how long to wait before provision
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
Default: 30,
|
2018-07-19 16:53:00 +00:00
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
if old == "" {
|
|
|
|
return true // old empty ok
|
|
|
|
}
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2018-07-13 17:25:37 +00:00
|
|
|
},
|
|
|
|
"ciuser": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"cipassword": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"searchdomain": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"nameserver": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"sshkeys": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2018-07-16 18:57:46 +00:00
|
|
|
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
|
|
|
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
|
|
|
},
|
2018-07-13 17:25:37 +00:00
|
|
|
},
|
|
|
|
"ipconfig0": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
"ipconfig1": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
2018-07-29 01:39:11 +00:00
|
|
|
"preprovision": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: true,
|
|
|
|
ConflictsWith: []string{"ssh_forward_ip", "ssh_user", "ssh_private_key", "os_type", "os_network_config"},
|
|
|
|
},
|
2019-07-13 23:48:45 +00:00
|
|
|
"pool": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
2017-02-09 21:36:31 +00:00
|
|
|
},
|
|
|
|
}
|
2017-02-09 04:53:24 +00:00
|
|
|
}
|
|
|
|
|
2018-07-13 17:25:37 +00:00
|
|
|
var rxIPconfig = regexp.MustCompile("ip6?=([0-9a-fA-F:\\.]+)")
|
|
|
|
|
2017-02-09 04:53:24 +00:00
|
|
|
func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
|
2017-07-21 01:35:01 +00:00
|
|
|
pconf := meta.(*providerConfiguration)
|
|
|
|
pmParallelBegin(pconf)
|
|
|
|
client := pconf.Client
|
2017-02-10 19:43:21 +00:00
|
|
|
vmName := d.Get("name").(string)
|
2018-07-29 01:39:11 +00:00
|
|
|
networks := d.Get("network").(*schema.Set)
|
2019-06-05 18:32:29 +00:00
|
|
|
qemuNetworks := DevicesSetToMap(networks)
|
2018-07-29 01:39:11 +00:00
|
|
|
disks := d.Get("disk").(*schema.Set)
|
2019-06-05 18:32:29 +00:00
|
|
|
qemuDisks := DevicesSetToMap(disks)
|
2019-08-06 14:05:34 +00:00
|
|
|
serials := d.Get("serial").(*schema.Set)
|
|
|
|
qemuSerials := DevicesSetToMap(serials)
|
2018-07-29 01:39:11 +00:00
|
|
|
|
2017-02-09 04:53:24 +00:00
|
|
|
config := pxapi.ConfigQemu{
|
2017-02-10 19:43:21 +00:00
|
|
|
Name: vmName,
|
|
|
|
Description: d.Get("desc").(string),
|
2018-07-29 01:39:11 +00:00
|
|
|
Onboot: d.Get("onboot").(bool),
|
2019-08-05 18:00:29 +00:00
|
|
|
Boot: d.Get("boot").(string),
|
|
|
|
BootDisk: d.Get("bootdisk").(string),
|
2019-05-16 23:09:53 +00:00
|
|
|
Agent: d.Get("agent").(int),
|
2017-02-10 19:43:21 +00:00
|
|
|
Memory: d.Get("memory").(int),
|
|
|
|
QemuCores: d.Get("cores").(int),
|
|
|
|
QemuSockets: d.Get("sockets").(int),
|
2019-08-06 07:48:51 +00:00
|
|
|
QemuCpu: d.Get("cpu").(string),
|
|
|
|
QemuNuma: d.Get("numa").(bool),
|
2019-08-06 10:38:48 +00:00
|
|
|
Hotplug: d.Get("hotplug").(string),
|
2019-08-05 09:00:26 +00:00
|
|
|
Scsihw: d.Get("scsihw").(string),
|
2017-02-10 19:43:21 +00:00
|
|
|
QemuOs: d.Get("qemu_os").(string),
|
2018-07-29 01:39:11 +00:00
|
|
|
QemuNetworks: qemuNetworks,
|
|
|
|
QemuDisks: qemuDisks,
|
2019-08-06 14:05:34 +00:00
|
|
|
QemuSerials: qemuSerials,
|
2018-07-29 01:39:11 +00:00
|
|
|
// Cloud-init.
|
2018-07-13 17:25:37 +00:00
|
|
|
CIuser: d.Get("ciuser").(string),
|
|
|
|
CIpassword: d.Get("cipassword").(string),
|
|
|
|
Searchdomain: d.Get("searchdomain").(string),
|
|
|
|
Nameserver: d.Get("nameserver").(string),
|
|
|
|
Sshkeys: d.Get("sshkeys").(string),
|
|
|
|
Ipconfig0: d.Get("ipconfig0").(string),
|
|
|
|
Ipconfig1: d.Get("ipconfig1").(string),
|
2018-07-29 01:39:11 +00:00
|
|
|
// Deprecated single disk config.
|
|
|
|
Storage: d.Get("storage").(string),
|
2018-11-12 21:25:17 +00:00
|
|
|
DiskSize: d.Get("disk_gb").(float64),
|
2018-07-29 01:39:11 +00:00
|
|
|
// Deprecated single nic config.
|
|
|
|
QemuNicModel: d.Get("nic").(string),
|
|
|
|
QemuBrige: d.Get("bridge").(string),
|
|
|
|
QemuVlanTag: d.Get("vlan").(int),
|
|
|
|
QemuMacAddr: d.Get("mac").(string),
|
2017-02-09 21:36:31 +00:00
|
|
|
}
|
2017-02-13 21:09:55 +00:00
|
|
|
log.Print("[DEBUG] checking for duplicate name")
|
2017-02-10 19:43:21 +00:00
|
|
|
dupVmr, _ := client.GetVmRefByName(vmName)
|
|
|
|
|
2017-02-28 17:30:44 +00:00
|
|
|
forceCreate := d.Get("force_create").(bool)
|
|
|
|
targetNode := d.Get("target_node").(string)
|
2019-07-13 23:48:45 +00:00
|
|
|
pool := d.Get("pool").(string)
|
2017-02-28 17:30:44 +00:00
|
|
|
|
|
|
|
if dupVmr != nil && forceCreate {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-28 17:30:44 +00:00
|
|
|
return fmt.Errorf("Duplicate VM name (%s) with vmId: %d. Set force_create=false to recycle", vmName, dupVmr.VmId())
|
|
|
|
} else if dupVmr != nil && dupVmr.Node() != targetNode {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-28 17:30:44 +00:00
|
|
|
return fmt.Errorf("Duplicate VM name (%s) with vmId: %d on different target_node=%s", vmName, dupVmr.VmId(), dupVmr.Node())
|
2017-02-10 19:43:21 +00:00
|
|
|
}
|
2017-02-09 21:36:31 +00:00
|
|
|
|
2017-02-28 17:30:44 +00:00
|
|
|
vmr := dupVmr
|
|
|
|
|
|
|
|
if vmr == nil {
|
|
|
|
// get unique id
|
2017-07-21 01:35:01 +00:00
|
|
|
nextid, err := nextVmId(pconf)
|
2017-02-09 21:36:31 +00:00
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-09 21:36:31 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-02-28 17:30:44 +00:00
|
|
|
vmr = pxapi.NewVmRef(nextid)
|
2017-02-13 21:09:55 +00:00
|
|
|
|
2019-07-13 23:48:45 +00:00
|
|
|
// set target node and pool
|
2017-02-28 17:30:44 +00:00
|
|
|
vmr.SetNode(targetNode)
|
2019-07-13 23:48:45 +00:00
|
|
|
if pool != "" {
|
|
|
|
vmr.SetPool(pool)
|
|
|
|
}
|
|
|
|
|
2017-02-28 17:30:44 +00:00
|
|
|
// check if ISO or clone
|
|
|
|
if d.Get("clone").(string) != "" {
|
2019-09-30 11:23:40 +00:00
|
|
|
fullClone := 1
|
|
|
|
if !d.Get("full_clone").(bool) {
|
|
|
|
fullClone = 0
|
|
|
|
}
|
|
|
|
config.FullClone = &fullClone
|
|
|
|
|
2017-02-28 17:30:44 +00:00
|
|
|
sourceVmr, err := client.GetVmRefByName(d.Get("clone").(string))
|
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-28 17:30:44 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Print("[DEBUG] cloning VM")
|
|
|
|
err = config.CloneVm(sourceVmr, vmr, client)
|
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-28 17:30:44 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-02-13 21:09:55 +00:00
|
|
|
|
2017-07-21 01:35:01 +00:00
|
|
|
// give sometime to proxmox to catchup
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
err = prepareDiskSize(client, vmr, qemuDisks)
|
2017-02-28 17:30:44 +00:00
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-28 17:30:44 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if d.Get("iso").(string) != "" {
|
|
|
|
config.QemuIso = d.Get("iso").(string)
|
|
|
|
err := config.CreateVm(vmr, client)
|
2017-02-13 21:09:55 +00:00
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-13 21:09:55 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-01-12 18:04:16 +00:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("Either clone or iso must be set")
|
2017-02-13 21:09:55 +00:00
|
|
|
}
|
2017-02-28 17:30:44 +00:00
|
|
|
} else {
|
|
|
|
log.Printf("[DEBUG] recycling VM vmId: %d", vmr.VmId())
|
2017-03-23 21:24:25 +00:00
|
|
|
|
|
|
|
client.StopVm(vmr)
|
|
|
|
|
|
|
|
err := config.UpdateConfig(vmr, client)
|
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-03-23 21:24:25 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-07-21 01:35:01 +00:00
|
|
|
|
|
|
|
// give sometime to proxmox to catchup
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
err = prepareDiskSize(client, vmr, qemuDisks)
|
2017-02-09 21:36:31 +00:00
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-09 21:36:31 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2017-05-24 17:18:06 +00:00
|
|
|
d.SetId(resourceId(targetNode, "qemu", vmr.VmId()))
|
2017-02-10 19:43:21 +00:00
|
|
|
|
2017-07-21 01:35:01 +00:00
|
|
|
// give sometime to proxmox to catchup
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
2017-02-13 21:09:55 +00:00
|
|
|
log.Print("[DEBUG] starting VM")
|
2017-02-28 17:30:44 +00:00
|
|
|
_, err := client.StartVm(vmr)
|
2017-02-09 21:36:31 +00:00
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-09 21:36:31 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-07-13 17:25:37 +00:00
|
|
|
|
2019-01-08 22:52:34 +00:00
|
|
|
err = initConnInfo(d, pconf, client, vmr, &config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
// Apply pre-provision if enabled.
|
|
|
|
preprovision(d, pconf, client, vmr, true)
|
2017-07-21 01:35:01 +00:00
|
|
|
|
2017-02-09 04:53:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-10 19:43:21 +00:00
|
|
|
func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
|
2017-07-21 01:35:01 +00:00
|
|
|
pconf := meta.(*providerConfiguration)
|
|
|
|
pmParallelBegin(pconf)
|
|
|
|
client := pconf.Client
|
2019-01-30 20:25:00 +00:00
|
|
|
_, _, vmID, err := parseResourceId(d.Id())
|
|
|
|
if err != nil {
|
|
|
|
pmParallelEnd(pconf)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
vmr := pxapi.NewVmRef(vmID)
|
|
|
|
_, err = client.GetVmInfo(vmr)
|
2017-02-13 21:09:55 +00:00
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-13 21:09:55 +00:00
|
|
|
return err
|
|
|
|
}
|
2018-07-29 01:39:11 +00:00
|
|
|
configDisksSet := d.Get("disk").(*schema.Set)
|
2019-06-05 18:32:29 +00:00
|
|
|
qemuDisks := DevicesSetToMap(configDisksSet)
|
2018-07-29 01:39:11 +00:00
|
|
|
configNetworksSet := d.Get("network").(*schema.Set)
|
2019-06-05 18:32:29 +00:00
|
|
|
qemuNetworks := DevicesSetToMap(configNetworksSet)
|
2019-08-06 14:05:34 +00:00
|
|
|
serials := d.Get("serial").(*schema.Set)
|
|
|
|
qemuSerials := DevicesSetToMap(serials)
|
2018-07-29 01:39:11 +00:00
|
|
|
|
2017-02-14 00:08:21 +00:00
|
|
|
config := pxapi.ConfigQemu{
|
2019-01-30 20:25:00 +00:00
|
|
|
Name: d.Get("name").(string),
|
2017-02-14 00:08:21 +00:00
|
|
|
Description: d.Get("desc").(string),
|
2018-07-29 01:39:11 +00:00
|
|
|
Onboot: d.Get("onboot").(bool),
|
2019-08-05 18:00:29 +00:00
|
|
|
Boot: d.Get("boot").(string),
|
|
|
|
BootDisk: d.Get("bootdisk").(string),
|
2019-05-23 12:06:24 +00:00
|
|
|
Agent: d.Get("agent").(int),
|
2017-02-14 00:08:21 +00:00
|
|
|
Memory: d.Get("memory").(int),
|
|
|
|
QemuCores: d.Get("cores").(int),
|
|
|
|
QemuSockets: d.Get("sockets").(int),
|
2019-08-06 07:48:51 +00:00
|
|
|
QemuCpu: d.Get("cpu").(string),
|
|
|
|
QemuNuma: d.Get("numa").(bool),
|
2019-08-06 10:38:48 +00:00
|
|
|
Hotplug: d.Get("hotplug").(string),
|
2019-08-05 09:00:26 +00:00
|
|
|
Scsihw: d.Get("scsihw").(string),
|
2017-02-14 00:08:21 +00:00
|
|
|
QemuOs: d.Get("qemu_os").(string),
|
2018-07-29 01:39:11 +00:00
|
|
|
QemuNetworks: qemuNetworks,
|
|
|
|
QemuDisks: qemuDisks,
|
2019-08-06 14:05:34 +00:00
|
|
|
QemuSerials: qemuSerials,
|
2018-07-29 01:39:11 +00:00
|
|
|
// Cloud-init.
|
2018-07-13 17:25:37 +00:00
|
|
|
CIuser: d.Get("ciuser").(string),
|
|
|
|
CIpassword: d.Get("cipassword").(string),
|
|
|
|
Searchdomain: d.Get("searchdomain").(string),
|
|
|
|
Nameserver: d.Get("nameserver").(string),
|
|
|
|
Sshkeys: d.Get("sshkeys").(string),
|
|
|
|
Ipconfig0: d.Get("ipconfig0").(string),
|
|
|
|
Ipconfig1: d.Get("ipconfig1").(string),
|
2018-07-29 01:39:11 +00:00
|
|
|
// Deprecated single disk config.
|
|
|
|
Storage: d.Get("storage").(string),
|
2018-11-12 21:25:17 +00:00
|
|
|
DiskSize: d.Get("disk_gb").(float64),
|
2018-07-29 01:39:11 +00:00
|
|
|
// Deprecated single nic config.
|
|
|
|
QemuNicModel: d.Get("nic").(string),
|
|
|
|
QemuBrige: d.Get("bridge").(string),
|
|
|
|
QemuVlanTag: d.Get("vlan").(int),
|
|
|
|
QemuMacAddr: d.Get("mac").(string),
|
2017-02-14 00:08:21 +00:00
|
|
|
}
|
|
|
|
|
2017-07-21 15:34:57 +00:00
|
|
|
err = config.UpdateConfig(vmr, client)
|
|
|
|
if err != nil {
|
|
|
|
pmParallelEnd(pconf)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// give sometime to proxmox to catchup
|
|
|
|
time.Sleep(5 * time.Second)
|
2017-02-14 00:08:21 +00:00
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
prepareDiskSize(client, vmr, qemuDisks)
|
2017-02-14 00:08:21 +00:00
|
|
|
|
2017-07-21 15:34:57 +00:00
|
|
|
// give sometime to proxmox to catchup
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
// Start VM only if it wasn't running.
|
|
|
|
vmState, err := client.GetVmState(vmr)
|
|
|
|
if err == nil && vmState["status"] == "stopped" {
|
|
|
|
log.Print("[DEBUG] starting VM")
|
|
|
|
_, err = client.StartVm(vmr)
|
|
|
|
} else if err != nil {
|
2017-07-21 15:34:57 +00:00
|
|
|
pmParallelEnd(pconf)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-08 22:52:34 +00:00
|
|
|
err = initConnInfo(d, pconf, client, vmr, &config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
// Apply pre-provision if enabled.
|
|
|
|
preprovision(d, pconf, client, vmr, false)
|
|
|
|
|
2017-07-21 15:34:57 +00:00
|
|
|
// give sometime to bootup
|
|
|
|
time.Sleep(9 * time.Second)
|
2017-02-10 19:43:21 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-09 21:36:31 +00:00
|
|
|
func resourceVmQemuRead(d *schema.ResourceData, meta interface{}) error {
|
2017-07-21 01:35:01 +00:00
|
|
|
pconf := meta.(*providerConfiguration)
|
|
|
|
pmParallelBegin(pconf)
|
|
|
|
client := pconf.Client
|
2019-01-30 20:25:00 +00:00
|
|
|
_, _, vmID, err := parseResourceId(d.Id())
|
2017-02-10 19:43:21 +00:00
|
|
|
if err != nil {
|
2019-01-30 20:25:00 +00:00
|
|
|
pmParallelEnd(pconf)
|
2019-05-23 11:39:39 +00:00
|
|
|
d.SetId("")
|
2019-01-30 20:25:00 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
vmr := pxapi.NewVmRef(vmID)
|
|
|
|
_, err = client.GetVmInfo(vmr)
|
|
|
|
if err != nil {
|
|
|
|
pmParallelEnd(pconf)
|
2017-02-10 19:43:21 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
config, err := pxapi.NewConfigQemuFromApi(vmr, client)
|
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-10 19:43:21 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-05-24 17:18:06 +00:00
|
|
|
d.SetId(resourceId(vmr.Node(), "qemu", vmr.VmId()))
|
2017-02-10 19:58:10 +00:00
|
|
|
d.Set("target_node", vmr.Node())
|
2017-02-10 19:43:21 +00:00
|
|
|
d.Set("name", config.Name)
|
|
|
|
d.Set("desc", config.Description)
|
2018-07-29 01:39:11 +00:00
|
|
|
d.Set("onboot", config.Onboot)
|
2019-08-05 18:00:29 +00:00
|
|
|
d.Set("boot", config.Boot)
|
|
|
|
d.Set("bootdisk", config.BootDisk)
|
2019-05-22 02:24:15 +00:00
|
|
|
d.Set("agent", config.Agent)
|
2017-02-10 19:43:21 +00:00
|
|
|
d.Set("memory", config.Memory)
|
|
|
|
d.Set("cores", config.QemuCores)
|
|
|
|
d.Set("sockets", config.QemuSockets)
|
2019-08-06 07:48:51 +00:00
|
|
|
d.Set("cpu", config.QemuCpu)
|
|
|
|
d.Set("numa", config.QemuNuma)
|
2019-08-06 10:38:48 +00:00
|
|
|
d.Set("hotplug", config.Hotplug)
|
2019-08-05 09:00:26 +00:00
|
|
|
d.Set("scsihw", config.Scsihw)
|
2017-02-10 19:43:21 +00:00
|
|
|
d.Set("qemu_os", config.QemuOs)
|
2018-07-29 01:39:11 +00:00
|
|
|
// Cloud-init.
|
2018-07-13 17:25:37 +00:00
|
|
|
d.Set("ciuser", config.CIuser)
|
|
|
|
d.Set("cipassword", config.CIpassword)
|
|
|
|
d.Set("searchdomain", config.Searchdomain)
|
|
|
|
d.Set("nameserver", config.Nameserver)
|
|
|
|
d.Set("sshkeys", config.Sshkeys)
|
|
|
|
d.Set("ipconfig0", config.Ipconfig0)
|
|
|
|
d.Set("ipconfig1", config.Ipconfig1)
|
2018-07-29 01:39:11 +00:00
|
|
|
// Disks.
|
|
|
|
configDisksSet := d.Get("disk").(*schema.Set)
|
2019-06-25 22:02:18 +00:00
|
|
|
activeDisksSet := UpdateDevicesSet(configDisksSet, config.QemuDisks)
|
2018-07-29 01:39:11 +00:00
|
|
|
d.Set("disk", activeDisksSet)
|
|
|
|
// Networks.
|
|
|
|
configNetworksSet := d.Get("network").(*schema.Set)
|
2019-06-25 22:02:18 +00:00
|
|
|
activeNetworksSet := UpdateDevicesSet(configNetworksSet, config.QemuNetworks)
|
2018-07-29 01:39:11 +00:00
|
|
|
d.Set("network", activeNetworksSet)
|
|
|
|
// Deprecated single disk config.
|
|
|
|
d.Set("storage", config.Storage)
|
|
|
|
d.Set("disk_gb", config.DiskSize)
|
|
|
|
d.Set("storage_type", config.StorageType)
|
|
|
|
// Deprecated single nic config.
|
|
|
|
d.Set("nic", config.QemuNicModel)
|
|
|
|
d.Set("bridge", config.QemuBrige)
|
|
|
|
d.Set("vlan", config.QemuVlanTag)
|
|
|
|
d.Set("mac", config.QemuMacAddr)
|
2019-07-13 23:48:45 +00:00
|
|
|
d.Set("pool", vmr.Pool())
|
2019-08-06 14:05:34 +00:00
|
|
|
//Serials
|
|
|
|
configSerialsSet := d.Get("serial").(*schema.Set)
|
|
|
|
activeSerialSet := UpdateDevicesSet(configSerialsSet, config.QemuSerials)
|
|
|
|
d.Set("serial", activeSerialSet)
|
2018-07-13 17:25:37 +00:00
|
|
|
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-10 19:43:21 +00:00
|
|
|
return nil
|
2017-02-09 04:53:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func resourceVmQemuDelete(d *schema.ResourceData, meta interface{}) error {
|
2017-07-21 01:35:01 +00:00
|
|
|
pconf := meta.(*providerConfiguration)
|
|
|
|
pmParallelBegin(pconf)
|
|
|
|
client := pconf.Client
|
2017-02-28 17:30:44 +00:00
|
|
|
vmId, _ := strconv.Atoi(path.Base(d.Id()))
|
2017-02-15 00:00:54 +00:00
|
|
|
vmr := pxapi.NewVmRef(vmId)
|
|
|
|
_, err := client.StopVm(vmr)
|
|
|
|
if err != nil {
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-15 00:00:54 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-07-21 01:35:01 +00:00
|
|
|
// give sometime to proxmox to catchup
|
|
|
|
time.Sleep(2 * time.Second)
|
2017-02-15 00:00:54 +00:00
|
|
|
_, err = client.DeleteVm(vmr)
|
2017-07-21 01:35:01 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-09 04:53:24 +00:00
|
|
|
return err
|
|
|
|
}
|
2017-02-28 17:30:44 +00:00
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
// Increase disk size if original disk was smaller than new disk.
|
|
|
|
func prepareDiskSize(
|
|
|
|
client *pxapi.Client,
|
|
|
|
vmr *pxapi.VmRef,
|
|
|
|
diskConfMap pxapi.QemuDevices,
|
|
|
|
) error {
|
2017-02-28 17:30:44 +00:00
|
|
|
clonedConfig, err := pxapi.NewConfigQemuFromApi(vmr, client)
|
2019-01-09 16:46:14 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-08 21:28:43 +00:00
|
|
|
//log.Printf("%s", clonedConfig)
|
|
|
|
for diskID, diskConf := range diskConfMap {
|
2018-07-29 01:39:11 +00:00
|
|
|
diskName := fmt.Sprintf("%v%v", diskConf["type"], diskID)
|
|
|
|
|
2019-01-09 16:46:14 +00:00
|
|
|
diskSize := diskSizeGB(diskConf["size"])
|
2018-07-29 01:39:11 +00:00
|
|
|
|
|
|
|
if _, diskExists := clonedConfig.QemuDisks[diskID]; !diskExists {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-09 16:46:14 +00:00
|
|
|
|
|
|
|
clonedDiskSize := diskSizeGB(clonedConfig.QemuDisks[diskID]["size"])
|
2018-07-29 01:39:11 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
diffSize := int(math.Ceil(diskSize - clonedDiskSize))
|
|
|
|
if diskSize > clonedDiskSize {
|
|
|
|
log.Print("[DEBUG] resizing disk " + diskName)
|
|
|
|
_, err = client.ResizeQemuDisk(vmr, diskName, diffSize)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-01-09 16:46:14 +00:00
|
|
|
func diskSizeGB(dcSize interface{}) float64 {
|
|
|
|
var diskSize float64
|
|
|
|
switch dcSize.(type) {
|
|
|
|
case string:
|
2019-08-16 17:17:00 +00:00
|
|
|
diskString := strings.ToUpper(dcSize.(string))
|
|
|
|
re := regexp.MustCompile("([0-9]+)([A-Z]*)")
|
|
|
|
diskArray := re.FindStringSubmatch(diskString)
|
2019-09-04 19:45:02 +00:00
|
|
|
|
2019-08-16 17:17:00 +00:00
|
|
|
diskSize, _ = strconv.ParseFloat(diskArray[1], 64)
|
2019-09-04 19:45:02 +00:00
|
|
|
|
2019-08-16 17:17:00 +00:00
|
|
|
if len(diskArray) >= 3 {
|
|
|
|
switch diskArray[2] {
|
|
|
|
case "G", "GB":
|
|
|
|
//Nothing to do
|
|
|
|
case "M", "MB":
|
|
|
|
diskSize /= 1000
|
|
|
|
case "K", "KB":
|
|
|
|
diskSize /= 1000000
|
|
|
|
}
|
|
|
|
}
|
2019-01-09 16:46:14 +00:00
|
|
|
case float64:
|
|
|
|
diskSize = dcSize.(float64)
|
|
|
|
}
|
|
|
|
return diskSize
|
|
|
|
}
|
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
// Converting from schema.TypeSet to map of id and conf for each device,
|
|
|
|
// which will be sent to Proxmox API.
|
2019-06-05 18:32:29 +00:00
|
|
|
func DevicesSetToMap(devicesSet *schema.Set) pxapi.QemuDevices {
|
2018-07-29 01:39:11 +00:00
|
|
|
|
|
|
|
devicesMap := pxapi.QemuDevices{}
|
|
|
|
|
|
|
|
for _, set := range devicesSet.List() {
|
|
|
|
setMap, isMap := set.(map[string]interface{})
|
|
|
|
if isMap {
|
|
|
|
setID := setMap["id"].(int)
|
|
|
|
devicesMap[setID] = setMap
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return devicesMap
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update schema.TypeSet with new values comes from Proxmox API.
|
|
|
|
// TODO: Maybe it's better to create a new Set instead add to current one.
|
2019-06-25 22:02:18 +00:00
|
|
|
func UpdateDevicesSet(
|
2018-07-29 01:39:11 +00:00
|
|
|
devicesSet *schema.Set,
|
|
|
|
devicesMap pxapi.QemuDevices,
|
|
|
|
) *schema.Set {
|
|
|
|
|
2019-06-05 18:32:29 +00:00
|
|
|
configDevicesMap := DevicesSetToMap(devicesSet)
|
2019-06-25 22:02:18 +00:00
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
activeDevicesMap := updateDevicesDefaults(devicesMap, configDevicesMap)
|
|
|
|
|
|
|
|
for _, setConf := range devicesSet.List() {
|
|
|
|
devicesSet.Remove(setConf)
|
|
|
|
setConfMap := setConf.(map[string]interface{})
|
|
|
|
deviceID := setConfMap["id"].(int)
|
|
|
|
// Value type should be one of types allowed by Terraform schema types.
|
|
|
|
for key, value := range activeDevicesMap[deviceID] {
|
|
|
|
// This nested switch is used for nested config like in `net[n]`,
|
|
|
|
// where Proxmox uses `key=<0|1>` in string" at the same time
|
|
|
|
// a boolean could be used in ".tf" files.
|
|
|
|
switch setConfMap[key].(type) {
|
|
|
|
case bool:
|
|
|
|
switch value.(type) {
|
|
|
|
// If the key is bool and value is int (which comes from Proxmox API),
|
|
|
|
// should be converted to bool (as in ".tf" conf).
|
|
|
|
case int:
|
|
|
|
sValue := strconv.Itoa(value.(int))
|
|
|
|
bValue, err := strconv.ParseBool(sValue)
|
|
|
|
if err == nil {
|
|
|
|
setConfMap[key] = bValue
|
|
|
|
}
|
|
|
|
// If value is bool, which comes from Terraform conf, add it directly.
|
|
|
|
case bool:
|
|
|
|
setConfMap[key] = value
|
|
|
|
}
|
|
|
|
// Anything else will be added as it is.
|
|
|
|
default:
|
|
|
|
setConfMap[key] = value
|
|
|
|
}
|
|
|
|
devicesSet.Add(setConfMap)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return devicesSet
|
|
|
|
}
|
|
|
|
|
|
|
|
// Because default values are not stored in Proxmox, so the API returns only active values.
|
|
|
|
// So to prevent Terraform doing unnecessary diffs, this function reads default values
|
|
|
|
// from Terraform itself, and fill empty fields.
|
|
|
|
func updateDevicesDefaults(
|
|
|
|
activeDevicesMap pxapi.QemuDevices,
|
|
|
|
configDevicesMap pxapi.QemuDevices,
|
|
|
|
) pxapi.QemuDevices {
|
|
|
|
|
|
|
|
for deviceID, deviceConf := range configDevicesMap {
|
|
|
|
if _, ok := activeDevicesMap[deviceID]; !ok {
|
|
|
|
activeDevicesMap[deviceID] = configDevicesMap[deviceID]
|
|
|
|
}
|
|
|
|
for key, value := range deviceConf {
|
|
|
|
if _, ok := activeDevicesMap[deviceID][key]; !ok {
|
|
|
|
activeDevicesMap[deviceID][key] = value
|
|
|
|
}
|
|
|
|
}
|
2017-07-21 15:34:57 +00:00
|
|
|
}
|
2018-07-29 01:39:11 +00:00
|
|
|
return activeDevicesMap
|
|
|
|
}
|
|
|
|
|
2019-01-08 22:52:34 +00:00
|
|
|
func initConnInfo(
|
2018-07-29 01:39:11 +00:00
|
|
|
d *schema.ResourceData,
|
|
|
|
pconf *providerConfiguration,
|
|
|
|
client *pxapi.Client,
|
|
|
|
vmr *pxapi.VmRef,
|
2019-01-08 22:52:34 +00:00
|
|
|
config *pxapi.ConfigQemu) error {
|
|
|
|
|
|
|
|
sshPort := "22"
|
|
|
|
sshHost := ""
|
|
|
|
var err error
|
|
|
|
if config.HasCloudInit() {
|
|
|
|
if d.Get("ssh_forward_ip") != nil {
|
|
|
|
sshHost = d.Get("ssh_forward_ip").(string)
|
|
|
|
}
|
|
|
|
if sshHost == "" {
|
|
|
|
// parse IP address out of ipconfig0
|
|
|
|
ipMatch := rxIPconfig.FindStringSubmatch(d.Get("ipconfig0").(string))
|
|
|
|
sshHost = ipMatch[1]
|
|
|
|
}
|
2019-09-04 19:45:02 +00:00
|
|
|
// Check if we got a speficied port
|
|
|
|
if strings.Contains(sshHost, ":") {
|
|
|
|
sshParts := strings.Split(sshHost, ":")
|
|
|
|
sshHost = sshParts[0]
|
|
|
|
sshPort = sshParts[1]
|
|
|
|
}
|
2019-01-08 22:52:34 +00:00
|
|
|
} else {
|
2018-07-29 01:39:11 +00:00
|
|
|
log.Print("[DEBUG] setting up SSH forward")
|
2019-01-08 22:52:34 +00:00
|
|
|
sshPort, err = pxapi.SshForwardUsernet(vmr, client)
|
2017-02-28 17:30:44 +00:00
|
|
|
if err != nil {
|
2018-07-29 01:39:11 +00:00
|
|
|
pmParallelEnd(pconf)
|
2017-02-28 17:30:44 +00:00
|
|
|
return err
|
|
|
|
}
|
2019-01-08 22:52:34 +00:00
|
|
|
sshHost = d.Get("ssh_forward_ip").(string)
|
|
|
|
}
|
2018-07-29 01:39:11 +00:00
|
|
|
|
2019-01-08 22:52:34 +00:00
|
|
|
// Done with proxmox API, end parallel and do the SSH things
|
|
|
|
pmParallelEnd(pconf)
|
|
|
|
|
|
|
|
d.SetConnInfo(map[string]string{
|
|
|
|
"type": "ssh",
|
|
|
|
"host": sshHost,
|
|
|
|
"port": sshPort,
|
|
|
|
"user": d.Get("ssh_user").(string),
|
|
|
|
"private_key": d.Get("ssh_private_key").(string),
|
|
|
|
"pm_api_url": client.ApiUrl,
|
|
|
|
"pm_user": client.Username,
|
|
|
|
"pm_password": client.Password,
|
2019-08-02 12:01:52 +00:00
|
|
|
"pm_otp": client.Otp,
|
2019-01-08 22:52:34 +00:00
|
|
|
"pm_tls_insecure": "true", // TODO - pass pm_tls_insecure state around, but if we made it this far, default insecure
|
|
|
|
})
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Internal pre-provision.
|
|
|
|
func preprovision(
|
|
|
|
d *schema.ResourceData,
|
|
|
|
pconf *providerConfiguration,
|
|
|
|
client *pxapi.Client,
|
|
|
|
vmr *pxapi.VmRef,
|
|
|
|
systemPreProvision bool,
|
|
|
|
) error {
|
2018-07-29 01:39:11 +00:00
|
|
|
|
2019-01-08 22:52:34 +00:00
|
|
|
if d.Get("preprovision").(bool) {
|
2018-07-29 01:39:11 +00:00
|
|
|
|
|
|
|
if systemPreProvision {
|
|
|
|
switch d.Get("os_type").(string) {
|
|
|
|
|
|
|
|
case "ubuntu":
|
|
|
|
// give sometime to bootup
|
|
|
|
time.Sleep(9 * time.Second)
|
2019-01-08 22:52:34 +00:00
|
|
|
err := preProvisionUbuntu(d)
|
2018-07-29 01:39:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
case "centos":
|
|
|
|
// give sometime to bootup
|
|
|
|
time.Sleep(9 * time.Second)
|
2019-01-08 22:52:34 +00:00
|
|
|
err := preProvisionCentos(d)
|
2018-07-29 01:39:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2019-01-08 22:52:34 +00:00
|
|
|
case "cloud-init":
|
|
|
|
// wait for OS too boot awhile...
|
|
|
|
log.Print("[DEBUG] sleeping for OS bootup...")
|
|
|
|
time.Sleep(time.Duration(d.Get("ci_wait").(int)) * time.Second)
|
|
|
|
|
2018-07-29 01:39:11 +00:00
|
|
|
default:
|
|
|
|
return fmt.Errorf("Unknown os_type: %s", d.Get("os_type").(string))
|
|
|
|
}
|
|
|
|
}
|
2017-02-28 17:30:44 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|