From 628429410d62459a4db4f0232a0c1f8ac3effe89 Mon Sep 17 00:00:00 2001 From: scuzhanglei Date: Thu, 25 Aug 2022 13:41:13 +0800 Subject: [PATCH] feat: Add interface mac field (#35) --- cmd/virt-prerunner/main.go | 14 +++++---- ...rt.virtink.smartx.com_virtualmachines.yaml | 2 ++ pkg/apis/virt/v1alpha1/types.go | 1 + pkg/controller/vm_controller.go | 12 ++++++++ pkg/controller/vm_webhook.go | 30 +++++++++++++++++++ pkg/controller/vm_webhook_test.go | 22 ++++++++++++++ 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/cmd/virt-prerunner/main.go b/cmd/virt-prerunner/main.go index 93f4ab1..23e5bb2 100644 --- a/cmd/virt-prerunner/main.go +++ b/cmd/virt-prerunner/main.go @@ -212,7 +212,8 @@ func buildVMConfig(ctx context.Context, vm *virtv1alpha1.VirtualMachine) (*cloud vmConfig.Net = append(vmConfig.Net, &netConfig) case iface.Masquerade != nil: netConfig := cloudhypervisor.NetConfig{ - Id: iface.Name, + Id: iface.Name, + Mac: iface.MAC, } if err := setupMasqueradeNetwork(linkName, iface.Masquerade.CIDR, &netConfig); err != nil { return nil, fmt.Errorf("setup masquerade network: %s", err) @@ -377,14 +378,17 @@ func setupMasqueradeNetwork(linkName string, cidr string, netConfig *cloudhyperv } tapName := fmt.Sprintf("tap-%s", linkName) - tap, err := createTap(bridge, tapName) - if err != nil { + if _, err := createTap(bridge, tapName); err != nil { return fmt.Errorf("create tap: %s", err) } netConfig.Tap = tapName - netConfig.Mac = tap.Attrs().HardwareAddr.String() - if err := startDHCPServer(bridgeName, tap.Attrs().HardwareAddr, vmIPNet, bridgeIP); err != nil { + vmMAC, err := net.ParseMAC(netConfig.Mac) + if err != nil { + return fmt.Errorf("parse VM MAC: %s", err) + } + + if err := startDHCPServer(bridgeName, vmMAC, vmIPNet, bridgeIP); err != nil { return fmt.Errorf("start DHCP server: %s", err) } return nil diff --git a/deploy/crd/virt.virtink.smartx.com_virtualmachines.yaml b/deploy/crd/virt.virtink.smartx.com_virtualmachines.yaml index b864502..77560ce 100644 --- a/deploy/crd/virt.virtink.smartx.com_virtualmachines.yaml +++ b/deploy/crd/virt.virtink.smartx.com_virtualmachines.yaml @@ -890,6 +890,8 @@ spec: properties: bridge: type: object + mac: + type: string masquerade: properties: cidr: diff --git a/pkg/apis/virt/v1alpha1/types.go b/pkg/apis/virt/v1alpha1/types.go index e387adc..d9fb09c 100644 --- a/pkg/apis/virt/v1alpha1/types.go +++ b/pkg/apis/virt/v1alpha1/types.go @@ -82,6 +82,7 @@ type Disk struct { type Interface struct { Name string `json:"name"` + MAC string `json:"mac,omitempty"` InterfaceBindingMethod `json:",inline"` } diff --git a/pkg/controller/vm_controller.go b/pkg/controller/vm_controller.go index 90ede86..0ab32a2 100644 --- a/pkg/controller/vm_controller.go +++ b/pkg/controller/vm_controller.go @@ -433,11 +433,23 @@ func (r *VMReconciler) buildVMPod(ctx context.Context, vm *virtv1alpha1.VirtualM var networks []netv1.NetworkSelectionElement for i, network := range vm.Spec.Networks { + var iface *virtv1alpha1.Interface + for j := range vm.Spec.Instance.Interfaces { + if vm.Spec.Instance.Interfaces[j].Name == network.Name { + iface = &vm.Spec.Instance.Interfaces[j] + break + } + } + if iface == nil { + return nil, fmt.Errorf("interface not found for network: %s", network.Name) + } + switch { case network.Multus != nil: networks = append(networks, netv1.NetworkSelectionElement{ Name: network.Multus.NetworkName, InterfaceRequest: fmt.Sprintf("net%d", i), + MacRequest: iface.MAC, }) var nad netv1.NetworkAttachmentDefinition diff --git a/pkg/controller/vm_webhook.go b/pkg/controller/vm_webhook.go index 16d062f..2e7fdb5 100644 --- a/pkg/controller/vm_webhook.go +++ b/pkg/controller/vm_webhook.go @@ -2,6 +2,7 @@ package controller import ( "context" + "crypto/rand" "encoding/json" "fmt" "net" @@ -79,6 +80,14 @@ func MutateVM(ctx context.Context, vm *virtv1alpha1.VirtualMachine, oldVM *virtv } for i := range vm.Spec.Instance.Interfaces { + if vm.Spec.Instance.Interfaces[i].MAC == "" { + mac, err := generateMAC() + if err != nil { + return fmt.Errorf("generate MAC: %s", err) + } + vm.Spec.Instance.Interfaces[i].MAC = mac.String() + } + if vm.Spec.Instance.Interfaces[i].Bridge == nil && vm.Spec.Instance.Interfaces[i].Masquerade == nil && vm.Spec.Instance.Interfaces[i].SRIOV == nil { vm.Spec.Instance.Interfaces[i].InterfaceBindingMethod = virtv1alpha1.InterfaceBindingMethod{ Bridge: &virtv1alpha1.InterfaceBridge{}, @@ -323,10 +332,22 @@ func ValidateInterface(ctx context.Context, iface *virtv1alpha1.Interface, field if iface.Name == "" { errs = append(errs, field.Required(fieldPath.Child("name"), "")) } + errs = append(errs, ValidateMAC(iface.MAC, fieldPath.Child("mac"))...) errs = append(errs, ValidateInterfaceBindingMethod(ctx, &iface.InterfaceBindingMethod, fieldPath)...) return errs } +func ValidateMAC(mac string, fieldPath *field.Path) field.ErrorList { + var errs field.ErrorList + if mac == "" { + errs = append(errs, field.Required(fieldPath, "")) + } + if _, err := net.ParseMAC(mac); err != nil { + errs = append(errs, field.Invalid(fieldPath, mac, err.Error())) + } + return errs +} + func ValidateInterfaceBindingMethod(ctx context.Context, bindingMethod *virtv1alpha1.InterfaceBindingMethod, fieldPath *field.Path) field.ErrorList { var errs field.ErrorList if bindingMethod == nil { @@ -619,3 +640,12 @@ func ValidateMultusNetworkSource(ctx context.Context, source *virtv1alpha1.Multu } return errs } + +func generateMAC() (net.HardwareAddr, error) { + prefix := []byte{0x52, 0x54, 0x00} + suffix := make([]byte, 3) + if _, err := rand.Read(suffix); err != nil { + return nil, fmt.Errorf("rand: %s", err) + } + return net.HardwareAddr(append(prefix, suffix...)), nil +} diff --git a/pkg/controller/vm_webhook_test.go b/pkg/controller/vm_webhook_test.go index fb94495..0be8f4e 100644 --- a/pkg/controller/vm_webhook_test.go +++ b/pkg/controller/vm_webhook_test.go @@ -30,6 +30,7 @@ func TestValidateVM(t *testing.T) { InterfaceBindingMethod: virtv1alpha1.InterfaceBindingMethod{ Bridge: &virtv1alpha1.InterfaceBridge{}, }, + MAC: "c6:1c:ba:0a:45:88", }}, }, Volumes: []virtv1alpha1.Volume{{ @@ -205,6 +206,20 @@ func TestValidateVM(t *testing.T) { return vm }(), invalidFields: []string{"spec.instance.interfaces[0].name"}, + }, { + vm: func() *virtv1alpha1.VirtualMachine { + vm := validVM.DeepCopy() + vm.Spec.Instance.Interfaces[0].MAC = "" + return vm + }(), + invalidFields: []string{"spec.instance.interfaces[0].mac"}, + }, { + vm: func() *virtv1alpha1.VirtualMachine { + vm := validVM.DeepCopy() + vm.Spec.Instance.Interfaces[0].MAC = "01:1c:ba:0a:45:8" + return vm + }(), + invalidFields: []string{"spec.instance.interfaces[0].mac"}, }, { vm: func() *virtv1alpha1.VirtualMachine { vm := validVM.DeepCopy() @@ -339,6 +354,13 @@ func TestMutateVM(t *testing.T) { assert: func(vm *virtv1alpha1.VirtualMachine) { assert.Equal(t, "1Gi", vm.Spec.Instance.Memory.Size.String()) }, + }, { + vm: func() *virtv1alpha1.VirtualMachine { + return oldVM.DeepCopy() + }(), + assert: func(vm *virtv1alpha1.VirtualMachine) { + assert.NotEmpty(t, vm.Spec.Instance.Interfaces[0].MAC) + }, }, { vm: func() *virtv1alpha1.VirtualMachine { return oldVM.DeepCopy()