2018-07-16 15:08:18 +02:00
// Copyright 2018 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2021-10-03 13:35:24 +02:00
//go:build !nonetclass && linux
// +build !nonetclass,linux
2018-07-16 15:08:18 +02:00
package collector
import (
2021-03-03 08:57:16 +01:00
"errors"
2018-07-16 15:08:18 +02:00
"fmt"
2024-09-11 10:51:28 +02:00
"log/slog"
2023-05-02 15:25:05 +02:00
"net"
2021-03-03 08:57:16 +01:00
"os"
2018-07-16 15:08:18 +02:00
"regexp"
2024-04-19 17:15:54 +02:00
"sync"
2018-07-16 15:08:18 +02:00
2023-03-07 09:25:05 +01:00
"github.com/alecthomas/kingpin/v2"
2018-07-16 15:08:18 +02:00
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs/sysfs"
)
var (
netclassIgnoredDevices = kingpin . Flag ( "collector.netclass.ignored-devices" , "Regexp of net devices to ignore for netclass collector." ) . Default ( "^$" ) . String ( )
2021-03-04 10:32:45 +01:00
netclassInvalidSpeed = kingpin . Flag ( "collector.netclass.ignore-invalid-speed" , "Ignore devices where the speed is invalid. This will be the default behavior in 2.x." ) . Bool ( )
2022-11-29 11:22:25 +01:00
netclassNetlink = kingpin . Flag ( "collector.netclass.netlink" , "Use netlink to gather stats instead of /proc/net/dev." ) . Default ( "false" ) . Bool ( )
2018-07-16 15:08:18 +02:00
)
type netClassCollector struct {
2019-04-10 18:16:12 +02:00
fs sysfs . FS
2018-07-16 15:08:18 +02:00
subsystem string
ignoredDevicesPattern * regexp . Regexp
metricDescs map [ string ] * prometheus . Desc
2024-04-19 17:15:54 +02:00
metricDescsMu sync . Mutex
2024-09-11 10:51:28 +02:00
logger * slog . Logger
2018-07-16 15:08:18 +02:00
}
func init ( ) {
registerCollector ( "netclass" , defaultEnabled , NewNetClassCollector )
}
// NewNetClassCollector returns a new Collector exposing network class stats.
2024-09-11 10:51:28 +02:00
func NewNetClassCollector ( logger * slog . Logger ) ( Collector , error ) {
2019-04-10 18:16:12 +02:00
fs , err := sysfs . NewFS ( * sysPath )
if err != nil {
2019-11-29 14:51:31 +01:00
return nil , fmt . Errorf ( "failed to open sysfs: %w" , err )
2019-04-10 18:16:12 +02:00
}
2018-07-16 15:08:18 +02:00
pattern := regexp . MustCompile ( * netclassIgnoredDevices )
return & netClassCollector {
2019-04-10 18:16:12 +02:00
fs : fs ,
2018-07-16 15:08:18 +02:00
subsystem : "network" ,
ignoredDevicesPattern : pattern ,
metricDescs : map [ string ] * prometheus . Desc { } ,
2019-12-31 17:19:37 +01:00
logger : logger ,
2018-07-16 15:08:18 +02:00
} , nil
}
func ( c * netClassCollector ) Update ( ch chan <- prometheus . Metric ) error {
2022-11-29 11:22:25 +01:00
if * netclassNetlink {
return c . netClassRTNLUpdate ( ch )
}
return c . netClassSysfsUpdate ( ch )
}
func ( c * netClassCollector ) netClassSysfsUpdate ( ch chan <- prometheus . Metric ) error {
2019-04-10 18:16:12 +02:00
netClass , err := c . getNetClassInfo ( )
2018-07-16 15:08:18 +02:00
if err != nil {
2021-03-03 08:57:16 +01:00
if errors . Is ( err , os . ErrNotExist ) || errors . Is ( err , os . ErrPermission ) {
2024-09-11 10:51:28 +02:00
c . logger . Debug ( "Could not read netclass file" , "err" , err )
2021-03-03 08:57:16 +01:00
return ErrNoData
}
2020-06-15 22:27:14 +02:00
return fmt . Errorf ( "could not get net class info: %w" , err )
2018-07-16 15:08:18 +02:00
}
for _ , ifaceInfo := range netClass {
upDesc := prometheus . NewDesc (
prometheus . BuildFQName ( namespace , c . subsystem , "up" ) ,
2019-02-07 15:59:32 +01:00
"Value is 1 if operstate is 'up', 0 otherwise." ,
[ ] string { "device" } ,
2018-07-16 15:08:18 +02:00
nil ,
)
upValue := 0.0
if ifaceInfo . OperState == "up" {
upValue = 1.0
}
2019-02-07 15:59:32 +01:00
ch <- prometheus . MustNewConstMetric ( upDesc , prometheus . GaugeValue , upValue , ifaceInfo . Name )
infoDesc := prometheus . NewDesc (
prometheus . BuildFQName ( namespace , c . subsystem , "info" ) ,
"Non-numeric data from /sys/class/net/<iface>, value is always 1." ,
2023-05-02 15:25:05 +02:00
[ ] string { "device" , "address" , "broadcast" , "duplex" , "operstate" , "adminstate" , "ifalias" } ,
2019-02-07 15:59:32 +01:00
nil ,
)
infoValue := 1.0
2023-05-02 15:25:05 +02:00
ch <- prometheus . MustNewConstMetric ( infoDesc , prometheus . GaugeValue , infoValue , ifaceInfo . Name , ifaceInfo . Address , ifaceInfo . Broadcast , ifaceInfo . Duplex , ifaceInfo . OperState , getAdminState ( ifaceInfo . Flags ) , ifaceInfo . IfAlias )
2018-07-16 15:08:18 +02:00
2022-11-21 16:38:04 +01:00
pushMetric ( ch , c . getFieldDesc ( "address_assign_type" ) , "address_assign_type" , ifaceInfo . AddrAssignType , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "carrier" ) , "carrier" , ifaceInfo . Carrier , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "carrier_changes_total" ) , "carrier_changes_total" , ifaceInfo . CarrierChanges , prometheus . CounterValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "carrier_up_changes_total" ) , "carrier_up_changes_total" , ifaceInfo . CarrierUpCount , prometheus . CounterValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "carrier_down_changes_total" ) , "carrier_down_changes_total" , ifaceInfo . CarrierDownCount , prometheus . CounterValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "device_id" ) , "device_id" , ifaceInfo . DevID , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "dormant" ) , "dormant" , ifaceInfo . Dormant , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "flags" ) , "flags" , ifaceInfo . Flags , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "iface_id" ) , "iface_id" , ifaceInfo . IfIndex , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "iface_link" ) , "iface_link" , ifaceInfo . IfLink , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "iface_link_mode" ) , "iface_link_mode" , ifaceInfo . LinkMode , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "mtu_bytes" ) , "mtu_bytes" , ifaceInfo . MTU , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "name_assign_type" ) , "name_assign_type" , ifaceInfo . NameAssignType , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "net_dev_group" ) , "net_dev_group" , ifaceInfo . NetDevGroup , prometheus . GaugeValue , ifaceInfo . Name )
2018-07-16 15:08:18 +02:00
if ifaceInfo . Speed != nil {
2021-03-04 10:32:45 +01:00
// Some devices return -1 if the speed is unknown.
if * ifaceInfo . Speed >= 0 || ! * netclassInvalidSpeed {
speedBytes := int64 ( * ifaceInfo . Speed * 1000 * 1000 / 8 )
2022-11-21 16:38:04 +01:00
pushMetric ( ch , c . getFieldDesc ( "speed_bytes" ) , "speed_bytes" , speedBytes , prometheus . GaugeValue , ifaceInfo . Name )
2021-03-04 10:32:45 +01:00
}
2018-07-16 15:08:18 +02:00
}
2022-11-21 16:38:04 +01:00
pushMetric ( ch , c . getFieldDesc ( "transmit_queue_length" ) , "transmit_queue_length" , ifaceInfo . TxQueueLen , prometheus . GaugeValue , ifaceInfo . Name )
pushMetric ( ch , c . getFieldDesc ( "protocol_type" ) , "protocol_type" , ifaceInfo . Type , prometheus . GaugeValue , ifaceInfo . Name )
2018-07-16 15:08:18 +02:00
}
return nil
}
2022-11-21 16:38:04 +01:00
func ( c * netClassCollector ) getFieldDesc ( name string ) * prometheus . Desc {
2024-04-19 17:15:54 +02:00
c . metricDescsMu . Lock ( )
defer c . metricDescsMu . Unlock ( )
2022-11-21 16:38:04 +01:00
fieldDesc , exists := c . metricDescs [ name ]
if ! exists {
fieldDesc = prometheus . NewDesc (
prometheus . BuildFQName ( namespace , c . subsystem , name ) ,
2022-11-29 11:22:25 +01:00
fmt . Sprintf ( "Network device property: %s" , name ) ,
2022-11-21 16:38:04 +01:00
[ ] string { "device" } ,
nil ,
)
c . metricDescs [ name ] = fieldDesc
}
2018-07-16 15:08:18 +02:00
2022-11-21 16:38:04 +01:00
return fieldDesc
2018-07-16 15:08:18 +02:00
}
2019-04-10 18:16:12 +02:00
func ( c * netClassCollector ) getNetClassInfo ( ) ( sysfs . NetClass , error ) {
2021-04-19 16:21:06 +02:00
netClass := sysfs . NetClass { }
netDevices , err := c . fs . NetClassDevices ( )
2018-07-16 15:08:18 +02:00
if err != nil {
2021-03-03 08:57:16 +01:00
return netClass , err
2018-07-16 15:08:18 +02:00
}
2021-04-19 16:21:06 +02:00
for _ , device := range netDevices {
2019-04-10 18:16:12 +02:00
if c . ignoredDevicesPattern . MatchString ( device ) {
2021-04-19 16:21:06 +02:00
continue
2018-07-16 15:08:18 +02:00
}
2021-04-19 16:21:06 +02:00
interfaceClass , err := c . fs . NetClassByIface ( device )
if err != nil {
return netClass , err
}
netClass [ device ] = * interfaceClass
2018-07-16 15:08:18 +02:00
}
return netClass , nil
}
2023-05-02 15:25:05 +02:00
func getAdminState ( flags * int64 ) string {
if flags == nil {
return "unknown"
}
if * flags & int64 ( net . FlagUp ) == 1 {
return "up"
}
return "down"
}