diff --git a/cni/network/invoker_cns_test.go b/cni/network/invoker_cns_test.go index 82cdb0a9e0..bf655b915a 100644 --- a/cni/network/invoker_cns_test.go +++ b/cni/network/invoker_cns_test.go @@ -2151,3 +2151,343 @@ func TestShallowCopyIpamAddConfigOptions(t *testing.T) { nullRes := nullOpts.shallowCopyIpamAddConfigOptions() require.Equal(t, map[string]interface{}{}, nullRes) } + +// Test addBackendNICToResult() and configureSecondaryAddResult() to update secondary interfaces to cni Result +func TestAddNICsToCNIResult(t *testing.T) { + require := require.New(t) //nolint further usage of require without passing t + + macAddress := "bc:9a:78:56:34:12" + newMacAddress := "bc:9a:78:56:34:45" + newParsedMacAddress, _ := net.ParseMAC(newMacAddress) + newNCGatewayIPAddress := "40.0.0.1" + + pnpID := "PCI\\VEN_15B3&DEV_101C&SUBSYS_000715B3&REV_00\\5&8c5acce&0&0" + newPnpID := "PCI\\VEN_15B3&DEV_101C&SUBSYS_000715B3&REV_00\\5&8c5acce&0&1" + + newPodIPConfig := &cns.IPSubnet{ + IPAddress: "30.1.1.10", + PrefixLength: 24, + } + + newIP, newIPNet, _ := newPodIPConfig.GetIPNet() + + type fields struct { + podName string + podNamespace string + cnsClient cnsclient + } + + type args struct { + nwCfg *cni.NetworkConfig + args *cniSkel.CmdArgs + hostSubnetPrefix *net.IPNet + options map[string]interface{} + info IPResultInfo + podIPConfig *cns.IPSubnet + } + + tests := []struct { + name string + fields fields + args args + wantSecondaryInterfacesInfo map[string]network.InterfaceInfo + }{ + { + name: "add new backendNIC to cni Result", + 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, + }, + { + MacAddress: macAddress, + 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{}{}, + // update new pnpID, macAddress + info: IPResultInfo{ + pnpID: newPnpID, + macAddress: newMacAddress, + nicType: cns.BackendNIC, + }, + }, + wantSecondaryInterfacesInfo: map[string]network.InterfaceInfo{ + macAddress: { + MacAddress: newParsedMacAddress, + PnPID: newPnpID, + NICType: cns.BackendNIC, + }, + }, + }, + { + name: "add new delegatedVMNIC to cni Result", + 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.NodeNetworkInterfaceFrontendNIC, + MacAddress: macAddress, + }, + }, + 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{}{}, + // update podIPConfig + podIPConfig: newPodIPConfig, + // update new mac address and ncGatewayIPAddress + info: IPResultInfo{ + macAddress: newMacAddress, + nicType: cns.NodeNetworkInterfaceFrontendNIC, + ncGatewayIPAddress: newNCGatewayIPAddress, + }, + }, + wantSecondaryInterfacesInfo: map[string]network.InterfaceInfo{ + macAddress: { + MacAddress: newParsedMacAddress, + NICType: cns.NodeNetworkInterfaceFrontendNIC, + IPConfigs: []*network.IPConfig{ + { + Address: net.IPNet{ + IP: newIP, + Mask: newIPNet.Mask, + }, + Gateway: net.ParseIP(newNCGatewayIPAddress), + }, + }, + Routes: []network.RouteInfo{}, + }, + }, + }, + { + name: "add new accelnetNIC to cni Result", + 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: macAddress, + }, + }, + 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{}{}, + // update podIPConfig + podIPConfig: newPodIPConfig, + // update new mac address and ncGatewayIPAddress + info: IPResultInfo{ + macAddress: newMacAddress, + nicType: cns.NodeNetworkInterfaceFrontendNIC, + ncGatewayIPAddress: newNCGatewayIPAddress, + }, + }, + wantSecondaryInterfacesInfo: map[string]network.InterfaceInfo{ + macAddress: { + MacAddress: newParsedMacAddress, + NICType: cns.NodeNetworkInterfaceFrontendNIC, + IPConfigs: []*network.IPConfig{ + { + Address: net.IPNet{ + IP: newIP, + Mask: newIPNet.Mask, + }, + Gateway: net.ParseIP(newNCGatewayIPAddress), + }, + }, + Routes: []network.RouteInfo{}, + }, + }, + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + invoker := &CNSIPAMInvoker{ + podName: tt.fields.podName, + podNamespace: tt.fields.podNamespace, + cnsClient: tt.fields.cnsClient, + } + ipamAddResult, err := invoker.Add(IPAMAddConfig{nwCfg: tt.args.nwCfg, args: tt.args.args, options: tt.args.options}) + if err != nil { + t.Fatalf("Failed to create ipamAddResult due to error: %v", err) + } + + for _, ifInfo := range ipamAddResult.interfaceInfo { + if ifInfo.NICType == cns.BackendNIC { + // add new backendNIC info to cni Result + err := addBackendNICToResult(&tt.args.info, &ipamAddResult, macAddress) + if err != nil { + t.Fatalf("Failed to add backend NIC to cni Result due to error %v", err) + } + fmt.Printf("want:%+v\nrest:%+v\n", tt.wantSecondaryInterfacesInfo, ipamAddResult.interfaceInfo[macAddress]) + require.EqualValues(tt.wantSecondaryInterfacesInfo[macAddress], ipamAddResult.interfaceInfo[macAddress], "incorrect response for IB") + } + if ifInfo.NICType == cns.NodeNetworkInterfaceFrontendNIC || ifInfo.NICType == cns.NodeNetworkInterfaceAccelnetFrontendNIC { + // add new secondaryInterfaceNIC to cni Result + err := configureSecondaryAddResult(&tt.args.info, &ipamAddResult, tt.args.podIPConfig, macAddress) + if err != nil { + t.Fatalf("Failed to add secondary interface NIC %s to cni Result due to error %v", ifInfo.NICType, err) + } + fmt.Printf("want:%+v\nrest:%+v\n", tt.wantSecondaryInterfacesInfo, ipamAddResult.interfaceInfo[macAddress]) + require.EqualValues(tt.wantSecondaryInterfacesInfo[macAddress], ipamAddResult.interfaceInfo[macAddress], "incorrect response for delegatedVMNIC/accelnetNIC") + } + } + }) + } +} diff --git a/cni/network/network.go b/cni/network/network.go index 0404604e60..2f8aabe816 100644 --- a/cni/network/network.go +++ b/cni/network/network.go @@ -686,9 +686,11 @@ func (plugin *NetPlugin) findMasterInterface(opt *createEpInfoOpt) string { return plugin.findMasterInterfaceBySubnet(opt.ipamAddConfig.nwCfg, &opt.ifInfo.HostSubnetPrefix) 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) - return opt.ifInfo.Name + case cns.BackendNIC: + // if windows swiftv2 has right network drivers, there will be an NDIS interface while the VFs are mounted + // when the VF is dismounted, this interface will go away + // return an unique interface name to containerd + return ibInterfacePrefix + strconv.Itoa(opt.endpointIndex) default: return "" } diff --git a/cni/network/network_windows_test.go b/cni/network/network_windows_test.go index 25a45fb17e..e5d0ce307b 100644 --- a/cni/network/network_windows_test.go +++ b/cni/network/network_windows_test.go @@ -591,7 +591,7 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { netNs string nwCfg *cni.NetworkConfig interfaceInfo *network.InterfaceInfo - want net.HardwareAddr + want string wantErr bool }{ { @@ -613,7 +613,7 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { MacAddress: parsedMacAddress, NICType: cns.NodeNetworkInterfaceFrontendNIC, }, - want: parsedMacAddress, + want: swiftv2NetworkNamePrefix + parsedMacAddress.String(), wantErr: false, }, { @@ -635,7 +635,7 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { MacAddress: parsedMacAddress, NICType: cns.BackendNIC, }, - want: parsedMacAddress, + want: swiftv2NetworkNamePrefix + parsedMacAddress.String(), wantErr: false, }, { @@ -657,7 +657,47 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { MacAddress: parsedMacAddress, NICType: cns.NodeNetworkInterfaceAccelnetFrontendNIC, }, - want: parsedMacAddress, + want: swiftv2NetworkNamePrefix + parsedMacAddress.String(), + wantErr: false, + }, + { + name: "Unhappy path: Get Network Name from CNS for swiftv2 AccelnetNIC with empty interfaceInfo", + 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{}, // return empty network name with empty interfaceInfo + want: "", + wantErr: false, + }, + { + name: "Unhappy path: Get Network Name from CNS for swiftv2 AccelnetNIC with invalid nicType", + 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: "invalidNICType", + }, // return empty network name with invalid nic type + want: "", wantErr: false, }, } @@ -666,22 +706,20 @@ func TestGetNetworkNameSwiftv2FromCNS(t *testing.T) { tt := tt t.Run(tt.name, func(t *testing.T) { t.Log(tt.interfaceInfo) + // compare networkNamess networkName, err := tt.plugin.getNetworkName(tt.netNs, tt.interfaceInfo, tt.nwCfg) if tt.wantErr { require.Error(t, err) } else { - expectedMacAddress := swiftv2NetworkNamePrefix + tt.want.String() - require.NoError(t, err) - require.Equal(t, expectedMacAddress, networkName) + require.Equal(t, tt.want, networkName) } + // compare networkIDs networkID, err := tt.plugin.getNetworkID(tt.netNs, tt.interfaceInfo, tt.nwCfg) if tt.wantErr { require.Error(t, err) } else { - expectedMacAddress := swiftv2NetworkNamePrefix + tt.want.String() - require.NoError(t, err) - require.Equal(t, expectedMacAddress, networkID) + require.Equal(t, tt.want, networkID) } }) } diff --git a/network/endpoint_windows.go b/network/endpoint_windows.go index c912552846..3e927b0b61 100644 --- a/network/endpoint_windows.go +++ b/network/endpoint_windows.go @@ -709,7 +709,7 @@ func getLocationPath(instanceID string, plc platform.ExecClient) (string, error) return locationPath, nil } -// Get PnP device state +// Get PnP device state; PnP device objects represent the mounted/dismounted IB VFs // return devpkeyDeviceIsPresent and devpkeyDeviceProblemCode func getPnpDeviceState(instanceID string, plc platform.ExecClient) (string, string, error) { //nolint // get if device is present