Skip to content

Commit

Permalink
Added multiple target nodes instead of a single node. (#766)
Browse files Browse the repository at this point in the history
* added data_ha_group.go to fetch HA Groups

* renamed variable in data ha group file

* added the possibility to add a node with a set of possible nodes (high availability)

* minor code fixes

---------

Co-authored-by: Pascal Kutscha <[email protected]>
  • Loading branch information
PascalKu and Pascal Kutscha authored Dec 6, 2023
1 parent 3716125 commit 8f9e2ab
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 10 deletions.
68 changes: 68 additions & 0 deletions proxmox/data_ha_group.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package proxmox

import (
"sort"

"github.com/Telmate/proxmox-api-go/proxmox"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func DataHAGroup() *schema.Resource {
return &schema.Resource{
Read: dataReadHAGroup,
Schema: map[string]*schema.Schema{
"group_name": {
Type: schema.TypeString,
Required: true,
},
"nodes": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"type": {
Type: schema.TypeString,
Computed: true,
},
"restricted": {
Type: schema.TypeBool,
Computed: true,
},
"nofailback": {
Type: schema.TypeBool,
Computed: true,
},
"comment": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

func dataReadHAGroup(data *schema.ResourceData, meta interface{}) (err error) {
pconf := meta.(*providerConfiguration)
lock := pmParallelBegin(pconf)
defer lock.unlock()

client := pconf.Client

var group *proxmox.HAGroup
group, err = client.GetHAGroupByName(data.Get("group_name").(string))
if err != nil {
return err
}

nodes := group.Nodes
sort.Strings(nodes)

data.SetId(group.Group)
_ = data.Set("nodes", nodes)
_ = data.Set("type", group.Type)
_ = data.Set("restricted", group.Restricted)
_ = data.Set("nofailback", group.NoFailback)
_ = data.Set("comment", group.Comment)
return nil
}
4 changes: 4 additions & 0 deletions proxmox/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ func Provider() *schema.Provider {
// TODO - proxmox_vm_qemu_template
},

DataSourcesMap: map[string]*schema.Resource{
"proxmox_ha_groups": DataHAGroup(),
},

ConfigureFunc: providerConfigure,
}
}
Expand Down
81 changes: 73 additions & 8 deletions proxmox/resource_vm_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"log"
"math/rand"
"net"
"net/url"
"path"
Expand Down Expand Up @@ -84,9 +85,17 @@ func resourceVmQemu() *schema.Resource {
},
"target_node": {
Type: schema.TypeString,
Required: true,
Optional: true,
Description: "The node where VM goes to",
},
"target_nodes": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Description: "A list of nodes where VM goes to",
},
"bios": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -1028,7 +1037,26 @@ func resourceVmQemuCreate(ctx context.Context, d *schema.ResourceData, meta inte
dupVmr, _ := client.GetVmRefByName(vmName)

forceCreate := d.Get("force_create").(bool)
targetNode := d.Get("target_node").(string)

var targetNodes []string
targetNodesRaw := d.Get("target_nodes").([]interface{})
targetNodes = make([]string, len(targetNodesRaw))
for i, raw := range targetNodesRaw {
targetNodes[i] = raw.(string)
}

var targetNode string

if targetNodes == nil || len(targetNodes) == 0 {
targetNode = d.Get("target_node").(string)
} else {
targetNode = targetNodes[rand.Intn(len(targetNodes))]
}

if targetNode == "" {
return diag.FromErr(fmt.Errorf("VM name (%s) has no target node! Please use target_node or target_nodes to set a specific node! %v", vmName, targetNodes))
}

pool := d.Get("pool").(string)

if dupVmr != nil && forceCreate {
Expand Down Expand Up @@ -1279,10 +1307,15 @@ func resourceVmQemuUpdate(ctx context.Context, d *schema.ResourceData, meta inte

d.Partial(true)
if d.HasChange("target_node") {
_, err := client.MigrateNode(vmr, d.Get("target_node").(string), true)
if err != nil {
return diag.FromErr(err)
// check if it must be migrated manually or it has been migrated by the promox high availability system
if vmr.Node() != d.Get("target_node").(string) {
_, err := client.MigrateNode(vmr, d.Get("target_node").(string), true)
if err != nil {
return diag.FromErr(err)
}
}

// update target node
vmr.SetNode(d.Get("target_node").(string))
}
d.Partial(false)
Expand Down Expand Up @@ -1590,12 +1623,45 @@ func resourceVmQemuRead(ctx context.Context, d *schema.ResourceData, meta interf
// Try to get information on the vm. If this call err's out
// that indicates the VM does not exist. We indicate that to terraform
// by calling a SetId("")
_, err = client.GetVmInfo(vmr)
if err != nil {

// loop through all virtual servers...?
var targetNodeVMR string = ""
var targetNodes []string
targetNodesRaw := d.Get("target_nodes").([]interface{})
targetNodes = make([]string, len(targetNodesRaw))
for i, raw := range targetNodesRaw {
targetNodes[i] = raw.(string)
}

if targetNodes == nil || len(targetNodes) == 0 {
_, err = client.GetVmInfo(vmr)
if err != nil {
logger.Debug().Int("vmid", vmID).Err(err).Msg("failed to get vm info")
d.SetId("")
return nil
}
targetNodeVMR = vmr.Node()
} else {
for _, targetNode := range targetNodes {
vmr.SetNode(targetNode)
_, err = client.GetVmInfo(vmr)
if err != nil {
d.SetId("")
}

d.SetId(resourceId(vmr.Node(), "qemu", vmr.VmId()))
logger.Debug().Any("Setting node id to", d.Get(vmr.Node()))
targetNodeVMR = targetNode
break
}
}

if targetNodeVMR == "" {
logger.Debug().Int("vmid", vmID).Err(err).Msg("failed to get vm info")
d.SetId("")
return nil
}

config, err := pxapi.NewConfigQemuFromApi(vmr, client)
if err != nil {
return diag.FromErr(err)
Expand Down Expand Up @@ -1625,7 +1691,6 @@ func resourceVmQemuRead(ctx context.Context, d *schema.ResourceData, meta interf
logger.Debug().Int("vmid", vmID).Msgf("[READ] Received Config from Proxmox API: %+v", config)

d.SetId(resourceId(vmr.Node(), "qemu", vmr.VmId()))
d.Set("target_node", vmr.Node())
d.Set("name", config.Name)
d.Set("desc", config.Description)
d.Set("bios", config.Bios)
Expand Down
10 changes: 8 additions & 2 deletions proxmox/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,8 +363,14 @@ func resourceDataToFlatValues(d *schema.ResourceData, resource *schema.Resource)
values, _ := schemaSetToFlatValues(d.Get(key).(*schema.Set), value.Elem.(*schema.Resource))
flatValues[key] = values
case schema.TypeList:
values, _ := schemaListToFlatValues(d.Get(key).([]interface{}), value.Elem.(*schema.Resource))
flatValues[key] = values
_, ok := value.Elem.(*schema.Schema)

if ok {
flatValues[key] = value.Elem.(*schema.Schema)
} else {
values, _ := schemaListToFlatValues(d.Get(key).([]interface{}), value.Elem.(*schema.Resource))
flatValues[key] = values
}
default:
flatValues[key] = "? Print Not Implemented ?"
}
Expand Down

0 comments on commit 8f9e2ab

Please sign in to comment.