From 0f0374c2eb1c0e09afa4c6c249c867ad6b099a37 Mon Sep 17 00:00:00 2001 From: Serge Logvinov Date: Thu, 13 Feb 2025 17:22:44 +0200 Subject: [PATCH] feat: custom instance type Now, we can set a custom instance type using the smbios1[sku] argument Signed-off-by: Serge Logvinov --- pkg/cluster/client.go | 22 +++++++-- pkg/proxmox/instances.go | 30 ++++++++--- pkg/proxmox/instances_test.go | 93 ++++++++++++++++++++++++++--------- 3 files changed, 109 insertions(+), 36 deletions(-) diff --git a/pkg/cluster/client.go b/pkg/cluster/client.go index 558a108..b6d957c 100644 --- a/pkg/cluster/client.go +++ b/pkg/cluster/client.go @@ -180,7 +180,7 @@ func (c *Cluster) FindVMByUUID(ctx context.Context, uuid string) (*pxapi.VmRef, } if config["smbios1"] != nil { - if c.getUUID(config["smbios1"].(string)) == uuid { //nolint:errcheck + if c.getSMBSetting(config, "uuid") == uuid { return vmr, region, nil } } @@ -202,13 +202,27 @@ func (c *Cluster) GetVMName(vmInfo map[string]interface{}) string { // GetVMUUID returns the VM UUID. func (c *Cluster) GetVMUUID(vmInfo map[string]interface{}) string { if vmInfo["smbios1"] != nil { - return c.getUUID(vmInfo["smbios1"].(string)) //nolint:errcheck + return c.getSMBSetting(vmInfo, "uuid") } return "" } -func (c *Cluster) getUUID(smbios string) string { +// GetVMSKU returns the VM instance type name. +func (c *Cluster) GetVMSKU(vmInfo map[string]interface{}) string { + if vmInfo["smbios1"] != nil { + return c.getSMBSetting(vmInfo, "sku") + } + + return "" +} + +func (c *Cluster) getSMBSetting(vmInfo map[string]interface{}, name string) string { + smbios, ok := vmInfo["smbios1"].(string) + if !ok { + return "" + } + for _, l := range strings.Split(smbios, ",") { if l == "" || l == "base64=1" { continue @@ -220,7 +234,7 @@ func (c *Cluster) getUUID(smbios string) string { } for k, v := range parsedParameter { - if k == "uuid" { + if k == name { decodedString, err := base64.StdEncoding.DecodeString(v[0]) if err != nil { decodedString = []byte(v[0]) diff --git a/pkg/proxmox/instances.go b/pkg/proxmox/instances.go index 62923ad..258db55 100644 --- a/pkg/proxmox/instances.go +++ b/pkg/proxmox/instances.go @@ -19,6 +19,8 @@ package proxmox import ( "context" "fmt" + "regexp" + "strconv" "strings" pxapi "github.com/Telmate/proxmox-api-go/proxmox" @@ -38,6 +40,8 @@ type instances struct { provider cluster.Provider } +var instanceTypeNameRegexp = regexp.MustCompile(`(^[a-zA-Z0-9_.-]+)$`) + func newInstances(client *cluster.Cluster, provider cluster.Provider) *instances { return &instances{ c: client, @@ -225,7 +229,7 @@ func (i *instances) getInstance(ctx context.Context, node *v1.Node) (*pxapi.VmRe mc := metrics.NewMetricContext("getVmInfo") - vmInfo, err := px.GetVmInfo(ctx, vmRef) + vmConfig, err := px.GetVmConfig(ctx, vmRef) if mc.ObserveRequest(err) != nil { if strings.Contains(err.Error(), "not found") { return nil, "", cloudprovider.InstanceNotFound @@ -234,13 +238,13 @@ func (i *instances) getInstance(ctx context.Context, node *v1.Node) (*pxapi.VmRe return nil, "", err } - if i.c.GetVMName(vmInfo) != node.Name && i.c.GetVMUUID(vmInfo) != node.Status.NodeInfo.SystemUUID { - klog.Errorf("instances.getInstance() vm.name(%s) != node.name(%s) with uuid=%s", i.c.GetVMName(vmInfo), node.Name, node.Status.NodeInfo.SystemUUID) + if i.c.GetVMName(vmConfig) != node.Name || i.c.GetVMUUID(vmConfig) != node.Status.NodeInfo.SystemUUID { + klog.Errorf("instances.getInstance() vm.name(%s) != node.name(%s) with uuid=%s", i.c.GetVMName(vmConfig), node.Name, node.Status.NodeInfo.SystemUUID) return nil, "", cloudprovider.InstanceNotFound } - klog.V(5).Infof("instances.getInstance() vmInfo %+v", vmInfo) + klog.V(5).Infof("instances.getInstance() vmConfig %+v", vmConfig) return vmRef, region, nil } @@ -253,16 +257,26 @@ func (i *instances) getInstanceType(ctx context.Context, vmRef *pxapi.VmRef, reg mc := metrics.NewMetricContext("getVmInfo") - vmInfo, err := px.GetVmInfo(ctx, vmRef) + vmConfig, err := px.GetVmConfig(ctx, vmRef) if mc.ObserveRequest(err) != nil { return "", err } - if vmInfo["maxcpu"] == nil || vmInfo["maxmem"] == nil { + sku := i.c.GetVMSKU(vmConfig) + if sku != "" && instanceTypeNameRegexp.MatchString(sku) { + return sku, nil + } + + if vmConfig["cores"] == nil || vmConfig["memory"] == nil { return "", fmt.Errorf("instances.getInstanceType() failed to get instance type") } + memory, err := strconv.Atoi(vmConfig["memory"].(string)) //nolint:errcheck + if err != nil { + return "", err + } + return fmt.Sprintf("%.0fVCPU-%.0fGB", - vmInfo["maxcpu"].(float64), //nolint:errcheck - vmInfo["maxmem"].(float64)/1024/1024/1024), nil //nolint:errcheck + vmConfig["cores"].(float64), //nolint:errcheck + float64(memory)/1024), nil } diff --git a/pkg/proxmox/instances_test.go b/pkg/proxmox/instances_test.go index a4406dd..2240711 100644 --- a/pkg/proxmox/instances_test.go +++ b/pkg/proxmox/instances_test.go @@ -66,22 +66,20 @@ clusters: return httpmock.NewJsonResponse(200, map[string]interface{}{ "data": []interface{}{ map[string]interface{}{ - "node": "pve-1", - "type": "qemu", - "vmid": 100, - "name": "cluster-1-node-1", - "maxcpu": 4, - "maxmem": 10 * 1024 * 1024 * 1024, - "smbios1": "uuid=8af7110d-bfad-407a-a663-9527d10a6583", + "node": "pve-1", + "type": "qemu", + "vmid": 100, + "name": "cluster-1-node-1", + "maxcpu": 4, + "maxmem": 10 * 1024 * 1024 * 1024, }, map[string]interface{}{ - "node": "pve-2", - "type": "qemu", - "vmid": 101, - "name": "cluster-1-node-2", - "maxcpu": 2, - "maxmem": 5 * 1024 * 1024 * 1024, - "smbios1": "uuid=5d04cb23-ea78-40a3-af2e-dd54798dc887", + "node": "pve-2", + "type": "qemu", + "vmid": 101, + "name": "cluster-1-node-2", + "maxcpu": 2, + "maxmem": 5 * 1024 * 1024 * 1024, }, }, }) @@ -93,13 +91,12 @@ clusters: return httpmock.NewJsonResponse(200, map[string]interface{}{ "data": []interface{}{ map[string]interface{}{ - "node": "pve-3", - "type": "qemu", - "vmid": 100, - "name": "cluster-2-node-1", - "maxcpu": 1, - "maxmem": 2 * 1024 * 1024 * 1024, - "smbios1": "uuid=3d3db687-89dd-473e-8463-6599f25b36a8", + "node": "pve-3", + "type": "qemu", + "vmid": 100, + "name": "cluster-2-node-1", + "maxcpu": 1, + "maxmem": 2 * 1024 * 1024 * 1024, }, }, }) @@ -110,9 +107,12 @@ clusters: func(_ *http.Request) (*http.Response, error) { return httpmock.NewJsonResponse(200, map[string]interface{}{ "data": map[string]interface{}{ + "name": "cluster-1-node-1", "node": "pve-1", "type": "qemu", "vmid": 100, + "cores": 4, + "memory": "10240", "smbios1": "uuid=8af7110d-bfad-407a-a663-9527d10a6583", }, }) @@ -123,9 +123,12 @@ clusters: func(_ *http.Request) (*http.Response, error) { return httpmock.NewJsonResponse(200, map[string]interface{}{ "data": map[string]interface{}{ + "name": "cluster-1-node-2", "node": "pve-2", "type": "qemu", "vmid": 101, + "cores": 2, + "memory": "5120", "smbios1": "uuid=5d04cb23-ea78-40a3-af2e-dd54798dc887", }, }) @@ -136,10 +139,13 @@ clusters: func(_ *http.Request) (*http.Response, error) { return httpmock.NewJsonResponse(200, map[string]interface{}{ "data": map[string]interface{}{ + "name": "cluster-2-node-1", "node": "pve-3", "type": "qemu", "vmid": 100, - "smbios1": "uuid=3d3db687-89dd-473e-8463-6599f25b36a8", + "cores": 1, + "memory": "2048", + "smbios1": "uuid=3d3db687-89dd-473e-8463-6599f25b36a8,sku=YzEubWVkaXVt", }, }) }, @@ -237,6 +243,11 @@ func (ts *ccmTestSuite) TestInstanceExists() { Spec: v1.NodeSpec{ ProviderID: "proxmox://cluster-1/100", }, + Status: v1.NodeStatus{ + NodeInfo: v1.NodeSystemInfo{ + SystemUUID: "8af7110d-bfad-407a-a663-9527d10a6583", + }, + }, }, expected: true, }, @@ -255,7 +266,24 @@ func (ts *ccmTestSuite) TestInstanceExists() { }, }, }, - expected: true, + expected: false, + }, + { + msg: "NodeExistsWithDifferentUUID", + node: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster-1-node-1", + }, + Spec: v1.NodeSpec{ + ProviderID: "proxmox://cluster-1/100", + }, + Status: v1.NodeStatus{ + NodeInfo: v1.NodeSystemInfo{ + SystemUUID: "8af7110d-0000-0000-0000-9527d10a6583", + }, + }, + }, + expected: false, }, { msg: "NodeExistsWithDifferentNameAndUUID", @@ -405,6 +433,23 @@ func (ts *ccmTestSuite) TestInstanceShutdown() { }, expected: false, }, + { + msg: "NodeExistsWithDifferentNameAndUUID", + node: &v1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster-1-node-3", + }, + Spec: v1.NodeSpec{ + ProviderID: "proxmox://cluster-1/100", + }, + Status: v1.NodeStatus{ + NodeInfo: v1.NodeSystemInfo{ + SystemUUID: "8af7110d-0000-0000-0000-9527d10a6583", + }, + }, + }, + expected: false, + }, } for _, testCase := range tests { @@ -586,7 +631,7 @@ func (ts *ccmTestSuite) TestInstanceMetadata() { Address: "cluster-2-node-1", }, }, - InstanceType: "1VCPU-2GB", + InstanceType: "c1.medium", Region: "cluster-2", Zone: "pve-3", },