//go:build linux // +build linux package netlink import ( "flag" "fmt" "math/rand" "net" "os" "strconv" "strings" "syscall" "testing" "github.com/stretchr/testify/assert" "github.com/vishvananda/netlink/nl" ) func TestDevLinkGetDeviceList(t *testing.T) { minKernelRequired(t, 4, 12) t.Cleanup(setUpNetlinkTestWithKModule(t, "devlink")) _, err := DevLinkGetDeviceList() assert.NoError(t, err) } func TestDevLinkGetDeviceByName(t *testing.T) { minKernelRequired(t, 4, 12) t.Cleanup(setUpNetlinkTestWithKModule(t, "devlink")) _, err := DevLinkGetDeviceByName("foo", "bar") assert.NoError(t, err) } func TestDevLinkSetEswitchMode(t *testing.T) { minKernelRequired(t, 4, 12) t.Cleanup(setUpNetlinkTestWithKModule(t, "devlink")) dev, err := DevLinkGetDeviceByName("foo", "bar") assert.NoError(t, err) err = DevLinkSetEswitchMode(dev, "switchdev") assert.NoError(t, err) err = DevLinkSetEswitchMode(dev, "legacy") assert.NoError(t, err) } func logPort(t *testing.T, port *DevlinkPort) { type field struct { key string value string } fields := []field{} fields = append(fields, field{key: "bus", value: port.BusName}) fields = append(fields, field{key: "device", value: port.DeviceName}) fields = append(fields, field{key: "port_index", value: strconv.Itoa(int(port.PortIndex))}) fields = append(fields, field{key: "port_type", value: strconv.Itoa(int(port.PortType))}) fields = append(fields, field{key: "port_flavour", value: strconv.Itoa(int(port.PortFlavour))}) fields = append(fields, field{key: "netdev_name", value: port.NetdeviceName}) fields = append(fields, field{key: "netdev_index", value: strconv.Itoa(int(port.NetdevIfIndex))}) fields = append(fields, field{key: "rdma_dev_name", value: port.RdmaDeviceName}) if port.Fn != nil { fields = append(fields, field{key: "hw_addr", value: port.Fn.HwAddr.String()}) fields = append(fields, field{key: "state", value: strconv.Itoa(int(port.Fn.State))}) fields = append(fields, field{key: "op_state", value: strconv.Itoa(int(port.Fn.OpState))}) } if port.PortNumber != nil { fields = append(fields, field{key: "port_number", value: strconv.Itoa(int(*port.PortNumber))}) } if port.PfNumber != nil { fields = append(fields, field{key: "pf_number", value: strconv.Itoa(int(*port.PfNumber))}) } if port.VfNumber != nil { fields = append(fields, field{key: "vf_number", value: strconv.Itoa(int(*port.VfNumber))}) } if port.SfNumber != nil { fields = append(fields, field{key: "sf_number", value: strconv.Itoa(int(*port.SfNumber))}) } if port.ControllerNumber != nil { fields = append(fields, field{key: "controller_number", value: strconv.Itoa(int(*port.ControllerNumber))}) } if port.External != nil { fields = append(fields, field{key: "external", value: strconv.FormatBool(*port.External)}) } fieldsStr := []string{} for _, field := range fields { fieldsStr = append(fieldsStr, fmt.Sprintf("%s=%s", field.key, field.value)) } t.Log(strings.Join(fieldsStr, " ")) } func TestDevLinkGetAllPortList(t *testing.T) { minKernelRequired(t, 5, 4) ports, err := DevLinkGetAllPortList() assert.NoError(t, err) t.Log("devlink port count = ", len(ports)) for _, port := range ports { logPort(t, port) } } func TestDevLinkAddDelSfPort(t *testing.T) { var addAttrs DevLinkPortAddAttrs minKernelRequired(t, 5, 13) if bus == "" || device == "" { t.Log("devlink bus and device are empty, skipping test") return } dev, err := DevLinkGetDeviceByName(bus, device) assert.NoError(t, err) addAttrs.SfNumberValid = true addAttrs.SfNumber = uint32(sfnum) addAttrs.PfNumber = 0 port, err := DevLinkPortAdd(dev.BusName, dev.DeviceName, 7, addAttrs) assert.NoError(t, err) t.Log(*port) if port.Fn != nil { t.Log("function attributes = ", *port.Fn) } err = DevLinkPortDel(dev.BusName, dev.DeviceName, port.PortIndex) assert.NoError(t, err) } func TestDevLinkSfPortFnSet(t *testing.T) { var addAttrs DevLinkPortAddAttrs var stateAttr DevlinkPortFnSetAttrs minKernelRequired(t, 5, 12) if bus == "" || device == "" { t.Log("devlink bus and device are empty, skipping test") return } dev, err := DevLinkGetDeviceByName(bus, device) assert.NoError(t, err) addAttrs.SfNumberValid = true addAttrs.SfNumber = uint32(sfnum) addAttrs.PfNumber = 0 port, err := DevLinkPortAdd(dev.BusName, dev.DeviceName, 7, addAttrs) assert.NoError(t, err) // cleanup defer func() { err := DevLinkPortDel(dev.BusName, dev.DeviceName, port.PortIndex) assert.NoError(t, err) }() t.Log(*port) if port.Fn != nil { t.Log("function attributes = ", *port.Fn) } macAttr := DevlinkPortFnSetAttrs{ FnAttrs: DevlinkPortFn{ HwAddr: net.HardwareAddr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}, }, HwAddrValid: true, } err = DevlinkPortFnSet(dev.BusName, dev.DeviceName, port.PortIndex, macAttr) assert.NoError(t, err, "failed to call DevlinkPortFnSet to set mac attribute") stateAttr.FnAttrs.State = 1 stateAttr.StateValid = true err = DevlinkPortFnSet(dev.BusName, dev.DeviceName, port.PortIndex, stateAttr) assert.NoError(t, err, "failed to call DevlinkPortFnSet to set state attribute") gotPort, err := DevLinkGetPortByIndex(dev.BusName, dev.DeviceName, port.PortIndex) assert.NoError(t, err, "failed to call DevLinkGetPortByIndex to get port") t.Log(*gotPort) if gotPort.Fn != nil { t.Log(*gotPort.Fn) } } var bus string var device string var sfnum uint func init() { flag.StringVar(&bus, "bus", "", "devlink device bus name") flag.StringVar(&device, "device", "", "devlink device devicename") flag.UintVar(&sfnum, "sfnum", 0, "devlink port sfnumber") } func TestDevlinkGetDeviceInfoByNameAsMap(t *testing.T) { info, err := pkgHandle.DevlinkGetDeviceInfoByNameAsMap("pci", "0000:00:00.0", mockDevlinkInfoGetter) assert.NoError(t, err) testInfo := devlinkTestInfoParesd() for k, v := range info { assert.Equal(t, testInfo[k], v, "value %s retrieved for key %s is not equal to %s", v, k, testInfo[k]) } } func TestDevlinkGetDeviceInfoByName(t *testing.T) { info, err := pkgHandle.DevlinkGetDeviceInfoByName("pci", "0000:00:00.0", mockDevlinkInfoGetter) assert.NoError(t, err) testInfo := parseInfoData(devlinkTestInfoParesd()) assert.True(t, areInfoStructsEqual(info, testInfo), "info structures are not equal") } func TestDevlinkGetDeviceInfoByNameAsMapFail(t *testing.T) { _, err := pkgHandle.DevlinkGetDeviceInfoByNameAsMap("pci", "0000:00:00.0", mockDevlinkInfoGetterEmpty) assert.Error(t, err) } func TestDevlinkGetDeviceInfoByNameFail(t *testing.T) { _, err := pkgHandle.DevlinkGetDeviceInfoByName("pci", "0000:00:00.0", mockDevlinkInfoGetterEmpty) assert.Error(t, err) } func mockDevlinkInfoGetter(bus, device string) ([]byte, error) { return devlinkInfo(), nil } func mockDevlinkInfoGetterEmpty(bus, device string) ([]byte, error) { return []byte{}, nil } func devlinkInfo() []byte { return []byte{51, 1, 0, 0, 8, 0, 1, 0, 112, 99, 105, 0, 17, 0, 2, 0, 48, 48, 48, 48, 58, 56, 52, 58, 48, 48, 46, 48, 0, 0, 0, 0, 8, 0, 98, 0, 105, 99, 101, 0, 28, 0, 99, 0, 51, 48, 45, 56, 57, 45, 97, 51, 45, 102, 102, 45, 102, 102, 45, 99, 97, 45, 48, 53, 45, 54, 56, 0, 36, 0, 100, 0, 13, 0, 103, 0, 98, 111, 97, 114, 100, 46, 105, 100, 0, 0, 0, 0, 15, 0, 104, 0, 75, 56, 53, 53, 56, 53, 45, 48, 48, 48, 0, 0, 28, 0, 101, 0, 12, 0, 103, 0, 102, 119, 46, 109, 103, 109, 116, 0, 10, 0, 104, 0, 53, 46, 52, 46, 53, 0, 0, 0, 28, 0, 101, 0, 16, 0, 103, 0, 102, 119, 46, 109, 103, 109, 116, 46, 97, 112, 105, 0, 8, 0, 104, 0, 49, 46, 55, 0, 40, 0, 101, 0, 18, 0, 103, 0, 102, 119, 46, 109, 103, 109, 116, 46, 98, 117, 105, 108, 100, 0, 0, 0, 15, 0, 104, 0, 48, 120, 51, 57, 49, 102, 55, 54, 52, 48, 0, 0, 32, 0, 101, 0, 12, 0, 103, 0, 102, 119, 46, 117, 110, 100, 105, 0, 13, 0, 104, 0, 49, 46, 50, 56, 57, 56, 46, 48, 0, 0, 0, 0, 32, 0, 101, 0, 16, 0, 103, 0, 102, 119, 46, 112, 115, 105, 100, 46, 97, 112, 105, 0, 9, 0, 104, 0, 50, 46, 52, 50, 0, 0, 0, 0, 40, 0, 101, 0, 17, 0, 103, 0, 102, 119, 46, 98, 117, 110, 100, 108, 101, 95, 105, 100, 0, 0, 0, 0, 15, 0, 104, 0, 48, 120, 56, 48, 48, 48, 55, 48, 54, 98, 0, 0, 48, 0, 101, 0, 16, 0, 103, 0, 102, 119, 46, 97, 112, 112, 46, 110, 97, 109, 101, 0, 27, 0, 104, 0, 73, 67, 69, 32, 79, 83, 32, 68, 101, 102, 97, 117, 108, 116, 32, 80, 97, 99, 107, 97, 103, 101, 0, 0, 32, 0, 101, 0, 11, 0, 103, 0, 102, 119, 46, 97, 112, 112, 0, 0, 13, 0, 104, 0, 49, 46, 51, 46, 50, 52, 46, 48, 0, 0, 0, 0, 44, 0, 101, 0, 21, 0, 103, 0, 102, 119, 46, 97, 112, 112, 46, 98, 117, 110, 100, 108, 101, 95, 105, 100, 0, 0, 0, 0, 15, 0, 104, 0, 48, 120, 99, 48, 48, 48, 48, 48, 48, 49, 0, 0, 44, 0, 101, 0, 15, 0, 103, 0, 102, 119, 46, 110, 101, 116, 108, 105, 115, 116, 0, 0, 21, 0, 104, 0, 50, 46, 52, 48, 46, 50, 48, 48, 48, 45, 51, 46, 49, 54, 46, 48, 0, 0, 0, 0, 44, 0, 101, 0, 21, 0, 103, 0, 102, 119, 46, 110, 101, 116, 108, 105, 115, 116, 46, 98, 117, 105, 108, 100, 0, 0, 0, 0, 15, 0, 104, 0, 48, 120, 54, 55, 54, 97, 52, 56, 57, 100, 0, 0} } func devlinkTestInfoParesd() map[string]string { return map[string]string{ "board.id": "K85585-000", "fw.app": "1.3.24.0", "fw.app.bundle_id": "0xc0000001", "fw.app.name": "ICE OS Default Package", "fw.bundle_id": "0x8000706b", "fw.mgmt": "5.4.5", "fw.mgmt.api": "1.7", "fw.mgmt.build": "0x391f7640", "fw.netlist": "2.40.2000-3.16.0", "fw.netlist.build": "0x676a489d", "fw.psid.api": "2.42", "fw.undi": "1.2898.0", "driver": "ice", "serialNumber": "30-89-a3-ff-ff-ca-05-68", } } func areInfoStructsEqual(first *DevlinkDeviceInfo, second *DevlinkDeviceInfo) bool { if first.FwApp != second.FwApp || first.FwAppBoundleID != second.FwAppBoundleID || first.FwAppName != second.FwAppName || first.FwBoundleID != second.FwBoundleID || first.FwMgmt != second.FwMgmt || first.FwMgmtAPI != second.FwMgmtAPI || first.FwMgmtBuild != second.FwMgmtBuild || first.FwNetlist != second.FwNetlist || first.FwNetlistBuild != second.FwNetlistBuild || first.FwPsidAPI != second.FwPsidAPI || first.BoardID != second.BoardID || first.FwUndi != second.FwUndi || first.Driver != second.Driver || first.SerialNumber != second.SerialNumber { return false } return true } func TestDevlinkGetDeviceResources(t *testing.T) { minKernelRequired(t, 5, 11) t.Cleanup(setUpNetlinkTestWithKModule(t, "devlink")) if bus == "" || device == "" { //TODO: setup netdevsim device instead of getting device from flags t.Log("devlink bus and device are empty, skipping test") t.SkipNow() } res, err := DevlinkGetDeviceResources(bus, device) assert.NoError(t, err, "failed to get device(%s/%s) resources", bus, device) assert.Equal(t, bus, res.Bus, "mismatching bus") assert.Equal(t, device, res.Device, "mismatching device") t.Logf("Resources: %+v", res) } // devlink device parameters can be tested with netdevsim // function will create netdevsim/netdevsim virtual device that can be used for testing // netdevsim module should be loaded to run devlink param tests func setupDevlinkDeviceParamTest(t *testing.T) (string, string, func()) { t.Helper() skipUnlessRoot(t) skipUnlessKModuleLoaded(t, "netdevsim") testDevID := strconv.Itoa(1000 + rand.Intn(1000)) err := os.WriteFile("/sys/bus/netdevsim/new_device", []byte(testDevID), 0755) assert.NoError(t, err, "can't create netdevsim test device %s", testDevID) return "netdevsim", "netdevsim" + testDevID, func() { _ = os.WriteFile("/sys/bus/netdevsim/del_device", []byte(testDevID), 0755) } } func TestDevlinkGetDeviceParams(t *testing.T) { busName, deviceName, cleanupFunc := setupDevlinkDeviceParamTest(t) defer cleanupFunc() params, err := DevlinkGetDeviceParams(busName, deviceName) assert.NoError(t, err, "failed to get device(%s/%s) parameters", busName, deviceName) assert.NotEmpty(t, params, "parameters list is empty") for _, p := range params { validateDeviceParams(t, p) } } func TestDevlinkGetDeviceParamByName(t *testing.T) { busName, deviceName, cleanupFunc := setupDevlinkDeviceParamTest(t) defer cleanupFunc() param, err := DevlinkGetDeviceParamByName(busName, deviceName, "max_macs") assert.NoError(t, err, "failed to get device(%s/%s) parameter max_macs", busName, deviceName) validateDeviceParams(t, param) } func TestDevlinkSetDeviceParam(t *testing.T) { busName, deviceName, cleanupFunc := setupDevlinkDeviceParamTest(t) defer cleanupFunc() err := DevlinkSetDeviceParam(busName, deviceName, "max_macs", nl.DEVLINK_PARAM_CMODE_DRIVERINIT, uint32(8)) assert.NoError(t, err, "failed to set max_macs for device(%s/%s)", busName, deviceName) param, err := DevlinkGetDeviceParamByName(busName, deviceName, "max_macs") assert.NoError(t, err, "failed to get device(%s/%s) parameter max_macs", busName, deviceName) validateDeviceParams(t, param) v, ok := param.Values[0].Data.(uint32) assert.True(t, ok, "unexpected value") assert.Equal(t, v, uint32(8), "value not set") } func validateDeviceParams(t *testing.T, p *DevlinkParam) { if p.Name == "" { t.Fatal("Name field not set") } if p.Name == "max_macs" && !p.IsGeneric { t.Fatal("IsGeneric should be true for generic parameter") } // test1 is a driver-specific parameter in netdevsim device, check should // also path on HW devices if p.Name == "test1" && p.IsGeneric { t.Fatal("IsGeneric should be false for driver-specific parameter") } switch p.Type { case nl.DEVLINK_PARAM_TYPE_U8, nl.DEVLINK_PARAM_TYPE_U16, nl.DEVLINK_PARAM_TYPE_U32, nl.DEVLINK_PARAM_TYPE_STRING, nl.DEVLINK_PARAM_TYPE_BOOL: default: t.Fatal("Type has unexpected value") } if len(p.Values) == 0 { t.Fatal("Values are not set") } for _, v := range p.Values { switch v.CMODE { case nl.DEVLINK_PARAM_CMODE_RUNTIME, nl.DEVLINK_PARAM_CMODE_DRIVERINIT, nl.DEVLINK_PARAM_CMODE_PERMANENT: default: t.Fatal("CMODE has unexpected value") } if p.Name == "max_macs" { _, ok := v.Data.(uint32) if !ok { t.Fatalf("value max_macs has wrong type: %T, expected: uint32", v.Data) } } if p.Name == "test1" { _, ok := v.Data.(bool) if !ok { t.Fatalf("value test1 has wrong type: %T, expected: bool", v.Data) } } } } func testGetDevlinkPortCommonAttrs() []*nl.RtAttr { nlAttrs := []*nl.RtAttr{} nlAttrs = append(nlAttrs, nl.NewRtAttr(nl.DEVLINK_ATTR_BUS_NAME, nl.ZeroTerminated("pci")), nl.NewRtAttr(nl.DEVLINK_ATTR_DEV_NAME, nl.ZeroTerminated("0000:08:00.0")), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_INDEX, nl.Uint32Attr(131071)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_TYPE, nl.Uint16Attr(nl.DEVLINK_PORT_TYPE_ETH)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NETDEV_NAME, nl.ZeroTerminated("eth0")), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NETDEV_IFINDEX, nl.Uint32Attr(5)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_IBDEV_NAME, nl.ZeroTerminated("rdma0")), ) return nlAttrs } func testAddDevlinkPortPhysicalAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { nlAttrs = append(nlAttrs, nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PHYSICAL)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_NUMBER, nl.Uint32Attr(1)), ) return nlAttrs } func testAddDevlinkPortPfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { nlAttrs = append(nlAttrs, nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_PF)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(1)), ) return nlAttrs } func testAddDevlinkPortVfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { nlAttrs = append(nlAttrs, nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_VF)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(0)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_VF_NUMBER, nl.Uint16Attr(4)), ) nlAttrs = testAddDevlinkPortFnAttrs(nlAttrs) return nlAttrs } func testAddDevlinkPortSfAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { nlAttrs = append(nlAttrs, nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FLAVOUR, nl.Uint16Attr(nl.DEVLINK_PORT_FLAVOUR_PCI_SF)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_PF_NUMBER, nl.Uint16Attr(0)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_PCI_SF_NUMBER, nl.Uint32Attr(123)), ) nlAttrs = testAddDevlinkPortFnAttrs(nlAttrs) return nlAttrs } func testNlAttrsToNetlinkRouteAttrs(nlAttrs []*nl.RtAttr) []syscall.NetlinkRouteAttr { attrs := []syscall.NetlinkRouteAttr{} for _, attr := range nlAttrs { attrs = append(attrs, syscall.NetlinkRouteAttr{Attr: syscall.RtAttr(attr.RtAttr), Value: attr.Data}) } return attrs } func testAddDevlinkPortFnAttrs(nlAttrs []*nl.RtAttr) []*nl.RtAttr { hwAddr, _ := net.ParseMAC("00:11:22:33:44:55") hwAddrAttr := nl.NewRtAttr(nl.DEVLINK_PORT_FUNCTION_ATTR_HW_ADDR, []byte(hwAddr)) raw := hwAddrAttr.Serialize() nlAttr := nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_FUNCTION, raw) return append(nlAttrs, nlAttr) } func testAddDevlinkPortControllerAttrs(nlAttrs []*nl.RtAttr, controllerNumber uint32, external bool) []*nl.RtAttr { extVal := uint8(0) if external { extVal = 1 } nlAttrs = append(nlAttrs, nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_CONTROLLER_NUMBER, nl.Uint32Attr(controllerNumber)), nl.NewRtAttr(nl.DEVLINK_ATTR_PORT_EXTERNAL, nl.Uint8Attr(extVal)), ) return nlAttrs } func testAssertCommonAttrs(t *testing.T, port *DevlinkPort) { assert.Equal(t, "pci", port.BusName) assert.Equal(t, "0000:08:00.0", port.DeviceName) assert.Equal(t, uint32(131071), port.PortIndex) assert.Equal(t, uint16(nl.DEVLINK_PORT_TYPE_ETH), port.PortType) assert.Equal(t, "eth0", port.NetdeviceName) assert.Equal(t, uint32(5), port.NetdevIfIndex) assert.Equal(t, "rdma0", port.RdmaDeviceName) } func TestDevlinkPortParseAttributes(t *testing.T) { t.Run("flavor physical", func(t *testing.T) { nlAttrs := testGetDevlinkPortCommonAttrs() nlAttrs = testAddDevlinkPortPhysicalAttrs(nlAttrs) attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) port := &DevlinkPort{} err := port.parseAttributes(attrs) assert.NoError(t, err) testAssertCommonAttrs(t, port) assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PHYSICAL), port.PortFlavour) assert.Equal(t, uint32(1), *port.PortNumber) assert.Nil(t, port.Fn) assert.Nil(t, port.PfNumber) assert.Nil(t, port.VfNumber) assert.Nil(t, port.SfNumber) assert.Nil(t, port.ControllerNumber) assert.Nil(t, port.External) }) t.Run("flavor pcipf", func(t *testing.T) { nlAttrs := testGetDevlinkPortCommonAttrs() nlAttrs = testAddDevlinkPortPfAttrs(nlAttrs) attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) port := &DevlinkPort{} err := port.parseAttributes(attrs) assert.NoError(t, err) testAssertCommonAttrs(t, port) assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_PF), port.PortFlavour) assert.Equal(t, uint16(1), *port.PfNumber) assert.Nil(t, port.Fn) assert.Nil(t, port.PortNumber) assert.Nil(t, port.VfNumber) assert.Nil(t, port.SfNumber) assert.Nil(t, port.ControllerNumber) assert.Nil(t, port.External) }) t.Run("flavor pcivf", func(t *testing.T) { nlAttrs := testGetDevlinkPortCommonAttrs() nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) port := &DevlinkPort{} err := port.parseAttributes(attrs) assert.NoError(t, err) testAssertCommonAttrs(t, port) assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_VF), port.PortFlavour) assert.Equal(t, uint16(0), *port.PfNumber) assert.Equal(t, uint16(4), *port.VfNumber) assert.Equal(t, "00:11:22:33:44:55", port.Fn.HwAddr.String()) assert.Nil(t, port.PortNumber) assert.Nil(t, port.SfNumber) assert.Nil(t, port.ControllerNumber) assert.Nil(t, port.External) }) t.Run("flavor pcisf", func(t *testing.T) { nlAttrs := testGetDevlinkPortCommonAttrs() nlAttrs = testAddDevlinkPortSfAttrs(nlAttrs) attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) port := &DevlinkPort{} err := port.parseAttributes(attrs) assert.NoError(t, err) testAssertCommonAttrs(t, port) assert.Equal(t, uint16(nl.DEVLINK_PORT_FLAVOUR_PCI_SF), port.PortFlavour) assert.Equal(t, uint16(0), *port.PfNumber) assert.Equal(t, uint32(123), *port.SfNumber) assert.Equal(t, "00:11:22:33:44:55", port.Fn.HwAddr.String()) assert.Nil(t, port.PortNumber) assert.Nil(t, port.VfNumber) assert.Nil(t, port.ControllerNumber) assert.Nil(t, port.External) }) t.Run("port with controller - external false", func(t *testing.T) { nlAttrs := testGetDevlinkPortCommonAttrs() nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) nlAttrs = testAddDevlinkPortControllerAttrs(nlAttrs, 0, false) attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) port := &DevlinkPort{} err := port.parseAttributes(attrs) assert.NoError(t, err) assert.Equal(t, uint32(0), *port.ControllerNumber) assert.Equal(t, false, *port.External) }) t.Run("port with controller - external true", func(t *testing.T) { nlAttrs := testGetDevlinkPortCommonAttrs() nlAttrs = testAddDevlinkPortVfAttrs(nlAttrs) nlAttrs = testAddDevlinkPortControllerAttrs(nlAttrs, 1, true) attrs := testNlAttrsToNetlinkRouteAttrs(nlAttrs) port := &DevlinkPort{} err := port.parseAttributes(attrs) assert.NoError(t, err) assert.Equal(t, uint32(1), *port.ControllerNumber) assert.Equal(t, true, *port.External) }) }