From 2ff47634d8e3c1944a48aba7aceff2458a6e9eb3 Mon Sep 17 00:00:00 2001 From: Gaurav Mehta Date: Fri, 7 Mar 2025 12:17:16 +1100 Subject: [PATCH 1/2] change how network name lookup is perform when importing vmware based vm's to allow importing of VM's using Dswitch backed networks change how network name lookup is perform when importing vmware based vm's to allow importing of VM's using Dswitch backed networks --- pkg/source/vmware/client.go | 93 ++++++++++++++++++++++++++++---- pkg/source/vmware/client_test.go | 5 +- 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/pkg/source/vmware/client.go b/pkg/source/vmware/client.go index 1f54bf0..da805b6 100644 --- a/pkg/source/vmware/client.go +++ b/pkg/source/vmware/client.go @@ -14,6 +14,7 @@ import ( "github.com/vmware/govmomi/nfc" "github.com/vmware/govmomi/object" "github.com/vmware/govmomi/session" + "github.com/vmware/govmomi/view" "github.com/vmware/govmomi/vim25" "github.com/vmware/govmomi/vim25/mo" "github.com/vmware/govmomi/vim25/soap" @@ -31,8 +32,9 @@ import ( type Client struct { ctx context.Context *govmomi.Client - tmpCerts string - dc string + tmpCerts string + dc string + networkMapping map[string]string } func NewClient(ctx context.Context, endpoint string, dc string, secret *corev1.Secret) (*Client, error) { @@ -91,6 +93,11 @@ func NewClient(ctx context.Context, endpoint string, dc string, secret *corev1.S vmwareClient.ctx = ctx vmwareClient.Client = c vmwareClient.dc = dc + networkMap, err := GenerateNetworkMapByRef(ctx, c.Client) + if err != nil { + return nil, fmt.Errorf("error generating network map during client initialisation: %v", err) + } + vmwareClient.networkMapping = networkMap return vmwareClient, nil } @@ -316,7 +323,7 @@ func (c *Client) GenerateVirtualMachine(vm *migration.VirtualMachineImport) (*ku } // Need CPU, Socket, Memory, VirtualNIC information to perform the mapping - networkInfo := identifyNetworkCards(o.Config.Hardware.Device) + networkInfo := identifyNetworkCards(c.networkMapping, o.Config.Hardware.Device) vmSpec := kubevirt.VirtualMachineSpec{ RunStrategy: &[]kubevirt.VirtualMachineRunStrategy{kubevirt.RunStrategyRerunOnFailure}[0], @@ -438,38 +445,58 @@ type networkInfo struct { MappedNetwork string } -func identifyNetworkCards(devices []types.BaseVirtualDevice) []networkInfo { +func identifyNetworkCards(networkMap map[string]string, devices []types.BaseVirtualDevice) []networkInfo { var resp []networkInfo for _, d := range devices { switch d := d.(type) { case *types.VirtualVmxnet: obj := d + summary := identifyNetworkName(networkMap, *obj.GetVirtualDevice()) + if summary == "" { + summary = obj.DeviceInfo.GetDescription().Summary + } resp = append(resp, networkInfo{ - NetworkName: obj.DeviceInfo.GetDescription().Summary, + NetworkName: summary, MAC: obj.MacAddress, }) case *types.VirtualE1000e: obj := d + summary := identifyNetworkName(networkMap, *obj.GetVirtualDevice()) + if summary == "" { + summary = obj.DeviceInfo.GetDescription().Summary + } resp = append(resp, networkInfo{ - NetworkName: obj.DeviceInfo.GetDescription().Summary, + NetworkName: summary, MAC: obj.MacAddress, }) case *types.VirtualE1000: obj := d + summary := identifyNetworkName(networkMap, *obj.GetVirtualDevice()) + if summary == "" { + summary = obj.DeviceInfo.GetDescription().Summary + } resp = append(resp, networkInfo{ - NetworkName: obj.DeviceInfo.GetDescription().Summary, + NetworkName: summary, MAC: obj.MacAddress, }) case *types.VirtualVmxnet3: obj := d + summary := identifyNetworkName(networkMap, *obj.GetVirtualDevice()) + if summary == "" { + summary = obj.DeviceInfo.GetDescription().Summary + } resp = append(resp, networkInfo{ - NetworkName: obj.DeviceInfo.GetDescription().Summary, + NetworkName: summary, MAC: obj.MacAddress, }) case *types.VirtualVmxnet2: obj := d + summary := identifyNetworkName(networkMap, *obj.GetVirtualDevice()) + if summary == "" { + summary = obj.DeviceInfo.GetDescription().Summary + } resp = append(resp, networkInfo{ - NetworkName: obj.DeviceInfo.GetDescription().Summary, + NetworkName: summary, MAC: obj.MacAddress, }) } @@ -510,3 +537,51 @@ func (c *Client) SanitizeVirtualMachineImport(vm *migration.VirtualMachineImport return nil } + +// GenerateNetworkMayByRef lists all networks defined in the DC and converts them to +// network id: network name +// this subsequently used to map a network id to network name if needed based on the type +// of backing device for a nic +func GenerateNetworkMapByRef(ctx context.Context, c *vim25.Client) (map[string]string, error) { + networks, err := generateNetworkList(ctx, c) + if err != nil { + return nil, err + } + returnMap := make(map[string]string, len(networks)) + for _, v := range networks { + returnMap[v.Reference().Value] = v.Name + } + return returnMap, nil +} + +func generateNetworkList(ctx context.Context, c *vim25.Client) ([]mo.Network, error) { + var networks []mo.Network + manager := view.NewManager(c) + networkView, err := manager.CreateContainerView(ctx, c.ServiceContent.RootFolder, []string{"Network"}, true) + if err != nil { + return networks, fmt.Errorf("error generating network container view: %v", err) + } + defer networkView.Destroy(ctx) + if err := networkView.Retrieve(ctx, []string{"Network"}, nil, &networks); err != nil { + return networks, fmt.Errorf("error retreiving networks: %v", err) + } + return networks, nil +} + +// identifyNetworkName uses the backing device for a nic to identify network name correctly +// in case of a nic using a Distributed VSwitch the summary returned from device is of the form +// DVSwitch : HEX NUMBER which breaks network lookup. As a result we need to identify the network name +// from the PortGroupKey +func identifyNetworkName(networkMap map[string]string, device types.VirtualDevice) string { + var summary string + backing := device.Backing + switch b := backing.(type) { + case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo: + obj := b + summary = networkMap[obj.Port.PortgroupKey] + case *types.VirtualEthernetCardNetworkBackingInfo: + obj := b + summary = obj.DeviceName + } + return summary +} diff --git a/pkg/source/vmware/client_test.go b/pkg/source/vmware/client_test.go index 75faea0..289297c 100644 --- a/pkg/source/vmware/client_test.go +++ b/pkg/source/vmware/client_test.go @@ -284,15 +284,16 @@ func Test_identifyNetworkCards(t *testing.T) { err = vmObj.Properties(c.ctx, vmObj.Reference(), []string{}, &o) assert.NoError(err, "expected no error looking up vmObj properties") - networkInfo := identifyNetworkCards(o.Config.Hardware.Device) + networkInfo := identifyNetworkCards(c.networkMapping, o.Config.Hardware.Device) assert.Len(networkInfo, 1, "expected to find only 1 item in the networkInfo") + t.Log(networkInfo) networkMapping := []migration.NetworkMapping{ { SourceNetwork: "dummyNetwork", DestinationNetwork: "harvester1", }, { - SourceNetwork: "DVSwitch: fea97929-4b2d-5972-b146-930c6d0b4014", + SourceNetwork: "DC0_DVPG0", DestinationNetwork: "pod-network", }, } From 5eadbfd6530b79ed4b35527190ae14491bfdf9c3 Mon Sep 17 00:00:00 2001 From: Gaurav Mehta Date: Tue, 15 Apr 2025 10:18:22 +1000 Subject: [PATCH 2/2] staging changes --- .../v1beta1/virtualmachines.go | 1 + pkg/source/vmware/client.go | 59 ++++++++++++++++--- pkg/source/vmware/client_test.go | 22 +++++++ 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/pkg/apis/migration.harvesterhci.io/v1beta1/virtualmachines.go b/pkg/apis/migration.harvesterhci.io/v1beta1/virtualmachines.go index 8f0633b..96c295d 100644 --- a/pkg/apis/migration.harvesterhci.io/v1beta1/virtualmachines.go +++ b/pkg/apis/migration.harvesterhci.io/v1beta1/virtualmachines.go @@ -92,4 +92,5 @@ const ( VirtualMachineImageFailed condition.Cond = "VirtualMachineImageFailed" VirtualMachineExportFailed condition.Cond = "VMExportFailed" VirtualMachineMigrationFailed ImportStatus = "VMMigrationFailed" + SkipPreflightChecks string = "harvesterhci.io/skip-preflight-checks" ) diff --git a/pkg/source/vmware/client.go b/pkg/source/vmware/client.go index da805b6..5c21dff 100644 --- a/pkg/source/vmware/client.go +++ b/pkg/source/vmware/client.go @@ -132,10 +132,15 @@ func (c *Client) Verify() error { func (c *Client) PreFlightChecks(vm *migration.VirtualMachineImport) (err error) { // Check the source network mappings. - f := find.NewFinder(c.Client.Client, true) - dc := c.dc - if !strings.HasPrefix(c.dc, "/") { - dc = fmt.Sprintf("/%s", c.dc) + if vm.Annotations != nil { + if _, ok := vm.Annotations[migration.SkipPreflightChecks]; ok { + return nil + } + } + + networkMap, err := GenerateNetworkMapByName(c.ctx, c.Client.Client) + if err != nil { + return fmt.Errorf("error generating network map: %v", err) } for _, nm := range vm.Spec.Mapping { logrus.WithFields(logrus.Fields{ @@ -144,11 +149,9 @@ func (c *Client) PreFlightChecks(vm *migration.VirtualMachineImport) (err error) "sourceNetwork": nm.SourceNetwork, }).Info("Checking the source network as part of the preflight checks") - // The path looks like `//network/`. - path := filepath.Join(dc, "/network", nm.SourceNetwork) - _, err := f.Network(c.ctx, path) - if err != nil { - return fmt.Errorf("error getting source network '%s': %v", nm.SourceNetwork, err) + elements := strings.Split(nm.SourceNetwork, "/") + if _, ok := networkMap[elements[len(elements)-1]]; ok { + return nil } } @@ -551,6 +554,20 @@ func GenerateNetworkMapByRef(ctx context.Context, c *vim25.Client) (map[string]s for _, v := range networks { returnMap[v.Reference().Value] = v.Name } + logrus.Infof("generated networkMapByRef: %v", returnMap) + return returnMap, nil +} + +func GenerateNetworkMapByName(ctx context.Context, c *vim25.Client) (map[string]string, error) { + networks, err := generateNetworkList(ctx, c) + if err != nil { + return nil, err + } + returnMap := make(map[string]string, len(networks)) + for _, v := range networks { + returnMap[v.Name] = v.Reference().Value + } + logrus.Infof("generated networkMapByName: %v", returnMap) return returnMap, nil } @@ -578,10 +595,34 @@ func identifyNetworkName(networkMap map[string]string, device types.VirtualDevic switch b := backing.(type) { case *types.VirtualEthernetCardDistributedVirtualPortBackingInfo: obj := b + logrus.Infof("looking up portgroupkey: %v", obj.Port.PortgroupKey) summary = networkMap[obj.Port.PortgroupKey] case *types.VirtualEthernetCardNetworkBackingInfo: obj := b + logrus.Infof("using devicename: %v", obj.DeviceName) summary = obj.DeviceName } return summary } + +func (c *Client) ListNetworks() error { + mgr := view.NewManager(c.Client.Client) + + v, err := mgr.CreateContainerView(c.ctx, c.ServiceContent.RootFolder, []string{"Network"}, true) + if err != nil { + return fmt.Errorf("error creating view %v", err) + } + + defer v.Destroy(c.ctx) + var networks []mo.Network + err = v.Retrieve(c.ctx, []string{"Network"}, nil, &networks) + if err != nil { + return fmt.Errorf("error fetching networks: %v", err) + } + + for _, net := range networks { + fmt.Printf("%s: %s\n", net.Name, net.Reference()) + } + + return nil +} diff --git a/pkg/source/vmware/client_test.go b/pkg/source/vmware/client_test.go index 289297c..7d6526a 100644 --- a/pkg/source/vmware/client_test.go +++ b/pkg/source/vmware/client_test.go @@ -305,3 +305,25 @@ func Test_identifyNetworkCards(t *testing.T) { noMappedInfo := mapNetworkCards(networkInfo, noNetworkMapping) assert.Len(noMappedInfo, 0, "expected to find no item in the mapped networkinfo") } + +func Test_ListNetworks(t *testing.T) { + ctx := context.TODO() + endpoint := fmt.Sprintf("https://thinkpad.local:%s/sdk", "8989") + dc := "DC0" + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: "default", + }, + Data: map[string][]byte{ + "username": []byte("user"), + "password": []byte("pass"), + }, + } + + c, err := NewClient(ctx, endpoint, dc, secret) + assert := require.New(t) + assert.NoError(err) + err = c.ListNetworks() + assert.NoError(err) +}