2015-09-26 08:36:40 -07:00
// Copyright 2015 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 04:35:24 -07:00
//go:build !noipvs
2015-05-05 05:49:35 -07:00
// +build !noipvs
package collector
import (
2020-06-15 13:27:14 -07:00
"errors"
2015-05-05 05:49:35 -07:00
"fmt"
2024-09-11 01:51:28 -07:00
"log/slog"
2017-07-26 06:20:28 -07:00
"os"
2020-06-02 01:52:00 -07:00
"sort"
2015-05-05 05:49:35 -07:00
"strconv"
2020-06-02 01:52:00 -07:00
"strings"
2015-05-05 05:49:35 -07:00
2023-03-07 00:25:05 -08:00
"github.com/alecthomas/kingpin/v2"
2015-05-05 05:49:35 -07:00
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/procfs"
)
type ipvsCollector struct {
Collector
fs procfs . FS
2020-06-02 01:52:00 -07:00
backendLabels [ ] string
2016-12-28 06:21:31 -08:00
backendConnectionsActive , backendConnectionsInact , backendWeight typedDesc
connections , incomingPackets , outgoingPackets , incomingBytes , outgoingBytes typedDesc
2024-09-11 01:51:28 -07:00
logger * slog . Logger
2015-05-05 05:49:35 -07:00
}
2020-06-02 01:52:00 -07:00
type ipvsBackendStatus struct {
ActiveConn uint64
InactConn uint64
Weight uint64
}
const (
ipvsLabelLocalAddress = "local_address"
ipvsLabelLocalPort = "local_port"
ipvsLabelRemoteAddress = "remote_address"
ipvsLabelRemotePort = "remote_port"
ipvsLabelProto = "proto"
ipvsLabelLocalMark = "local_mark"
)
var (
fullIpvsBackendLabels = [ ] string {
ipvsLabelLocalAddress ,
ipvsLabelLocalPort ,
ipvsLabelRemoteAddress ,
ipvsLabelRemotePort ,
ipvsLabelProto ,
ipvsLabelLocalMark ,
}
ipvsLabels = kingpin . Flag ( "collector.ipvs.backend-labels" , "Comma separated list for IPVS backend stats labels." ) . Default ( strings . Join ( fullIpvsBackendLabels , "," ) ) . String ( )
)
2015-05-05 05:49:35 -07:00
func init ( ) {
2017-09-28 06:06:26 -07:00
registerCollector ( "ipvs" , defaultEnabled , NewIPVSCollector )
2015-05-05 05:49:35 -07:00
}
// NewIPVSCollector sets up a new collector for IPVS metrics. It accepts the
// "procfs" config parameter to override the default proc location (/proc).
2024-09-11 01:51:28 -07:00
func NewIPVSCollector ( logger * slog . Logger ) ( Collector , error ) {
2019-12-31 08:19:37 -08:00
return newIPVSCollector ( logger )
2015-05-05 05:49:35 -07:00
}
2024-09-11 01:51:28 -07:00
func newIPVSCollector ( logger * slog . Logger ) ( * ipvsCollector , error ) {
2015-05-05 05:49:35 -07:00
var (
c ipvsCollector
err error
2015-05-20 11:04:49 -07:00
subsystem = "ipvs"
2015-05-05 05:49:35 -07:00
)
2020-06-02 01:52:00 -07:00
if c . backendLabels , err = c . parseIpvsLabels ( * ipvsLabels ) ; err != nil {
return nil , err
}
2019-12-31 08:19:37 -08:00
c . logger = logger
2015-09-26 05:53:46 -07:00
c . fs , err = procfs . NewFS ( * procPath )
2015-05-05 05:49:35 -07:00
if err != nil {
2019-11-29 05:51:31 -08:00
return nil , fmt . Errorf ( "failed to open procfs: %w" , err )
2015-05-05 05:49:35 -07:00
}
2016-12-28 06:21:31 -08:00
c . connections = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "connections_total" ) ,
2016-12-28 06:21:31 -08:00
"The total number of connections made." ,
nil , nil ,
) , prometheus . CounterValue }
c . incomingPackets = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "incoming_packets_total" ) ,
2016-12-28 06:21:31 -08:00
"The total number of incoming packets." ,
nil , nil ,
) , prometheus . CounterValue }
c . outgoingPackets = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "outgoing_packets_total" ) ,
2016-12-28 06:21:31 -08:00
"The total number of outgoing packets." ,
nil , nil ,
) , prometheus . CounterValue }
c . incomingBytes = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "incoming_bytes_total" ) ,
2016-12-28 06:21:31 -08:00
"The total amount of incoming data." ,
nil , nil ,
) , prometheus . CounterValue }
c . outgoingBytes = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "outgoing_bytes_total" ) ,
2016-12-28 06:21:31 -08:00
"The total amount of outgoing data." ,
nil , nil ,
) , prometheus . CounterValue }
c . backendConnectionsActive = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "backend_connections_active" ) ,
2016-12-28 06:21:31 -08:00
"The current active connections by local and remote address." ,
2020-06-02 01:52:00 -07:00
c . backendLabels , nil ,
2016-12-28 06:21:31 -08:00
) , prometheus . GaugeValue }
c . backendConnectionsInact = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "backend_connections_inactive" ) ,
2016-12-28 06:21:31 -08:00
"The current inactive connections by local and remote address." ,
2020-06-02 01:52:00 -07:00
c . backendLabels , nil ,
2016-12-28 06:21:31 -08:00
) , prometheus . GaugeValue }
c . backendWeight = typedDesc { prometheus . NewDesc (
2017-09-28 06:06:26 -07:00
prometheus . BuildFQName ( namespace , subsystem , "backend_weight" ) ,
2016-12-28 06:21:31 -08:00
"The current backend weight by local and remote address." ,
2020-06-02 01:52:00 -07:00
c . backendLabels , nil ,
2016-12-28 06:21:31 -08:00
) , prometheus . GaugeValue }
2015-05-05 05:49:35 -07:00
return & c , nil
}
func ( c * ipvsCollector ) Update ( ch chan <- prometheus . Metric ) error {
2019-06-12 11:47:16 -07:00
ipvsStats , err := c . fs . IPVSStats ( )
2015-05-05 05:49:35 -07:00
if err != nil {
2017-07-26 06:20:28 -07:00
// Cannot access ipvs metrics, report no error.
2020-06-15 13:27:14 -07:00
if errors . Is ( err , os . ErrNotExist ) {
2024-09-11 01:51:28 -07:00
c . logger . Debug ( "ipvs collector metrics are not available for this system" )
2020-02-19 07:11:29 -08:00
return ErrNoData
2017-07-26 06:20:28 -07:00
}
2020-06-15 13:27:14 -07:00
return fmt . Errorf ( "could not get IPVS stats: %w" , err )
2015-05-05 05:49:35 -07:00
}
2016-12-28 06:21:31 -08:00
ch <- c . connections . mustNewConstMetric ( float64 ( ipvsStats . Connections ) )
ch <- c . incomingPackets . mustNewConstMetric ( float64 ( ipvsStats . IncomingPackets ) )
ch <- c . outgoingPackets . mustNewConstMetric ( float64 ( ipvsStats . OutgoingPackets ) )
ch <- c . incomingBytes . mustNewConstMetric ( float64 ( ipvsStats . IncomingBytes ) )
ch <- c . outgoingBytes . mustNewConstMetric ( float64 ( ipvsStats . OutgoingBytes ) )
2015-05-05 05:49:35 -07:00
2019-06-12 11:47:16 -07:00
backendStats , err := c . fs . IPVSBackendStatus ( )
2015-05-05 05:49:35 -07:00
if err != nil {
2020-06-15 13:27:14 -07:00
return fmt . Errorf ( "could not get backend status: %w" , err )
2015-05-05 05:49:35 -07:00
}
2020-06-02 01:52:00 -07:00
sums := map [ string ] ipvsBackendStatus { }
labelValues := map [ string ] [ ] string { }
2015-05-05 05:49:35 -07:00
for _ , backend := range backendStats {
2019-08-27 05:24:11 -07:00
localAddress := ""
if backend . LocalAddress . String ( ) != "<nil>" {
localAddress = backend . LocalAddress . String ( )
}
2020-06-02 01:52:00 -07:00
kv := make ( [ ] string , len ( c . backendLabels ) )
for i , label := range c . backendLabels {
var labelValue string
switch label {
case ipvsLabelLocalAddress :
labelValue = localAddress
case ipvsLabelLocalPort :
labelValue = strconv . FormatUint ( uint64 ( backend . LocalPort ) , 10 )
case ipvsLabelRemoteAddress :
labelValue = backend . RemoteAddress . String ( )
case ipvsLabelRemotePort :
labelValue = strconv . FormatUint ( uint64 ( backend . RemotePort ) , 10 )
case ipvsLabelProto :
labelValue = backend . Proto
case ipvsLabelLocalMark :
labelValue = backend . LocalMark
}
kv [ i ] = labelValue
2015-05-05 05:49:35 -07:00
}
2020-06-02 01:52:00 -07:00
key := strings . Join ( kv , "-" )
status := sums [ key ]
status . ActiveConn += backend . ActiveConn
status . InactConn += backend . InactConn
status . Weight += backend . Weight
sums [ key ] = status
labelValues [ key ] = kv
}
for key , status := range sums {
kv := labelValues [ key ]
ch <- c . backendConnectionsActive . mustNewConstMetric ( float64 ( status . ActiveConn ) , kv ... )
ch <- c . backendConnectionsInact . mustNewConstMetric ( float64 ( status . InactConn ) , kv ... )
ch <- c . backendWeight . mustNewConstMetric ( float64 ( status . Weight ) , kv ... )
2015-05-05 05:49:35 -07:00
}
return nil
}
2020-06-02 01:52:00 -07:00
func ( c * ipvsCollector ) parseIpvsLabels ( labelString string ) ( [ ] string , error ) {
labels := strings . Split ( labelString , "," )
labelSet := make ( map [ string ] bool , len ( labels ) )
results := make ( [ ] string , 0 , len ( labels ) )
for _ , label := range labels {
if label != "" {
labelSet [ label ] = true
}
}
for _ , label := range fullIpvsBackendLabels {
if labelSet [ label ] {
results = append ( results , label )
}
delete ( labelSet , label )
}
if len ( labelSet ) > 0 {
keys := make ( [ ] string , 0 , len ( labelSet ) )
for label := range labelSet {
keys = append ( keys , label )
}
sort . Strings ( keys )
return nil , fmt . Errorf ( "unknown IPVS backend labels: %q" , strings . Join ( keys , ", " ) )
}
return results , nil
}