mirror of
https://github.com/letic/terraform-provider-proxmox.git
synced 2024-10-03 01:01:05 +00:00
add onboot, multi disks, and multi nic
This commit is contained in:
parent
6d51c5bf55
commit
217a5cf262
22
README.md
22
README.md
@ -78,13 +78,27 @@ resource "proxmox_vm_qemu" "prepprovision-test" {
|
||||
target_node = "proxmox1-xx"
|
||||
|
||||
clone = "terraform-ubuntu1404-template"
|
||||
storage = "local"
|
||||
cores = 3
|
||||
sockets = 1
|
||||
memory = 2560
|
||||
disk_gb = 4
|
||||
nic = "virtio"
|
||||
network {
|
||||
id = 0
|
||||
model = "virtio"
|
||||
}
|
||||
network {
|
||||
id = 1
|
||||
model = "virtio"
|
||||
bridge = "vmbr1"
|
||||
}
|
||||
disk {
|
||||
id = 0
|
||||
type = virtio
|
||||
storage = local-lvm
|
||||
storage_type = lvm
|
||||
size = 4G
|
||||
backup = true
|
||||
}
|
||||
preprovision = true
|
||||
ssh_forward_ip = "10.0.0.1"
|
||||
ssh_user = "terraform"
|
||||
ssh_private_key = <<EOF
|
||||
@ -117,7 +131,7 @@ You can start from either an ISO or clone an existing VM.
|
||||
Optimally, you could create a VM resource you will use a clone base with an ISO, and make the rest of the VM resources depend on that base "template" and clone it.
|
||||
|
||||
Interesting parameters:
|
||||
|
||||
**preprovision** - to enable or disable internal pre-provisioning (e.g. if you already have another way to provision VMs). Conflicts with: `ssh_forward_ip`, `ssh_user`, `ssh_private_key`, `os_type`, `os_network_config`.
|
||||
**os_type** -
|
||||
* cloud-init - from Proxmox 5.2
|
||||
* ubuntu -(https://github.com/Telmate/terraform-ubuntu-proxmox-iso)
|
||||
|
@ -61,7 +61,7 @@ func applyFn(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
time.Sleep(10 * time.Second)
|
||||
vmParams := map[string]string{
|
||||
vmParams := map[string]interface{}{
|
||||
"net1": data.Get("net1").(string),
|
||||
}
|
||||
_, err = client.SetVmConfig(vmr, vmParams)
|
||||
|
@ -42,9 +42,10 @@ func resourceVmQemu() *schema.Resource {
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"ssh_forward_ip": {
|
||||
Type: schema.TypeString,
|
||||
"onboot": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: true,
|
||||
},
|
||||
"iso": {
|
||||
Type: schema.TypeString,
|
||||
@ -56,31 +57,10 @@ func resourceVmQemu() *schema.Resource {
|
||||
Optional: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
"storage": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
"storage_type": {
|
||||
Type: schema.TypeString,
|
||||
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)
|
||||
},
|
||||
},
|
||||
"qemu_os": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
Default: "l26",
|
||||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||||
if old == "" {
|
||||
return true // reading empty ok
|
||||
}
|
||||
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
||||
},
|
||||
},
|
||||
"memory": {
|
||||
Type: schema.TypeInt,
|
||||
@ -94,9 +74,120 @@ func resourceVmQemu() *schema.Resource {
|
||||
Type: schema.TypeInt,
|
||||
Required: true,
|
||||
},
|
||||
"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,
|
||||
Computed: true,
|
||||
},
|
||||
"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.
|
||||
"disk_gb": {
|
||||
Type: schema.TypeFloat,
|
||||
Required: true,
|
||||
Deprecated: "Use `disk.size` instead",
|
||||
Optional: true,
|
||||
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
|
||||
// bigger ok
|
||||
oldf, _ := strconv.ParseFloat(old, 64)
|
||||
@ -104,19 +195,51 @@ func resourceVmQemu() *schema.Resource {
|
||||
return oldf >= newf
|
||||
},
|
||||
},
|
||||
"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.
|
||||
"nic": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Deprecated: "Use `network` instead",
|
||||
Optional: true,
|
||||
},
|
||||
"bridge": {
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
Deprecated: "Use `network.bridge` instead",
|
||||
Optional: true,
|
||||
},
|
||||
"vlan": {
|
||||
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)
|
||||
},
|
||||
},
|
||||
"os_type": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
@ -129,6 +252,10 @@ func resourceVmQemu() *schema.Resource {
|
||||
return strings.TrimSpace(old) == strings.TrimSpace(new)
|
||||
},
|
||||
},
|
||||
"ssh_forward_ip": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"ssh_user": {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
@ -147,16 +274,6 @@ func resourceVmQemu() *schema.Resource {
|
||||
Optional: true,
|
||||
Default: false,
|
||||
},
|
||||
"mac": {
|
||||
Type: schema.TypeString,
|
||||
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)
|
||||
},
|
||||
},
|
||||
"ci_wait": { // how long to wait before provision
|
||||
Type: schema.TypeInt,
|
||||
Optional: true,
|
||||
@ -199,6 +316,12 @@ func resourceVmQemu() *schema.Resource {
|
||||
Type: schema.TypeString,
|
||||
Optional: true,
|
||||
},
|
||||
"preprovision": {
|
||||
Type: schema.TypeBool,
|
||||
Optional: true,
|
||||
Default: true,
|
||||
ConflictsWith: []string{"ssh_forward_ip", "ssh_user", "ssh_private_key", "os_type", "os_network_config"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -210,20 +333,23 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
pmParallelBegin(pconf)
|
||||
client := pconf.Client
|
||||
vmName := d.Get("name").(string)
|
||||
networks := d.Get("network").(*schema.Set)
|
||||
qemuNetworks := devicesSetToMap(networks)
|
||||
disks := d.Get("disk").(*schema.Set)
|
||||
qemuDisks := devicesSetToMap(disks)
|
||||
diskGB := d.Get("disk_gb").(float64)
|
||||
|
||||
config := pxapi.ConfigQemu{
|
||||
Name: vmName,
|
||||
Description: d.Get("desc").(string),
|
||||
Storage: d.Get("storage").(string),
|
||||
Onboot: d.Get("onboot").(bool),
|
||||
Memory: d.Get("memory").(int),
|
||||
QemuCores: d.Get("cores").(int),
|
||||
QemuSockets: d.Get("sockets").(int),
|
||||
DiskSize: diskGB,
|
||||
QemuOs: d.Get("qemu_os").(string),
|
||||
QemuNicModel: d.Get("nic").(string),
|
||||
QemuBrige: d.Get("bridge").(string),
|
||||
QemuVlanTag: d.Get("vlan").(int),
|
||||
QemuMacAddr: d.Get("mac").(string),
|
||||
QemuNetworks: qemuNetworks,
|
||||
QemuDisks: qemuDisks,
|
||||
// Cloud-init.
|
||||
CIuser: d.Get("ciuser").(string),
|
||||
CIpassword: d.Get("cipassword").(string),
|
||||
Searchdomain: d.Get("searchdomain").(string),
|
||||
@ -231,6 +357,14 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
Sshkeys: d.Get("sshkeys").(string),
|
||||
Ipconfig0: d.Get("ipconfig0").(string),
|
||||
Ipconfig1: d.Get("ipconfig1").(string),
|
||||
// Deprecated single disk config.
|
||||
Storage: d.Get("storage").(string),
|
||||
DiskSize: diskGB,
|
||||
// Deprecated single nic config.
|
||||
QemuNicModel: d.Get("nic").(string),
|
||||
QemuBrige: d.Get("bridge").(string),
|
||||
QemuVlanTag: d.Get("vlan").(int),
|
||||
QemuMacAddr: d.Get("mac").(string),
|
||||
}
|
||||
log.Print("[DEBUG] checking for duplicate name")
|
||||
dupVmr, _ := client.GetVmRefByName(vmName)
|
||||
@ -275,7 +409,7 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
// give sometime to proxmox to catchup
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
err = prepareDiskSize(client, vmr, diskGB)
|
||||
err = prepareDiskSize(client, vmr, qemuDisks)
|
||||
if err != nil {
|
||||
pmParallelEnd(pconf)
|
||||
return err
|
||||
@ -303,7 +437,7 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
// give sometime to proxmox to catchup
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
err = prepareDiskSize(client, vmr, diskGB)
|
||||
err = prepareDiskSize(client, vmr, qemuDisks)
|
||||
if err != nil {
|
||||
pmParallelEnd(pconf)
|
||||
return err
|
||||
@ -321,68 +455,8 @@ func resourceVmQemuCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
sshPort := "22"
|
||||
sshHost := ""
|
||||
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]
|
||||
}
|
||||
} else {
|
||||
log.Print("[DEBUG] setting up SSH forward")
|
||||
sshPort, err = pxapi.SshForwardUsernet(vmr, client)
|
||||
if err != nil {
|
||||
pmParallelEnd(pconf)
|
||||
return err
|
||||
}
|
||||
sshHost = d.Get("ssh_forward_ip").(string)
|
||||
}
|
||||
|
||||
// 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,
|
||||
"pm_tls_insecure": "true", // TODO - pass pm_tls_insecure state around, but if we made it this far, default insecure
|
||||
})
|
||||
|
||||
switch d.Get("os_type").(string) {
|
||||
|
||||
case "ubuntu":
|
||||
// give sometime to bootup
|
||||
time.Sleep(9 * time.Second)
|
||||
err = preProvisionUbuntu(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "centos":
|
||||
// give sometime to bootup
|
||||
time.Sleep(9 * time.Second)
|
||||
err = preProvisionCentos(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Unknown os_type: %s", d.Get("os_type").(string))
|
||||
}
|
||||
// Apply pre-provision if enabled.
|
||||
preprovision(d, pconf, client, vmr, true)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -397,20 +471,23 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
return err
|
||||
}
|
||||
vmName := d.Get("name").(string)
|
||||
configDisksSet := d.Get("disk").(*schema.Set)
|
||||
qemuDisks := devicesSetToMap(configDisksSet)
|
||||
configNetworksSet := d.Get("network").(*schema.Set)
|
||||
qemuNetworks := devicesSetToMap(configNetworksSet)
|
||||
diskGB := d.Get("disk_gb").(float64)
|
||||
|
||||
config := pxapi.ConfigQemu{
|
||||
Name: vmName,
|
||||
Description: d.Get("desc").(string),
|
||||
Storage: d.Get("storage").(string),
|
||||
Onboot: d.Get("onboot").(bool),
|
||||
Memory: d.Get("memory").(int),
|
||||
QemuCores: d.Get("cores").(int),
|
||||
QemuSockets: d.Get("sockets").(int),
|
||||
DiskSize: diskGB,
|
||||
QemuOs: d.Get("qemu_os").(string),
|
||||
QemuNicModel: d.Get("nic").(string),
|
||||
QemuBrige: d.Get("bridge").(string),
|
||||
QemuVlanTag: d.Get("vlan").(int),
|
||||
QemuMacAddr: d.Get("mac").(string),
|
||||
QemuNetworks: qemuNetworks,
|
||||
QemuDisks: qemuDisks,
|
||||
// Cloud-init.
|
||||
CIuser: d.Get("ciuser").(string),
|
||||
CIpassword: d.Get("cipassword").(string),
|
||||
Searchdomain: d.Get("searchdomain").(string),
|
||||
@ -418,6 +495,14 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
Sshkeys: d.Get("sshkeys").(string),
|
||||
Ipconfig0: d.Get("ipconfig0").(string),
|
||||
Ipconfig1: d.Get("ipconfig1").(string),
|
||||
// Deprecated single disk config.
|
||||
Storage: d.Get("storage").(string),
|
||||
DiskSize: diskGB,
|
||||
// Deprecated single nic config.
|
||||
QemuNicModel: d.Get("nic").(string),
|
||||
QemuBrige: d.Get("bridge").(string),
|
||||
QemuVlanTag: d.Get("vlan").(int),
|
||||
QemuMacAddr: d.Get("mac").(string),
|
||||
}
|
||||
|
||||
err = config.UpdateConfig(vmr, client)
|
||||
@ -429,31 +514,24 @@ func resourceVmQemuUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
// give sometime to proxmox to catchup
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
prepareDiskSize(client, vmr, diskGB)
|
||||
prepareDiskSize(client, vmr, qemuDisks)
|
||||
|
||||
// give sometime to proxmox to catchup
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
// 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)
|
||||
|
||||
if err != nil {
|
||||
} else if err != nil {
|
||||
pmParallelEnd(pconf)
|
||||
return err
|
||||
}
|
||||
|
||||
sshPort, err := pxapi.SshForwardUsernet(vmr, client)
|
||||
if err != nil {
|
||||
pmParallelEnd(pconf)
|
||||
return err
|
||||
}
|
||||
d.SetConnInfo(map[string]string{
|
||||
"type": "ssh",
|
||||
"host": d.Get("ssh_forward_ip").(string),
|
||||
"port": sshPort,
|
||||
"user": d.Get("ssh_user").(string),
|
||||
"private_key": d.Get("ssh_private_key").(string),
|
||||
})
|
||||
// Apply pre-provision if enabled.
|
||||
preprovision(d, pconf, client, vmr, false)
|
||||
|
||||
pmParallelEnd(pconf)
|
||||
|
||||
// give sometime to bootup
|
||||
@ -478,18 +556,12 @@ func resourceVmQemuRead(d *schema.ResourceData, meta interface{}) error {
|
||||
d.Set("target_node", vmr.Node())
|
||||
d.Set("name", config.Name)
|
||||
d.Set("desc", config.Description)
|
||||
d.Set("storage", config.Storage)
|
||||
d.Set("storage_type", config.StorageType)
|
||||
d.Set("onboot", config.Onboot)
|
||||
d.Set("memory", config.Memory)
|
||||
d.Set("cores", config.QemuCores)
|
||||
d.Set("sockets", config.QemuSockets)
|
||||
d.Set("disk_gb", config.DiskSize)
|
||||
d.Set("qemu_os", config.QemuOs)
|
||||
d.Set("nic", config.QemuNicModel)
|
||||
d.Set("bridge", config.QemuBrige)
|
||||
d.Set("vlan", config.QemuVlanTag)
|
||||
d.Set("mac", config.QemuMacAddr)
|
||||
|
||||
// Cloud-init.
|
||||
d.Set("ciuser", config.CIuser)
|
||||
d.Set("cipassword", config.CIpassword)
|
||||
d.Set("searchdomain", config.Searchdomain)
|
||||
@ -497,6 +569,23 @@ func resourceVmQemuRead(d *schema.ResourceData, meta interface{}) error {
|
||||
d.Set("sshkeys", config.Sshkeys)
|
||||
d.Set("ipconfig0", config.Ipconfig0)
|
||||
d.Set("ipconfig1", config.Ipconfig1)
|
||||
// Disks.
|
||||
configDisksSet := d.Get("disk").(*schema.Set)
|
||||
activeDisksSet := updateDevicesSet(configDisksSet, config.QemuDisks)
|
||||
d.Set("disk", activeDisksSet)
|
||||
// Networks.
|
||||
configNetworksSet := d.Get("network").(*schema.Set)
|
||||
activeNetworksSet := updateDevicesSet(configNetworksSet, config.QemuNetworks)
|
||||
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)
|
||||
|
||||
pmParallelEnd(pconf)
|
||||
return nil
|
||||
@ -526,18 +615,182 @@ func resourceVmQemuDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func prepareDiskSize(client *pxapi.Client, vmr *pxapi.VmRef, diskGB float64) error {
|
||||
// Increase disk size if original disk was smaller than new disk.
|
||||
func prepareDiskSize(
|
||||
client *pxapi.Client,
|
||||
vmr *pxapi.VmRef,
|
||||
diskConfMap pxapi.QemuDevices,
|
||||
) error {
|
||||
clonedConfig, err := pxapi.NewConfigQemuFromApi(vmr, client)
|
||||
for _, diskConf := range diskConfMap {
|
||||
diskID := diskConf["id"].(int)
|
||||
diskName := fmt.Sprintf("%v%v", diskConf["type"], diskID)
|
||||
|
||||
diskSizeGB := diskConf["size"].(string)
|
||||
diskSize, _ := strconv.ParseFloat(strings.Trim(diskSizeGB, "G"), 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if diskGB > clonedConfig.DiskSize {
|
||||
log.Print("[DEBUG] resizing disk " + clonedConfig.StorageType)
|
||||
diffSize := int(math.Ceil(diskGB - clonedConfig.DiskSize))
|
||||
_, err = client.ResizeQemuDisk(vmr, clonedConfig.StorageType+"0", diffSize)
|
||||
|
||||
if _, diskExists := clonedConfig.QemuDisks[diskID]; !diskExists {
|
||||
return err
|
||||
}
|
||||
clonedDiskSizeGB := clonedConfig.QemuDisks[diskID]["size"].(string)
|
||||
clonedDiskSize, _ := strconv.ParseFloat(strings.Trim(clonedDiskSizeGB, "G"), 64)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// Converting from schema.TypeSet to map of id and conf for each device,
|
||||
// which will be sent to Proxmox API.
|
||||
func devicesSetToMap(devicesSet *schema.Set) pxapi.QemuDevices {
|
||||
|
||||
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.
|
||||
func updateDevicesSet(
|
||||
devicesSet *schema.Set,
|
||||
devicesMap pxapi.QemuDevices,
|
||||
) *schema.Set {
|
||||
|
||||
configDevicesMap := devicesSetToMap(devicesSet)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
return activeDevicesMap
|
||||
}
|
||||
|
||||
// Internal pre-provision.
|
||||
func preprovision(
|
||||
d *schema.ResourceData,
|
||||
pconf *providerConfiguration,
|
||||
client *pxapi.Client,
|
||||
vmr *pxapi.VmRef,
|
||||
systemPreProvision bool,
|
||||
) error {
|
||||
|
||||
if d.Get("preprovision").(bool) {
|
||||
log.Print("[DEBUG] setting up SSH forward")
|
||||
sshPort, err := pxapi.SshForwardUsernet(vmr, client)
|
||||
if err != nil {
|
||||
pmParallelEnd(pconf)
|
||||
return err
|
||||
}
|
||||
|
||||
// Done with proxmox API, end parallel and do the SSH things
|
||||
pmParallelEnd(pconf)
|
||||
|
||||
d.SetConnInfo(map[string]string{
|
||||
"type": "ssh",
|
||||
"host": d.Get("ssh_forward_ip").(string),
|
||||
"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,
|
||||
})
|
||||
|
||||
if systemPreProvision {
|
||||
switch d.Get("os_type").(string) {
|
||||
|
||||
case "ubuntu":
|
||||
// give sometime to bootup
|
||||
time.Sleep(9 * time.Second)
|
||||
err = preProvisionUbuntu(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case "centos":
|
||||
// give sometime to bootup
|
||||
time.Sleep(9 * time.Second)
|
||||
err = preProvisionCentos(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Unknown os_type: %s", d.Get("os_type").(string))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user