From 6956a12edf0f00ae1f0890a06e0b5c7d940b8ad1 Mon Sep 17 00:00:00 2001 From: Volker Theile Date: Tue, 29 Apr 2025 13:24:30 +0200 Subject: [PATCH] Log the origin VM specification for better troubleshooting (#73) When importing a VM from VMware or OpenStack, there is no log output of the origin VM spec that is going to be imported. In some situations this information would be helpful for troubleshooting and to understand what is going wrong. For better processing of the amount of data the specification should be printed as JSON to be able to prettify it externally and to inspect it easily. Related to: https://github.com/harvester/harvester/issues/8013 Signed-off-by: Volker Theile --- pkg/source/openstack/client.go | 9 ++++++++ pkg/source/vmware/client.go | 9 ++++++++ pkg/util/logrus.go | 26 +++++++++++++++++++++ pkg/util/logrus_test.go | 41 ++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 pkg/util/logrus.go create mode 100644 pkg/util/logrus_test.go diff --git a/pkg/source/openstack/client.go b/pkg/source/openstack/client.go index a5dac3c..33ad507 100644 --- a/pkg/source/openstack/client.go +++ b/pkg/source/openstack/client.go @@ -31,6 +31,7 @@ import ( migration "github.com/harvester/vm-import-controller/pkg/apis/migration.harvesterhci.io/v1beta1" "github.com/harvester/vm-import-controller/pkg/server" + "github.com/harvester/vm-import-controller/pkg/util" ) const ( @@ -416,6 +417,14 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku return nil, fmt.Errorf("error finding VM in GenerateVirtualMachine: %v", err) } + // Log the origin VM specification for better troubleshooting. + // Note, JSON is used to be able to prettify the output for better readability. + logrus.WithFields(util.FieldsToJSON(logrus.Fields{ + "name": vm.Name, + "namespace": vm.Namespace, + "spec": vmObj, + }, []string{"spec"})).Info("Origin spec of the VM to be imported") + flavorObj, err := flavors.Get(c.ctx, c.computeClient, vmObj.Flavor["id"].(string)).Extract() if err != nil { return nil, fmt.Errorf("error looking up flavor: %v", err) diff --git a/pkg/source/vmware/client.go b/pkg/source/vmware/client.go index 1f54bf0..df7ad93 100644 --- a/pkg/source/vmware/client.go +++ b/pkg/source/vmware/client.go @@ -26,6 +26,7 @@ import ( migration "github.com/harvester/vm-import-controller/pkg/apis/migration.harvesterhci.io/v1beta1" "github.com/harvester/vm-import-controller/pkg/qemu" "github.com/harvester/vm-import-controller/pkg/server" + "github.com/harvester/vm-import-controller/pkg/util" ) type Client struct { @@ -315,6 +316,14 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku return nil, err } + // Log the origin VM specification for better troubleshooting. + // Note, JSON is used to be able to prettify the output for better readability. + logrus.WithFields(util.FieldsToJSON(logrus.Fields{ + "name": vm.Name, + "namespace": vm.Namespace, + "spec": o, + }, []string{"spec"})).Info("Origin spec of the VM to be imported") + // Need CPU, Socket, Memory, VirtualNIC information to perform the mapping networkInfo := identifyNetworkCards(o.Config.Hardware.Device) diff --git a/pkg/util/logrus.go b/pkg/util/logrus.go new file mode 100644 index 0000000..4b6ecbe --- /dev/null +++ b/pkg/util/logrus.go @@ -0,0 +1,26 @@ +package util + +import ( + "encoding/json" + + "github.com/sirupsen/logrus" +) + +// FieldsToJSON is a helper function to convert specified fields in a logrus.Fields map to JSON strings. +func FieldsToJSON(fields logrus.Fields, keys []string) logrus.Fields { + for _, k := range keys { + v, ok := fields[k] + if ok { + vBytes, err := json.Marshal(v) + if err != nil { + logrus.WithFields(logrus.Fields{ + "key": k, + "value": v, + }).Errorf("Failed to marshall field: %v", err) + } else { + fields[k] = string(vBytes) + } + } + } + return fields +} diff --git a/pkg/util/logrus_test.go b/pkg/util/logrus_test.go new file mode 100644 index 0000000..c6eca1a --- /dev/null +++ b/pkg/util/logrus_test.go @@ -0,0 +1,41 @@ +package util + +import ( + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" +) + +func TestLogJSON_success(t *testing.T) { + type myStruct struct { + ID int `json:"id"` + Name string `json:"name"` + } + assert := require.New(t) + fields := logrus.Fields{ + "key1": "value1", + "key2": myStruct{ID: 1, Name: "foo"}, + "key3": 4815162342, + } + + FieldsToJSON(fields, []string{"key2"}) + assert.Equal(fields["key1"], "value1", "expected value not to modified") + assert.JSONEq(fields["key2"].(string), `{"id":1,"name":"foo"}`, "expected value to be marshalled to JSON") + assert.Equal(fields["key3"], 4815162342, "expected value not to modified") +} + +func TestLogJSON_fail(t *testing.T) { + type myStruct struct { + Name string + Secret chan int + } + assert := require.New(t) + fields := logrus.Fields{ + "key1": myStruct{Name: "John", Secret: make(chan int)}, // this will fail to marshal + "key2": "value2", + } + + FieldsToJSON(fields, []string{"key1"}) + assert.IsType(fields["key1"], myStruct{}, "expected value not to marshalled to JSON") +}