From ae690d2a70a75f1c9e823c6cad0819a465f48edf Mon Sep 17 00:00:00 2001 From: Paul Yu <129891899+paulyufan2@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:08:00 -0400 Subject: [PATCH] add accelnet support on CNI (#2853) * add accelnet support for CNI * add uts for AccelnetNIC on CNI * add more uts * fix an ut * fix uts * add ut to endpoint_test * add endpoint impl test cases for accelnet * gofummpt windows test * fix logic of accelnet * fix logic of accelnet * modify uts * remove an ut * fix one ut issue * accelnet interface should set default route * fix some uts * remove an ib ut * fix comments * fix comments and add uts * add more uts * fix an linter issue * fix comments * add comment for iov flag * fix comments * add endpoint deletion * add HNSV2 check * add UT to make sure endpoint and networ deletion called * add a new test to make infraNIC network is not deleted * add errMsg * fix error msg * add windows test cases for endpoint state deletion * fix linter issue * fix a linter issue * remove hardcode hcniov flag * comments fix * add uts for transparent network deletion * fix comment when hns id is empty * fix the UT when hns id is empty * skip linter issue * change the delegatedVMNIC to NodeNetworkInterfaceFrontendNIC * fix an ut * fix add accelnet policy setting ut --- cni/network/invoker_cns.go | 6 +- cni/network/invoker_cns_test.go | 367 +++++++++++++- cni/network/invoker_mock.go | 25 +- cni/network/multitenancy_test.go | 12 +- cni/network/network.go | 2 +- cni/network/network_test.go | 231 ++++++++- cni/network/network_windows.go | 3 +- cni/network/network_windows_test.go | 43 +- network/endpoint_linux.go | 6 +- network/endpoint_test.go | 35 +- network/endpoint_windows.go | 19 +- network/endpoint_windows_test.go | 451 ++++++++++++++++-- network/hnswrapper/hnsv2wrapper.go | 4 + network/hnswrapper/hnsv2wrapperfake.go | 14 + network/hnswrapper/hnsv2wrapperinterface.go | 1 + network/hnswrapper/hnsv2wrapperwithtimeout.go | 4 + network/manager.go | 24 +- network/manager_test.go | 61 ++- network/network.go | 2 +- network/network_linux.go | 3 +- network/network_windows.go | 21 +- network/network_windows_test.go | 155 ++++++ network/policy/policy_windows.go | 30 ++ network/policy/policy_windows_test.go | 10 + network/secondary_endpoint_linux_test.go | 2 +- 25 files changed, 1392 insertions(+), 139 deletions(-) diff --git a/cni/network/invoker_cns.go b/cni/network/invoker_cns.go index 1af10b1d31..01126247c5 100644 --- a/cni/network/invoker_cns.go +++ b/cni/network/invoker_cns.go @@ -169,8 +169,8 @@ func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, erro // Do we want to leverage this lint skip in other places of our code? key := invoker.getInterfaceInfoKey(info.nicType, info.macAddress) switch info.nicType { - case cns.DelegatedVMNIC: - // only handling single v4 PodIPInfo for DelegatedVMNIC at the moment, will have to update once v6 gets added + case cns.NodeNetworkInterfaceFrontendNIC, cns.NodeNetworkInterfaceAccelnetFrontendNIC: + // only handling single v4 PodIPInfo for NodeNetworkInterfaceFrontendNIC and AccelnetNIC at the moment, will have to update once v6 gets added if !info.skipDefaultRoutes { numInterfacesWithDefaultRoutes++ } @@ -525,7 +525,7 @@ func addBackendNICToResult(info *IPResultInfo, addResult *IPAMAddResult, key str } func (invoker *CNSIPAMInvoker) getInterfaceInfoKey(nicType cns.NICType, macAddress string) string { - if nicType == cns.DelegatedVMNIC || nicType == cns.BackendNIC { + if nicType == cns.NodeNetworkInterfaceFrontendNIC || nicType == cns.BackendNIC || nicType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { return macAddress } return string(nicType) diff --git a/cni/network/invoker_cns_test.go b/cni/network/invoker_cns_test.go index ef06e395af..8a75f645be 100644 --- a/cni/network/invoker_cns_test.go +++ b/cni/network/invoker_cns_test.go @@ -286,7 +286,7 @@ func TestCNSIPAMInvoker_Add_Overlay(t *testing.T) { IPAddress: "20.240.1.242", PrefixLength: 24, }, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: macAddress, }, }, @@ -333,7 +333,7 @@ func TestCNSIPAMInvoker_Add_Overlay(t *testing.T) { }, }, Routes: []network.RouteInfo{}, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: parsedMacAddress, // secondaries don't have a host subnet prefix }, @@ -381,7 +381,7 @@ func TestCNSIPAMInvoker_Add_Overlay(t *testing.T) { IPAddress: "20.240.1.242", PrefixLength: 24, }, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: "bad mac", }, }, @@ -448,7 +448,7 @@ func TestCNSIPAMInvoker_Add_Overlay(t *testing.T) { IPAddress: "bad ip", PrefixLength: 24, }, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: macAddress, }, }, @@ -493,7 +493,7 @@ func TestCNSIPAMInvoker_Add_Overlay(t *testing.T) { } for _, ifInfo := range ipamAddResult.interfaceInfo { - if ifInfo.NICType == cns.DelegatedVMNIC { + if ifInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC { fmt.Printf("want:%+v\nrest:%+v\n", tt.wantSecondaryInterfacesInfo, ifInfo) if len(tt.wantSecondaryInterfacesInfo.IPConfigs) > 0 { require.EqualValues(tt.wantSecondaryInterfacesInfo, ifInfo, "incorrect response for delegatedNIC") @@ -801,7 +801,7 @@ func TestCNSIPAMInvoker_Add(t *testing.T) { for _, ifInfo := range ipamAddResult.interfaceInfo { require.NotEqual("", string(ifInfo.NICType), "nictype should be auto populated if empty") - if ifInfo.NICType == cns.DelegatedVMNIC { + if ifInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC { fmt.Printf("want:%+v\nrest:%+v\n", tt.wantMultitenantResult, ifInfo) if len(tt.wantMultitenantResult.IPConfigs) > 0 { require.Equalf(tt.wantMultitenantResult, ifInfo, "incorrect multitenant response") @@ -1442,10 +1442,12 @@ func Test_getInterfaceInfoKey(t *testing.T) { inv := &CNSIPAMInvoker{} dummyMAC := "12:34:56:78:9a:bc" require.Equal(string(cns.InfraNIC), inv.getInterfaceInfoKey(cns.InfraNIC, dummyMAC)) - require.Equal(dummyMAC, inv.getInterfaceInfoKey(cns.DelegatedVMNIC, dummyMAC)) - require.Equal("", inv.getInterfaceInfoKey(cns.DelegatedVMNIC, "")) + require.Equal(dummyMAC, inv.getInterfaceInfoKey(cns.NodeNetworkInterfaceFrontendNIC, dummyMAC)) + require.Equal("", inv.getInterfaceInfoKey(cns.NodeNetworkInterfaceFrontendNIC, "")) require.Equal(dummyMAC, inv.getInterfaceInfoKey(cns.BackendNIC, dummyMAC)) require.Equal("", inv.getInterfaceInfoKey(cns.BackendNIC, "")) + require.Equal(dummyMAC, inv.getInterfaceInfoKey(cns.NodeNetworkInterfaceAccelnetFrontendNIC, dummyMAC)) + require.Equal("", inv.getInterfaceInfoKey(cns.NodeNetworkInterfaceAccelnetFrontendNIC, "")) } func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { @@ -1457,6 +1459,9 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { ibMacAddress := "bc:9a:78:56:34:12" ibParsedMacAddress, _ := net.ParseMAC(ibMacAddress) + accelnetAddress := "ab:cd:ef:12:34:56" + accelnetParsedMacAddress, _ := net.ParseMAC(accelnetAddress) + pnpID := "PCI\\VEN_15B3&DEV_101C&SUBSYS_000715B3&REV_00\\5&8c5acce&0&0" type fields struct { @@ -1505,7 +1510,7 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { PrimaryIP: "10.0.0.2", Subnet: "10.0.0.1/24", }, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: macAddress, }, }, @@ -1536,7 +1541,7 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { }, }, Routes: []network.RouteInfo{}, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: parsedMacAddress, }, }, @@ -1567,7 +1572,7 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { PrimaryIP: "10.0.0.2", Subnet: "10.0.0.1/24", }, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: macAddress, SkipDefaultRoutes: false, }, @@ -1604,7 +1609,7 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { }, }, Routes: []network.RouteInfo{}, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: parsedMacAddress, }, ibMacAddress: { @@ -1615,6 +1620,107 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { }, wantErr: false, }, + { + name: "Test happy CNI add with InfraNIC + AccelnetNIC interfaces", + fields: fields{ + podName: testPodInfo.PodName, + podNamespace: testPodInfo.PodNamespace, + cnsClient: &MockCNSClient{ + require: require, + requestIPs: requestIPsHandler{ + ipconfigArgument: cns.IPConfigsRequest{ + PodInterfaceID: "testcont-testifname1", + InfraContainerID: "testcontainerid1", + OrchestratorContext: marshallPodInfo(testPodInfo), + }, + result: &cns.IPConfigsResponse{ + PodIPInfo: []cns.PodIpInfo{ + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "10.0.1.10", + PrefixLength: 24, + }, + NetworkContainerPrimaryIPConfig: cns.IPConfiguration{ + IPSubnet: cns.IPSubnet{ + IPAddress: "10.0.1.0", + PrefixLength: 24, + }, + DNSServers: nil, + GatewayIPAddress: "10.0.0.1", + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "10.0.0.1", + PrimaryIP: "10.0.0.1", + Subnet: "10.0.0.0/24", + }, + NICType: cns.InfraNIC, + SkipDefaultRoutes: true, + }, + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "20.1.1.10", + PrefixLength: 24, + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "20.0.0.1", + PrimaryIP: "20.0.0.2", + Subnet: "20.0.0.1/24", + }, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: accelnetAddress, + SkipDefaultRoutes: false, + }, + }, + Response: cns.Response{ + ReturnCode: 0, + Message: "", + }, + }, + err: nil, + }, + }, + }, + args: args{ + nwCfg: &cni.NetworkConfig{}, + args: &cniSkel.CmdArgs{ + ContainerID: "testcontainerid1", + Netns: "testnetns1", + IfName: "testifname1", + }, + hostSubnetPrefix: getCIDRNotationForAddress("10.0.0.1/24"), + options: map[string]interface{}{}, + }, + wantDefaultResult: network.InterfaceInfo{ + IPConfigs: []*network.IPConfig{ + { + Address: *getCIDRNotationForAddress("10.0.1.10/24"), + Gateway: net.ParseIP("10.0.0.1"), + }, + }, + Routes: []network.RouteInfo{ + { + Dst: network.Ipv4DefaultRouteDstPrefix, + Gw: net.ParseIP("10.0.0.1"), + }, + }, + NICType: cns.InfraNIC, + SkipDefaultRoutes: true, + HostSubnetPrefix: *parseCIDR("10.0.0.0/24"), + }, + wantSecondaryInterfacesInfo: map[string]network.InterfaceInfo{ + accelnetAddress: { + IPConfigs: []*network.IPConfig{ + { + Address: *getCIDRNotationForAddress("20.1.1.10/24"), + }, + }, + Routes: []network.RouteInfo{}, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: accelnetParsedMacAddress, + }, + }, + wantErr: false, + }, { name: "Test happy CNI add with InfraNIC + DelegatedNIC + BackendNIC interfaces", fields: fields{ @@ -1661,7 +1767,7 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { PrimaryIP: "20.0.0.2", Subnet: "20.0.0.1/24", }, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: macAddress, SkipDefaultRoutes: false, }, @@ -1715,7 +1821,7 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { }, }, Routes: []network.RouteInfo{}, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, MacAddress: parsedMacAddress, }, ibMacAddress: { @@ -1726,6 +1832,228 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { }, wantErr: false, }, + { + name: "Test happy CNI add with InfraNIC + AccelnetNIC + BackendNIC interfaces", + fields: fields{ + podName: testPodInfo.PodName, + podNamespace: testPodInfo.PodNamespace, + cnsClient: &MockCNSClient{ + require: require, + requestIPs: requestIPsHandler{ + ipconfigArgument: cns.IPConfigsRequest{ + PodInterfaceID: "testcont-testifname1", + InfraContainerID: "testcontainerid1", + OrchestratorContext: marshallPodInfo(testPodInfo), + }, + result: &cns.IPConfigsResponse{ + PodIPInfo: []cns.PodIpInfo{ + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "10.0.1.10", + PrefixLength: 24, + }, + NetworkContainerPrimaryIPConfig: cns.IPConfiguration{ + IPSubnet: cns.IPSubnet{ + IPAddress: "10.0.1.0", + PrefixLength: 24, + }, + DNSServers: nil, + GatewayIPAddress: "10.0.0.1", + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "10.0.0.1", + PrimaryIP: "10.0.0.1", + Subnet: "10.0.0.0/24", + }, + NICType: cns.InfraNIC, + SkipDefaultRoutes: true, + }, + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "30.1.1.10", + PrefixLength: 24, + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "30.0.0.1", + PrimaryIP: "30.0.0.2", + Subnet: "30.0.0.1/24", + }, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: accelnetAddress, + SkipDefaultRoutes: false, + }, + { + MacAddress: ibMacAddress, + NICType: cns.BackendNIC, + PnPID: pnpID, + }, + }, + Response: cns.Response{ + ReturnCode: 0, + Message: "", + }, + }, + err: nil, + }, + }, + }, + args: args{ + nwCfg: &cni.NetworkConfig{}, + args: &cniSkel.CmdArgs{ + ContainerID: "testcontainerid1", + Netns: "testnetns1", + IfName: "testifname1", + }, + hostSubnetPrefix: getCIDRNotationForAddress("10.0.0.1/24"), + options: map[string]interface{}{}, + }, + wantDefaultResult: network.InterfaceInfo{ + IPConfigs: []*network.IPConfig{ + { + Address: *getCIDRNotationForAddress("10.0.1.10/24"), + Gateway: net.ParseIP("10.0.0.1"), + }, + }, + Routes: []network.RouteInfo{ + { + Dst: network.Ipv4DefaultRouteDstPrefix, + Gw: net.ParseIP("10.0.0.1"), + }, + }, + NICType: cns.InfraNIC, + SkipDefaultRoutes: true, + HostSubnetPrefix: *parseCIDR("10.0.0.0/24"), + }, + wantSecondaryInterfacesInfo: map[string]network.InterfaceInfo{ + accelnetAddress: { + IPConfigs: []*network.IPConfig{ + { + Address: *getCIDRNotationForAddress("30.1.1.10/24"), + }, + }, + Routes: []network.RouteInfo{}, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: accelnetParsedMacAddress, + }, + ibMacAddress: { + NICType: cns.BackendNIC, + MacAddress: ibParsedMacAddress, + PnPID: pnpID, + }, + }, + wantErr: false, + }, + { + name: "Test unhappy CNI add with InfraNIC + AccelnetNIC + BackendNIC interfaces", + fields: fields{ + podName: testPodInfo.PodName, + podNamespace: testPodInfo.PodNamespace, + cnsClient: &MockCNSClient{ + require: require, + requestIPs: requestIPsHandler{ + ipconfigArgument: cns.IPConfigsRequest{ + PodInterfaceID: "testcont-testifname1", + InfraContainerID: "testcontainerid1", + OrchestratorContext: marshallPodInfo(testPodInfo), + }, + result: &cns.IPConfigsResponse{ + PodIPInfo: []cns.PodIpInfo{ + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "10.0.1.10", + PrefixLength: 24, + }, + NetworkContainerPrimaryIPConfig: cns.IPConfiguration{ + IPSubnet: cns.IPSubnet{ + IPAddress: "10.0.1.0", + PrefixLength: 24, + }, + DNSServers: nil, + GatewayIPAddress: "10.0.0.1", + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "10.0.0.1", + PrimaryIP: "10.0.0.1", + Subnet: "10.0.0.0/24", + }, + NICType: cns.InfraNIC, + SkipDefaultRoutes: false, + }, + { + PodIPConfig: cns.IPSubnet{ + IPAddress: "30.1.1.10", + PrefixLength: 24, + }, + HostPrimaryIPInfo: cns.HostIPInfo{ + Gateway: "30.0.0.1", + PrimaryIP: "30.0.0.2", + Subnet: "30.0.0.1/24", + }, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: "invalid mac", + SkipDefaultRoutes: false, + }, + { + MacAddress: ibMacAddress, + NICType: cns.BackendNIC, + PnPID: "invalid pnpID", + }, + }, + Response: cns.Response{ + ReturnCode: 0, + Message: "", + }, + }, + err: nil, + }, + }, + }, + args: args{ + nwCfg: &cni.NetworkConfig{}, + args: &cniSkel.CmdArgs{ + ContainerID: "testcontainerid1", + Netns: "testnetns1", + IfName: "testifname1", + }, + hostSubnetPrefix: getCIDRNotationForAddress("10.0.0.1/24"), + options: map[string]interface{}{}, + }, + wantDefaultResult: network.InterfaceInfo{ + IPConfigs: []*network.IPConfig{ + { + Address: *getCIDRNotationForAddress("10.0.1.10/24"), + Gateway: net.ParseIP("10.0.0.1"), + }, + }, + Routes: []network.RouteInfo{ + { + Dst: network.Ipv4DefaultRouteDstPrefix, + Gw: net.ParseIP("10.0.0.1"), + }, + }, + NICType: cns.InfraNIC, + SkipDefaultRoutes: true, + HostSubnetPrefix: *parseCIDR("10.0.0.0/24"), + }, + wantSecondaryInterfacesInfo: map[string]network.InterfaceInfo{ + accelnetAddress: { + IPConfigs: []*network.IPConfig{ + { + Address: *getCIDRNotationForAddress("30.1.1.10/24"), + }, + }, + Routes: []network.RouteInfo{}, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: accelnetParsedMacAddress, + }, + ibMacAddress: { + NICType: cns.BackendNIC, + MacAddress: ibParsedMacAddress, + PnPID: pnpID, + }, + }, + wantErr: true, + }, } for _, tt := range tests { tt := tt @@ -1750,12 +2078,17 @@ func TestCNSIPAMInvoker_Add_SwiftV2(t *testing.T) { if ifInfo.NICType == cns.BackendNIC { fmt.Printf("want:%+v\nrest:%+v\n", tt.wantSecondaryInterfacesInfo, ipamAddResult.interfaceInfo[ibMacAddress]) - require.EqualValues(tt.wantSecondaryInterfacesInfo[ibMacAddress], ipamAddResult.interfaceInfo[ibMacAddress], "incorrect multitenant response for IB") + require.EqualValues(tt.wantSecondaryInterfacesInfo[ibMacAddress], ipamAddResult.interfaceInfo[ibMacAddress], "incorrect response for IB") } - if ifInfo.NICType == cns.DelegatedVMNIC { + if ifInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC { fmt.Printf("want:%+v\nrest:%+v\n", tt.wantSecondaryInterfacesInfo[macAddress], ipamAddResult.interfaceInfo[macAddress]) - require.EqualValues(tt.wantSecondaryInterfacesInfo[macAddress], ipamAddResult.interfaceInfo[macAddress], "incorrect multitenant response for Delegated") + require.EqualValues(tt.wantSecondaryInterfacesInfo[macAddress], ipamAddResult.interfaceInfo[macAddress], "incorrect response for Delegated") + } + + if ifInfo.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { + fmt.Printf("want:%+v\nrest:%+v\n", tt.wantSecondaryInterfacesInfo[accelnetAddress], ipamAddResult.interfaceInfo[accelnetAddress]) + require.EqualValues(tt.wantSecondaryInterfacesInfo[accelnetAddress], ipamAddResult.interfaceInfo[accelnetAddress], "incorrect response for Accelnet") } } }) diff --git a/cni/network/invoker_mock.go b/cni/network/invoker_mock.go index 1461436260..8961288769 100644 --- a/cni/network/invoker_mock.go +++ b/cni/network/invoker_mock.go @@ -20,8 +20,9 @@ const ( var ( errV4 = errors.New("v4 fail") errV6 = errors.New("v6 Fail") - errDelegatedVMNIC = errors.New("delegatedVMNIC fail") + errDelegatedVMNIC = errors.New("NodeNetworkInterfaceFrontendNIC fail") errDeleteIpam = errors.New("delete fail") + errAccelnetVMNIC = errors.New("accelnetNIC fail") ) type MockIpamInvoker struct { @@ -30,17 +31,21 @@ type MockIpamInvoker struct { v6Fail bool delegatedVMNIC bool delegatedVMNICFail bool + accelnetNIC bool + accelnetNICFail bool ipMap map[string]bool customReturn map[string]network.InterfaceInfo } -func NewMockIpamInvoker(ipv6, v4Fail, v6Fail, delegatedVMNIC, delegatedVMNICFail bool) *MockIpamInvoker { +func NewMockIpamInvoker(ipv6, v4Fail, v6Fail, delegatedVMNIC, delegatedVMNICFail, accelnetNIC, accelnetNICFail bool) *MockIpamInvoker { return &MockIpamInvoker{ isIPv6: ipv6, v4Fail: v4Fail, v6Fail: v6Fail, delegatedVMNIC: delegatedVMNIC, delegatedVMNICFail: delegatedVMNICFail, + accelnetNIC: accelnetNIC, + accelnetNICFail: accelnetNICFail, ipMap: make(map[string]bool), } } @@ -108,7 +113,21 @@ func (invoker *MockIpamInvoker) Add(opt IPAMAddConfig) (ipamAddResult IPAMAddRes ipRes = append(ipRes, &network.IPConfig{Address: *ipnet}) ipamAddResult.interfaceInfo[string(cns.InfraNIC)] = network.InterfaceInfo{ IPConfigs: ipRes, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, + } + } + + if invoker.accelnetNIC { + if invoker.accelnetNICFail { + return IPAMAddResult{}, errAccelnetVMNIC + } + + ipStr := "30.30.30.30/32" + _, ipnet, _ := net.ParseCIDR(ipStr) + ipRes = append(ipRes, &network.IPConfig{Address: *ipnet}) + ipamAddResult.interfaceInfo[string(cns.InfraNIC)] = network.InterfaceInfo{ + IPConfigs: ipRes, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, } } diff --git a/cni/network/multitenancy_test.go b/cni/network/multitenancy_test.go index 4b730d4b2e..9e43535484 100644 --- a/cni/network/multitenancy_test.go +++ b/cni/network/multitenancy_test.go @@ -287,7 +287,7 @@ func TestCleanupMultitenancyResources(t *testing.T) { }, infraIPNet: &cniTypesCurr.Result{}, plugin: &NetPlugin{ - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), }, }, expected: args{ @@ -298,7 +298,7 @@ func TestCleanupMultitenancyResources(t *testing.T) { }, infraIPNet: &cniTypesCurr.Result{}, plugin: &NetPlugin{ - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), }, }, }, @@ -413,7 +413,7 @@ func TestGetMultiTenancyCNIResult(t *testing.T) { IPAM: cni.IPAM{Type: "azure-vnet-ipam"}, }, plugin: &NetPlugin{ - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), multitenancyClient: &Multitenancy{ netioshim: &mockNetIOShim{}, cnsclient: &MockCNSClient{ @@ -629,7 +629,7 @@ func TestGetMultiTenancyCNIResultUnsupportedAPI(t *testing.T) { IPAM: cni.IPAM{Type: "azure-vnet-ipam"}, }, plugin: &NetPlugin{ - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), multitenancyClient: &Multitenancy{ netioshim: &mockNetIOShim{}, cnsclient: &MockCNSClient{ @@ -769,7 +769,7 @@ func TestGetMultiTenancyCNIResultNotFound(t *testing.T) { IPAM: cni.IPAM{Type: "azure-vnet-ipam"}, }, plugin: &NetPlugin{ - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), multitenancyClient: &Multitenancy{ netioshim: &mockNetIOShim{}, cnsclient: &MockCNSClient{ @@ -804,7 +804,7 @@ func TestGetMultiTenancyCNIResultNotFound(t *testing.T) { IPAM: cni.IPAM{Type: "azure-vnet-ipam"}, }, plugin: &NetPlugin{ - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), multitenancyClient: &Multitenancy{ netioshim: &mockNetIOShim{}, cnsclient: &MockCNSClient{ diff --git a/cni/network/network.go b/cni/network/network.go index a78ef7b91f..0572b1a2ec 100644 --- a/cni/network/network.go +++ b/cni/network/network.go @@ -683,7 +683,7 @@ func (plugin *NetPlugin) findMasterInterface(opt *createEpInfoOpt) string { switch opt.ifInfo.NICType { case cns.InfraNIC: return plugin.findMasterInterfaceBySubnet(opt.ipamAddConfig.nwCfg, &opt.ifInfo.HostSubnetPrefix) - case cns.DelegatedVMNIC: + case cns.NodeNetworkInterfaceFrontendNIC, cns.NodeNetworkInterfaceAccelnetFrontendNIC: return plugin.findInterfaceByMAC(opt.ifInfo.MacAddress.String()) case cns.BackendNIC: // TODO: how to find interface with IB NIC by mac address opt.ifInfo.Name = ibInterfacePrefix + strconv.Itoa(opt.endpointIndex) diff --git a/cni/network/network_test.go b/cni/network/network_test.go index 6d19ce6734..1e8dfa5eb5 100644 --- a/cni/network/network_test.go +++ b/cni/network/network_test.go @@ -78,7 +78,7 @@ func GetTestResources() *NetPlugin { plugin.report = &telemetry.CNIReport{} mockNetworkManager := acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)) plugin.nm = mockNetworkManager - plugin.ipamInvoker = NewMockIpamInvoker(isIPv6, false, false, false, false) + plugin.ipamInvoker = NewMockIpamInvoker(isIPv6, false, false, false, false, false, false) return plugin } @@ -394,9 +394,9 @@ func TestIpamAddFail(t *testing.T) { for i, method := range tt.methods { fmt.Println("method", method, "wanterr", tt.wantErr[i]) if tt.wantErr[i] { - plugin.ipamInvoker = NewMockIpamInvoker(false, true, false, false, false) + plugin.ipamInvoker = NewMockIpamInvoker(false, true, false, false, false, false, false) } else { - plugin.ipamInvoker = NewMockIpamInvoker(false, false, false, false, false) + plugin.ipamInvoker = NewMockIpamInvoker(false, false, false, false, false, false, false) } if tt.wantEndpointErr { @@ -459,7 +459,7 @@ func TestIpamDeleteFail(t *testing.T) { err := plugin.Add(tt.args) require.NoError(t, err) - plugin.ipamInvoker = NewMockIpamInvoker(false, true, false, false, false) + plugin.ipamInvoker = NewMockIpamInvoker(false, true, false, false, false, false, false) err = plugin.Delete(args) if tt.wantErr { require.Error(t, err) @@ -489,7 +489,7 @@ func TestAddDualStack(t *testing.T) { plugin: &NetPlugin{ Plugin: cniPlugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(true, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(true, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -500,7 +500,7 @@ func TestAddDualStack(t *testing.T) { plugin: &NetPlugin{ Plugin: cniPlugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(true, false, true, false, false), + ipamInvoker: NewMockIpamInvoker(true, false, true, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -546,7 +546,7 @@ func TestPluginGet(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -558,7 +558,7 @@ func TestPluginGet(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -571,7 +571,7 @@ func TestPluginGet(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -1199,7 +1199,7 @@ func TestPluginSwiftV2Add(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, true, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, true, false, true, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, netClient: &InterfaceGetterMock{ @@ -1216,7 +1216,7 @@ func TestPluginSwiftV2Add(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, true, true), + ipamInvoker: NewMockIpamInvoker(false, false, false, true, true, true, true), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, netClient: &InterfaceGetterMock{ @@ -1227,20 +1227,20 @@ func TestPluginSwiftV2Add(t *testing.T) { }, args: args, wantErr: true, - wantErrMsg: "IPAM Invoker Add failed with error: failed to add ipam invoker: delegatedVMNIC fail", + wantErrMsg: "IPAM Invoker Add failed with error: failed to add ipam invoker: NodeNetworkInterfaceFrontendNIC fail", }, { - name: "SwiftV2 EndpointClient Add fail", + name: "SwiftV2 EndpointClient Add fail with NodeNetworkInterfaceFrontendNIC", plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(func(ep *acnnetwork.EndpointInfo) error { - if ep.NICType == cns.DelegatedVMNIC { + if ep.NICType == cns.NodeNetworkInterfaceFrontendNIC { return acnnetwork.NewErrorMockEndpointClient("AddEndpoints Delegated VM NIC failed") //nolint:wrapcheck // ignore wrapping for test } return nil })), - ipamInvoker: NewMockIpamInvoker(false, false, false, true, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, true, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, netClient: &InterfaceGetterMock{ @@ -1254,11 +1254,51 @@ func TestPluginSwiftV2Add(t *testing.T) { wantErrMsg: "failed to create endpoint: MockEndpointClient Error : AddEndpoints Delegated VM NIC failed", }, { - name: "SwiftV2 Find Interface By MAC Address Fail", + name: "SwiftV2 EndpointClient Add fail with AccelnetNIC", + plugin: &NetPlugin{ + Plugin: plugin, + nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(func(ep *acnnetwork.EndpointInfo) error { + if ep.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { + return acnnetwork.NewErrorMockEndpointClient("AddEndpoints Accelnet VM NIC failed") //nolint:wrapcheck // ignore wrapping for test + } + + return nil + })), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, true, false), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + netClient: &InterfaceGetterMock{ + interfaces: []net.Interface{ + {Name: "eth0"}, + }, + }, + }, + args: args, + wantErr: true, + wantErrMsg: "failed to create endpoint: MockEndpointClient Error : AddEndpoints Accelnet VM NIC failed", + }, + { + name: "SwiftV2 Find Interface By MAC Address Fail with delegated VM NIC", plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, true, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, true, false, false, false), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + netClient: &InterfaceGetterMock{ + interfaces: []net.Interface{}, + }, + }, + args: args, + wantErr: true, + wantErrMsg: "Failed to find the master interface", + }, + { + name: "SwiftV2 Find Interface By MAC Address Fail with Accelnet VM NIC", + plugin: &NetPlugin{ + Plugin: plugin, + nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, true, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, netClient: &InterfaceGetterMock{ @@ -1274,7 +1314,7 @@ func TestPluginSwiftV2Add(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, netClient: &InterfaceGetterMock{ @@ -1353,7 +1393,7 @@ func TestPluginSwiftV2MultipleAddDelete(t *testing.T) { nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), ipamInvoker: NewCustomMockIpamInvoker(map[string]acnnetwork.InterfaceInfo{ "eth0-1": { - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, "eth0": { NICType: cns.InfraNIC, @@ -1371,6 +1411,59 @@ func TestPluginSwiftV2MultipleAddDelete(t *testing.T) { wantErr: false, wantNumEps: 2, }, + { + name: "SwiftV2 Add Delegated and Accelnet", + plugin: &NetPlugin{ + Plugin: plugin, + nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), + ipamInvoker: NewCustomMockIpamInvoker(map[string]acnnetwork.InterfaceInfo{ + "eth0-1": { + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + }, + "eth0": { + NICType: cns.NodeNetworkInterfaceFrontendNIC, + }, + }), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + netClient: &InterfaceGetterMock{ + interfaces: []net.Interface{ + {Name: "eth0"}, + }, + }, + }, + args: args, + wantErr: false, + wantNumEps: 2, + }, + { + name: "SwiftV2 Add Infra and Delegated and Accelnet", + plugin: &NetPlugin{ + Plugin: plugin, + nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), + ipamInvoker: NewCustomMockIpamInvoker(map[string]acnnetwork.InterfaceInfo{ + "eth0-2": { + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + }, + "eth0-1": { + NICType: cns.NodeNetworkInterfaceFrontendNIC, + }, + "eth0": { + NICType: cns.InfraNIC, + }, + }), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + netClient: &InterfaceGetterMock{ + interfaces: []net.Interface{ + {Name: "eth0"}, + }, + }, + }, + args: args, + wantErr: false, + wantNumEps: 3, + }, { name: "SwiftV2 Add Infra and InfiniteBand", plugin: &NetPlugin{ @@ -1403,10 +1496,35 @@ func TestPluginSwiftV2MultipleAddDelete(t *testing.T) { nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), ipamInvoker: NewCustomMockIpamInvoker(map[string]acnnetwork.InterfaceInfo{ "eth0": { - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, "eth0-1": { - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, + }, + }), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + netClient: &InterfaceGetterMock{ + interfaces: []net.Interface{ + {Name: "eth0"}, + }, + }, + }, + args: args, + wantErr: false, + wantNumEps: 2, + }, + { + name: "SwiftV2 Add Two Accelnet", + plugin: &NetPlugin{ + Plugin: plugin, + nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(nil)), + ipamInvoker: NewCustomMockIpamInvoker(map[string]acnnetwork.InterfaceInfo{ + "eth0": { + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + }, + "eth0-1": { + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, }, }), report: &telemetry.CNIReport{}, @@ -1424,11 +1542,11 @@ func TestPluginSwiftV2MultipleAddDelete(t *testing.T) { { // creates 2 endpoints, the first succeeds, the second doesn't // ensures that delete is called to clean up the first endpoint that succeeded - name: "SwiftV2 Partial Add fail", + name: "SwiftV2 Partial Add fail with Delegated VM NIC", plugin: &NetPlugin{ Plugin: plugin, nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(func(ep *acnnetwork.EndpointInfo) error { - if ep.NICType == cns.DelegatedVMNIC { + if ep.NICType == cns.NodeNetworkInterfaceFrontendNIC { return acnnetwork.NewErrorMockEndpointClient("AddEndpoints Delegated VM NIC failed") //nolint:wrapcheck // ignore wrapping for test } @@ -1439,7 +1557,7 @@ func TestPluginSwiftV2MultipleAddDelete(t *testing.T) { NICType: cns.InfraNIC, }, "eth0-1": { - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, }), report: &telemetry.CNIReport{}, @@ -1455,6 +1573,70 @@ func TestPluginSwiftV2MultipleAddDelete(t *testing.T) { wantErr: true, wantErrMsg: "failed to create endpoint: MockEndpointClient Error : AddEndpoints Delegated VM NIC failed", }, + { + name: "SwiftV2 Partial Add fail with Accelnet VM NIC", + plugin: &NetPlugin{ + Plugin: plugin, + nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(func(ep *acnnetwork.EndpointInfo) error { + if ep.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { + return acnnetwork.NewErrorMockEndpointClient("AddEndpoints Accelnet VM NIC failed") //nolint:wrapcheck // ignore wrapping for test + } + + return nil + })), + ipamInvoker: NewCustomMockIpamInvoker(map[string]acnnetwork.InterfaceInfo{ + "eth0": { + NICType: cns.NodeNetworkInterfaceFrontendNIC, + }, + "eth0-1": { + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + }, + }), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + netClient: &InterfaceGetterMock{ + interfaces: []net.Interface{ + {Name: "eth0"}, + }, + }, + }, + args: args, + wantNumEps: 0, + wantErr: true, + wantErrMsg: "failed to create endpoint: MockEndpointClient Error : AddEndpoints Accelnet VM NIC failed", + }, + { + name: "SwiftV2 Partial Add fail with Infra+Accelnet VM NIC", + plugin: &NetPlugin{ + Plugin: plugin, + nm: acnnetwork.NewMockNetworkmanager(acnnetwork.NewMockEndpointClient(func(ep *acnnetwork.EndpointInfo) error { + if ep.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { + return acnnetwork.NewErrorMockEndpointClient("AddEndpoints Accelnet VM NIC failed") //nolint:wrapcheck // ignore wrapping for test + } + + return nil + })), + ipamInvoker: NewCustomMockIpamInvoker(map[string]acnnetwork.InterfaceInfo{ + "eth0": { + NICType: cns.InfraNIC, + }, + "eth0-1": { + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + }, + }), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + netClient: &InterfaceGetterMock{ + interfaces: []net.Interface{ + {Name: "eth0"}, + }, + }, + }, + args: args, + wantNumEps: 0, + wantErr: true, + wantErrMsg: "failed to create endpoint: MockEndpointClient Error : AddEndpoints Accelnet VM NIC failed", + }, } for _, tt := range tests { @@ -1479,6 +1661,7 @@ func TestPluginSwiftV2MultipleAddDelete(t *testing.T) { err = tt.plugin.Delete(tt.args) require.NoError(t, err) + endpoints, _ = tt.plugin.nm.GetAllEndpoints(localNwCfg.Name) require.Condition(t, assert.Comparison(func() bool { return len(endpoints) == 0 })) }) diff --git a/cni/network/network_windows.go b/cni/network/network_windows.go index 6306cc15e6..d78865e5ac 100644 --- a/cni/network/network_windows.go +++ b/cni/network/network_windows.go @@ -73,7 +73,8 @@ func (plugin *NetPlugin) getNetworkName(netNs string, interfaceInfo *network.Int determineWinVer() // Swiftv2 L1VH Network Name swiftv2NetworkNamePrefix := "azure-" - if interfaceInfo != nil && (interfaceInfo.NICType == cns.DelegatedVMNIC || interfaceInfo.NICType == cns.BackendNIC) { + if interfaceInfo != nil && (interfaceInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC || interfaceInfo.NICType == cns.BackendNIC || + interfaceInfo.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC) { logger.Info("swiftv2", zap.String("network name", interfaceInfo.MacAddress.String())) return swiftv2NetworkNamePrefix + interfaceInfo.MacAddress.String(), nil } diff --git a/cni/network/network_windows_test.go b/cni/network/network_windows_test.go index b6375fb7dd..5898c0b0c9 100644 --- a/cni/network/network_windows_test.go +++ b/cni/network/network_windows_test.go @@ -93,7 +93,7 @@ func TestPluginSecondAddSamePodWindows(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -112,7 +112,7 @@ func TestPluginSecondAddSamePodWindows(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -478,7 +478,7 @@ func TestGetNetworkNameFromCNS(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -511,7 +511,7 @@ func TestGetNetworkNameFromCNS(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -544,7 +544,7 @@ func TestGetNetworkNameFromCNS(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -577,7 +577,7 @@ func TestGetNetworkNameFromCNS(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -610,7 +610,7 @@ func TestGetNetworkNameFromCNS(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, false, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -651,7 +651,6 @@ func TestGetNetworkNameFromCNS(t *testing.T) { } func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { - // TODO: Add Accelnet NIC test to this test plugin, _ := cni.NewPlugin("name", "0.3.0") macAddress := "00:00:5e:00:53:01" @@ -672,7 +671,7 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, true, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, true, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -684,7 +683,7 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { interfaceInfo: &network.InterfaceInfo{ Name: "swiftv2L1VHDelegatedInterface", MacAddress: parsedMacAddress, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, want: parsedMacAddress, wantErr: false, @@ -694,7 +693,7 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { plugin: &NetPlugin{ Plugin: plugin, nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), - ipamInvoker: NewMockIpamInvoker(false, false, false, true, false), + ipamInvoker: NewMockIpamInvoker(false, false, false, true, false, false, false), report: &telemetry.CNIReport{}, tb: &telemetry.TelemetryBuffer{}, }, @@ -711,6 +710,28 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { want: parsedMacAddress, wantErr: false, }, + { + name: "Get Network Name from CNS for swiftv2 AccelnetNIC", + plugin: &NetPlugin{ + Plugin: plugin, + nm: network.NewMockNetworkmanager(network.NewMockEndpointClient(nil)), + ipamInvoker: NewMockIpamInvoker(false, false, false, false, false, true, false), + report: &telemetry.CNIReport{}, + tb: &telemetry.TelemetryBuffer{}, + }, + netNs: "azure", + nwCfg: &cni.NetworkConfig{ + CNIVersion: "0.3.0", + MultiTenancy: false, + }, + interfaceInfo: &network.InterfaceInfo{ + Name: "swiftv2L1VHAccelnetInterface", + MacAddress: parsedMacAddress, + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + }, + want: parsedMacAddress, + wantErr: false, + }, } for _, tt := range tests { diff --git a/network/endpoint_linux.go b/network/endpoint_linux.go index 6854d2e347..faca6c4c97 100644 --- a/network/endpoint_linux.go +++ b/network/endpoint_linux.go @@ -165,7 +165,7 @@ func (nw *network) newEndpointImpl( } else if nw.Mode != opModeTransparent { logger.Info("Bridge client") epClient = NewLinuxBridgeEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, plc) - } else if epInfo.NICType == cns.DelegatedVMNIC { + } else if epInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC { logger.Info("Secondary client") epClient = NewSecondaryEndpointClient(nl, netioCli, plc, nsc, ep) } else { @@ -286,12 +286,12 @@ func (nw *network) deleteEndpointImpl(nl netlink.NetlinkInterface, plc platform. epClient = NewLinuxBridgeEndpointClient(nw.extIf, ep.HostIfName, "", nw.Mode, nl, plc) } else { // delete if secondary interfaces populated or endpoint of type delegated (new way) - if len(ep.SecondaryInterfaces) > 0 || ep.NICType == cns.DelegatedVMNIC { + if len(ep.SecondaryInterfaces) > 0 || ep.NICType == cns.NodeNetworkInterfaceFrontendNIC { epClient = NewSecondaryEndpointClient(nl, nioc, plc, nsc, ep) epClient.DeleteEndpointRules(ep) //nolint:errcheck // ignore error epClient.DeleteEndpoints(ep) - if ep.NICType == cns.DelegatedVMNIC { + if ep.NICType == cns.NodeNetworkInterfaceFrontendNIC { // if the ep itself is of type secondary (new way), don't use transparent client below return nil } diff --git a/network/endpoint_test.go b/network/endpoint_test.go index 6b2a748820..3835af0f26 100644 --- a/network/endpoint_test.go +++ b/network/endpoint_test.go @@ -246,7 +246,7 @@ var _ = Describe("Test Endpoint", func() { Data: make(map[string]interface{}), IfName: eth0IfName, MasterIfName: "masterIfName", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, } epInfo.Data[VlanIDKey] = 100 @@ -267,6 +267,31 @@ var _ = Describe("Test Endpoint", func() { Expect(ep.IfName).To(Equal("masterIfName")) }) }) + Context("When endpoint added accelnet", func() { + epInfo := &EndpointInfo{ + EndpointID: "768e8deb-eth1", + Data: make(map[string]interface{}), + IfName: eth0IfName, + MasterIfName: "accelnetNIC", + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + } + + It("should have fields set", func() { + nw2 := &network{ + Endpoints: map[string]*endpoint{}, + extIf: &externalInterface{IPv4Gateway: net.ParseIP("192.168.0.1")}, + } + ep, err := nw2.newEndpointImpl(nil, netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), + netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo) + Expect(err).NotTo(HaveOccurred()) + Expect(ep).NotTo(BeNil()) + Expect(ep.Id).To(Equal(epInfo.EndpointID)) + Expect(ep.Gateways).NotTo(BeNil()) + Expect(len(ep.Gateways)).To(Equal(1)) + Expect(ep.Gateways[0].String()).To(Equal("192.168.0.1")) + Expect(ep.IfName).To(Equal("accelnetNIC")) + }) + }) Context("When endpoint add failed", func() { It("Should not be added to the network", func() { nw := &network{ @@ -310,7 +335,7 @@ var _ = Describe("Test Endpoint", func() { secondaryEpInfo := &EndpointInfo{ // When we create the secondary endpoint infos while looping over the interface infos, we pass in the same endpoint id EndpointID: "768e8deb-eth1", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, Routes: []RouteInfo{{Dst: *ipnet}}, } @@ -388,7 +413,7 @@ var _ = Describe("Test Endpoint", func() { }, { ContainerID: "0ea7476f26d192f067abdc8b3df43ce3cdbe324386e1c010cb48de87eefef480", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, } Expect(validateEndpoints(eps)).To(BeNil()) @@ -403,7 +428,7 @@ var _ = Describe("Test Endpoint", func() { }, { ContainerID: "0ea7476f26d192f067abdc8b3df43ce3cdbe324386e1c010cb48de87eefef481", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, } Expect(validateEndpoints(eps)).ToNot(BeNil()) @@ -418,7 +443,7 @@ var _ = Describe("Test Endpoint", func() { }, { ContainerID: "", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, } Expect(validateEndpoints(eps)).ToNot(BeNil()) diff --git a/network/endpoint_windows.go b/network/endpoint_windows.go index 05506fd537..c912552846 100644 --- a/network/endpoint_windows.go +++ b/network/endpoint_windows.go @@ -306,9 +306,9 @@ func (nw *network) configureHcnEndpoint(epInfo *EndpointInfo) (*hcn.HostComputeE } // macAddress type for InfraNIC is like "60:45:bd:12:45:65" - // if NICType is delegatedVMNIC, convert the macaddress format + // if NICType is delegatedVMNIC or AccelnetNIC, convert the macaddress format macAddress := epInfo.MacAddress.String() - if epInfo.NICType == cns.DelegatedVMNIC { + if epInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC || epInfo.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { // convert the format of macAddress that HNS can accept, i.e, "60-45-bd-12-45-65" if NIC type is delegated NIC macAddress = strings.Join(strings.Split(macAddress, ":"), "-") } @@ -321,6 +321,16 @@ func (nw *network) configureHcnEndpoint(epInfo *EndpointInfo) (*hcn.HostComputeE return nil, err } + // add hcnEndpoint policy for accelnet + if epInfo.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { + endpointPolicy, err := policy.AddAccelnetPolicySetting() + if err != nil { + logger.Error("Failed to set iov endpoint policy", zap.Error(err)) + return nil, errors.Wrapf(err, "Failed to set iov endpoint policy for endpointId :%s", epInfo.EndpointID) + } + hcnEndpoint.Policies = append(hcnEndpoint.Policies, endpointPolicy) + } + for _, route := range epInfo.Routes { hcnRoute := hcn.Route{ NextHop: route.Gw.String(), @@ -518,6 +528,11 @@ func (nw *network) deleteEndpointImpl(_ netlink.NetlinkInterface, _ platform.Exe return nil } + if ep.HnsId == "" { + logger.Error("No HNS id found. Skip endpoint deletion", zap.Any("nicType", ep.NICType), zap.String("containerId", ep.ContainerID)) + return fmt.Errorf("No HNS id found. Skip endpoint deletion for nicType %v, containerID %s", ep.NICType, ep.ContainerID) //nolint + } + if useHnsV2, err := UseHnsV2(ep.NetNs); useHnsV2 { if err != nil { return err diff --git a/network/endpoint_windows_test.go b/network/endpoint_windows_test.go index c24647eb5d..4b6588cd36 100644 --- a/network/endpoint_windows_test.go +++ b/network/endpoint_windows_test.go @@ -20,12 +20,17 @@ import ( "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/hnswrapper" "github.com/Azure/azure-container-networking/platform" + "github.com/Microsoft/hcsshim/hcn" ) var ( - instanceID = "12345-abcde-789" - locationPath = "12345-abcde-789-fea14" - pnpID = "PCI\\VEN_15B3&DEV_101C&SUBSYS_000715B3&REV_00\\5&8c5acce&0&0" + instanceID = "12345-abcde-789" + locationPath = "12345-abcde-789-fea14" + pnpID = "PCI\\VEN_15B3&DEV_101C&SUBSYS_000715B3&REV_00\\5&8c5acce&0&0" + macAddress = "60-45-bd-12-45-65" + networkID = "azure-23:34:56:78:90:ab" + networkName = "l1vh" + l1vhNetworkType = hcn.Transparent ) func TestNewAndDeleteEndpointImplHnsV2(t *testing.T) { @@ -108,6 +113,33 @@ func TestDeleteEndpointImplHnsV2ForIB(t *testing.T) { } } +// Test endpoint deletion when hns id is empty +func TestDeleteEndpointImplHnsV2WithEmptyHNSID(t *testing.T) { + nw := &network{ + Endpoints: map[string]*endpoint{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + Hnsv2 = hnswrapper.Hnsv2wrapperwithtimeout{ + Hnsv2: hnswrapper.NewHnsv2wrapperFake(), + } + + ep := endpoint{ + HnsId: "", + IfName: "eth1", + MacAddress: net.HardwareAddr("00:00:5e:00:53:01"), + NICType: cns.NodeNetworkInterfaceFrontendNIC, + } + + // should return nil because HnsID is empty + mockCli := NewMockEndpointClient(nil) + err := nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), &ep) + if err != nil { + t.Fatal("endpoint deletion gets executed") + } +} + func TestNewEndpointImplHnsv2Timesout(t *testing.T) { nw := &network{ Endpoints: map[string]*endpoint{}, @@ -280,20 +312,11 @@ func TestDisableVFDeviceHappyPath(t *testing.T) { err := disableVFDevice(instanceID, nm.plClient) if err != nil { - t.Fatal("Failed to test disable VF happy path") + t.Fatalf("Failed to test disable VF happy path due to %v", err) } } -func TestDisableVFDeviceUnHappyPathOne(t *testing.T) { - // set unhappy path - mockExecClient := platform.NewMockExecClient(true) - err := disableVFDevice(instanceID, mockExecClient) - if err == nil { - t.Fatal("Failed to test disable VF unhappy path") - } -} - -func TestDisableVFDeviceUnHappyPathTwo(t *testing.T) { +func TestDisableVFDeviceUnHappyPath(t *testing.T) { mockExecClient := platform.NewMockExecClient(false) // set unhappy path mockExecClient.SetPowershellCommandResponder(func(cmd string) (string, error) { @@ -302,7 +325,7 @@ func TestDisableVFDeviceUnHappyPathTwo(t *testing.T) { err := disableVFDevice(instanceID, mockExecClient) if err == nil { - t.Fatal("Failed to test disable VF unhappy path") + t.Fatalf("Failed to test disable VF unhappy path") } } @@ -323,15 +346,20 @@ func TestGetLocationPathHappyPath(t *testing.T) { _, err := getLocationPath(instanceID, nm.plClient) if err != nil { - t.Fatal("Failed to test get locationPath happy path") + t.Fatalf("Failed to test get locationPath happy path due to %v", err) } } func TestGetLocationPathUnHappyPath(t *testing.T) { - mockExecClient := platform.NewMockExecClient(true) + mockExecClient := platform.NewMockExecClient(false) + // set unhappy path + mockExecClient.SetPowershellCommandResponder(func(cmd string) (string, error) { + return failedCaseReturn, errTestFailure + }) + _, err := getLocationPath(instanceID, mockExecClient) if err == nil { - t.Fatal("Failed to test get locationPath unhappy path") + t.Fatal("Failed to test get location path on unhappy path") } } @@ -352,20 +380,11 @@ func TestDismountVFDeviceHappyPath(t *testing.T) { err := dismountVFDevice(locationPath, nm.plClient) if err != nil { - t.Fatal("Failed to test dismount vf device happy path") - } -} - -func TestDismountVFDeviceUnHappyPathOne(t *testing.T) { - // set unhappy path - mockExecClient := platform.NewMockExecClient(true) - err := dismountVFDevice(instanceID, mockExecClient) - if err == nil { - t.Fatal("Failed to test dismount VF unhappy path") + t.Fatalf("Failed to test dismount vf device happy path due to %v", err) } } -func TestDismountVFDeviceUnHappyPathTwo(t *testing.T) { +func TestDismountVFDeviceUnHappyPath(t *testing.T) { mockExecClient := platform.NewMockExecClient(false) // set unhappy path mockExecClient.SetPowershellCommandResponder(func(cmd string) (string, error) { @@ -396,12 +415,17 @@ func TestGetPnPDeviceIDHappyPath(t *testing.T) { _, err := getPnPDeviceID(instanceID, nm.plClient) if err != nil { - t.Fatal("Failed to test get pnp device id happy path") + t.Fatalf("Failed to test get pnp device id happy path due to %v", err) } } func TestGetPnPDeviceIDUnHappyPath(t *testing.T) { - mockExecClient := platform.NewMockExecClient(true) + mockExecClient := platform.NewMockExecClient(false) + // set unhappy path + mockExecClient.SetPowershellCommandResponder(func(cmd string) (string, error) { + return failedCaseReturn, errTestFailure + }) + _, err := getPnPDeviceID(instanceID, mockExecClient) if err == nil { t.Fatal("Failed to test get pnp device id unhappy path") @@ -426,12 +450,17 @@ func TestGetPnPDeviceStateHappyPath(t *testing.T) { _, _, err := getPnpDeviceState(instanceID, nm.plClient) if err != nil { - t.Fatal("Failed to test happy path") + t.Fatalf("Failed to test happy path due to %v", err) } } func TestGetPnPDeviceStateUnHappyPath(t *testing.T) { - mockExecClient := platform.NewMockExecClient(true) + mockExecClient := platform.NewMockExecClient(false) + // set unhappy path + mockExecClient.SetPowershellCommandResponder(func(cmd string) (string, error) { + return failedCaseReturn, errTestFailure + }) + _, _, err := getPnpDeviceState(instanceID, mockExecClient) if err == nil { t.Fatal("Failed to test get pnp device state unhappy path") @@ -466,7 +495,7 @@ func TestNewEndpointImplHnsv2ForIBHappyPath(t *testing.T) { netio.NewMockNetIO(false, 0), NewMockEndpointClient(nil), NewMockNamespaceClient(), iptables.NewClient(), epInfo) if endpoint != nil || err != nil { - t.Fatal("Endpoint is created for IB") + t.Fatalf("Endpoint is created for IB due to %v", err) } } @@ -478,7 +507,6 @@ func TestNewEndpointImplHnsv2ForIBUnHappyPath(t *testing.T) { // this hnsv2 variable overwrites the package level variable in network // we do this to avoid passing around os specific objects in platform agnostic code hnsFake := hnswrapper.NewHnsv2wrapperFake() - Hnsv2 = hnswrapper.Hnsv2wrapperwithtimeout{ Hnsv2: hnsFake, HnsCallTimeout: 5 * time.Second, @@ -504,3 +532,356 @@ func TestNewEndpointImplHnsv2ForIBUnHappyPath(t *testing.T) { t.Fatalf("Unexpected Error:%v; Error should be %v", err, platform.ErrMockExec) } } + +func TestCreateAndDeleteEndpointImplHnsv2ForDelegatedHappyPath(t *testing.T) { + nw := &network{ + Endpoints: map[string]*endpoint{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + hnsFake := hnswrapper.NewHnsv2wrapperFake() + + Hnsv2 = hnswrapper.Hnsv2wrapperwithtimeout{ + Hnsv2: hnsFake, + HnsCallTimeout: 5 * time.Second, + } + + epInfo := &EndpointInfo{ + EndpointID: "768e8deb-eth1", + Data: make(map[string]interface{}), + IfName: "eth1", + NICType: cns.NodeNetworkInterfaceFrontendNIC, + MacAddress: net.HardwareAddr("00:00:5e:00:53:01"), + } + + // Happy Path to create and delete endpoint for delegated NIC + ep, err := nw.newEndpointImplHnsV2(nil, epInfo) + if err != nil { + t.Fatalf("Failed to create endpoint for Delegated NIC due to %v", err) + } + + mockCli := NewMockEndpointClient(nil) + err = nw.deleteEndpointImpl(netlink.NewMockNetlink(false, ""), platform.NewMockExecClient(false), mockCli, netio.NewMockNetIO(false, 0), NewMockNamespaceClient(), iptables.NewClient(), ep) + if err != nil { + t.Fatalf("Failed to delete endpoint for Delegated NIC due to %v", err) + } +} + +// Test: if hnsID is empty, the endpoint and network cannot be deleted +func TestCreateAndDeleteEndpointStateForAccelnetNICWithEmptyHNSId(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + hnsFake := hnswrapper.NewHnsv2wrapperFake() + + Hnsv2 = hnswrapper.Hnsv2wrapperwithtimeout{ + Hnsv2: hnsFake, + HnsCallTimeout: 5 * time.Second, + } + + // create network for AccelnetNIC + accelnetNetwork := &hcn.HostComputeNetwork{ + Id: networkID, + Name: networkName, + } + _, err := Hnsv2.CreateNetwork(accelnetNetwork) + if err != nil { + t.Fatalf("Failed to create network for accelnetNIC due to %v", err) + } + + // make sure two networks are created: + networks := hnsFake.Cache.GetNetworks() + if len(networks) != 1 { + t.Fatal("Failed to create one network for accelnetNIC") + } + + // create endpoint for accelnetNIC + accelnetEndpointID := "accelnetEndpoint" + acclnetEndpoint := &hcn.HostComputeEndpoint{ + Id: accelnetEndpointID, + Name: accelnetEndpointID, + HostComputeNetwork: networkID, + MacAddress: macAddress, + } + + _, err = Hnsv2.CreateEndpoint(acclnetEndpoint) + if err != nil { + t.Fatalf("Failed to create endpoint for accelnetNIC due to %v", err) + } + + // make sure two endpoints are created: + endpoints := hnsFake.Cache.GetEndpoints() + if len(endpoints) != 1 { + t.Fatal("Failed to create an endpoint for accelnetNIC") + } + + accelnetEpInfo := &EndpointInfo{ + EndpointID: accelnetEndpointID, + Data: make(map[string]interface{}), + IfName: "eth1", + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: net.HardwareAddr(macAddress), + HNSNetworkID: networkID, + } + + // mock DeleteEndpointState() to make sure endpoint and network is deleted from cache + // network and endpoint should be deleted from cache for accelnetnic + err = nm.DeleteEndpointState(networkID, accelnetEpInfo) + if err == nil { + t.Fatal("Successfully delete network when hns ID is empty") + } +} + +// mock to invoke endpointState deletion with InfraNIC + AccelnetNIC +func TestDeleteEndpointStateForInfraAccelnetNIC(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + hnsFake := hnswrapper.NewHnsv2wrapperFake() + + Hnsv2 = hnswrapper.Hnsv2wrapperwithtimeout{ + Hnsv2: hnsFake, + HnsCallTimeout: 5 * time.Second, + } + + // create network for InfraNIC + infraNetworkID := "azure" + infraNetwork := &hcn.HostComputeNetwork{ + Name: infraNetworkID, + } + + _, err := Hnsv2.CreateNetwork(infraNetwork) + if err != nil { + t.Fatalf("Failed to create network for infraNIC due to %v", err) + } + + // create network for AccelnetNIC + accelnetNetwork := &hcn.HostComputeNetwork{ + Id: networkID, + Name: networkName, + Type: l1vhNetworkType, + } + _, err = Hnsv2.CreateNetwork(accelnetNetwork) + if err != nil { + t.Fatalf("Failed to create network for AccelnetNIC due to %v", err) + } + + // make sure two networks are created: + networks := hnsFake.Cache.GetNetworks() + if len(networks) != 2 { + t.Fatal("Failed to create two networks for infraNIC and AccelnetNIC") + } + + // create endpoint for InfraNIC + infraEndpointID := "infraEndpoint" + infraEndpoint := &hcn.HostComputeEndpoint{ + Id: infraEndpointID, + Name: infraEndpointID, + HostComputeNetwork: infraNetworkID, + } + + _, err = Hnsv2.CreateEndpoint(infraEndpoint) + if err != nil { + t.Fatalf("Failed to create endpoint for infraNIC due to %v", err) + } + + // create endpoint for accelnet NIC + accelnetEndpointID := "accelnetEndpoint" + accelnetEndpoint := &hcn.HostComputeEndpoint{ + Id: accelnetEndpointID, + Name: accelnetEndpointID, + HostComputeNetwork: accelnetEndpointID, + MacAddress: macAddress, + } + + _, err = Hnsv2.CreateEndpoint(accelnetEndpoint) + if err != nil { + t.Fatalf("Failed to create endpoint for AccelnetNIC due to %v", err) + } + + // make sure two endpoints are created: + endpoints := hnsFake.Cache.GetEndpoints() + if len(endpoints) != 2 { + t.Fatal("Failed to create two endpoints for infraNIC and AccelnetNIC") + } + + infraEpInfo := &EndpointInfo{ + EndpointID: infraEndpointID, + Data: make(map[string]interface{}), + IfName: "eth0", + NICType: cns.InfraNIC, + HNSEndpointID: infraEndpointID, + HNSNetworkID: infraNetworkID, + } + + accelnetEpInfo := &EndpointInfo{ + EndpointID: accelnetEndpointID, + Data: make(map[string]interface{}), + IfName: "eth1", + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: net.HardwareAddr(macAddress), + HNSEndpointID: accelnetEndpointID, + HNSNetworkID: networkID, + } + + // mock DeleteEndpointState() to make sure endpoint and network is deleted from cache + // network and endpoint should be deleted from cache for accelnetNIC + err = nm.DeleteEndpointState(networkID, accelnetEpInfo) + if err != nil { + t.Fatalf("Failed to delete endpoint for accelnetNIC state due to %v", err) + } + + // endpoint should be deleted from cache for accelnetNIC and network is still there + err = nm.DeleteEndpointState(infraNetworkID, infraEpInfo) + if err != nil { + t.Fatalf("Failed to delete endpoint for infraNIC state due to %v", err) + } + + // check cache if endpoints are deleted + endpoints = hnsFake.Cache.GetEndpoints() + if len(endpoints) != 0 { + t.Fatalf("Not all endpoints are deleted, the remaining endpoints are %v", endpoints) + } + + // check cache if accelnet network is deleted and infra network is still there + networks = hnsFake.Cache.GetNetworks() + if len(networks) != 1 { + t.Fatalf("Failed to delete networks") + } + + if _, ok := networks[infraNetworkID]; !ok { + t.Fatal("Network for InfraNIC does not exist") + } +} + +// mock to nivoke endpointState deletion with InfraNIC + DelegatedNIC +func TestDeleteEndpointStateForInfraDelegatedNIC(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + hnsFake := hnswrapper.NewHnsv2wrapperFake() + + Hnsv2 = hnswrapper.Hnsv2wrapperwithtimeout{ + Hnsv2: hnsFake, + HnsCallTimeout: 5 * time.Second, + } + + // create network for InfraNIC + infraNetworkID := "azure" + infraNetwork := &hcn.HostComputeNetwork{ + Name: infraNetworkID, + } + + _, err := Hnsv2.CreateNetwork(infraNetwork) + if err != nil { + t.Fatalf("Failed to create network for infraNIC due to %v", err) + } + + // create network for DelegatedNIC + delegatedNetwork := &hcn.HostComputeNetwork{ + Id: networkID, + Name: networkName, + Type: l1vhNetworkType, + } + _, err = Hnsv2.CreateNetwork(delegatedNetwork) + if err != nil { + t.Fatalf("Failed to create network for delegatedNIC due to %v", err) + } + + // make sure two networks are created: + networks := hnsFake.Cache.GetNetworks() + if len(networks) != 2 { + t.Fatal("Failed to create two networks for infraNIC and delegatedNIC") + } + + // create endpoint for InfraNIC + infraEndpointID := "infraEndpoint" + infraEndpoint := &hcn.HostComputeEndpoint{ + Id: infraEndpointID, + Name: infraEndpointID, + HostComputeNetwork: infraNetworkID, + } + + _, err = Hnsv2.CreateEndpoint(infraEndpoint) + if err != nil { + t.Fatalf("Failed to create endpoint for infraNIC due to %v", err) + } + + // create endpoint for delegated NIC + deletegatedEndpointID := "delegatedEndpoint" + delegatedEndpoint := &hcn.HostComputeEndpoint{ + Id: deletegatedEndpointID, + Name: deletegatedEndpointID, + HostComputeNetwork: deletegatedEndpointID, + MacAddress: macAddress, + } + + _, err = Hnsv2.CreateEndpoint(delegatedEndpoint) + if err != nil { + t.Fatalf("Failed to create endpoint for delegatedNIC due to %v", err) + } + + // make sure two endpoints are created: + endpoints := hnsFake.Cache.GetEndpoints() + if len(endpoints) != 2 { + t.Fatal("Failed to create two endpoints for infraNIC and delegatedNIC") + } + + infraEpInfo := &EndpointInfo{ + EndpointID: infraEndpointID, + Data: make(map[string]interface{}), + IfName: "eth0", + NICType: cns.InfraNIC, + HNSEndpointID: infraEndpointID, + HNSNetworkID: infraNetworkID, + } + + delegatedEpInfo := &EndpointInfo{ + EndpointID: deletegatedEndpointID, + Data: make(map[string]interface{}), + IfName: "eth1", + NICType: cns.NodeNetworkInterfaceFrontendNIC, + MacAddress: net.HardwareAddr(macAddress), + HNSEndpointID: deletegatedEndpointID, + HNSNetworkID: networkID, + } + + // mock DeleteEndpointState() to make sure endpoint and network is deleted from cache + // network and endpoint should be deleted from cache for delegatedNIC + err = nm.DeleteEndpointState(networkID, delegatedEpInfo) + if err != nil { + t.Fatalf("Failed to delete endpoint for delegatedNIC state due to %v", err) + } + + // endpoint should be deleted from cache for delegatedNIC and network is still there + err = nm.DeleteEndpointState(infraNetworkID, infraEpInfo) + if err != nil { + t.Fatalf("Failed to delete endpoint for delegatedNIC state due to %v", err) + } + + // check cache if endpoints are deleted + endpoints = hnsFake.Cache.GetEndpoints() + if len(endpoints) != 0 { + t.Fatalf("Not all endpoints are deleted, the remaining endpoints are %v", endpoints) + } + + // check cache if delegated network is deleted and infra network is still there + networks = hnsFake.Cache.GetNetworks() + if len(networks) != 1 { + t.Fatalf("Failed to delete networks") + } + + if _, ok := networks[infraNetworkID]; !ok { + t.Fatal("Network for InfraNIC does not exist") + } +} diff --git a/network/hnswrapper/hnsv2wrapper.go b/network/hnswrapper/hnsv2wrapper.go index 592cb22b8f..a52fcc1d52 100644 --- a/network/hnswrapper/hnsv2wrapper.go +++ b/network/hnswrapper/hnsv2wrapper.go @@ -79,3 +79,7 @@ func (Hnsv2wrapper) ApplyEndpointPolicy(endpoint *hcn.HostComputeEndpoint, reque func (Hnsv2wrapper) GetEndpointByName(endpointName string) (*hcn.HostComputeEndpoint, error) { return hcn.GetEndpointByName(endpointName) // nolint:wrapcheck // no need to wrap check for this wrapper } + +func (Hnsv2wrapper) HNSV2Supported() error { + return hcn.V2ApiSupported() // nolint:wrapcheck // no need to wrap check for this wrapper +} diff --git a/network/hnswrapper/hnsv2wrapperfake.go b/network/hnswrapper/hnsv2wrapperfake.go index 0793709c99..219c6ddd52 100644 --- a/network/hnswrapper/hnsv2wrapperfake.go +++ b/network/hnswrapper/hnsv2wrapperfake.go @@ -44,6 +44,10 @@ func delayHnsCall(delay time.Duration) { time.Sleep(delay) } +func (f Hnsv2wrapperFake) HNSV2Supported() error { + return nil +} + // NewMockIOShim is dependent on this function never returning an error func (f Hnsv2wrapperFake) CreateNetwork(network *hcn.HostComputeNetwork) (*hcn.HostComputeNetwork, error) { f.Lock() @@ -347,6 +351,16 @@ type FakeHNSCache struct { endpoints map[string]*FakeHostComputeEndpoint } +// get networks from fake HNS cache +func (fCache FakeHNSCache) GetNetworks() map[string]*FakeHostComputeNetwork { + return fCache.networks +} + +// get endpoints from fake HNS cache +func (fCache FakeHNSCache) GetEndpoints() map[string]*FakeHostComputeEndpoint { + return fCache.endpoints +} + // SetPolicy returns the first SetPolicy found with this ID in any network. func (fCache FakeHNSCache) SetPolicy(setID string) *hcn.SetPolicySetting { for _, network := range fCache.networks { diff --git a/network/hnswrapper/hnsv2wrapperinterface.go b/network/hnswrapper/hnsv2wrapperinterface.go index 36efd304be..11af5863ae 100644 --- a/network/hnswrapper/hnsv2wrapperinterface.go +++ b/network/hnswrapper/hnsv2wrapperinterface.go @@ -26,4 +26,5 @@ type HnsV2WrapperInterface interface { ListEndpointsQuery(query hcn.HostComputeQuery) ([]hcn.HostComputeEndpoint, error) ApplyEndpointPolicy(endpoint *hcn.HostComputeEndpoint, requestType hcn.RequestType, endpointPolicy hcn.PolicyEndpointRequest) error GetEndpointByName(endpointName string) (*hcn.HostComputeEndpoint, error) + HNSV2Supported() error } diff --git a/network/hnswrapper/hnsv2wrapperwithtimeout.go b/network/hnswrapper/hnsv2wrapperwithtimeout.go index 4bd5ca55f2..07f9c03a90 100644 --- a/network/hnswrapper/hnsv2wrapperwithtimeout.go +++ b/network/hnswrapper/hnsv2wrapperwithtimeout.go @@ -404,3 +404,7 @@ func (h Hnsv2wrapperwithtimeout) GetEndpointByName(endpointName string) (*hcn.Ho return nil, errors.Wrapf(ErrHNSCallTimeout, "GetEndpointByName %w , timeout value is %s seconds", h.HnsCallTimeout.String()) } } + +func (h Hnsv2wrapperwithtimeout) HNSV2Supported() error { + return nil +} diff --git a/network/manager.go b/network/manager.go index f5c35436d2..607ec6cc1e 100644 --- a/network/manager.go +++ b/network/manager.go @@ -530,22 +530,18 @@ func (nm *networkManager) DeleteEndpointState(networkID string, epInfo *Endpoint IfName: epInfo.IfName, // TODO: For stateless cni linux populate IfName here to use in deletion in secondary endpoint client } logger.Info("Deleting endpoint with", zap.String("Endpoint Info: ", epInfo.PrettyString()), zap.String("HNISID : ", ep.HnsId)) - // do not need to Delete HNS endpoint if the there is no HNS in state - if ep.HnsId != "" { - err := nw.deleteEndpointImpl(netlink.NewNetlink(), platform.NewExecClient(logger), nil, nil, nil, nil, ep) - if err != nil { - return err - } - } - if epInfo.NICType == cns.DelegatedVMNIC { - // we are currently assuming stateless is not running in linux - // CHECK: could this affect linux? (if it does, it could disconnect external interface, is that okay?) - // bad only when 1) stateless and 2) linux and 3) delegated vmnics exist - logger.Info("Deleting endpoint because delegated vmnic detected", zap.String("HNSNetworkID", nw.HnsId)) - err := nm.deleteNetworkImpl(nw) - // no need to clean up state in stateless + + err := nw.deleteEndpointImpl(netlink.NewNetlink(), platform.NewExecClient(logger), nil, nil, nil, nil, ep) + if err != nil { return err } + + err = nm.deleteNetworkImpl(nw, ep.NICType) + // no need to clean up state in stateless + if err != nil { + return errors.Wrap(err, "Failed to delete HNS Network") + } + return nil } diff --git a/network/manager_test.go b/network/manager_test.go index 64db76b345..e18b1c1ba0 100644 --- a/network/manager_test.go +++ b/network/manager_test.go @@ -304,7 +304,7 @@ var _ = Describe("Test Manager", func() { "12345678-1": { Id: "12345678-1", ContainerID: "12345678", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, }, }, @@ -319,7 +319,7 @@ var _ = Describe("Test Manager", func() { Expect(len(epInfos)).To(Equal(2)) Expect(epInfos[0].EndpointID).To(Equal("12345678-1")) - Expect(epInfos[0].NICType).To(Equal(cns.DelegatedVMNIC)) + Expect(epInfos[0].NICType).To(Equal(cns.NodeNetworkInterfaceFrontendNIC)) Expect(epInfos[0].NetworkID).To(Equal("other")) Expect(epInfos[1].EndpointID).To(Equal("12345678-eth0")) @@ -393,7 +393,7 @@ var _ = Describe("Test Manager", func() { HnsEndpointID: "hnsID2", HnsNetworkID: "hnsNetworkID2", MacAddress: "22:34:56:78:9a:bc", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, }, }, PodName: "test-pod", @@ -425,7 +425,7 @@ var _ = Describe("Test Manager", func() { IfName: "ifName2", HostIfName: "hostVeth2", HNSEndpointID: "hnsID2", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, HNSNetworkID: "hnsNetworkID2", MacAddress: net.HardwareAddr("22:34:56:78:9a:bc"), ContainerID: endpointID, @@ -439,8 +439,8 @@ var _ = Describe("Test Manager", func() { }) }) Describe("Test stateless generateCNSIPInfoMap", func() { - Context("When converting from cni to cns", func() { - It("Should generate the cns endpoint info data from the endpoint structs", func() { + Context("When converting from cni to cns with different combinations", func() { + It("Should generate the cns endpoint info data from the endpoint structs for infraNIC+DelegatedVMNIC", func() { mac1, _ := net.ParseMAC("12:34:56:78:9a:bc") mac2, _ := net.ParseMAC("22:34:56:78:9a:bc") endpoints := []*endpoint{ @@ -454,7 +454,7 @@ var _ = Describe("Test Manager", func() { }, { IfName: "eth1", - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, HnsId: "hnsEndpointID2", HNSNetworkID: "hnsNetworkID2", HostIfName: "hostIfName2", @@ -478,7 +478,52 @@ var _ = Describe("Test Manager", func() { Expect(cnsEpInfos).To(HaveKey("eth1")) Expect(cnsEpInfos["eth1"]).To(Equal( &restserver.IPInfo{ - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, + HnsEndpointID: "hnsEndpointID2", + HnsNetworkID: "hnsNetworkID2", + HostVethName: "hostIfName2", + MacAddress: "22:34:56:78:9a:bc", + }, + )) + }) + It("Should generate the cns endpoint info data from the endpoint structs for infraNIC+AccelnetNIC", func() { + mac1, _ := net.ParseMAC("12:34:56:78:9a:bc") + mac2, _ := net.ParseMAC("22:34:56:78:9a:bc") + endpoints := []*endpoint{ + { + IfName: "eth0", + NICType: cns.InfraNIC, + HnsId: "hnsEndpointID1", + HNSNetworkID: "hnsNetworkID1", + HostIfName: "hostIfName1", + MacAddress: mac1, + }, + { + IfName: "eth1", + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + HnsId: "hnsEndpointID2", + HNSNetworkID: "hnsNetworkID2", + HostIfName: "hostIfName2", + MacAddress: mac2, + }, + } + cnsEpInfos := generateCNSIPInfoMap(endpoints) + Expect(len(cnsEpInfos)).To(Equal(2)) + + Expect(cnsEpInfos["eth0"]).To(Equal( + &restserver.IPInfo{ + NICType: cns.InfraNIC, + HnsEndpointID: "hnsEndpointID1", + HnsNetworkID: "hnsNetworkID1", + HostVethName: "hostIfName1", + MacAddress: "12:34:56:78:9a:bc", + }, + )) + + Expect(cnsEpInfos).To(HaveKey("eth1")) + Expect(cnsEpInfos["eth1"]).To(Equal( + &restserver.IPInfo{ + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, HnsEndpointID: "hnsEndpointID2", HnsNetworkID: "hnsNetworkID2", HostVethName: "hostIfName2", diff --git a/network/network.go b/network/network.go index edcaa1daf7..31537b6522 100644 --- a/network/network.go +++ b/network/network.go @@ -242,7 +242,7 @@ func (nm *networkManager) deleteNetwork(networkID string) error { } // Call the OS-specific implementation. - err = nm.deleteNetworkImpl(nw) + err = nm.deleteNetworkImpl(nw, cns.InfraNIC) if err != nil { return err } diff --git a/network/network_linux.go b/network/network_linux.go index 21eaaca7e3..ad3d673ade 100644 --- a/network/network_linux.go +++ b/network/network_linux.go @@ -9,6 +9,7 @@ import ( "strconv" "strings" + "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/iptables" "github.com/Azure/azure-container-networking/netio" "github.com/Azure/azure-container-networking/netlink" @@ -147,7 +148,7 @@ func (nm *networkManager) handleCommonOptions(ifName string, nwInfo *EndpointInf } // DeleteNetworkImpl deletes an existing container network. -func (nm *networkManager) deleteNetworkImpl(nw *network) error { +func (nm *networkManager) deleteNetworkImpl(nw *network, _ cns.NICType) error { var networkClient NetworkClient if nw.VlanId != 0 { diff --git a/network/network_windows.go b/network/network_windows.go index f217a6c0c9..5174f3294f 100644 --- a/network/network_windows.go +++ b/network/network_windows.go @@ -58,7 +58,7 @@ func UseHnsV2(netNs string) (bool, error) { var err error if _, err = uuid.Parse(netNs); err == nil { useHnsV2 = true - if err = hcn.V2ApiSupported(); err != nil { + if err = Hnsv2.HNSV2Supported(); err != nil { logger.Info("HNSV2 is not supported on this windows platform") } } @@ -297,11 +297,20 @@ func (nm *networkManager) configureHcnNetwork(nwInfo *EndpointInfo, extIf *exter return nil, errNetworkModeInvalid } - if nwInfo.NICType == cns.DelegatedVMNIC { + // DelegatedNIC flag: hcn.DisableHostPort(1024) + if nwInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC { hcnNetwork.Type = hcn.Transparent hcnNetwork.Flags = hcn.DisableHostPort } + // AccelnetNIC flag: hcn.EnableIov(9216) + // For L1VH with accelnet, hcn.DisableHostPort and hcn.EnableIov must be configured + // To set this, need do OR operation: hcnNetwork.flags = hcn.DisableHostPort | hcn.EnableIov: (1024 + 8192 = 9216) + if nwInfo.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { + hcnNetwork.Type = hcn.Transparent + hcnNetwork.Flags = hcn.DisableHostPort | hcn.EnableIov + } + // Populate subnets. for _, subnet := range nwInfo.Subnets { hnsSubnet := hcn.Subnet{ @@ -441,7 +450,13 @@ func (nm *networkManager) newNetworkImpl(nwInfo *EndpointInfo, extIf *externalIn } // DeleteNetworkImpl deletes an existing container network. -func (nm *networkManager) deleteNetworkImpl(nw *network) error { +func (nm *networkManager) deleteNetworkImpl(nw *network, nicType cns.NICType) error { + if nicType != cns.NodeNetworkInterfaceFrontendNIC && nicType != cns.NodeNetworkInterfaceAccelnetFrontendNIC { //nolint + return nil + } + + logger.Info("Deleting HNS network", zap.String("networkID", nw.HnsId), zap.Any("nictype", nicType)) + if useHnsV2, err := UseHnsV2(nw.NetNs); useHnsV2 { if err != nil { return err diff --git a/network/network_windows_test.go b/network/network_windows_test.go index 56310353da..9d647e6cb2 100644 --- a/network/network_windows_test.go +++ b/network/network_windows_test.go @@ -437,3 +437,158 @@ func TestNewNetworkImplHnsV2ForBackendNIC(t *testing.T) { t.Fatal("HNS network is created with BackendNIC interface") } } + +// mock hns network creation and deletion for DelegatedNIC +func TestNewAndDeleteNetworkImplHnsV2ForDelegated(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + Hnsv2 = hnswrapper.NewHnsv2wrapperFake() + + nwInfo := &EndpointInfo{ + NetworkID: "d3e97a83-ba4c-45d5-ba88-dc56757ece28", + MasterIfName: "eth0", + Mode: "bridge", + NICType: cns.NodeNetworkInterfaceFrontendNIC, + MacAddress: net.HardwareAddr("12:34:56:78:9a:bc"), + } + + extInterface := &externalInterface{ + Name: "eth0", + Subnets: []string{"subnet1", "subnet2"}, + } + + network, err := nm.newNetworkImplHnsV2(nwInfo, extInterface) + if err != nil { + fmt.Printf("+%v", err) + t.Fatal(err) + } + + err = nm.deleteNetworkImpl(network, cns.NodeNetworkInterfaceFrontendNIC) + + if err != nil { + fmt.Printf("+%v", err) + t.Fatal(err) + } +} + +// mock hns network creation and deletion for AccelnetNIC +func TestNewAndDeleteNetworkImplHnsV2ForAccelnet(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + Hnsv2 = hnswrapper.NewHnsv2wrapperFake() + + nwInfo := &EndpointInfo{ + NetworkID: "d3e97a83-ba4c-45d5-ba88-dc56757ece28", + MasterIfName: "eth0", + Mode: "bridge", + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + MacAddress: net.HardwareAddr("12:34:56:78:9a:bc"), + } + + extInterface := &externalInterface{ + Name: "eth0", + Subnets: []string{"subnet1", "subnet2"}, + } + + network, err := nm.newNetworkImplHnsV2(nwInfo, extInterface) + if err != nil { + fmt.Printf("+%v", err) + t.Fatal(err) + } + + err = nm.deleteNetworkImpl(network, cns.NodeNetworkInterfaceAccelnetFrontendNIC) + + if err != nil { + fmt.Printf("+%v", err) + t.Fatal(err) + } +} + +func TestSkipNetworkDeletion(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // should return nil if nicType is Backend + err := nm.deleteNetworkImpl(nil, cns.BackendNIC) + if err != nil { + fmt.Printf("+%v", err) + t.Fatal(err) + } +} + +func TestTransparentNetworkCreationForAccelnet(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + Hnsv2 = hnswrapper.NewHnsv2wrapperFake() + + nwInfo := &EndpointInfo{ + NetworkID: "d3e97a83-ba4c-45d5-ba88-dc56757ece28", + MasterIfName: "eth1", + Mode: "bridge", + NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, + } + + extInterface := &externalInterface{ + Name: "eth0", + Subnets: []string{"subnet1", "subnet2"}, + } + + _, err := nm.newNetworkImplHnsV2(nwInfo, extInterface) + if err != nil { + fmt.Printf("+%v", err) + t.Fatal(err) + } + + // create a network again with same name and it should return error for transparent network + _, err = nm.newNetworkImplHnsV2(nwInfo, extInterface) + if err == nil { + t.Fatal("network creation does not return error") + } +} + +func TestTransparentNetworkCreationForDelegated(t *testing.T) { + nm := &networkManager{ + ExternalInterfaces: map[string]*externalInterface{}, + } + + // this hnsv2 variable overwrites the package level variable in network + // we do this to avoid passing around os specific objects in platform agnostic code + Hnsv2 = hnswrapper.NewHnsv2wrapperFake() + + nwInfo := &EndpointInfo{ + NetworkID: "d3e97a83-ba4c-45d5-ba88-dc56757ece28", + MasterIfName: "eth0", + Mode: "bridge", + NICType: cns.NodeNetworkInterfaceFrontendNIC, + } + + extInterface := &externalInterface{ + Name: "eth1", + Subnets: []string{"subnet1", "subnet2"}, + } + + _, err := nm.newNetworkImplHnsV2(nwInfo, extInterface) + if err != nil { + fmt.Printf("+%v", err) + t.Fatal(err) + } + + // create a network again with same name and it should return error for transparent network + _, err = nm.newNetworkImplHnsV2(nwInfo, extInterface) + if err == nil { + t.Fatal("network creation does not return error") + } +} diff --git a/network/policy/policy_windows.go b/network/policy/policy_windows.go index f76b091a8f..608f41607c 100644 --- a/network/policy/policy_windows.go +++ b/network/policy/policy_windows.go @@ -24,6 +24,14 @@ const ( // CnetAddressSpace indicates constant for the key string CnetAddressSpace = "cnetAddressSpace" + + // Accelnet NIC IOV policy setting flags + // The interrupt moderation value for I/O virtualization(IOV) offloading + interruptModeration = 0 + // The weight assigned to this port for I/0 virtualization(IOV) offloading; + iovOffloadWeight = 100 + // The number of queue pairs requested for this port for I/0 virtualization(IOV) offloading + queuePairsRequested = 1 ) type KVPairRoutePolicy struct { @@ -550,3 +558,25 @@ func AddNATPolicyV2(vip string, destinations []string) (hcn.EndpointPolicy, erro } return endpointPolicy, err } + +// AddAccelnetPolicySetting returns serialized endpoint IOV policy +// IOV(I/O virtualization) is the accelnet from networking point of view == VF = Accelnet +// To enable IOV, must set iovOffloadWeight to 100 +func AddAccelnetPolicySetting() (hcn.EndpointPolicy, error) { + accelnetPolicy := hcn.IovPolicySetting{ + InterruptModeration: interruptModeration, + IovOffloadWeight: iovOffloadWeight, + QueuePairsRequested: queuePairsRequested, + } + + rawPolicy, err := json.Marshal(accelnetPolicy) + if err != nil { + return hcn.EndpointPolicy{}, errors.Wrap(err, "Failed to marshal accelnet policy") + } + endpointPolicy := hcn.EndpointPolicy{ + Type: hcn.IOV, + Settings: rawPolicy, + } + + return endpointPolicy, nil +} diff --git a/network/policy/policy_windows_test.go b/network/policy/policy_windows_test.go index 95bbf06a94..0fa59845f4 100644 --- a/network/policy/policy_windows_test.go +++ b/network/policy/policy_windows_test.go @@ -47,4 +47,14 @@ var _ = Describe("Windows Policies", func() { Expect(string(generatedPolicy.Settings)).To(Equal(expected_policy)) }) }) + + Describe("Test AddAccelnetPolicySetting", func() { + It("Should marshall the policy correctly", func() { + expectedPolicy := `{"IovOffloadWeight":100,"QueuePairsRequested":1}` + + generatedPolicy, err := AddAccelnetPolicySetting() + Expect(err).To(BeNil()) + Expect(string(generatedPolicy.Settings)).To(Equal(expectedPolicy)) + }) + }) }) diff --git a/network/secondary_endpoint_linux_test.go b/network/secondary_endpoint_linux_test.go index 6073101b32..2d8ca05569 100644 --- a/network/secondary_endpoint_linux_test.go +++ b/network/secondary_endpoint_linux_test.go @@ -206,7 +206,7 @@ func TestSecondaryDeleteEndpoints(t *testing.T) { Dst: net.IPNet{IP: net.ParseIP("192.168.0.4"), Mask: net.CIDRMask(ipv4FullMask, ipv4Bits)}, }, }, - NICType: cns.DelegatedVMNIC, + NICType: cns.NodeNetworkInterfaceFrontendNIC, SecondaryInterfaces: map[string]*InterfaceInfo{ "eth1": { Name: "eth1",