bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/ifstat_linux.go (about)

     1  package collectors
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"path/filepath"
     8  	"regexp"
     9  	"strings"
    10  	"time"
    11  
    12  	"bosun.org/cmd/scollector/conf"
    13  	"bosun.org/metadata"
    14  	"bosun.org/opentsdb"
    15  	"bosun.org/util"
    16  )
    17  
    18  func init() {
    19  	registerInit(func(c *conf.Conf) {
    20  		if c.IfaceExpr != "" {
    21  			ifstatRE = regexp.MustCompile(fmt.Sprintf("(%s):(.*)", c.IfaceExpr))
    22  		}
    23  
    24  		collectors = append(collectors, &IntervalCollector{F: c_ifstat_linux})
    25  		collectors = append(collectors, &IntervalCollector{F: c_ipcount_linux})
    26  		collectors = append(collectors, &IntervalCollector{F: c_if_team_linux})
    27  		collectors = append(collectors, &IntervalCollector{F: c_if_bond_linux})
    28  	})
    29  }
    30  
    31  var netFields = []struct {
    32  	key  string
    33  	rate metadata.RateType
    34  	unit metadata.Unit
    35  }{
    36  	{"bytes", metadata.Counter, metadata.Bytes},
    37  	{"packets", metadata.Counter, metadata.Count},
    38  	{"errs", metadata.Counter, metadata.Count},
    39  	{"dropped", metadata.Counter, metadata.Count},
    40  	{"fifo.errs", metadata.Counter, metadata.Count},
    41  	{"frame.errs", metadata.Counter, metadata.Count},
    42  	{"compressed", metadata.Counter, metadata.Count},
    43  	{"multicast", metadata.Counter, metadata.Count},
    44  	{"bytes", metadata.Counter, metadata.Bytes},
    45  	{"packets", metadata.Counter, metadata.Count},
    46  	{"errs", metadata.Counter, metadata.Count},
    47  	{"dropped", metadata.Counter, metadata.Count},
    48  	{"fifo.errs", metadata.Counter, metadata.Count},
    49  	{"collisions", metadata.Counter, metadata.Count},
    50  	{"carrier.errs", metadata.Counter, metadata.Count},
    51  	{"compressed", metadata.Counter, metadata.Count},
    52  }
    53  
    54  var ifstatRE = regexp.MustCompile(`\s*(eth\d+|em\d+_\d+/\d+|em\d+_\d+|em\d+|` +
    55  	`bond\d+|team\d+|` +
    56  	`p\d+p\d+_\d+/\d+|p\d+p\d+_\d+|p\d+p\d+|` +
    57  	`en[[:alnum:]]+|wl[[:alnum:]]+|ww[[:alnum:]]+` + // Systemd predictable interface names
    58  	`):(.*)`)
    59  
    60  func c_ipcount_linux() (opentsdb.MultiDataPoint, error) {
    61  	var md opentsdb.MultiDataPoint
    62  	v4c := 0
    63  	v6c := 0
    64  	err := util.ReadCommand(func(line string) error {
    65  		tl := strings.TrimSpace(line)
    66  		if strings.HasPrefix(tl, "inet ") {
    67  			v4c++
    68  		}
    69  		if strings.HasPrefix(tl, "inet6 ") {
    70  			v6c++
    71  		}
    72  		return nil
    73  	}, "ip", "addr", "list")
    74  	if err != nil {
    75  		return md, err
    76  	}
    77  	Add(&md, "linux.net.ip_count", v4c, opentsdb.TagSet{"version": "4"}, metadata.Gauge, "IP_Addresses", "")
    78  	Add(&md, "linux.net.ip_count", v6c, opentsdb.TagSet{"version": "6"}, metadata.Gauge, "IP_Addresses", "")
    79  	return md, nil
    80  }
    81  
    82  func c_ifstat_linux() (opentsdb.MultiDataPoint, error) {
    83  	var md opentsdb.MultiDataPoint
    84  	direction := func(i int) string {
    85  		if i >= 8 {
    86  			return "out"
    87  		} else {
    88  			return "in"
    89  		}
    90  	}
    91  	err := readLine("/proc/net/dev", func(s string) error {
    92  		m := ifstatRE.FindStringSubmatch(s)
    93  		if m == nil {
    94  			return nil
    95  		}
    96  		intf := m[1]
    97  		stats := strings.Fields(m[2])
    98  		tags := opentsdb.TagSet{"iface": intf}
    99  		var bond_string string
   100  		if strings.HasPrefix(intf, "bond") || strings.HasPrefix(intf, "team") {
   101  			bond_string = "bond."
   102  		}
   103  		// Detect speed of the interface in question
   104  		_ = readLine("/sys/class/net/"+intf+"/speed", func(speed string) error {
   105  			Add(&md, "linux.net."+bond_string+"ifspeed", speed, tags, metadata.Gauge, metadata.Megabit, "")
   106  			Add(&md, "os.net."+bond_string+"ifspeed", speed, tags, metadata.Gauge, metadata.Megabit, "")
   107  			return nil
   108  		})
   109  		for i, v := range stats {
   110  			Add(&md, "linux.net."+bond_string+strings.Replace(netFields[i].key, ".", "_", -1), v, opentsdb.TagSet{
   111  				"iface":     intf,
   112  				"direction": direction(i),
   113  			}, netFields[i].rate, netFields[i].unit, "")
   114  			if i < 4 || (i >= 8 && i < 12) {
   115  				Add(&md, "os.net."+bond_string+strings.Replace(netFields[i].key, ".", "_", -1), v, opentsdb.TagSet{
   116  					"iface":     intf,
   117  					"direction": direction(i),
   118  				}, netFields[i].rate, netFields[i].unit, "")
   119  
   120  			}
   121  		}
   122  		return nil
   123  	})
   124  	return md, err
   125  }
   126  
   127  const (
   128  	linuxNetBondSlaveIsUpDesc = "The status of the bonded or teamed interface."
   129  	linuxNetBondSlaveCount    = "The number of slaves on the bonded or teamed interface."
   130  )
   131  
   132  func c_if_bond_linux() (opentsdb.MultiDataPoint, error) {
   133  	var md opentsdb.MultiDataPoint
   134  	const bondingPath = "/proc/net/bonding"
   135  	bondDevices, err := ioutil.ReadDir(bondingPath)
   136  	if err != nil {
   137  		return md, nil
   138  	}
   139  	for _, fi := range bondDevices {
   140  		var iface string
   141  		var slaveCount int
   142  		if err := readLine(filepath.Join(bondingPath, fi.Name()), func(s string) error {
   143  			f := strings.SplitN(s, ":", 2)
   144  			if len(f) != 2 {
   145  				return nil
   146  			}
   147  			f[0] = strings.TrimSpace(f[0])
   148  			f[1] = strings.TrimSpace(f[1])
   149  			if f[0] == "Slave Interface" {
   150  				iface = f[1]
   151  				slaveCount++
   152  			}
   153  			// TODO: This will probably need to be updated for other types of bonding beside LACP, but I have no examples available to work with at the moment
   154  			if f[0] == "MII Status" && iface != "" {
   155  				var status int
   156  				if f[1] == "up" {
   157  					status = 1
   158  				}
   159  				Add(&md, "linux.net.bond.slave.is_up", status, opentsdb.TagSet{"slave": iface, "bond": fi.Name()}, metadata.Gauge, metadata.Bool, linuxNetBondSlaveIsUpDesc)
   160  			}
   161  			return nil
   162  		}); err != nil {
   163  			return md, err
   164  		}
   165  		Add(&md, "linux.net.bond.slave.count", slaveCount, opentsdb.TagSet{"bond": fi.Name()}, metadata.Gauge, metadata.Count, linuxNetBondSlaveCount)
   166  	}
   167  	return md, nil
   168  }
   169  
   170  func c_if_team_linux() (opentsdb.MultiDataPoint, error) {
   171  	var md opentsdb.MultiDataPoint
   172  	getState := func(iname string) (TeamState, error) {
   173  		var ts TeamState
   174  		reader, err := util.Command(time.Second*5, nil, "teamdctl", iname, "state", "dump")
   175  		if err != nil {
   176  			return ts, err
   177  		}
   178  		err = json.NewDecoder(reader).Decode(&ts)
   179  		if err != nil {
   180  			return ts, err
   181  		}
   182  		return ts, nil
   183  	}
   184  	teamdFiles, err := ioutil.ReadDir("/var/run/teamd")
   185  	if err != nil {
   186  		return md, nil
   187  	}
   188  	for _, f := range teamdFiles {
   189  		name := f.Name()
   190  		if strings.HasSuffix(name, ".pid") {
   191  			name = strings.TrimSuffix(name, ".pid")
   192  			ts, err := getState(name)
   193  			if err != nil {
   194  				return md, err
   195  			}
   196  			var slaveCount int
   197  			var speed int64
   198  			for portName, port := range ts.TeamPorts {
   199  				slaveCount++
   200  				speed += int64(port.Link.Speed)
   201  				metadata.AddMeta("", opentsdb.TagSet{"iface": portName}, "master", name, true)
   202  				Add(&md, "linux.net.bond.slave.is_up", port.Link.Up, opentsdb.TagSet{"slave": portName, "bond": name}, metadata.Gauge, metadata.Bool, linuxNetBondSlaveIsUpDesc)
   203  			}
   204  			Add(&md, "os.net.bond.ifspeed", speed, opentsdb.TagSet{"bond": name}, metadata.Gauge, metadata.Megabit, osNetIfSpeedDesc)
   205  			Add(&md, "linux.net.bond.slave.count", slaveCount, opentsdb.TagSet{"bond": name}, metadata.Gauge, metadata.Count, linuxNetBondSlaveCount)
   206  		}
   207  	}
   208  	return md, nil
   209  }
   210  
   211  type TeamState struct {
   212  	TeamPorts map[string]TeamPort `json:"ports"`
   213  	Runner    struct {
   214  		Active       bool    `json:"active"`
   215  		FastRate     bool    `json:"fast_rate"`
   216  		SelectPolicy string  `json:"select_policy"`
   217  		SysPrio      float64 `json:"sys_prio"`
   218  	} `json:"runner"`
   219  	Setup struct {
   220  		Daemonized         bool    `json:"daemonized"`
   221  		DbusEnabled        bool    `json:"dbus_enabled"`
   222  		DebugLevel         float64 `json:"debug_level"`
   223  		KernelTeamModeName string  `json:"kernel_team_mode_name"`
   224  		Pid                float64 `json:"pid"`
   225  		PidFile            string  `json:"pid_file"`
   226  		RunnerName         string  `json:"runner_name"`
   227  		ZmqEnabled         bool    `json:"zmq_enabled"`
   228  	} `json:"setup"`
   229  	TeamDevice struct {
   230  		Ifinfo struct {
   231  			DevAddr    string  `json:"dev_addr"`
   232  			DevAddrLen float64 `json:"dev_addr_len"`
   233  			Ifindex    float64 `json:"ifindex"`
   234  			Ifname     string  `json:"ifname"`
   235  		} `json:"ifinfo"`
   236  	} `json:"team_device"`
   237  }
   238  
   239  type TeamPort struct {
   240  	Ifinfo struct {
   241  		DevAddr    string  `json:"dev_addr"`
   242  		DevAddrLen float64 `json:"dev_addr_len"`
   243  		Ifindex    float64 `json:"ifindex"`
   244  		Ifname     string  `json:"ifname"`
   245  	}
   246  	Link struct {
   247  		Duplex string  `json:"duplex"`
   248  		Speed  float64 `json:"speed"`
   249  		Up     bool    `json:"up"`
   250  	} `json:"link"`
   251  	LinkWatches struct {
   252  		List struct {
   253  			LinkWatch0 struct {
   254  				DelayDown float64 `json:"delay_down"`
   255  				DelayUp   float64 `json:"delay_up"`
   256  				Name      string  `json:"name"`
   257  				Up        bool    `json:"up"`
   258  			} `json:"link_watch_0"`
   259  		} `json:"list"`
   260  		Up bool `json:"up"`
   261  	} `json:"link_watches"`
   262  	Runner struct {
   263  		ActorLacpduInfo struct {
   264  			Key            float64 `json:"key"`
   265  			Port           float64 `json:"port"`
   266  			PortPriority   float64 `json:"port_priority"`
   267  			State          float64 `json:"state"`
   268  			System         string  `json:"system"`
   269  			SystemPriority float64 `json:"system_priority"`
   270  		} `json:"actor_lacpdu_info"`
   271  		Aggregator struct {
   272  			ID       float64 `json:"id"`
   273  			Selected bool    `json:"selected"`
   274  		} `json:"aggregator"`
   275  		Key               float64 `json:"key"`
   276  		PartnerLacpduInfo struct {
   277  			Key            float64 `json:"key"`
   278  			Port           float64 `json:"port"`
   279  			PortPriority   float64 `json:"port_priority"`
   280  			State          float64 `json:"state"`
   281  			System         string  `json:"system"`
   282  			SystemPriority float64 `json:"system_priority"`
   283  		} `json:"partner_lacpdu_info"`
   284  		Prio     float64 `json:"prio"`
   285  		Selected bool    `json:"selected"`
   286  		State    string  `json:"state"`
   287  	} `json:"runner"`
   288  }