@@ -20,12 +20,14 @@ import (
2020 "errors"
2121 "fmt"
2222 "io/fs"
23+ "path/filepath"
2324
2425 "github.com/alecthomas/kingpin/v2"
2526 "github.com/go-kit/log/level"
2627 "github.com/jsimonetti/rtnetlink"
2728 "github.com/mdlayher/ethtool"
2829 "github.com/prometheus/client_golang/prometheus"
30+ "github.com/prometheus/procfs/sysfs"
2931)
3032
3133var (
@@ -57,14 +59,27 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
5759 }
5860 }
5961
62+ // Get most attributes from Netlink
6063 lMsgs , err := c .getNetClassInfoRTNL ()
6164 if err != nil {
6265 return fmt .Errorf ("could not get net class info: %w" , err )
6366 }
67+
68+ relevantLinks := make ([]rtnetlink.LinkMessage , 0 , len (lMsgs ))
6469 for _ , msg := range lMsgs {
65- if c .ignoredDevicesPattern .MatchString (msg .Attributes .Name ) {
66- continue
70+ if ! c .ignoredDevicesPattern .MatchString (msg .Attributes .Name ) {
71+ relevantLinks = append ( relevantLinks , msg )
6772 }
73+ }
74+
75+ // Read sysfs for attributes that Netlink doesn't expose
76+ sysfsAttrs , err := getSysfsAttributes (relevantLinks )
77+ if err != nil {
78+ return fmt .Errorf ("could not get sysfs device info: %w" , err )
79+ }
80+
81+ // Parse all the info and update metrics
82+ for _ , msg := range relevantLinks {
6883 upDesc := prometheus .NewDesc (
6984 prometheus .BuildFQName (namespace , c .subsystem , "up" ),
7085 "Value is 1 if operstate is 'up', 0 otherwise." ,
@@ -96,12 +111,16 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
96111 duplex = lm .Duplex .String ()
97112 }
98113
114+ ifaceInfo := sysfsAttrs [msg .Attributes .Name ]
115+
99116 ch <- prometheus .MustNewConstMetric (infoDesc , prometheus .GaugeValue , infoValue , msg .Attributes .Name , msg .Attributes .Address .String (), msg .Attributes .Broadcast .String (), duplex , operstateStr [int (msg .Attributes .OperationalState )], ifalias )
100117
118+ pushMetric (ch , c .getFieldDesc ("address_assign_type" ), "address_assign_type" , ifaceInfo .AddrAssignType , prometheus .GaugeValue , msg .Attributes .Name )
101119 pushMetric (ch , c .getFieldDesc ("carrier" ), "carrier" , msg .Attributes .Carrier , prometheus .GaugeValue , msg .Attributes .Name )
102120 pushMetric (ch , c .getFieldDesc ("carrier_changes_total" ), "carrier_changes_total" , msg .Attributes .CarrierChanges , prometheus .CounterValue , msg .Attributes .Name )
103121 pushMetric (ch , c .getFieldDesc ("carrier_up_changes_total" ), "carrier_up_changes_total" , msg .Attributes .CarrierUpCount , prometheus .CounterValue , msg .Attributes .Name )
104122 pushMetric (ch , c .getFieldDesc ("carrier_down_changes_total" ), "carrier_down_changes_total" , msg .Attributes .CarrierDownCount , prometheus .CounterValue , msg .Attributes .Name )
123+ pushMetric (ch , c .getFieldDesc ("device_id" ), "device_id" , ifaceInfo .DevID , prometheus .GaugeValue , msg .Attributes .Name )
105124 pushMetric (ch , c .getFieldDesc ("flags" ), "flags" , msg .Flags , prometheus .GaugeValue , msg .Attributes .Name )
106125 pushMetric (ch , c .getFieldDesc ("iface_id" ), "iface_id" , msg .Index , prometheus .GaugeValue , msg .Attributes .Name )
107126 pushMetric (ch , c .getFieldDesc ("iface_link_mode" ), "iface_link_mode" , msg .Attributes .LinkMode , prometheus .GaugeValue , msg .Attributes .Name )
@@ -117,6 +136,7 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
117136 }
118137
119138 pushMetric (ch , c .getFieldDesc ("mtu_bytes" ), "mtu_bytes" , msg .Attributes .MTU , prometheus .GaugeValue , msg .Attributes .Name )
139+ pushMetric (ch , c .getFieldDesc ("name_assign_type" ), "name_assign_type" , ifaceInfo .NameAssignType , prometheus .GaugeValue , msg .Attributes .Name )
120140 pushMetric (ch , c .getFieldDesc ("net_dev_group" ), "net_dev_group" , msg .Attributes .NetDevGroup , prometheus .GaugeValue , msg .Attributes .Name )
121141 pushMetric (ch , c .getFieldDesc ("transmit_queue_length" ), "transmit_queue_length" , msg .Attributes .TxQueueLen , prometheus .GaugeValue , msg .Attributes .Name )
122142 pushMetric (ch , c .getFieldDesc ("protocol_type" ), "protocol_type" , msg .Type , prometheus .GaugeValue , msg .Attributes .Name )
@@ -186,3 +206,25 @@ func (c *netClassCollector) getLinkModes() ([]*ethtool.LinkMode, error) {
186206
187207 return lms , err
188208}
209+
210+ // getSysfsAttributes reads attributes that are absent from netlink but provided
211+ // by sysfs.
212+ func getSysfsAttributes (links []rtnetlink.LinkMessage ) (sysfs.NetClass , error ) {
213+ netClass := sysfs.NetClass {}
214+ for _ , msg := range links {
215+ interfaceClass := sysfs.NetClassIface {}
216+ ifName := msg .Attributes .Name
217+ devPath := filepath .Join ("/sys" , "class" , "net" , ifName )
218+
219+ // These three attributes hold a device-specific lock when
220+ // accessed, not the RTNL lock, so they are much less impactful
221+ // than reading most of the other attributes from sysfs.
222+ for _ , attr := range []string {"addr_assign_type" , "dev_id" , "name_assign_type" } {
223+ if err := sysfs .ParseNetClassAttribute (devPath , attr , & interfaceClass ); err != nil {
224+ return nil , err
225+ }
226+ }
227+ netClass [ifName ] = interfaceClass
228+ }
229+ return netClass , nil
230+ }
0 commit comments