Skip to content

Commit 36ff37e

Browse files
authored
Merge pull request #342 from jak3kaj/NicLinkInfo2
Add more fields to NIC
2 parents 29cf834 + 413cc04 commit 36ff37e

File tree

6 files changed

+108
-28
lines changed

6 files changed

+108
-28
lines changed

README.md

+16-1
Original file line numberDiff line numberDiff line change
@@ -711,10 +711,25 @@ Each `ghw.NIC` struct contains the following fields:
711711
device
712712
* `ghw.NIC.Capabilities` is an array of pointers to `ghw.NICCapability` structs
713713
that can describe the things the NIC supports. These capabilities match the
714-
returned values from the `ethtool -k <DEVICE>` call on Linux
714+
returned values from the `ethtool -k <DEVICE>` call on Linux as well as the
715+
AutoNegotiation and PauseFrameUse capabilities from `ethtool`.
715716
* `ghw.NIC.PCIAddress` is the PCI device address of the device backing the NIC.
716717
this is not-nil only if the backing device is indeed a PCI device; more backing
717718
devices (e.g. USB) will be added in future versions.
719+
* `ghw.NIC.Speed` is a string showing the current link speed. On Linux, this
720+
field will be present even if `ethtool` is not available.
721+
* `ghw.NIC.Duplex` is a string showing the current link duplex. On Linux, this
722+
field will be present even if `ethtool` is not available.
723+
* `ghw.NIC.SupportedLinkModes` is a string slice containing a list of
724+
supported link modes
725+
* `ghw.NIC.SupportedPorts` is a string slice containing the list of
726+
supported port types (MII, TP, FIBRE)
727+
* `ghw.NIC.SupportedFECModes` is a string slice containing a list of
728+
supported FEC Modes.
729+
* `ghw.NIC.AdvertisedLinkModes` is a string slice containing the
730+
link modes being advertised during auto negotiation.
731+
* `ghw.NIC.AdvertisedFECModes` is a string slice containing the FEC
732+
modes advertised during auto negotiation.
718733

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

pkg/net/net.go

+12-5
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,18 @@ type NICCapability struct {
2121
}
2222

2323
type NIC struct {
24-
Name string `json:"name"`
25-
MacAddress string `json:"mac_address"`
26-
IsVirtual bool `json:"is_virtual"`
27-
Capabilities []*NICCapability `json:"capabilities"`
28-
PCIAddress *string `json:"pci_address,omitempty"`
24+
Name string `json:"name"`
25+
MacAddress string `json:"mac_address"`
26+
IsVirtual bool `json:"is_virtual"`
27+
Capabilities []*NICCapability `json:"capabilities"`
28+
PCIAddress *string `json:"pci_address,omitempty"`
29+
Speed string `json:"speed"`
30+
Duplex string `json:"duplex"`
31+
SupportedLinkModes []string `json:"supported_link_modes,omitempty"`
32+
SupportedPorts []string `json:"supported_ports,omitempty"`
33+
SupportedFECModes []string `json:"supported_fec_modes,omitempty"`
34+
AdvertisedLinkModes []string `json:"advertised_link_modes,omitempty"`
35+
AdvertisedFECModes []string `json:"advertised_fec_modes,omitempty"`
2936
// TODO(fromani): add other hw addresses (USB) when we support them
3037
}
3138

pkg/net/net_linux.go

+32-8
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,11 @@ func nics(ctx *context.Context) []*NIC {
6868
mac := netDeviceMacAddress(paths, filename)
6969
nic.MacAddress = mac
7070
if etAvailable {
71-
nic.Capabilities = netDeviceCapabilities(ctx, filename)
71+
nic.netDeviceParseEthtool(ctx, filename)
7272
} else {
7373
nic.Capabilities = []*NICCapability{}
74+
// Sets NIC struct fields from data in SysFs
75+
nic.setNicAttrSysFs(paths, filename)
7476
}
7577

7678
nic.PCIAddress = netDevicePCIAddress(paths.SysClassNet, filename)
@@ -106,19 +108,29 @@ func ethtoolInstalled() bool {
106108
return err == nil
107109
}
108110

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

114115
// Get auto-negotiation and pause-frame-use capabilities from "ethtool" (with no options)
116+
// Populate Speed, Duplex, SupportedLinkModes, SupportedPorts, SupportedFECModes,
117+
// AdvertisedLinkModes, and AdvertisedFECModes attributes from "ethtool" output.
115118
cmd := exec.Command(path, dev)
116119
cmd.Stdout = &out
117120
err := cmd.Run()
118121
if err == nil {
119122
m := parseNicAttrEthtool(&out)
120-
caps = append(caps, autoNegCap(m))
121-
caps = append(caps, pauseFrameUseCap(m))
123+
n.Capabilities = append(n.Capabilities, autoNegCap(m))
124+
n.Capabilities = append(n.Capabilities, pauseFrameUseCap(m))
125+
126+
// Update NIC Attributes with ethtool output
127+
n.Speed = strings.Join(m["Speed"], "")
128+
n.Duplex = strings.Join(m["Duplex"], "")
129+
n.SupportedLinkModes = m["Supported link modes"]
130+
n.SupportedPorts = m["Supported ports"]
131+
n.SupportedFECModes = m["Supported FEC modes"]
132+
n.AdvertisedLinkModes = m["Advertised link modes"]
133+
n.AdvertisedFECModes = m["Advertised FEC modes"]
122134
} else {
123135
msg := fmt.Sprintf("could not grab NIC link info for %s: %s", dev, err)
124136
ctx.Warn(msg)
@@ -154,16 +166,14 @@ func netDeviceCapabilities(ctx *context.Context, dev string) []*NICCapability {
154166
scanner.Scan()
155167
for scanner.Scan() {
156168
line := strings.TrimPrefix(scanner.Text(), "\t")
157-
caps = append(caps, netParseEthtoolFeature(line))
169+
n.Capabilities = append(n.Capabilities, netParseEthtoolFeature(line))
158170
}
159171

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

165-
return caps
166-
167177
}
168178

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

252+
func (nic *NIC) setNicAttrSysFs(paths *linuxpath.Paths, dev string) {
253+
// Get speed and duplex from /sys/class/net/$DEVICE/ directory
254+
nic.Speed = readFile(filepath.Join(paths.SysClassNet, dev, "speed"))
255+
nic.Duplex = readFile(filepath.Join(paths.SysClassNet, dev, "duplex"))
256+
}
257+
258+
func readFile(path string) string {
259+
contents, err := ioutil.ReadFile(path)
260+
if err != nil {
261+
return ""
262+
}
263+
return strings.TrimSpace(string(contents))
264+
}
265+
242266
func autoNegCap(m map[string][]string) *NICCapability {
243267
autoNegotiation := NICCapability{Name: "auto-negotiation", IsEnabled: false, CanEnable: false}
244268

pkg/net/net_linux_test.go

+41-14
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"bytes"
1414
"os"
1515
"reflect"
16+
"strings"
1617
"testing"
1718
)
1819

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

6768
tests := []struct {
6869
input string
69-
expected []*NICCapability
70+
expected *NIC
7071
}{
7172
{
7273
input: `Settings for eth0:
@@ -96,28 +97,54 @@ func TestParseNicAttrEthtool(t *testing.T) {
9697
drv probe link
9798
Link detected: yes
9899
`,
99-
expected: []*NICCapability{
100-
{
101-
Name: "auto-negotiation",
102-
IsEnabled: true,
103-
CanEnable: true,
100+
expected: &NIC{
101+
Speed: "1000Mb/s",
102+
Duplex: "Full",
103+
SupportedPorts: []string{"TP"},
104+
AdvertisedLinkModes: []string{
105+
"10baseT/Half",
106+
"10baseT/Full",
107+
"100baseT/Half",
108+
"100baseT/Full",
109+
"1000baseT/Full",
104110
},
105-
{
106-
Name: "pause-frame-use",
107-
IsEnabled: false,
108-
CanEnable: false,
111+
SupportedLinkModes: []string{
112+
"10baseT/Half",
113+
"10baseT/Full",
114+
"100baseT/Half",
115+
"100baseT/Full",
116+
"1000baseT/Full",
117+
},
118+
Capabilities: []*NICCapability{
119+
{
120+
Name: "auto-negotiation",
121+
IsEnabled: true,
122+
CanEnable: true,
123+
},
124+
{
125+
Name: "pause-frame-use",
126+
IsEnabled: false,
127+
CanEnable: false,
128+
},
109129
},
110130
},
111131
},
112132
}
113133

114134
for x, test := range tests {
115135
m := parseNicAttrEthtool(bytes.NewBufferString(test.input))
116-
actual := make([]*NICCapability, 0)
117-
actual = append(actual, autoNegCap(m))
118-
actual = append(actual, pauseFrameUseCap(m))
136+
actual := &NIC{}
137+
actual.Speed = strings.Join(m["Speed"], "")
138+
actual.Duplex = strings.Join(m["Duplex"], "")
139+
actual.SupportedLinkModes = m["Supported link modes"]
140+
actual.SupportedPorts = m["Supported ports"]
141+
actual.SupportedFECModes = m["Supported FEC modes"]
142+
actual.AdvertisedLinkModes = m["Advertised link modes"]
143+
actual.AdvertisedFECModes = m["Advertised FEC modes"]
144+
actual.Capabilities = append(actual.Capabilities, autoNegCap(m))
145+
actual.Capabilities = append(actual.Capabilities, pauseFrameUseCap(m))
119146
if !reflect.DeepEqual(test.expected, actual) {
120-
t.Fatalf("In test %d\nExpected:\n%+v\nActual:\n%+v\n", x, test.expected, actual)
147+
t.Fatalf("In test %d\nExpected:\n%+v\nActual:\n%+v\n", x, *test.expected, *actual)
121148
}
122149
}
123150
}

pkg/util/util.go

+3
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ func ParseBool(str string) (bool, error) {
7171
"off": false,
7272
"yes": true,
7373
"no": false,
74+
// Return false instead of an error on empty strings
75+
// For example from empty files in SysClassNet/Device
76+
"": false,
7477
}
7578
if b, ok := ExtraBools[strings.ToLower(str)]; ok {
7679
return b, nil

pkg/util/util_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ func TestParseBool(t *testing.T) {
7575
item: "1",
7676
expected: true,
7777
},
78+
{
79+
item: "",
80+
expected: false,
81+
},
7882
{
7983
item: "on",
8084
expected: true,

0 commit comments

Comments
 (0)