-
Notifications
You must be signed in to change notification settings - Fork 320
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sys class net #82
Sys class net #82
Conversation
Thanks for the contribution. I'm traveling right now, but will try and get this reviewed soon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm generally not a fan of using reflect
, it trades compile time checks for a few saved lines. Static code would be preferred.
I won't object merging a reflect based implementation, but please ensure there are no linter errors (e.g. ensure all types and functions are properly documented).
sysfs/net_class.go
Outdated
"strings" | ||
) | ||
|
||
type interfaceNetClass struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The struct should be exported with a descriptive name, so that it'll be be included in the documentation https://godoc.org/github.com/prometheus/procfs/.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, does NetClassInterface
seem like a better name to you? I'm not sure whether using word interface
is really a good idea - can be misleading language-wise, on the other hand something like device
is misleading as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe following the terminology chosen by the kernel developers makes the most sense. Reading the kernel documentation, it should be either iface
or interface
. I'm generally not a fan of abbreviations and would slightly lean towards interface
. In the doc strings we could use interface (iface)
to make the context clear.
sysfs/net_class.go
Outdated
) | ||
|
||
type interfaceNetClass struct { | ||
Name string `fileName:"name"` // Interface name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm worried including this field can lead to confusion, as it's not actually an attribute in /sys/class/net/*/
. I see the benefit of having it here though. I'd remove the filename
attribute here to not further confuse a reader.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a correct note and mistake on my side, I'll remove the tag, thanks.
sysfs/net_class.go
Outdated
CarrierChanges uint64 `fileName:"carrier_changes"` // /sys/class/net/<iface>/carrier_changes | ||
CarrierUpCount uint64 `fileName:"carrier_up_count"` // /sys/class/net/<iface>/carrier_up_count | ||
CarrierDownCount uint64 `fileName:"carrier_down_count"` // /sys/class/net/<iface>/carrier_down_count | ||
DevId uint64 `fileName:"dev_id"` // /sys/class/net/<iface>/dev_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did not know that. Thanks!
sysfs/net_class.go
Outdated
IfIndex uint64 `fileName:"ifindex"` // /sys/class/net/<iface>/ifindex | ||
IfLink uint64 `fileName:"iflink"` // /sys/class/net/<iface>/iflink | ||
LinkMode uint64 `fileName:"link_mode"` // /sys/class/net/<iface>/link_mode | ||
Mtu uint64 `fileName:"mtu"` // /sys/class/net/<iface>/link_mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MTU
sysfs/net_class.go
Outdated
NameAssignType uint64 `fileName:"name_assign_type"` // /sys/class/net/<iface>/name_assign_type | ||
NetDevGroup uint64 `fileName:"netdev_group"` // /sys/class/net/<iface>/netdev_group | ||
OperState string `fileName:"operstate"` // /sys/class/net/<iface>/operstate | ||
PhysPortId string `fileName:"phys_port_id"` // /sys/class/net/<iface>/phys_port_id |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PhysPortID
sysfs/net_class.go
Outdated
Type uint64 `fileName:"type"` // /sys/class/net/<iface>/type | ||
} | ||
|
||
type NetClass map[string]interfaceNetClass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a godoc comment for that type and explain the map key.
sysfs/net_class.go
Outdated
|
||
type NetClass map[string]interfaceNetClass | ||
|
||
// NewNetDev returns kernel/system statistics read from /proc/net/dev. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
godoc comment is out of sync. Please check your code with golint sys/...
and go vet ./...
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I fixed the comment, but neither of those commands returned anything (neither before nor after the fix) not sure if that's correct or I'm doing sth wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are indeed no go vet errors in the packages fortunately. A couple of linter errors though in other files (nfs and bcache). Before your changes, golint also complained about the missing or wrong godoc strings. You should see some output with golint ./...
executed in the procfs repository root.
sysfs/net_class_test.go
Outdated
t.Errorf("want %d parsed class/net, have %d", want, have) | ||
} | ||
|
||
fields := []string{"AddrAssignType", "AddrLen", "Address", "Broadcast", "Carrier", "CarrierChanges", "CarrierUpCount", "CarrierDownCount", "DevId", "Dormant", "Duplex", "Flags", "IfAlias", "IfIndex", "IfLink", "LinkMode", "Mtu", "NameAssignType", "NetDevGroup", "OperState", "PhysPortId", "PhysPortName", "PhysSwitchId", "Speed", "TxQueueLen", "Type"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is all of this code necessary? Wouldn't it be enough to use reflect.DeepEqual(netClass, nc)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absolutely, did not know that. thanks!
sysfs/net_class.go
Outdated
} | ||
|
||
func (nc NetClass) parseInterfaceNetClass(devicePath string) (*interfaceNetClass, error) { | ||
fields := []string{"AddrAssignType", "AddrLen", "Address", "Broadcast", "Carrier", "CarrierChanges", "CarrierUpCount", "CarrierDownCount", "DevId", "Dormant", "Duplex", "Flags", "IfAlias", "IfIndex", "IfLink", "LinkMode", "Mtu", "NameAssignType", "NetDevGroup", "OperState", "PhysPortId", "PhysPortName", "PhysSwitchId", "Speed", "TxQueueLen", "Type"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you list all the field names again, given that you already use the reflect package? With reflect.ValueOf(&interfaceClass).NumField()
you can get the number of fields and with reflect.ValueOf(&interfaceClass).Field(index)
you can access a field by index.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
much better solution, thanks!
sysfs/net_class.go
Outdated
} | ||
fieldValue := interfaceElem.FieldByName(fieldName) | ||
|
||
fileContents, err := ioutil.ReadFile(devicePath + "/" + fieldType.Tag.Get("fileName")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There should be a check whether fileName tag is set before trying to open such file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definitely. Added, thanks for that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
sysfs/net_class_test.go
Outdated
} | ||
|
||
netClass := NetClass{ | ||
"eth0": {Name: "eth0", AddrAssignType: 3, AddrLen: 6, Address: "01:01:01:01:01:01", Broadcast: "ff:ff:ff:ff:ff:ff", Carrier: 1, CarrierChanges: 2, CarrierUpCount: 1, CarrierDownCount: 1, DevID: 32, Dormant: 1, Duplex: "full", Flags: 4867, IfAlias: "", IfIndex: 2, IfLink: 2, LinkMode: 1, MTU: 1500, NameAssignType: 2, NetDevGroup: 0, OperState: "up", PhysPortID: "", PhysPortName: "", PhysSwitchID: "", Speed: 1000, TxQueueLen: 1000, Type: 1}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd help readability to have a line break after each attribute/value pair.
sysfs/net_class.go
Outdated
return newNetClass(fs.Path("class/net")) | ||
} | ||
|
||
// NewNetClass returns info for all net interfaces read from /sys/class/net/<interface>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should start with a lowercase n to document the private function. I wonder why not just merging this fuction with the public NewNetClass function, given it's the only caller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
absolutely, merging.
sysfs/net_class.go
Outdated
"strings" | ||
) | ||
|
||
// NetClassInterface contains info from files in /sys/class/net/<interface> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The kernel documentation uses <iface>
, what about using the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makes sense. Omits the possible confusion, thanks!
sysfs/net_class.go
Outdated
) | ||
|
||
// NetClassInterface contains info from files in /sys/class/net/<interface> | ||
// for single interface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about adding for a single interface (iface).
in parenthesis, to emphasize that it's not about a language/golang interface. Please end all comment sentences with full stops (following the golang style guide).
sysfs/net_class.go
Outdated
} | ||
value := strings.TrimSpace(string(fileContents)) | ||
|
||
if fieldValue.Kind() == reflect.Uint64 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest to use a switch statement here and error handling in the default case, to ensure all value types are handled.
switch fieldValue.Kind() {
case reflect.Uint64:
// ...
default:
return nil, fmt.Errorf("unhandled type %q", fieldValue.Kind())
}
sysfs/net_class.go
Outdated
} | ||
|
||
// NetClass is collection of info for every interface in /sys/class/net. The map keys | ||
// are interface names. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably here again to make it clearer interface (iface)
.
@grobie Do you have any suggestions to replace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for your contribution, looks great!
My only remaining question is around error handling. We currently ignore all errors and silently omit the data or even the whole interface. As procfs/sysfs are low level packages, I'd expect errors returned to be in that case, so that the caller can decide how to handle such situations.
One common error case I can imagine are non-existent files in older kernel versions (for attributes which were only added later). For that case you could use os.IsNotExist(err)
and continue in that case without returning an error. For other file read errors or parser errors, I'd intuitively return an error. What do you think? Have you used the continue statements on purpose?
@SuperQ I often find static code easier to reason about then trying to abstract it away, my editor helps me with managing such problems. For a middleground between reflect usage and manually typed code I would look into |
@grobie Thanks for your thorough code review, I'm really glad for that! As for the error handling - yes I did use the When testing this on one of our debian machines, I got to a problem parsing the The other one is rather being cautious on my side, as I can imagine an interface dir not being accessible for some reason. In such case I don't think it is correct to return an error and ignoring all other ok ifaces, but that's really my point of view... |
@klatys We had a similar IO problem with |
In my experience strict error handling makes it easier to debug issues. I've witnessed many lengthy debug sessions due to silenced error swallowing. I'd suggest to handle the "file not exist" and "operation not supported" cases gracefully (and probably the EAGAIN error as well), but bubble up errors in all other cases. |
Ok, np. I implemented that and changed the error handling. besides the three file errors I had to add one other -
Hopefully there won't be any other errors for these "files". Also I ran this on several machines and discovered that some values (e.g. speed) can be negative (e.g. -1) thus the type is now Int64. |
* add net class parsing * change tag, rename var * fix coding style * fix data types for name_assign_type and phys_switch_id * code review fixes * code review fixes - clearer naming * better error handling
Related to prometheus/node_exporter#300