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 }