diff --git a/charts/kube-ovn/templates/kube-ovn-crd.yaml b/charts/kube-ovn/templates/kube-ovn-crd.yaml index fa994821913..565f54ebda6 100644 --- a/charts/kube-ovn/templates/kube-ovn-crd.yaml +++ b/charts/kube-ovn/templates/kube-ovn-crd.yaml @@ -1972,6 +1972,8 @@ spec: type: string u2oInterconnectionIP: type: string + u2oInterconnectionMAC: + type: string u2oInterconnectionVPC: type: string v4usingIPrange: diff --git a/dist/images/install.sh b/dist/images/install.sh index 73d8d62e002..ecfc9517bc1 100755 --- a/dist/images/install.sh +++ b/dist/images/install.sh @@ -2208,6 +2208,8 @@ spec: type: string u2oInterconnectionIP: type: string + u2oInterconnectionMAC: + type: string u2oInterconnectionVPC: type: string v4usingIPrange: diff --git a/mocks/pkg/ovs/interface.go b/mocks/pkg/ovs/interface.go index ec4550df6d1..9d07d923c12 100644 --- a/mocks/pkg/ovs/interface.go +++ b/mocks/pkg/ovs/interface.go @@ -668,17 +668,17 @@ func (mr *MockLogicalSwitchMockRecorder) CreateBareLogicalSwitch(lsName any) *go } // CreateLogicalSwitch mocks base method. -func (m *MockLogicalSwitch) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway string, needRouter, randomAllocateGW bool) error { +func (m *MockLogicalSwitch) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, gatewayMAC string, needRouter, randomAllocateGW bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateLogicalSwitch", lsName, lrName, cidrBlock, gateway, needRouter, randomAllocateGW) + ret := m.ctrl.Call(m, "CreateLogicalSwitch", lsName, lrName, cidrBlock, gateway, gatewayMAC, needRouter, randomAllocateGW) ret0, _ := ret[0].(error) return ret0 } // CreateLogicalSwitch indicates an expected call of CreateLogicalSwitch. -func (mr *MockLogicalSwitchMockRecorder) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, needRouter, randomAllocateGW any) *gomock.Call { +func (mr *MockLogicalSwitchMockRecorder) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, gatewayMAC, needRouter, randomAllocateGW any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLogicalSwitch", reflect.TypeOf((*MockLogicalSwitch)(nil).CreateLogicalSwitch), lsName, lrName, cidrBlock, gateway, needRouter, randomAllocateGW) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLogicalSwitch", reflect.TypeOf((*MockLogicalSwitch)(nil).CreateLogicalSwitch), lsName, lrName, cidrBlock, gateway, gatewayMAC, needRouter, randomAllocateGW) } // DeleteLogicalSwitch mocks base method. @@ -2727,17 +2727,17 @@ func (mr *MockNbClientMockRecorder) CreateLogicalRouterPort(lrName, lrpName, mac } // CreateLogicalSwitch mocks base method. -func (m *MockNbClient) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway string, needRouter, randomAllocateGW bool) error { +func (m *MockNbClient) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, gatewayMAC string, needRouter, randomAllocateGW bool) error { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateLogicalSwitch", lsName, lrName, cidrBlock, gateway, needRouter, randomAllocateGW) + ret := m.ctrl.Call(m, "CreateLogicalSwitch", lsName, lrName, cidrBlock, gateway, gatewayMAC, needRouter, randomAllocateGW) ret0, _ := ret[0].(error) return ret0 } // CreateLogicalSwitch indicates an expected call of CreateLogicalSwitch. -func (mr *MockNbClientMockRecorder) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, needRouter, randomAllocateGW any) *gomock.Call { +func (mr *MockNbClientMockRecorder) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, gatewayMAC, needRouter, randomAllocateGW any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLogicalSwitch", reflect.TypeOf((*MockNbClient)(nil).CreateLogicalSwitch), lsName, lrName, cidrBlock, gateway, needRouter, randomAllocateGW) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLogicalSwitch", reflect.TypeOf((*MockNbClient)(nil).CreateLogicalSwitch), lsName, lrName, cidrBlock, gateway, gatewayMAC, needRouter, randomAllocateGW) } // CreateLogicalSwitchPort mocks base method. diff --git a/pkg/apis/kubeovn/v1/types.go b/pkg/apis/kubeovn/v1/types.go index c71fecb5251..5f2c25d8146 100644 --- a/pkg/apis/kubeovn/v1/types.go +++ b/pkg/apis/kubeovn/v1/types.go @@ -242,6 +242,7 @@ type SubnetStatus struct { DHCPv4OptionsUUID string `json:"dhcpV4OptionsUUID"` DHCPv6OptionsUUID string `json:"dhcpV6OptionsUUID"` U2OInterconnectionIP string `json:"u2oInterconnectionIP"` + U2OInterconnectionMAC string `json:"u2oInterconnectionMAC"` U2OInterconnectionVPC string `json:"u2oInterconnectionVPC"` NatOutgoingPolicyRules []NatOutgoingPolicyRuleStatus `json:"natOutgoingPolicyRules"` } diff --git a/pkg/controller/init.go b/pkg/controller/init.go index c7f3c629f52..705f35d7ee9 100644 --- a/pkg/controller/init.go +++ b/pkg/controller/init.go @@ -15,6 +15,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/cache" "k8s.io/klog/v2" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -301,7 +302,20 @@ func (c *Controller) InitIPAM() error { u2oInterconnName := fmt.Sprintf(util.U2OInterconnName, subnet.Spec.Vpc, subnet.Name) u2oInterconnLrpName := fmt.Sprintf("%s-%s", subnet.Spec.Vpc, subnet.Name) if subnet.Status.U2OInterconnectionIP != "" { - if _, _, _, err = c.ipam.GetStaticAddress(u2oInterconnName, u2oInterconnLrpName, subnet.Status.U2OInterconnectionIP, nil, subnet.Name, true); err != nil { + var mac *string + if subnet.Status.U2OInterconnectionMAC != "" { + mac = ptr.To(subnet.Status.U2OInterconnectionMAC) + } else { + lrp, err := c.OVNNbClient.GetLogicalRouterPort(u2oInterconnLrpName, true) + if err != nil { + klog.Errorf("failed to get logical router port %s: %v", u2oInterconnLrpName, err) + return err + } + if lrp != nil { + mac = ptr.To(lrp.MAC) + } + } + if _, _, _, err = c.ipam.GetStaticAddress(u2oInterconnName, u2oInterconnLrpName, subnet.Status.U2OInterconnectionIP, mac, subnet.Name, true); err != nil { klog.Errorf("failed to init subnet %q u2o interconnection ip to ipam %v", subnet.Name, err) } } diff --git a/pkg/controller/subnet.go b/pkg/controller/subnet.go index f98e9ad80fa..ddc87be91bc 100644 --- a/pkg/controller/subnet.go +++ b/pkg/controller/subnet.go @@ -823,8 +823,10 @@ func (c *Controller) handleAddOrUpdateSubnet(key string) error { // 3. underlay subnet use physical gw, vpc has eip, lrp managed in vpc process, lrp ip is random allocation, not subnet gw gateway := subnet.Spec.Gateway + var gatewayMAC string if subnet.Status.U2OInterconnectionIP != "" && subnet.Spec.U2OInterconnection { gateway = subnet.Status.U2OInterconnectionIP + gatewayMAC = subnet.Status.U2OInterconnectionMAC } if err := c.clearOldU2OResource(subnet); err != nil { @@ -833,7 +835,7 @@ func (c *Controller) handleAddOrUpdateSubnet(key string) error { } // create or update logical switch - if err := c.OVNNbClient.CreateLogicalSwitch(subnet.Name, vpc.Status.Router, subnet.Spec.CIDRBlock, gateway, needRouter, randomAllocateGW); err != nil { + if err := c.OVNNbClient.CreateLogicalSwitch(subnet.Name, vpc.Status.Router, subnet.Spec.CIDRBlock, gateway, gatewayMAC, needRouter, randomAllocateGW); err != nil { klog.Errorf("create logical switch %s: %v", subnet.Name, err) return err } @@ -1959,7 +1961,7 @@ func (c *Controller) reconcileU2OInterconnectionIP(subnet *kubeovnv1.Subnet) err u2oInterconnLrpName := fmt.Sprintf("%s-%s", subnet.Spec.Vpc, subnet.Name) var v4ip, v6ip, mac string var err error - if subnet.Spec.U2OInterconnectionIP == "" && subnet.Status.U2OInterconnectionIP == "" { + if subnet.Spec.U2OInterconnectionIP == "" && (subnet.Status.U2OInterconnectionIP == "" || subnet.Status.U2OInterconnectionMAC == "") { v4ip, v6ip, mac, err = c.acquireIPAddress(subnet.Name, u2oInterconnName, u2oInterconnLrpName) if err != nil { klog.Errorf("failed to acquire underlay to overlay interconnection ip address for subnet %s, %v", subnet.Name, err) @@ -1992,6 +1994,7 @@ func (c *Controller) reconcileU2OInterconnectionIP(subnet *kubeovnv1.Subnet) err return err } + subnet.Status.U2OInterconnectionMAC = mac needCalcIP = true } } else if subnet.Status.U2OInterconnectionIP != "" { @@ -1999,6 +2002,7 @@ func (c *Controller) reconcileU2OInterconnectionIP(subnet *kubeovnv1.Subnet) err klog.Infof("release underlay to overlay interconnection ip address %s for subnet %s", subnet.Status.U2OInterconnectionIP, subnet.Name) c.ipam.ReleaseAddressByPod(u2oInterconnName, subnet.Name) subnet.Status.U2OInterconnectionIP = "" + subnet.Status.U2OInterconnectionMAC = "" subnet.Status.U2OInterconnectionVPC = "" if err := c.config.KubeOvnClient.KubeovnV1().IPs().Delete(context.Background(), u2oInterconnName, metav1.DeleteOptions{}); err != nil { diff --git a/pkg/ovs/interface.go b/pkg/ovs/interface.go index 1bb1b104299..0f6f72fdcd3 100644 --- a/pkg/ovs/interface.go +++ b/pkg/ovs/interface.go @@ -60,7 +60,7 @@ type BFD interface { } type LogicalSwitch interface { - CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway string, needRouter, randomAllocateGW bool) error + CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, gatewayMAC string, needRouter, randomAllocateGW bool) error CreateBareLogicalSwitch(lsName string) error LogicalSwitchUpdateLoadBalancers(lsName string, op ovsdb.Mutator, lbNames ...string) error LogicalSwitchUpdateOtherConfig(lsName string, op ovsdb.Mutator, otherConfig map[string]string) error diff --git a/pkg/ovs/ovn-nb-logical_switch.go b/pkg/ovs/ovn-nb-logical_switch.go index e2b248fc0b6..b029f14ff36 100644 --- a/pkg/ovs/ovn-nb-logical_switch.go +++ b/pkg/ovs/ovn-nb-logical_switch.go @@ -15,7 +15,7 @@ import ( ) // CreateLogicalSwitch create logical switch -func (c *OVNNbClient) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway string, needRouter, randomAllocateGW bool) error { +func (c *OVNNbClient) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway, gatewayMAC string, needRouter, randomAllocateGW bool) error { lspName := fmt.Sprintf("%s-%s", lsName, lrName) lrpName := fmt.Sprintf("%s-%s", lrName, lsName) @@ -41,7 +41,12 @@ func (c *OVNNbClient) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway str Name: lrpName, Networks: strings.Split(networks, ","), } - if err := c.UpdateLogicalRouterPort(lrp, &lrp.Networks); err != nil { + fields := []interface{}{&lrp.Networks} + if gatewayMAC != "" { + lrp.MAC = gatewayMAC + fields = append(fields, &lrp.MAC) + } + if err := c.UpdateLogicalRouterPort(lrp, fields...); err != nil { return fmt.Errorf("update logical router port %s", lrpName) } } else { @@ -51,7 +56,7 @@ func (c *OVNNbClient) CreateLogicalSwitch(lsName, lrName, cidrBlock, gateway str } if needRouter { - if err := c.CreateLogicalPatchPort(lsName, lrName, lspName, lrpName, networks, util.GenerateMac()); err != nil { + if err := c.CreateLogicalPatchPort(lsName, lrName, lspName, lrpName, networks, gatewayMAC); err != nil { return err } } else { diff --git a/pkg/ovs/ovn-nb-logical_switch_test.go b/pkg/ovs/ovn-nb-logical_switch_test.go index 79fea833d28..be7931e86d9 100644 --- a/pkg/ovs/ovn-nb-logical_switch_test.go +++ b/pkg/ovs/ovn-nb-logical_switch_test.go @@ -31,6 +31,7 @@ func (suite *OvnClientTestSuite) testCreateLogicalSwitch() { ovnClient := suite.ovnClient lsName := "test-create-ls-ls" lrName := "test-create-ls-lr" + mac := util.GenerateMac() lspName := fmt.Sprintf("%s-%s", lsName, lrName) lrpName := fmt.Sprintf("%s-%s", lrName, lsName) @@ -38,7 +39,7 @@ func (suite *OvnClientTestSuite) testCreateLogicalSwitch() { require.NoError(t, err) t.Run("create logical switch and router type port when logical switch does't exist and needRouter is true", func(t *testing.T) { - err = ovnClient.CreateLogicalSwitch(lsName, lrName, "192.168.2.0/24,fd00::c0a8:6400/120", "192.168.2.1,fd00::c0a8:6401", true, false) + err = ovnClient.CreateLogicalSwitch(lsName, lrName, "192.168.2.0/24,fd00::c0a8:6400/120", "192.168.2.1,fd00::c0a8:6401", mac, true, false) require.NoError(t, err) _, err := ovnClient.GetLogicalSwitch(lsName, false) @@ -47,26 +48,30 @@ func (suite *OvnClientTestSuite) testCreateLogicalSwitch() { _, err = ovnClient.GetLogicalSwitchPort(lspName, false) require.NoError(t, err) - _, err = ovnClient.GetLogicalRouterPort(lrpName, false) + lrp, err := ovnClient.GetLogicalRouterPort(lrpName, false) require.NoError(t, err) + require.NotNil(t, lrp) + require.Equal(t, mac, lrp.MAC) }) t.Run("only update networks when logical switch exist and router type port exist and needRouter is true", func(t *testing.T) { - err = ovnClient.CreateLogicalSwitch(lsName, lrName, "192.168.2.0/24,fd00::c0a8:9900/120", "192.168.2.1,fd00::c0a8:9901", true, false) + err = ovnClient.CreateLogicalSwitch(lsName, lrName, "192.168.2.0/24,fd00::c0a8:9900/120", "192.168.2.1,fd00::c0a8:9901", mac, true, false) require.NoError(t, err) lrp, err := ovnClient.GetLogicalRouterPort(lrpName, false) require.NoError(t, err) + require.NotNil(t, lrp) require.ElementsMatch(t, []string{"192.168.2.1/24", "fd00::c0a8:9901/120"}, lrp.Networks) + require.Equal(t, mac, lrp.MAC) }) t.Run("remove router type port when needRouter is false", func(t *testing.T) { - err = ovnClient.CreateLogicalSwitch(lsName, lrName, "192.168.2.0/24,fd00::c0a8:9900/120", "192.168.2.1,fd00::c0a8:9901", false, false) + err = ovnClient.CreateLogicalSwitch(lsName, lrName, "192.168.2.0/24,fd00::c0a8:9900/120", "192.168.2.1,fd00::c0a8:9901", "", false, false) require.NoError(t, err) }) t.Run("should no err when router type port doest't exist", func(t *testing.T) { - err = ovnClient.CreateLogicalSwitch(lsName+"-1", lrName+"-1", "192.168.2.0/24,fd00::c0a8:9900/120", "192.168.2.1,fd00::c0a8:9901", false, false) + err = ovnClient.CreateLogicalSwitch(lsName+"-1", lrName+"-1", "192.168.2.0/24,fd00::c0a8:9900/120", "192.168.2.1,fd00::c0a8:9901", "", false, false) require.NoError(t, err) }) } diff --git a/pkg/ovs/ovn-nb.go b/pkg/ovs/ovn-nb.go index 0eade0a348b..ec5b11ca826 100644 --- a/pkg/ovs/ovn-nb.go +++ b/pkg/ovs/ovn-nb.go @@ -55,6 +55,9 @@ func (c *OVNNbClient) CreateLogicalPatchPort(lsName, lrName, lspName, lrpName, i return err } } + if mac == "" { + mac = util.GenerateMac() + } /* create router port */ ops, err := c.CreateRouterPortOp(lsName, lrName, lspName, lrpName, ip, mac)