@@ -20,12 +20,14 @@ import (
20
20
"errors"
21
21
"fmt"
22
22
"io/fs"
23
+ "path/filepath"
23
24
24
25
"github.com/alecthomas/kingpin/v2"
25
26
"github.com/go-kit/log/level"
26
27
"github.com/jsimonetti/rtnetlink"
27
28
"github.com/mdlayher/ethtool"
28
29
"github.com/prometheus/client_golang/prometheus"
30
+ "github.com/prometheus/procfs/sysfs"
29
31
)
30
32
31
33
var (
@@ -57,14 +59,27 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
57
59
}
58
60
}
59
61
62
+ // Get most attributes from Netlink
60
63
lMsgs , err := c .getNetClassInfoRTNL ()
61
64
if err != nil {
62
65
return fmt .Errorf ("could not get net class info: %w" , err )
63
66
}
67
+
68
+ relevantLinks := make ([]rtnetlink.LinkMessage , 0 , len (lMsgs ))
64
69
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 )
67
72
}
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 {
68
83
upDesc := prometheus .NewDesc (
69
84
prometheus .BuildFQName (namespace , c .subsystem , "up" ),
70
85
"Value is 1 if operstate is 'up', 0 otherwise." ,
@@ -96,12 +111,16 @@ func (c *netClassCollector) netClassRTNLUpdate(ch chan<- prometheus.Metric) erro
96
111
duplex = lm .Duplex .String ()
97
112
}
98
113
114
+ ifaceInfo := sysfsAttrs [msg .Attributes .Name ]
115
+
99
116
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 )
100
117
118
+ pushMetric (ch , c .getFieldDesc ("address_assign_type" ), "address_assign_type" , ifaceInfo .AddrAssignType , prometheus .GaugeValue , msg .Attributes .Name )
101
119
pushMetric (ch , c .getFieldDesc ("carrier" ), "carrier" , msg .Attributes .Carrier , prometheus .GaugeValue , msg .Attributes .Name )
102
120
pushMetric (ch , c .getFieldDesc ("carrier_changes_total" ), "carrier_changes_total" , msg .Attributes .CarrierChanges , prometheus .CounterValue , msg .Attributes .Name )
103
121
pushMetric (ch , c .getFieldDesc ("carrier_up_changes_total" ), "carrier_up_changes_total" , msg .Attributes .CarrierUpCount , prometheus .CounterValue , msg .Attributes .Name )
104
122
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 )
105
124
pushMetric (ch , c .getFieldDesc ("flags" ), "flags" , msg .Flags , prometheus .GaugeValue , msg .Attributes .Name )
106
125
pushMetric (ch , c .getFieldDesc ("iface_id" ), "iface_id" , msg .Index , prometheus .GaugeValue , msg .Attributes .Name )
107
126
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
117
136
}
118
137
119
138
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 )
120
140
pushMetric (ch , c .getFieldDesc ("net_dev_group" ), "net_dev_group" , msg .Attributes .NetDevGroup , prometheus .GaugeValue , msg .Attributes .Name )
121
141
pushMetric (ch , c .getFieldDesc ("transmit_queue_length" ), "transmit_queue_length" , msg .Attributes .TxQueueLen , prometheus .GaugeValue , msg .Attributes .Name )
122
142
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) {
186
206
187
207
return lms , err
188
208
}
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