diff --git a/Godeps/_workspace/src/github.com/marc-barry/goprocinfo/linux/snmp.go b/Godeps/_workspace/src/github.com/marc-barry/goprocinfo/linux/snmp.go new file mode 100644 index 0000000..6fffc09 --- /dev/null +++ b/Godeps/_workspace/src/github.com/marc-barry/goprocinfo/linux/snmp.go @@ -0,0 +1,144 @@ +package linux + +import ( + "io/ioutil" + "reflect" + "strconv" + "strings" +) + +type Snmp struct { + // Ip + IpForwarding uint64 `json:"ip_forwarding"` + IpDefaultTTL uint64 `json:"ip_default_ttl"` + IpInReceives uint64 `json:"ip_in_receives"` + IpInHdrErrors uint64 `json:"ip_in_hdr_errors"` + IpInAddrErrors uint64 `json:"ip_in_addr_errors"` + IpForwDatagrams uint64 `json:"ip_forw_datagrams"` + IpInUnknownProtos uint64 `json:"ip_in_unknown_protos"` + IpInDiscards uint64 `json:"ip_in_discards"` + IpInDelivers uint64 `json:"ip_in_delivers"` + IpOutRequests uint64 `json:"ip_out_requests"` + IpOutDiscards uint64 `json:"ip_out_discards"` + IpOutNoRoutes uint64 `json:"ip_out_no_routes"` + IpReasmTimeout uint64 `json:"ip_reasm_timeout"` + IpReasmReqds uint64 `json:"ip_reasm_reqds"` + IpReasmOKs uint64 `json:"ip_reasm_oks"` + IpReasmFails uint64 `json:"ip_reasm_fails"` + IpFragOKs uint64 `json:"ip_frag_oks"` + IpFragFails uint64 `json:"ip_frag_fails"` + IpFragCreates uint64 `json:"ip_frag_creates"` + // Icmp + IcmpInMsgs uint64 `json:"icmp_in_msgs"` + IcmpInErrors uint64 `json:"icmp_in_errors"` + IcmpInCsumErrors uint64 `json:"icmp_in_csum_errors"` + IcmpInDestUnreachs uint64 `json:"icmp_in_dest_unreachs"` + IcmpInTimeExcds uint64 `json:"icmp_in_time_excds"` + IcmpInParmProbs uint64 `json:"icmp_in_parm_probs"` + IcmpInSrcQuenchs uint64 `json:"icmp_in_src_quenchs"` + IcmpInRedirects uint64 `json:"icmp_in_redirects"` + IcmpInEchos uint64 `json:"icmp_in_echos"` + IcmpInEchoReps uint64 `json:"icmp_in_echo_reps"` + IcmpInTimestamps uint64 `json:"icmp_in_timestamps"` + IcmpInTimestampReps uint64 `json:"icmp_in_timestamp_reps"` + IcmpInAddrMasks uint64 `json:"icmp_in_addr_masks"` + IcmpInAddrMaskReps uint64 `json:"icmp_in_addr_mask_reps"` + IcmpOutMsgs uint64 `json:"icmp_out_msgs"` + IcmpOutErrors uint64 `json:"icmp_out_errors"` + IcmpOutDestUnreachs uint64 `json:"icmp_out_dest_unreachs"` + IcmpOutTimeExcds uint64 `json:"icmp_out_time_excds"` + IcmpOutParmProbs uint64 `json:"icmp_out_parm_probs"` + IcmpOutSrcQuenchs uint64 `json:"icmp_out_src_quenchs"` + IcmpOutRedirects uint64 `json:"icmp_out_redirects"` + IcmpOutEchos uint64 `json:"icmp_out_echos"` + IcmpOutEchoReps uint64 `json:"icmp_out_echo_reps"` + IcmpOutTimestamps uint64 `json:"icmp_out_timestamps"` + IcmpOutTimestampReps uint64 `json:"icmp_out_timestamp_reps"` + IcmpOutAddrMasks uint64 `json:"icmp_out_addr_masks"` + IcmpOutAddrMaskReps uint64 `json:"icmp_out_addr_mask_reps"` + // IcmpMsg + IcmpMsgInType0 uint64 `json:"icmpmsg_in_type0"` + IcmpMsgInType3 uint64 `json:"icmpmsg_in_type3"` + IcmpMsgInType5 uint64 `json:"icmpmsg_in_type5"` + IcmpMsgInType8 uint64 `json:"icmpmsg_in_type8"` + IcmpMsgInType11 uint64 `json:"icmpmsg_in_type11"` + IcmpMsgInType13 uint64 `json:"icmpmsg_in_type13"` + IcmpMsgOutType0 uint64 `json:"icmpmsg_out_type0"` + IcmpMsgOutType3 uint64 `json:"icmpmsg_out_type3"` + IcmpMsgOutType8 uint64 `json:"icmpmsg_out_type8"` + IcmpMsgOutType14 uint64 `json:"icmpmsg_out_type14"` + IcmpMsgOutType69 uint64 `json:"icmpmsg_out_type69"` + // TCP + TcpRtoAlgorithm uint64 `json:"tcp_rto_algorithm"` + TcpRtoMin uint64 `json:"tcp_rto_min"` + TcpRtoMax uint64 `json:"tcp_rto_max"` + TcpMaxConn uint64 `json:"tcp_max_conn"` + TcpActiveOpens uint64 `json:"tcp_active_opens"` + TcpPassiveOpens uint64 `json:"tcp_passive_opens"` + TcpAttemptFails uint64 `json:"tcp_attempt_fails"` + TcpEstabResets uint64 `json:"tcp_estab_resets"` + TcpCurrEstab uint64 `json:"tcp_curr_estab"` + TcpInSegs uint64 `json:"tcp_in_segs"` + TcpOutSegs uint64 `json:"tcp_out_segs"` + TcpRetransSegs uint64 `json:"tcp_retrans_segs"` + TcpInErrs uint64 `json:"tcp_in_errs"` + TcpOutRsts uint64 `json:"tcp_out_rsts"` + TcpInCsumErrors uint64 `json:"tcp_in_csum_errors"` + // UDP + UdpInDatagrams uint64 `json:"udp_in_datagrams"` + UdpNoPorts uint64 `json:"udp_no_ports"` + UdpInErrors uint64 `json:"udp_in_errors"` + UdpOutDatagrams uint64 `json:"udp_out_datagrams"` + UdpRcvbufErrors uint64 `json:"udp_rcvbuf_errors"` + UdpSndbufErrors uint64 `json:"udp_sndbuf_errors"` + UdpInCsumErrors uint64 `json:"udp_in_csum_errors"` + // UDPLite + UdpLiteInDatagrams uint64 `json:"udp_lite_in_datagrams"` + UdpLiteNoPorts uint64 `json:"udp_lite_no_ports"` + UdpLiteInErrors uint64 `json:"udp_lite_in_errors"` + UdpLiteOutDatagrams uint64 `json:"udp_lite_out_datagrams"` + UdpLiteRcvbufErrors uint64 `json:"udp_lite_rcvbuf_errors"` + UdpLiteSndbufErrors uint64 `json:"udp_lite_sndbuf_errors"` + UdpLiteInCsumErrors uint64 `json:"udp_lite_in_csum_errors"` +} + +func ReadSnmp(path string) (*Snmp, error) { + data, err := ioutil.ReadFile(path) + + if err != nil { + return nil, err + } + + lines := strings.Split(string(data), "\n") + + // Maps an SNMP metric to its value (i.e. SyncookiesSent --> 0) + statMap := make(map[string]string) + + // patterns + // Ip: Forwarding DefaultTTL InReceives InHdrErrors... <-- header + // Ip: 2 64 9305753793 0 0 0 0 0... <-- values + + for i := 1; i < len(lines); i = i + 2 { + headers := strings.Fields(lines[i-1][strings.Index(lines[i-1], ":")+1:]) + values := strings.Fields(lines[i][strings.Index(lines[i], ":")+1:]) + protocol := strings.Replace(strings.Fields(lines[i-1])[0], ":","",-1) + + for j, header := range headers { + statMap[protocol + header] = values[j] + } + } + + var snmp Snmp = Snmp{} + + elem := reflect.ValueOf(&snmp).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + if val, ok := statMap[typeOfElem.Field(i).Name]; ok { + parsedVal, _ := strconv.ParseUint(val, 10, 64) + elem.Field(i).SetUint(parsedVal) + } + } + + return &snmp, nil +} diff --git a/linuxproc.go b/linuxproc.go index f8da92e..12c98d6 100644 --- a/linuxproc.go +++ b/linuxproc.go @@ -11,6 +11,7 @@ const ( CPUInfoPath = "/proc/cpuinfo" NetworkStatPath = "/proc/net/dev" NetstatStatPath = "/proc/net/netstat" + SnmpStatPath = "/proc/net/snmp" SockstatStatPath = "/proc/net/sockstat" ) @@ -44,6 +45,26 @@ func getNetworkDeviceStatsList() []Stat { return list } +func readSnmpStats() (*linuxproc.Snmp, error) { + return linuxproc.ReadSnmp(SnmpStatPath) +} + +func getSnmpStatsList() []Stat { + stat := linuxproc.Snmp{} + + elem := reflect.ValueOf(&stat).Elem() + typeOfElem := elem.Type() + + list := make([]Stat, 0) + + for i := 0; i < elem.NumField(); i++ { + field := typeOfElem.Field(i) + list = append(list, Stat{field.Name, strings.Join([]string{snmpStatsMetricPrefix, field.Tag.Get("json")}, "")}) + } + + return list +} + func readNetstatStats() (*linuxproc.Netstat, error) { return linuxproc.ReadNetstat(NetstatStatPath) } diff --git a/main.go b/main.go index 637263e..8271698 100644 --- a/main.go +++ b/main.go @@ -85,6 +85,11 @@ func main() { fmt.Println(stat.StatName + ":" + stat.MetricName) } fmt.Println("<---") + fmt.Println(SnmpStatPath) + for _, stat := range getSnmpStatsList() { + fmt.Println(stat.StatName + ":" + stat.MetricName) + } + fmt.Println("<---") os.Exit(0) } @@ -191,6 +196,7 @@ func startLoggers() { logNetworkDeviceStats() logNetstatStats() logSockstatStats() + logSnmpStats() } } }) @@ -206,6 +212,7 @@ func startCollectors() { collectNetworkDeviceStats() collectNetstatStats() collectSockstatStats() + collectSnmpStats() } } }) diff --git a/repodb.yml b/repodb.yml new file mode 100644 index 0000000..848c99a --- /dev/null +++ b/repodb.yml @@ -0,0 +1,3 @@ +classification: alpha +oncall: + - neteng diff --git a/script/compile b/script/compile new file mode 100755 index 0000000..435dd38 --- /dev/null +++ b/script/compile @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e +set -x + +script/build diff --git a/statsd.go b/statsd.go index ca1d348..cc34c45 100644 --- a/statsd.go +++ b/statsd.go @@ -12,6 +12,7 @@ const ( networkDeviceStatsMetricPrefix = "net.dev." netstatStatsMetricPrefix = "net.netstat." sockstatStatsMetricPrefix = "net.sockstat." + snmpStatsMetricPrefix = "net.snmp." ) type LoggedStat struct { @@ -112,6 +113,47 @@ func logNetstatStats() { fmt.Println(string(jsonBytes)) } +func collectSnmpStats() { + stats, err := readSnmpStats() + + if err != nil { + Log.WithField("error", err).Error("Error reading SNMP stats. Can't collect snmp stats.") + } + elem := reflect.ValueOf(stats).Elem() + typeOfElem := elem.Type() + + for i := 0; i < elem.NumField(); i++ { + field := typeOfElem.Field(i) + + value := elem.Field(i).Uint() + + _, collect := StatsMap[field.Name] + + if metricName := field.Tag.Get("json"); collect && metricName != "" { + if err := StatsdClient.Gauge(strings.Join([]string{snmpStatsMetricPrefix, metricName}, ""), float64(value), []string{}, 1); err != nil { + Log.WithField("error", err).Error("Couldn't submit event to statsd.") + } + } + } +} + +func logSnmpStats() { + stats, err := readSnmpStats() + + if err != nil { + Log.WithField("error", err).Error("Error reading SNMP stats. Can't log snmp stats.") + } + + loggedStat := LoggedStat{SnmpStatPath, time.Now(), stats} + + jsonBytes, err := json.Marshal(loggedStat) + if err != nil { + Log.WithField("error", err).Errorf("Error JSON marshalling: %+v.", stats) + } + + fmt.Println(string(jsonBytes)) +} + func collectSockstatStats() { stats, err := readSockstatStats()