0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-08-23 03:48:35 +00:00

Fix VM ID collision

This commit is contained in:
Dan Petersen 2020-04-03 22:12:44 +02:00
parent b1d16d641a
commit 9775ede37e
7 changed files with 108 additions and 13 deletions

View File

@ -3,6 +3,7 @@
BUG FIXES: BUG FIXES:
* library/virtual_environment_nodes: Fix node IP address format * library/virtual_environment_nodes: Fix node IP address format
* resource/virtual_environment_vm: Fix VM ID collision when `vm_id` is not specified
WORKAROUNDS: WORKAROUNDS:

1
go.sum
View File

@ -78,6 +78,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=

View File

@ -18,6 +18,9 @@ type CustomBool bool
// CustomCommaSeparatedList allows a JSON string to also be a string array. // CustomCommaSeparatedList allows a JSON string to also be a string array.
type CustomCommaSeparatedList []string type CustomCommaSeparatedList []string
// CustomInt allows a JSON integer value to also be a string.
type CustomInt int
// CustomLineBreakSeparatedList allows a multiline JSON string to also be a string array. // CustomLineBreakSeparatedList allows a multiline JSON string to also be a string array.
type CustomLineBreakSeparatedList []string type CustomLineBreakSeparatedList []string
@ -70,6 +73,25 @@ func (r *CustomCommaSeparatedList) UnmarshalJSON(b []byte) error {
return nil return nil
} }
// UnmarshalJSON converts a JSON value to an integer.
func (r *CustomInt) UnmarshalJSON(b []byte) error {
s := string(b)
if strings.HasPrefix(s, "\"") && strings.HasSuffix(s, "\"") {
s = s[1 : len(s)-1]
}
i, err := strconv.ParseInt(s, 10, 32)
if err != nil {
return err
}
*r = CustomInt(i)
return nil
}
// MarshalJSON converts a boolean to a JSON value. // MarshalJSON converts a boolean to a JSON value.
func (r *CustomLineBreakSeparatedList) MarshalJSON() ([]byte, error) { func (r *CustomLineBreakSeparatedList) MarshalJSON() ([]byte, error) {
s := strings.Join(*r, "\n") s := strings.Join(*r, "\n")

View File

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package proxmox
import (
"errors"
)
// GetClusterNextID retrieves the next free VM identifier for the cluster.
func (c *VirtualEnvironmentClient) GetClusterNextID(vmID *int) (*int, error) {
reqBody := &VirtualEnvironmentClusterNextIDRequestBody{
VMID: vmID,
}
resBody := &VirtualEnvironmentClusterNextIDResponseBody{}
err := c.DoRequest(hmGET, "cluster/nextid", reqBody, resBody)
if err != nil {
return nil, err
}
if resBody.Data == nil {
return nil, errors.New("The server did not include a data object in the response")
}
return (*int)(resBody.Data), nil
}

View File

@ -0,0 +1,15 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
package proxmox
// VirtualEnvironmentClusterNextIDRequestBody contains the data for a cluster next id request.
type VirtualEnvironmentClusterNextIDRequestBody struct {
VMID *int `json:"vmid,omitempty" url:"vmid,omitempty"`
}
// VirtualEnvironmentClusterNextIDResponseBody contains the body from a cluster next id response.
type VirtualEnvironmentClusterNextIDResponseBody struct {
Data *CustomInt `json:"data,omitempty"`
}

View File

@ -7,11 +7,22 @@ package proxmox
import ( import (
"errors" "errors"
"fmt" "fmt"
"log"
"net/url" "net/url"
"strings" "strings"
"sync"
"time" "time"
) )
const (
getVMIDStep = 1
)
var (
getVMIDCounter = -1
getVMIDCounterMutex = &sync.Mutex{}
)
// CloneVM clones a virtual machine. // CloneVM clones a virtual machine.
func (c *VirtualEnvironmentClient) CloneVM(nodeName string, vmID int, d *VirtualEnvironmentVMCloneRequestBody) error { func (c *VirtualEnvironmentClient) CloneVM(nodeName string, vmID int, d *VirtualEnvironmentVMCloneRequestBody) error {
return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/clone", url.PathEscape(nodeName), vmID), d, nil) return c.DoRequest(hmPOST, fmt.Sprintf("nodes/%s/qemu/%d/clone", url.PathEscape(nodeName), vmID), d, nil)
@ -45,30 +56,46 @@ func (c *VirtualEnvironmentClient) GetVM(nodeName string, vmID int) (*VirtualEnv
// GetVMID retrieves the next available VM identifier. // GetVMID retrieves the next available VM identifier.
func (c *VirtualEnvironmentClient) GetVMID() (*int, error) { func (c *VirtualEnvironmentClient) GetVMID() (*int, error) {
nodes, err := c.ListNodes() getVMIDCounterMutex.Lock()
defer getVMIDCounterMutex.Unlock()
if err != nil { if getVMIDCounter < 0 {
return nil, err nextVMID, err := c.GetClusterNextID(nil)
if err != nil {
return nil, err
}
if nextVMID == nil {
return nil, errors.New("Unable to retrieve the next available VM identifier")
}
getVMIDCounter = *nextVMID + getVMIDStep
log.Printf("[DEBUG] Determined next available VM identifier to be %d", *nextVMID)
return nextVMID, nil
} }
vmID := 100 vmID := getVMIDCounter
VMID:
for vmID <= 2147483637 { for vmID <= 2147483637 {
for _, n := range nodes { _, err := c.GetClusterNextID(&vmID)
err := c.DoRequest(hmGET, fmt.Sprintf("nodes/%s/qemu/%d/status/current", url.PathEscape(n.Name), vmID), nil, nil)
if err == nil { if err != nil {
vmID += 5 vmID += getVMIDStep
continue VMID continue
}
} }
getVMIDCounter = vmID + getVMIDStep
log.Printf("[DEBUG] Determined next available VM identifier to be %d", vmID)
return &vmID, nil return &vmID, nil
} }
return nil, errors.New("Unable to retrieve the next available VM identifier") return nil, errors.New("Unable to determine the next available VM identifier")
} }
// GetVMNetworkInterfacesFromAgent retrieves the network interfaces reported by the QEMU agent. // GetVMNetworkInterfacesFromAgent retrieves the network interfaces reported by the QEMU agent.

View File

@ -349,7 +349,7 @@ func getVLANIDsValidator() schema.SchemaValidateFunc {
func getVMIDValidator() schema.SchemaValidateFunc { func getVMIDValidator() schema.SchemaValidateFunc {
return func(i interface{}, k string) (ws []string, es []error) { return func(i interface{}, k string) (ws []string, es []error) {
min := 1 min := 100
max := 2147483647 max := 2147483647
v, ok := i.(int) v, ok := i.(int)