mirror of https://github.com/vishvananda/netlink
Support for devlink info command
This commit is contained in:
parent
74e723f230
commit
b10eb8fe5c
215
devlink_linux.go
215
devlink_linux.go
|
@ -3,6 +3,7 @@ package netlink
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink/nl"
|
"github.com/vishvananda/netlink/nl"
|
||||||
|
@ -65,6 +66,24 @@ type DevLinkPortAddAttrs struct {
|
||||||
ControllerValid bool
|
ControllerValid bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DevlinkDeviceInfo represents devlink info
|
||||||
|
type DevlinkDeviceInfo struct {
|
||||||
|
Driver string
|
||||||
|
SerialNumber string
|
||||||
|
BoardID string
|
||||||
|
FwApp string
|
||||||
|
FwAppBoundleID string
|
||||||
|
FwAppName string
|
||||||
|
FwBoundleID string
|
||||||
|
FwMgmt string
|
||||||
|
FwMgmtAPI string
|
||||||
|
FwMgmtBuild string
|
||||||
|
FwNetlist string
|
||||||
|
FwNetlistBuild string
|
||||||
|
FwPsidAPI string
|
||||||
|
FwUndi string
|
||||||
|
}
|
||||||
|
|
||||||
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
|
func parseDevLinkDeviceList(msgs [][]byte) ([]*DevlinkDevice, error) {
|
||||||
devices := make([]*DevlinkDevice, 0, len(msgs))
|
devices := make([]*DevlinkDevice, 0, len(msgs))
|
||||||
for _, m := range msgs {
|
for _, m := range msgs {
|
||||||
|
@ -511,3 +530,199 @@ func (h *Handle) DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, F
|
||||||
func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
|
func DevlinkPortFnSet(Bus string, Device string, PortIndex uint32, FnAttrs DevlinkPortFnSetAttrs) error {
|
||||||
return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs)
|
return pkgHandle.DevlinkPortFnSet(Bus, Device, PortIndex, FnAttrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// devlinkInfoGetter is function that is responsible for getting devlink info message
|
||||||
|
// this is introduced for test purpose
|
||||||
|
type devlinkInfoGetter func(bus, device string) ([]byte, error)
|
||||||
|
|
||||||
|
// DevlinkGetDeviceInfoByName returns devlink info for selected device,
|
||||||
|
// otherwise returns an error code.
|
||||||
|
// Equivalent to: `devlink dev info $dev`
|
||||||
|
func (h *Handle) DevlinkGetDeviceInfoByName(Bus string, Device string, getInfoMsg devlinkInfoGetter) (*DevlinkDeviceInfo, error) {
|
||||||
|
info, err := h.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, getInfoMsg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseInfoData(info), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DevlinkGetDeviceInfoByName returns devlink info for selected device,
|
||||||
|
// otherwise returns an error code.
|
||||||
|
// Equivalent to: `devlink dev info $dev`
|
||||||
|
func DevlinkGetDeviceInfoByName(Bus string, Device string) (*DevlinkDeviceInfo, error) {
|
||||||
|
return pkgHandle.DevlinkGetDeviceInfoByName(Bus, Device, pkgHandle.getDevlinkInfoMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
|
||||||
|
// otherwise returns an error code.
|
||||||
|
// Equivalent to: `devlink dev info $dev`
|
||||||
|
func (h *Handle) DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string, getInfoMsg devlinkInfoGetter) (map[string]string, error) {
|
||||||
|
response, err := getInfoMsg(Bus, Device)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := parseInfoMsg(response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DevlinkGetDeviceInfoByNameAsMap returns devlink info for selected device as a map,
|
||||||
|
// otherwise returns an error code.
|
||||||
|
// Equivalent to: `devlink dev info $dev`
|
||||||
|
func DevlinkGetDeviceInfoByNameAsMap(Bus string, Device string) (map[string]string, error) {
|
||||||
|
return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(Bus, Device, pkgHandle.getDevlinkInfoMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDevlinkInfo returns devlink info for target device,
|
||||||
|
// otherwise returns an error code.
|
||||||
|
func (d *DevlinkDevice) GetDevlinkInfo() (*DevlinkDeviceInfo, error) {
|
||||||
|
return pkgHandle.DevlinkGetDeviceInfoByName(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDevlinkInfoAsMap returns devlink info for target device as a map,
|
||||||
|
// otherwise returns an error code.
|
||||||
|
func (d *DevlinkDevice) GetDevlinkInfoAsMap() (map[string]string, error) {
|
||||||
|
return pkgHandle.DevlinkGetDeviceInfoByNameAsMap(d.BusName, d.DeviceName, pkgHandle.getDevlinkInfoMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handle) getDevlinkInfoMsg(bus, device string) ([]byte, error) {
|
||||||
|
_, req, err := h.createCmdReq(nl.DEVLINK_CMD_INFO_GET, bus, device)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := req.Execute(unix.NETLINK_GENERIC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response) < 1 {
|
||||||
|
return nil, fmt.Errorf("getDevlinkInfoMsg: message too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInfoMsg(msg []byte) (map[string]string, error) {
|
||||||
|
if len(msg) < nl.SizeofGenlmsg {
|
||||||
|
return nil, fmt.Errorf("parseInfoMsg: message too short")
|
||||||
|
}
|
||||||
|
|
||||||
|
info := make(map[string]string)
|
||||||
|
err := collectInfoData(msg[nl.SizeofGenlmsg:], info)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return info, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func collectInfoData(msg []byte, data map[string]string) error {
|
||||||
|
attrs, err := nl.ParseRouteAttr(msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, attr := range attrs {
|
||||||
|
switch attr.Attr.Type {
|
||||||
|
case nl.DEVLINK_ATTR_INFO_DRIVER_NAME:
|
||||||
|
data["driver"] = parseInfoValue(attr.Value)
|
||||||
|
case nl.DEVLINK_ATTR_INFO_SERIAL_NUMBER:
|
||||||
|
data["serialNumber"] = parseInfoValue(attr.Value)
|
||||||
|
case nl.DEVLINK_ATTR_INFO_VERSION_RUNNING, nl.DEVLINK_ATTR_INFO_VERSION_FIXED,
|
||||||
|
nl.DEVLINK_ATTR_INFO_VERSION_STORED:
|
||||||
|
key, value, err := getNestedInfoData(attr.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) == 0 {
|
||||||
|
return fmt.Errorf("collectInfoData: could not read attributes")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getNestedInfoData(msg []byte) (string, string, error) {
|
||||||
|
nestedAttrs, err := nl.ParseRouteAttr(msg)
|
||||||
|
|
||||||
|
var key, value string
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nestedAttrs) != 2 {
|
||||||
|
return "", "", fmt.Errorf("getNestedInfoData: too few attributes in nested structure")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, nestedAttr := range nestedAttrs {
|
||||||
|
switch nestedAttr.Attr.Type {
|
||||||
|
case nl.DEVLINK_ATTR_INFO_VERSION_NAME:
|
||||||
|
key = parseInfoValue(nestedAttr.Value)
|
||||||
|
case nl.DEVLINK_ATTR_INFO_VERSION_VALUE:
|
||||||
|
value = parseInfoValue(nestedAttr.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == "" {
|
||||||
|
return "", "", fmt.Errorf("getNestedInfoData: key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == "" {
|
||||||
|
return "", "", fmt.Errorf("getNestedInfoData: value not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInfoData(data map[string]string) *DevlinkDeviceInfo {
|
||||||
|
info := new(DevlinkDeviceInfo)
|
||||||
|
for key, value := range data {
|
||||||
|
switch key {
|
||||||
|
case "driver":
|
||||||
|
info.Driver = value
|
||||||
|
case "serialNumber":
|
||||||
|
info.SerialNumber = value
|
||||||
|
case "board.id":
|
||||||
|
info.BoardID = value
|
||||||
|
case "fw.app":
|
||||||
|
info.FwApp = value
|
||||||
|
case "fw.app.bundle_id":
|
||||||
|
info.FwAppBoundleID = value
|
||||||
|
case "fw.app.name":
|
||||||
|
info.FwAppName = value
|
||||||
|
case "fw.bundle_id":
|
||||||
|
info.FwBoundleID = value
|
||||||
|
case "fw.mgmt":
|
||||||
|
info.FwMgmt = value
|
||||||
|
case "fw.mgmt.api":
|
||||||
|
info.FwMgmtAPI = value
|
||||||
|
case "fw.mgmt.build":
|
||||||
|
info.FwMgmtBuild = value
|
||||||
|
case "fw.netlist":
|
||||||
|
info.FwNetlist = value
|
||||||
|
case "fw.netlist.build":
|
||||||
|
info.FwNetlistBuild = value
|
||||||
|
case "fw.psid.api":
|
||||||
|
info.FwPsidAPI = value
|
||||||
|
case "fw.undi":
|
||||||
|
info.FwUndi = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInfoValue(value []byte) string {
|
||||||
|
v := strings.ReplaceAll(string(value), "\x00", "")
|
||||||
|
return strings.TrimSpace(v)
|
||||||
|
}
|
||||||
|
|
112
devlink_test.go
112
devlink_test.go
|
@ -150,3 +150,115 @@ func init() {
|
||||||
flag.StringVar(&device, "device", "", "devlink device devicename")
|
flag.StringVar(&device, "device", "", "devlink device devicename")
|
||||||
flag.UintVar(&sfnum, "sfnum", 0, "devlink port sfnumber")
|
flag.UintVar(&sfnum, "sfnum", 0, "devlink port sfnumber")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDevlinkGetDeviceInfoByNameAsMap(t *testing.T) {
|
||||||
|
info, err := pkgHandle.DevlinkGetDeviceInfoByNameAsMap("pci", "0000:00:00.0", mockDevlinkInfoGetter)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
testInfo := devlinkTestInfoParesd()
|
||||||
|
for k, v := range info {
|
||||||
|
if testInfo[k] != v {
|
||||||
|
t.Fatal("Value", v, "retrieved for key", k, "is not equal to", testInfo[k])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevlinkGetDeviceInfoByName(t *testing.T) {
|
||||||
|
info, err := pkgHandle.DevlinkGetDeviceInfoByName("pci", "0000:00:00.0", mockDevlinkInfoGetter)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
testInfo := parseInfoData(devlinkTestInfoParesd())
|
||||||
|
if !areInfoStructsEqual(info, testInfo) {
|
||||||
|
t.Fatal("Info structures are not equal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevlinkGetDeviceInfoByNameAsMapFail(t *testing.T) {
|
||||||
|
_, err := pkgHandle.DevlinkGetDeviceInfoByNameAsMap("pci", "0000:00:00.0", mockDevlinkInfoGetterEmpty)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDevlinkGetDeviceInfoByNameFail(t *testing.T) {
|
||||||
|
_, err := pkgHandle.DevlinkGetDeviceInfoByName("pci", "0000:00:00.0", mockDevlinkInfoGetterEmpty)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ const (
|
||||||
DEVLINK_CMD_PORT_DEL = 8
|
DEVLINK_CMD_PORT_DEL = 8
|
||||||
DEVLINK_CMD_ESWITCH_GET = 29
|
DEVLINK_CMD_ESWITCH_GET = 29
|
||||||
DEVLINK_CMD_ESWITCH_SET = 30
|
DEVLINK_CMD_ESWITCH_SET = 30
|
||||||
|
DEVLINK_CMD_INFO_GET = 51
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -30,6 +31,13 @@ const (
|
||||||
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
|
DEVLINK_ATTR_ESWITCH_INLINE_MODE = 26
|
||||||
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
|
DEVLINK_ATTR_ESWITCH_ENCAP_MODE = 62
|
||||||
DEVLINK_ATTR_PORT_FLAVOUR = 77
|
DEVLINK_ATTR_PORT_FLAVOUR = 77
|
||||||
|
DEVLINK_ATTR_INFO_DRIVER_NAME = 98
|
||||||
|
DEVLINK_ATTR_INFO_SERIAL_NUMBER = 99
|
||||||
|
DEVLINK_ATTR_INFO_VERSION_FIXED = 100
|
||||||
|
DEVLINK_ATTR_INFO_VERSION_RUNNING = 101
|
||||||
|
DEVLINK_ATTR_INFO_VERSION_STORED = 102
|
||||||
|
DEVLINK_ATTR_INFO_VERSION_NAME = 103
|
||||||
|
DEVLINK_ATTR_INFO_VERSION_VALUE = 104
|
||||||
DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127
|
DEVLINK_ATTR_PORT_PCI_PF_NUMBER = 127
|
||||||
DEVLINK_ATTR_PORT_FUNCTION = 145
|
DEVLINK_ATTR_PORT_FUNCTION = 145
|
||||||
DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150
|
DEVLINK_ATTR_PORT_CONTROLLER_NUMBER = 150
|
||||||
|
|
Loading…
Reference in New Issue