diff options
author | Mike Crute <mike@crute.us> | 2020-10-13 22:20:55 +0000 |
---|---|---|
committer | Mike Crute <mike@crute.us> | 2020-10-13 22:20:55 +0000 |
commit | f8e6b9a6f1896c484de11131e36c4e065541af4b (patch) | |
tree | f27f1d320a8d85e3dcf2f8650423bd3223af8929 | |
parent | 3b035c8fa1f75c4c00e57acc14fb71dfd62e31ee (diff) | |
download | prometheus_node_collector-f8e6b9a6f1896c484de11131e36c4e065541af4b.tar.bz2 prometheus_node_collector-f8e6b9a6f1896c484de11131e36c4e065541af4b.tar.xz prometheus_node_collector-f8e6b9a6f1896c484de11131e36c4e065541af4b.zip |
Add conntrack_exec collector for Linux
-rw-r--r-- | collector/conntrack_exec_linux.go | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/collector/conntrack_exec_linux.go b/collector/conntrack_exec_linux.go new file mode 100644 index 0000000..1bce87a --- /dev/null +++ b/collector/conntrack_exec_linux.go | |||
@@ -0,0 +1,195 @@ | |||
1 | // Copyright 2015 The Prometheus Authors | ||
2 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
3 | // you may not use this file except in compliance with the License. | ||
4 | // You may obtain a copy of the License at | ||
5 | // | ||
6 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
7 | // | ||
8 | // Unless required by applicable law or agreed to in writing, software | ||
9 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
10 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
11 | // See the License for the specific language governing permissions and | ||
12 | // limitations under the License. | ||
13 | |||
14 | // +build !noconntrack | ||
15 | |||
16 | package collector | ||
17 | |||
18 | import ( | ||
19 | "encoding/xml" | ||
20 | "os/exec" | ||
21 | "strconv" | ||
22 | "time" | ||
23 | |||
24 | "github.com/go-kit/kit/log" | ||
25 | "github.com/prometheus/client_golang/prometheus" | ||
26 | ) | ||
27 | |||
28 | type ConntrackStats struct { | ||
29 | CollectTime int64 | ||
30 | Flows []FlowRecord | ||
31 | } | ||
32 | |||
33 | type FlowRecord struct { | ||
34 | L3Proto string | ||
35 | L4Proto string | ||
36 | SourceAddr string | ||
37 | SourcePort int | ||
38 | DestAddr string | ||
39 | DestPort int | ||
40 | PacketsIn int64 | ||
41 | PacketsOut int64 | ||
42 | BytesIn int64 | ||
43 | BytesOut int64 | ||
44 | } | ||
45 | |||
46 | func (m *ConntrackStats) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { | ||
47 | m.CollectTime = time.Now().Unix() | ||
48 | |||
49 | rd := struct { | ||
50 | Flows []struct { | ||
51 | Metas []struct { | ||
52 | Direction string `xml:"direction,attr"` | ||
53 | Layer3Meta struct { | ||
54 | ProtoName string `xml:"protoname,attr"` | ||
55 | SourceAddr string `xml:"src"` | ||
56 | DestAddr string `xml:"dst"` | ||
57 | } `xml:"layer3"` | ||
58 | Layer4Meta struct { | ||
59 | ProtoName string `xml:"protoname,attr"` | ||
60 | SourcePort int `xml:"sport"` | ||
61 | DestPort int `xml:"dport"` | ||
62 | } `xml:"layer4"` | ||
63 | Packets int64 `xml:"counters>packets"` | ||
64 | Bytes int64 `xml:"counters>bytes"` | ||
65 | } `xml:"meta"` | ||
66 | } `xml:"flow"` | ||
67 | }{} | ||
68 | |||
69 | err := d.DecodeElement(&rd, &start) | ||
70 | if err != nil { | ||
71 | return err | ||
72 | } | ||
73 | |||
74 | for _, f := range rd.Flows { | ||
75 | or := FlowRecord{} | ||
76 | |||
77 | for _, m := range f.Metas { | ||
78 | if m.Direction == "original" { | ||
79 | or.L3Proto = m.Layer3Meta.ProtoName | ||
80 | or.L4Proto = m.Layer4Meta.ProtoName | ||
81 | or.SourceAddr = m.Layer3Meta.SourceAddr | ||
82 | or.SourcePort = m.Layer4Meta.SourcePort | ||
83 | or.DestAddr = m.Layer3Meta.DestAddr | ||
84 | or.DestPort = m.Layer4Meta.DestPort | ||
85 | or.PacketsIn = m.Packets | ||
86 | or.BytesIn = m.Bytes | ||
87 | } else if m.Direction == "reply" { | ||
88 | or.PacketsOut = m.Packets | ||
89 | or.BytesOut = m.Bytes | ||
90 | } | ||
91 | } | ||
92 | |||
93 | m.Flows = append(m.Flows, or) | ||
94 | } | ||
95 | |||
96 | return nil | ||
97 | } | ||
98 | |||
99 | func loadStats(proto string, s *ConntrackStats) error { | ||
100 | cmd := exec.Command("/usr/sbin/conntrack", "-L", "-f", proto, "-o", "xml") | ||
101 | out, err := cmd.StdoutPipe() | ||
102 | if err != nil { | ||
103 | return err | ||
104 | } | ||
105 | |||
106 | if err := cmd.Start(); err != nil { | ||
107 | return err | ||
108 | } | ||
109 | |||
110 | dec := xml.NewDecoder(out) | ||
111 | if err = dec.Decode(s); err != nil { | ||
112 | return err | ||
113 | } | ||
114 | |||
115 | if err := cmd.Wait(); err != nil { | ||
116 | return err | ||
117 | } | ||
118 | |||
119 | return nil | ||
120 | } | ||
121 | |||
122 | func init() { | ||
123 | registerCollector("conntrack_exec", defaultDisabled, NewConntrackExecCollector) | ||
124 | } | ||
125 | |||
126 | func NewConntrackExecCollector(logger log.Logger) (Collector, error) { | ||
127 | subsystem := "conntrack_exec" | ||
128 | labels := []string{"l3", "l4", "saddr", "sport", "daddr", "dport"} | ||
129 | |||
130 | return &conntrackExecCollector{ | ||
131 | bytesIn: prometheus.NewDesc( | ||
132 | prometheus.BuildFQName(namespace, subsystem, "bytes_in"), | ||
133 | "Conntrack bytes in for flow", | ||
134 | labels, nil, | ||
135 | ), | ||
136 | bytesOut: prometheus.NewDesc( | ||
137 | prometheus.BuildFQName(namespace, subsystem, "bytes_out"), | ||
138 | "Conntrack bytes out for flow", | ||
139 | labels, nil, | ||
140 | ), | ||
141 | packetsIn: prometheus.NewDesc( | ||
142 | prometheus.BuildFQName(namespace, subsystem, "packets_in"), | ||
143 | "Conntrack packets in for flow", | ||
144 | labels, nil, | ||
145 | ), | ||
146 | packetsOut: prometheus.NewDesc( | ||
147 | prometheus.BuildFQName(namespace, subsystem, "packets_out"), | ||
148 | "Conntrack packets out for flow", | ||
149 | labels, nil, | ||
150 | ), | ||
151 | }, nil | ||
152 | } | ||
153 | |||
154 | type conntrackExecCollector struct { | ||
155 | bytesIn, bytesOut *prometheus.Desc | ||
156 | packetsIn, packetsOut *prometheus.Desc | ||
157 | logger log.Logger | ||
158 | } | ||
159 | |||
160 | func (c *conntrackExecCollector) Update(ch chan<- prometheus.Metric) error { | ||
161 | stats := &ConntrackStats{} | ||
162 | |||
163 | if err := loadStats("ipv4", stats); err != nil { | ||
164 | return err | ||
165 | } | ||
166 | |||
167 | if err := loadStats("ipv6", stats); err != nil { | ||
168 | return err | ||
169 | } | ||
170 | |||
171 | for _, f := range stats.Flows { | ||
172 | ch <- prometheus.MustNewConstMetric( | ||
173 | c.bytesIn, prometheus.CounterValue, | ||
174 | float64(f.BytesIn), | ||
175 | f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), | ||
176 | ) | ||
177 | ch <- prometheus.MustNewConstMetric( | ||
178 | c.bytesOut, prometheus.CounterValue, | ||
179 | float64(f.BytesOut), | ||
180 | f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), | ||
181 | ) | ||
182 | ch <- prometheus.MustNewConstMetric( | ||
183 | c.packetsIn, prometheus.CounterValue, | ||
184 | float64(f.PacketsIn), | ||
185 | f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), | ||
186 | ) | ||
187 | ch <- prometheus.MustNewConstMetric( | ||
188 | c.packetsOut, prometheus.CounterValue, | ||
189 | float64(f.PacketsOut), | ||
190 | f.L3Proto, f.L4Proto, f.SourceAddr, strconv.Itoa(f.SourcePort), f.DestAddr, strconv.Itoa(f.DestPort), | ||
191 | ) | ||
192 | } | ||
193 | |||
194 | return nil | ||
195 | } | ||