Merge lp:~bloodearnest/charms/precise/squid-reverseproxy/fix-stats-cron into lp:charms/squid-reverseproxy
- Precise Pangolin (12.04)
- fix-stats-cron
- Merge into trunk
Proposed by
Simon Davy
Status: | Merged |
---|---|
Merged at revision: | 47 |
Proposed branch: | lp:~bloodearnest/charms/precise/squid-reverseproxy/fix-stats-cron |
Merge into: | lp:charms/squid-reverseproxy |
Diff against target: |
340 lines (+170/-33) 4 files modified
files/squid_metrics.py (+137/-10) hooks/tests/test_metrics.py (+30/-21) templates/metrics_cronjob.template (+1/-1) templates/squid_metrics_ini.template (+2/-1) |
To merge this branch: | bzr merge lp:~bloodearnest/charms/precise/squid-reverseproxy/fix-stats-cron |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tom Haddon | Approve | ||
Review via email: mp+218103@code.launchpad.net |
Commit message
fix statsd wire format and send directly in metrics script
Description of the change
fix statsd wire format and send directly in metrics script
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'files/squid_metrics.py' |
2 | --- files/squid_metrics.py 2014-03-28 17:05:31 +0000 |
3 | +++ files/squid_metrics.py 2014-05-02 14:57:34 +0000 |
4 | @@ -1,10 +1,122 @@ |
5 | #!/bin/env python |
6 | import shlex |
7 | import sys |
8 | +import socket |
9 | import subprocess |
10 | -import time |
11 | import ConfigParser |
12 | |
13 | + |
14 | +METRIC_TYPES = { |
15 | + 'cacheSysVMsize': 'g', # Storage Mem size in KB |
16 | + 'cacheSysStorage': 'g', # Storage Swap size in KB |
17 | + 'cacheUptime': 'c', # The Uptime of the cache in timeticks |
18 | + 'cacheMemMaxSize': 'g', # The value of the cache_mem parameter in MB |
19 | + 'cacheSwapMaxSize': 'g', # The total of the cache_dir space allocated in MB |
20 | + 'cacheSwapHighWM': 'g', # Cache Swap High Water Mark |
21 | + 'cacheSwapLowWM': 'g', # Cache Swap Low Water Mark |
22 | + 'cacheUniqName': 'g', # Cache unique host name |
23 | + 'cacheSysPageFaults': 'c', # Page faults with physical i/o |
24 | + 'cacheSysNumReads': 'c', # HTTP I/O number of reads |
25 | + 'cacheMemUsage': 'g', # Total memory accounted for KB |
26 | + 'cacheCpuTime': 'c', # Amount of cpu seconds consumed |
27 | + 'cacheCpuUsage': 'g', # The percentage use of the CPU |
28 | + 'cacheMaxResSize': 'g', # Maximum Resident Size in KB |
29 | + 'cacheNumObjCount': 'g', # Number of objects stored by the cache |
30 | + 'cacheCurrentLRUExpiration': 'g', # Storage LRU Expiration Age |
31 | + 'cacheCurrentUnlinkRequests': 'g', # Requests given to unlinkd |
32 | + 'cacheCurrentUnusedFDescrCnt': 'g', # Available number of file descriptors |
33 | + 'cacheCurrentResFileDescrCnt': 'g', # Reserved number of file descriptors |
34 | + 'cacheCurrentFileDescrCnt': 'g', # Number of file descriptors in use |
35 | + 'cacheCurrentFileDescrMax': 'g', # Highest file descriptors in use |
36 | + 'cacheProtoClientHttpRequests': 'c', # Number of HTTP requests received |
37 | + 'cacheHttpHits': 'c', # Number of HTTP Hits sent to clients from cache |
38 | + 'cacheHttpErrors': 'c', # Number of HTTP Errors sent to clients |
39 | + 'cacheHttpInKb': 'c', # Number of HTTP KB's received from clients |
40 | + 'cacheHttpOutKb': 'c', # Number of HTTP KB's sent to clients |
41 | + 'cacheIcpPktsSent': 'c', # Number of ICP messages sent |
42 | + 'cacheIcpPktsRecv': 'c', # Number of ICP messages received |
43 | + 'cacheIcpKbSent': 'c', # Number of ICP KB's transmitted |
44 | + 'cacheIcpKbRecv': 'c', # Number of ICP KB's received |
45 | + 'cacheServerRequests': 'c', # All requests from the client for the cache server |
46 | + 'cacheServerErrors': 'c', # All errors for the cache server from client requests |
47 | + 'cacheServerInKb': 'c', # KB's of traffic received from servers |
48 | + 'cacheServerOutKb': 'c', # KB's of traffic sent to servers |
49 | + 'cacheCurrentSwapSize': 'g', # Storage Swap size |
50 | + 'cacheClients': 'g', # Number of clients accessing cache |
51 | + 'cacheMedianTime.1': 'g', # The value used to index the table 1/5/60 |
52 | + 'cacheMedianTime.5': 'g', # |
53 | + 'cacheMedianTime.60': 'g', # |
54 | + 'cacheHttpAllSvcTime.1': 'g', # HTTP all service time |
55 | + 'cacheHttpAllSvcTime.5': 'g', # |
56 | + 'cacheHttpAllSvcTime.60': 'g', # |
57 | + 'cacheHttpMissSvcTime.1': 'g', # HTTP miss service time |
58 | + 'cacheHttpMissSvcTime.5': 'g', # |
59 | + 'cacheHttpMissSvcTime.60': 'g', # |
60 | + 'cacheHttpNmSvcTime.1': 'g', # HTTP hit not-modified service time |
61 | + 'cacheHttpNmSvcTime.5': 'g', # |
62 | + 'cacheHttpNmSvcTime.60': 'g', # |
63 | + 'cacheHttpHitSvcTime.1': 'g', # HTTP hit service time |
64 | + 'cacheHttpHitSvcTime.5': 'g', # |
65 | + 'cacheHttpHitSvcTime.60': 'g', # |
66 | + 'cacheIcpQuerySvcTime.1': 'g', # ICP query service time |
67 | + 'cacheIcpQuerySvcTime.5': 'g', # |
68 | + 'cacheIcpQuerySvcTime.60': 'g', # |
69 | + 'cacheIcpReplySvcTime.1': 'g', # ICP reply service time |
70 | + 'cacheIcpReplySvcTime.5': 'g', # |
71 | + 'cacheIcpReplySvcTime.60': 'g', # |
72 | + 'cacheDnsSvcTime.1': 'g', # DNS service time |
73 | + 'cacheDnsSvcTime.5': 'g', # |
74 | + 'cacheDnsSvcTime.60': 'g', # |
75 | + 'cacheRequestHitRati.1': 'g', # Request Hit Ratios |
76 | + 'cacheRequestHitRati.5': 'g', # |
77 | + 'cacheRequestHitRati.60': 'g', # |
78 | + 'cacheRequestByteRati.1': 'g', # Byte Hit Ratios |
79 | + 'cacheRequestByteRati.5': 'g', # |
80 | + 'cacheRequestByteRati.60': 'g', # |
81 | + 'cacheHttpNhSvcTime.1': 'g', # HTTP refresh hit service time |
82 | + 'cacheHttpNhSvcTime.5': 'g', # |
83 | + 'cacheHttpNhSvcTime.60': 'g', # |
84 | + 'cacheIpEntries': 'g', # IP Cache Entries |
85 | + 'cacheIpRequests': 'c', # Number of IP Cache requests |
86 | + 'cacheIpHits': 'c', # Number of IP Cache hits |
87 | + 'cacheIpPendingHits': 'g', # Number of IP Cache pending hits |
88 | + 'cacheIpNegativeHits': 'c', # Number of IP Cache pending hits |
89 | + 'cacheIpMisses': 'c', # Number of IP Cache misses |
90 | + 'cacheBlockingGetHostByName': 'c', # Number of blocking gethostbyname requests |
91 | + 'cacheAttemptReleaseLckEntries': 'c', # Number of attempts to release locked IP |
92 | + 'cacheFqdnEntries': 'g', # FQDN Cache entries |
93 | + 'cacheFqdnRequests': 'c', # Number of FQDN Cache requests |
94 | + 'cacheFqdnHits': 'c', # Number of FQDN Cache hits |
95 | + 'cacheFqdnPendingHits': 'g', # Number of FQDN Cache pending hits |
96 | + 'cacheFqdnNegativeHits': 'c', # Number of FQDN Cache negative hits |
97 | + 'cacheFqdnMisses': 'c', # Number of FQDN Cache misses |
98 | + 'cacheBlockingGetHostByAddr': 'c', # Number of blocking gethostbyaddr requests |
99 | + 'cacheDnsRequests': 'c', # Number of external dnsserver requests |
100 | + 'cacheDnsReplies': 'c', # Number of external dnsserver replies |
101 | + 'cacheDnsNumberServers': 'c', # Number of external dnsserver processes |
102 | + 'cachePeerIndex': 'g', # A unique value, greater than zero for each cache_peer instance in the managed system. |
103 | + 'cachePeerPortHttp': 'g', # The port the peer listens for HTTP requests |
104 | + 'cachePeerPortIcp': 'g', # The port the peer listens for ICP requests should be 0 if not configured to send ICP requests |
105 | + 'cachePeerType': 'g', # Peer Type |
106 | + 'cachePeerState': 'g', # The operational state of this peer |
107 | + 'cachePeerPingsSent': 'c', # Number of pings sent to peer |
108 | + 'cachePeerPingsAcked': 'c', # Number of pings received from peer |
109 | + 'cachePeerFetches': 'c', # Number of times this peer was selected |
110 | + 'cachePeerRtt': 'g', # Last known round-trip time to the peer (in ms) |
111 | + 'cachePeerIgnored': 'c', # How many times this peer was ignored |
112 | + 'cachePeerKeepAlSent': 'c', # Number of keepalives sent |
113 | + 'cachePeerKeepAlRecv': 'c', # Number of keepalives received |
114 | + 'cacheClientHttpRequests': 'c', # Number of HTTP requests received from client |
115 | + 'cacheClientHttpKb': 'c', # Amount of total HTTP traffic to this client |
116 | + 'cacheClientHttpHits': 'c', # Number of hits in response to this client's HTTP requests |
117 | + 'cacheClientHTTPHitKb': 'c', # Amount of HTTP hit traffic in KB |
118 | + 'cacheClientIcpRequests': 'c', # Number of ICP requests received from client |
119 | + 'cacheClientIcpKb': 'c', # Amount of total ICP traffic to this client (child) |
120 | + 'cacheClientIcpHits': 'c', # Number of hits in response to this client's ICP requests |
121 | + 'cacheClientIcpHitKb': 'c', # Amount of ICP hit traffic in KB |
122 | +} |
123 | + |
124 | + |
125 | config_path = "/usr/local/etc/squid_metrics.ini" |
126 | cmd = "snmpwalk -v1 -Cc -Oq -c %s -m /usr/share/squid3/mib.txt localhost:%s" |
127 | |
128 | @@ -20,7 +132,8 @@ |
129 | return { |
130 | 'unit': config.get('metrics', 'unit'), |
131 | 'scheme': config.get('metrics', 'scheme'), |
132 | - 'port': config.get('metrics', 'port'), |
133 | + 'snmp_port': config.get('metrics', 'snmp_port'), |
134 | + 'statsd_hostport': config.get('metrics', 'statsd_hostport'), |
135 | 'community': config.get('metrics', 'community'), |
136 | 'metrics': config.get('metrics', 'metrics').strip().split(','), |
137 | } |
138 | @@ -45,9 +158,9 @@ |
139 | |
140 | def get_metrics(config): |
141 | |
142 | - tstamp = int(time.time()) |
143 | scheme = config['scheme'].replace('$UNIT', clean(config['unit'])) |
144 | - snmp_cmd = tuple(shlex.split(cmd % (config['community'], config['port']))) |
145 | + snmp_cmd = tuple( |
146 | + shlex.split(cmd % (config['community'], config['snmp_port']))) |
147 | |
148 | try: |
149 | raw_peer_names = get_snmp_value(snmp_cmd, 'cachePeerName') |
150 | @@ -59,6 +172,11 @@ |
151 | peer_names = dict(line[25:].split(' ') for line in lines) |
152 | |
153 | for metric in config['metrics']: |
154 | + |
155 | + if metric not in METRIC_TYPES: |
156 | + # TODO log failure? |
157 | + continue |
158 | + |
159 | # sadly, have to do one call per metric |
160 | try: |
161 | output = get_snmp_value(snmp_cmd, metric) |
162 | @@ -67,6 +185,8 @@ |
163 | # TODO log failure to read metric? |
164 | continue |
165 | |
166 | + metric_type = METRIC_TYPES[metric] |
167 | + |
168 | # output is like "SQUID-MIB::<key> <value>" |
169 | for line in output.split('\n'): |
170 | # ensure only what we asked, as snmpwalk can be overly generous |
171 | @@ -85,15 +205,22 @@ |
172 | name = scheme.replace('$METRIC', key) |
173 | else: |
174 | name = scheme + ".%s" % key |
175 | - yield (name, value, tstamp) |
176 | + yield (name, value, metric_type) |
177 | |
178 | |
179 | if __name__ == '__main__': |
180 | |
181 | - if len(sys.argv) > 1: |
182 | - config = load_config(sys.argv[1]) |
183 | - else: |
184 | - config = load_config(config_path) |
185 | + config = load_config(config_path) |
186 | + |
187 | + def send(x): |
188 | + print x |
189 | + |
190 | + if len(sys.argv) > 1 and sys.argv[1] == "send": |
191 | + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) |
192 | + host, port = config['statsd_hostport'].split(':') |
193 | + sock.connect((host, int(port))) |
194 | + def send(x): |
195 | + sock.send(x) |
196 | |
197 | for metric in get_metrics(config): |
198 | - print "%s %s %s" % metric |
199 | + send(("%s:%s|%s" % metric).encode('utf8')) |
200 | |
201 | === modified file 'hooks/tests/test_metrics.py' |
202 | --- hooks/tests/test_metrics.py 2014-03-28 17:05:31 +0000 |
203 | +++ hooks/tests/test_metrics.py 2014-05-02 14:57:34 +0000 |
204 | @@ -31,6 +31,7 @@ |
205 | self.config_get = self.add_patch("hooks.config_get") |
206 | self.local_unit = self.add_patch("hooks.local_unit") |
207 | self.log = self.add_patch("hooks.log") |
208 | + self.apt_install = self.add_patch("hooks.apt_install") |
209 | |
210 | self.config_get.return_value = { |
211 | 'metrics_sample_interval': 5, |
212 | @@ -78,11 +79,12 @@ |
213 | [metrics] |
214 | unit: unit/0 |
215 | scheme: scheme |
216 | - port: 1234 |
217 | + snmp_port: 1234 |
218 | + statsd_hostport: localhost:4321 |
219 | community: community |
220 | metrics: a,b,c |
221 | """).strip() |
222 | - self.assertEqual(config_write, expected_config) |
223 | + self.assertEqual(expected_config, config_write) |
224 | |
225 | # 2nd open |
226 | cron_open_args = self.open.mock_calls[4][1] |
227 | @@ -91,9 +93,9 @@ |
228 | cron_write = self.open.mock_calls[6][1][0] |
229 | expected_cron = textwrap.dedent(""" |
230 | # crontab for pushing squid metrics to statsd |
231 | - */5 * * * * root python /script /config | nc localhost 4321 |
232 | - """).strip() |
233 | - self.assertEqual(cron_write, expected_cron) |
234 | + */5 * * * * root python /script send |
235 | + """).lstrip() |
236 | + self.assertEqual(expected_cron, cron_write) |
237 | |
238 | |
239 | class MetricsJobTestCase(TestCase): |
240 | @@ -109,20 +111,27 @@ |
241 | self.config = { |
242 | 'unit': 'unit/0', |
243 | 'scheme': 'dev.$UNIT.$METRIC.5min', |
244 | - 'port': '1234', |
245 | + 'snmp_port': '1234', |
246 | + 'statsd_hostport': '127.0.0.1:8000', |
247 | 'community': 'public', |
248 | 'metrics': [], |
249 | } |
250 | |
251 | self.snmp = self.add_patch('squid_metrics.get_snmp_value') |
252 | - |
253 | - self.time = self.add_patch('squid_metrics.time.time') |
254 | - self.time.return_value = 1 |
255 | + patcher = patch.dict(squid_metrics.METRIC_TYPES, { |
256 | + 'first': 'c', |
257 | + 'second': 'c', |
258 | + 'third.5': 'g', |
259 | + 'fourth.60': 'g', |
260 | + 'cachePeerRtt': 'g', |
261 | + }) |
262 | + patcher.start() |
263 | + self.addCleanup(patcher.stop) |
264 | |
265 | def test_periods(self): |
266 | |
267 | self.config['metrics'] = [ |
268 | - 'first', 'second', 'third.5', 'forth.60' |
269 | + 'first', 'second', 'third.5', 'fourth.60' |
270 | ] |
271 | |
272 | self.snmp.side_effect = [ |
273 | @@ -130,16 +139,16 @@ |
274 | 'SQUID-MIB::first 1', |
275 | 'SQUID-MIB::second.0 2', |
276 | 'SQUID-MIB::third.5 3', |
277 | - 'SQUID-MIB::forth.60 4', |
278 | + 'SQUID-MIB::fourth.60 4', |
279 | ] |
280 | |
281 | metrics = list(squid_metrics.get_metrics(self.config)) |
282 | |
283 | self.assertEqual(metrics, [ |
284 | - ('dev.unit-0.first.5min', '1', 1), |
285 | - ('dev.unit-0.second.5min', '2', 1), |
286 | - ('dev.unit-0.third-5.5min', '3', 1), |
287 | - ('dev.unit-0.forth-60.5min', '4', 1), |
288 | + ('dev.unit-0.first.5min', '1', 'c'), |
289 | + ('dev.unit-0.second.5min', '2', 'c'), |
290 | + ('dev.unit-0.third-5.5min', '3', 'g'), |
291 | + ('dev.unit-0.fourth-60.5min', '4', 'g'), |
292 | ]) |
293 | |
294 | def test_peers_with_names(self): |
295 | @@ -163,9 +172,9 @@ |
296 | metrics = list(squid_metrics.get_metrics(self.config)) |
297 | |
298 | self.assertEqual(metrics, [ |
299 | - ('dev.unit-0.cachePeerRtt.one-com.5min', '1', 1), |
300 | - ('dev.unit-0.cachePeerRtt.two-org.5min', '2', 1), |
301 | - ('dev.unit-0.cachePeerRtt.three-net.5min', '3', 1), |
302 | + ('dev.unit-0.cachePeerRtt.one-com.5min', '1', 'g'), |
303 | + ('dev.unit-0.cachePeerRtt.two-org.5min', '2', 'g'), |
304 | + ('dev.unit-0.cachePeerRtt.three-net.5min', '3', 'g'), |
305 | ]) |
306 | |
307 | def test_peers_without_names(self): |
308 | @@ -185,7 +194,7 @@ |
309 | metrics = list(squid_metrics.get_metrics(self.config)) |
310 | |
311 | self.assertEqual(metrics, [ |
312 | - ('dev.unit-0.cachePeerRtt.1.5min', '1', 1), |
313 | - ('dev.unit-0.cachePeerRtt.2.5min', '2', 1), |
314 | - ('dev.unit-0.cachePeerRtt.3.5min', '3', 1), |
315 | + ('dev.unit-0.cachePeerRtt.1.5min', '1', 'g'), |
316 | + ('dev.unit-0.cachePeerRtt.2.5min', '2', 'g'), |
317 | + ('dev.unit-0.cachePeerRtt.3.5min', '3', 'g'), |
318 | ]) |
319 | |
320 | === modified file 'templates/metrics_cronjob.template' |
321 | --- templates/metrics_cronjob.template 2014-03-28 17:41:48 +0000 |
322 | +++ templates/metrics_cronjob.template 2014-05-02 14:57:34 +0000 |
323 | @@ -1,3 +1,3 @@ |
324 | # crontab for pushing squid metrics to statsd |
325 | -*/{{ interval }} * * * * root python {{ script }} {{ config }} | nc {{ statsd_host }} {{statsd_port }} |
326 | +*/{{ interval }} * * * * root python {{ script }} send |
327 | |
328 | |
329 | === modified file 'templates/squid_metrics_ini.template' |
330 | --- templates/squid_metrics_ini.template 2014-02-25 17:21:46 +0000 |
331 | +++ templates/squid_metrics_ini.template 2014-05-02 14:57:34 +0000 |
332 | @@ -1,6 +1,7 @@ |
333 | [metrics] |
334 | unit: {{ unit_name }} |
335 | scheme: {{ metrics_scheme }} |
336 | -port: {{ snmp_port }} |
337 | +snmp_port: {{ snmp_port }} |
338 | +statsd_hostport: {{ metrics_target }} |
339 | community: {{ snmp_community }} |
340 | metrics: {{ metrics_list|join(',') }} |
Looks good to me, and tested to work on a staging environment