Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -711,10 +711,25 @@ Each `ghw.NIC` struct contains the following fields:
device
* `ghw.NIC.Capabilities` is an array of pointers to `ghw.NICCapability` structs
that can describe the things the NIC supports. These capabilities match the
returned values from the `ethtool -k <DEVICE>` call on Linux
returned values from the `ethtool -k <DEVICE>` call on Linux as well as the
AutoNegotiation and PauseFrameUse capabilities from `ethtool`.
* `ghw.NIC.PCIAddress` is the PCI device address of the device backing the NIC.
this is not-nil only if the backing device is indeed a PCI device; more backing
devices (e.g. USB) will be added in future versions.
* `ghw.NIC.Speed` is a string showing the current link speed. On Linux, this
field will be present even if `ethtool` is not available.
* `ghw.NIC.Duplex` is a string showing the current link duplex. On Linux, this
field will be present even if `ethtool` is not available.
* `ghw.NIC.SupportedLinkModes` is a string slice containing a list of
supported link modes
* `ghw.NIC.SupportedPorts` is a string slice containing the list of
supported port types (MII, TP, FIBRE)
* `ghw.NIC.SupportedFECModes` is a string slice containing a list of
supported FEC Modes.
* `ghw.NIC.AdvertisedLinkModes` is a string slice containing the
link modes being advertised during auto negotiation.
* `ghw.NIC.AdvertisedFECModes` is a string slice containing the FEC
modes advertised during auto negotiation.

The `ghw.NICCapability` struct contains the following fields:

Expand Down
17 changes: 12 additions & 5 deletions pkg/net/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,18 @@ type NICCapability struct {
}

type NIC struct {
Name string `json:"name"`
MacAddress string `json:"mac_address"`
IsVirtual bool `json:"is_virtual"`
Capabilities []*NICCapability `json:"capabilities"`
PCIAddress *string `json:"pci_address,omitempty"`
Name string `json:"name"`
MacAddress string `json:"mac_address"`
IsVirtual bool `json:"is_virtual"`
Capabilities []*NICCapability `json:"capabilities"`
PCIAddress *string `json:"pci_address,omitempty"`
Speed string `json:"speed"`
Duplex string `json:"duplex"`
SupportedLinkModes []string `json:"supported_link_modes,omitempty"`
SupportedPorts []string `json:"supported_ports,omitempty"`
SupportedFECModes []string `json:"supported_fec_modes,omitempty"`
AdvertisedLinkModes []string `json:"advertised_link_modes,omitempty"`
AdvertisedFECModes []string `json:"advertised_fec_modes,omitempty"`
// TODO(fromani): add other hw addresses (USB) when we support them
}

Expand Down
40 changes: 32 additions & 8 deletions pkg/net/net_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,11 @@ func nics(ctx *context.Context) []*NIC {
mac := netDeviceMacAddress(paths, filename)
nic.MacAddress = mac
if etAvailable {
nic.Capabilities = netDeviceCapabilities(ctx, filename)
nic.netDeviceParseEthtool(ctx, filename)
} else {
nic.Capabilities = []*NICCapability{}
// Sets NIC struct fields from data in SysFs
nic.setNicAttrSysFs(paths, filename)
}

nic.PCIAddress = netDevicePCIAddress(paths.SysClassNet, filename)
Expand Down Expand Up @@ -106,19 +108,29 @@ func ethtoolInstalled() bool {
return err == nil
}

func netDeviceCapabilities(ctx *context.Context, dev string) []*NICCapability {
caps := make([]*NICCapability, 0)
func (n *NIC) netDeviceParseEthtool(ctx *context.Context, dev string) {
var out bytes.Buffer
path, _ := exec.LookPath("ethtool")

// Get auto-negotiation and pause-frame-use capabilities from "ethtool" (with no options)
// Populate Speed, Duplex, SupportedLinkModes, SupportedPorts, SupportedFECModes,
// AdvertisedLinkModes, and AdvertisedFECModes attributes from "ethtool" output.
cmd := exec.Command(path, dev)
cmd.Stdout = &out
err := cmd.Run()
if err == nil {
m := parseNicAttrEthtool(&out)
caps = append(caps, autoNegCap(m))
caps = append(caps, pauseFrameUseCap(m))
n.Capabilities = append(n.Capabilities, autoNegCap(m))
n.Capabilities = append(n.Capabilities, pauseFrameUseCap(m))

// Update NIC Attributes with ethtool output
n.Speed = strings.Join(m["Speed"], "")
n.Duplex = strings.Join(m["Duplex"], "")
n.SupportedLinkModes = m["Supported link modes"]
n.SupportedPorts = m["Supported ports"]
n.SupportedFECModes = m["Supported FEC modes"]
n.AdvertisedLinkModes = m["Advertised link modes"]
n.AdvertisedFECModes = m["Advertised FEC modes"]
} else {
msg := fmt.Sprintf("could not grab NIC link info for %s: %s", dev, err)
ctx.Warn(msg)
Expand Down Expand Up @@ -154,16 +166,14 @@ func netDeviceCapabilities(ctx *context.Context, dev string) []*NICCapability {
scanner.Scan()
for scanner.Scan() {
line := strings.TrimPrefix(scanner.Text(), "\t")
caps = append(caps, netParseEthtoolFeature(line))
n.Capabilities = append(n.Capabilities, netParseEthtoolFeature(line))
}

} else {
msg := fmt.Sprintf("could not grab NIC capabilities for %s: %s", dev, err)
ctx.Warn(msg)
}

return caps

}

// netParseEthtoolFeature parses a line from the ethtool -k output and returns
Expand Down Expand Up @@ -239,6 +249,20 @@ func netDevicePCIAddress(netDevDir, netDevName string) *string {
return &pciAddr
}

func (nic *NIC) setNicAttrSysFs(paths *linuxpath.Paths, dev string) {
// Get speed and duplex from /sys/class/net/$DEVICE/ directory
nic.Speed = readFile(filepath.Join(paths.SysClassNet, dev, "speed"))
nic.Duplex = readFile(filepath.Join(paths.SysClassNet, dev, "duplex"))
}

func readFile(path string) string {
contents, err := ioutil.ReadFile(path)
if err != nil {
return ""
}
return strings.TrimSpace(string(contents))
}

func autoNegCap(m map[string][]string) *NICCapability {
autoNegotiation := NICCapability{Name: "auto-negotiation", IsEnabled: false, CanEnable: false}

Expand Down
55 changes: 41 additions & 14 deletions pkg/net/net_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"bytes"
"os"
"reflect"
"strings"
"testing"
)

Expand Down Expand Up @@ -66,7 +67,7 @@ func TestParseNicAttrEthtool(t *testing.T) {

tests := []struct {
input string
expected []*NICCapability
expected *NIC
}{
{
input: `Settings for eth0:
Expand Down Expand Up @@ -96,28 +97,54 @@ func TestParseNicAttrEthtool(t *testing.T) {
drv probe link
Link detected: yes
`,
expected: []*NICCapability{
{
Name: "auto-negotiation",
IsEnabled: true,
CanEnable: true,
expected: &NIC{
Speed: "1000Mb/s",
Duplex: "Full",
SupportedPorts: []string{"TP"},
AdvertisedLinkModes: []string{
"10baseT/Half",
"10baseT/Full",
"100baseT/Half",
"100baseT/Full",
"1000baseT/Full",
},
{
Name: "pause-frame-use",
IsEnabled: false,
CanEnable: false,
SupportedLinkModes: []string{
"10baseT/Half",
"10baseT/Full",
"100baseT/Half",
"100baseT/Full",
"1000baseT/Full",
},
Capabilities: []*NICCapability{
{
Name: "auto-negotiation",
IsEnabled: true,
CanEnable: true,
},
{
Name: "pause-frame-use",
IsEnabled: false,
CanEnable: false,
},
},
},
},
}

for x, test := range tests {
m := parseNicAttrEthtool(bytes.NewBufferString(test.input))
actual := make([]*NICCapability, 0)
actual = append(actual, autoNegCap(m))
actual = append(actual, pauseFrameUseCap(m))
actual := &NIC{}
actual.Speed = strings.Join(m["Speed"], "")
actual.Duplex = strings.Join(m["Duplex"], "")
actual.SupportedLinkModes = m["Supported link modes"]
actual.SupportedPorts = m["Supported ports"]
actual.SupportedFECModes = m["Supported FEC modes"]
actual.AdvertisedLinkModes = m["Advertised link modes"]
actual.AdvertisedFECModes = m["Advertised FEC modes"]
actual.Capabilities = append(actual.Capabilities, autoNegCap(m))
actual.Capabilities = append(actual.Capabilities, pauseFrameUseCap(m))
if !reflect.DeepEqual(test.expected, actual) {
t.Fatalf("In test %d\nExpected:\n%+v\nActual:\n%+v\n", x, test.expected, actual)
t.Fatalf("In test %d\nExpected:\n%+v\nActual:\n%+v\n", x, *test.expected, *actual)
}
}
}
3 changes: 3 additions & 0 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ func ParseBool(str string) (bool, error) {
"off": false,
"yes": true,
"no": false,
// Return false instead of an error on empty strings
// For example from empty files in SysClassNet/Device
"": false,
}
if b, ok := ExtraBools[strings.ToLower(str)]; ok {
return b, nil
Expand Down
4 changes: 4 additions & 0 deletions pkg/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ func TestParseBool(t *testing.T) {
item: "1",
expected: true,
},
{
item: "",
expected: false,
},
{
item: "on",
expected: true,
Expand Down