Skip to content

Commit

Permalink
feat: find node by uuid
Browse files Browse the repository at this point in the history
In some setups, the Proxmox VM name may differ from the Linux hostname.
To reliably identify a VM within a Proxmox cluster, we can use the system's UUID

Signed-off-by: Serge Logvinov <serge.logvinov@sinextra.dev>
  • Loading branch information
sergelogvinov committed Sep 14, 2024
1 parent b81ad14 commit 5876cd4
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 10 deletions.
10 changes: 2 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,6 @@ github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0q
github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0=
github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
Expand Down Expand Up @@ -216,8 +214,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
Expand All @@ -228,8 +224,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA=
golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand All @@ -255,8 +249,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down
66 changes: 66 additions & 0 deletions pkg/cluster/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ package cluster

import (
"crypto/tls"
"encoding/base64"
"fmt"
"net/http"
"net/url"
"os"
"strings"

Expand Down Expand Up @@ -107,3 +109,67 @@ func (c *Cluster) FindVMByName(name string) (*pxapi.VmRef, string, error) {

return nil, "", fmt.Errorf("vm '%s' not found", name)
}

// FindVMByUUID find a VM by uuid in all Proxmox clusters.
func (c *Cluster) FindVMByUUID(uuid string) (*pxapi.VmRef, string, error) {
for region, px := range c.proxmox {
vms, err := px.GetResourceList("vm")
if err != nil {
return nil, "", fmt.Errorf("error get resources %v", err)
}

for vmii := range vms {
vm, ok := vms[vmii].(map[string]interface{})
if !ok {
return nil, "", fmt.Errorf("failed to cast response to map, vm: %v", vm)
}

if vm["type"].(string) != "qemu" {
continue
}

vmr := pxapi.NewVmRef(int(vm["vmid"].(float64)))
vmr.SetNode(vm["node"].(string))
vmr.SetVmType("qemu")

config, err := px.GetVmConfig(vmr)
if err != nil {
return nil, "", err
}

if config["smbios1"] != nil {
if c.getUUID(config["smbios1"].(string)) == uuid {
return vmr, region, nil
}
}
}
}

return nil, "", fmt.Errorf("vm with uuid '%s' not found", uuid)
}

func (c *Cluster) getUUID(smbios string) string {
for _, l := range strings.Split(smbios, ",") {
if l == "" || l == "base64=1" {
continue
}

parsedParameter, err := url.ParseQuery(l)
if err != nil {
return ""
}

for k, v := range parsedParameter {
if k == "uuid" {
decodedString, err := base64.StdEncoding.DecodeString(v[0])
if err != nil {
decodedString = []byte(v[0])
}

return string(decodedString)
}
}
}

return ""
}
23 changes: 21 additions & 2 deletions pkg/proxmox/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ func newInstances(client *cluster.Cluster) *instances {
func (i *instances) InstanceExists(_ context.Context, node *v1.Node) (bool, error) {
klog.V(4).InfoS("instances.InstanceExists() called", "node", klog.KRef("", node.Name))

if node.Spec.ProviderID == "" {
klog.V(4).InfoS("instances.InstanceExists() empty providerID, omitting unmanaged node", "node", klog.KObj(node))

return true, nil
}

if !strings.HasPrefix(node.Spec.ProviderID, provider.ProviderName) {
klog.V(4).InfoS("instances.InstanceExists() omitting unmanaged node", "node", klog.KObj(node), "providerID", node.Spec.ProviderID)

Expand All @@ -73,6 +79,12 @@ func (i *instances) InstanceExists(_ context.Context, node *v1.Node) (bool, erro
func (i *instances) InstanceShutdown(_ context.Context, node *v1.Node) (bool, error) {
klog.V(4).InfoS("instances.InstanceShutdown() called", "node", klog.KRef("", node.Name))

if node.Spec.ProviderID == "" {
klog.V(4).InfoS("instances.InstanceShutdown() empty providerID, omitting unmanaged node", "node", klog.KObj(node))

return false, nil
}

if !strings.HasPrefix(node.Spec.ProviderID, provider.ProviderName) {
klog.V(4).InfoS("instances.InstanceShutdown() omitting unmanaged node", "node", klog.KObj(node), "providerID", node.Spec.ProviderID)

Expand Down Expand Up @@ -122,13 +134,20 @@ func (i *instances) InstanceMetadata(_ context.Context, node *v1.Node) (*cloudpr

providerID := node.Spec.ProviderID
if providerID == "" {
klog.V(4).InfoS("instances.InstanceMetadata() empty providerID, trying find by Name", "node", klog.KObj(node))
uuid := node.Status.NodeInfo.SystemUUID

klog.V(4).InfoS("instances.InstanceMetadata() empty providerID, trying find node", "node", klog.KObj(node), "uuid", uuid)

mc := metrics.NewMetricContext("findVmByName")

vmRef, region, err = i.c.FindVMByName(node.Name)
if mc.ObserveRequest(err) != nil {
return nil, fmt.Errorf("instances.InstanceMetadata() - failed to find instance by name %s: %v, skipped", node.Name, err)
mc := metrics.NewMetricContext("findVmByUUID")

vmRef, region, err = i.c.FindVMByUUID(uuid)
if mc.ObserveRequest(err) != nil {
return nil, fmt.Errorf("instances.InstanceMetadata() - failed to find instance by name/uuid %s: %v, skipped", node.Name, err)
}
}
} else if !strings.HasPrefix(node.Spec.ProviderID, provider.ProviderName) {
klog.V(4).InfoS("instances.InstanceMetadata() omitting unmanaged node", "node", klog.KObj(node), "providerID", node.Spec.ProviderID)
Expand Down

0 comments on commit 5876cd4

Please sign in to comment.