2 """ Test creation of a PCP web daemon via the requests module -*- python -*- """
4 # Copyright (C) 2013-2015 Red Hat Inc.
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the
8 # Free Software Foundation; either version 2 of the License, or (at your
9 # option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 import requests
, argparse
, subprocess
, os
, time
19 parser
= argparse
.ArgumentParser(description
='test_webapi.py pmwebapi check')
20 parser
.add_argument('--host', default
='localhost')
21 parser
.add_argument('--port', default
=44323)
22 args
= parser
.parse_args()
24 url
= 'http://' + args
.host
+ ':' + str(args
.port
) + '/'
25 devnull
= os
.open(os
.devnull
, os
.O_RDWR
)
26 os
.unsetenv('http_proxy')
27 os
.unsetenv('HTTP_PROXY')
29 # ------------------------------------------------------------------------
31 # test - create contexts
32 req
= requests
.get(url
=url
+ 'pmapi/context?local=foo&polltimeout=2')
34 ctx_local
= resp
['context']
35 print('Received PM_CONTEXT_LOCAL #' + str(ctx_local
))
37 req
= requests
.get(url
=url
+ 'pmapi/context?hostname=' + args
.host
+ '&polltimeout=2')
39 ctx_host
= resp
['context']
40 print('Received PM_CONTEXT_HOST #' + str(ctx_host
))
42 # ------------------------------------------------------------------------
44 # all these should get an error
45 req
= requests
.get(url
=url
+ 'pmapi/context?archivefile=/dev/null')
46 print('bad archive /dev/null response code ' + str(req
.status_code
))
48 req
= requests
.get(url
=url
+ 'pmapi/context?archivefile=../etc/passwd')
49 print('bad archive ../etc/passwd response code ' + str(req
.status_code
))
51 req
= requests
.get(url
=url
+ 'pmapi/context?archivefile=/etc/passwd')
52 print('bad archive /etc/passwd response code ' + str(req
.status_code
))
54 req
= requests
.get(url
=url
+ 'pmapi/context?archivefile=../../etc/shadow')
55 print('bad archive ../../etc/shadow response code ' + str(req
.status_code
))
57 req
= requests
.get(url
=url
+ 'pmapi/NOSUCHAPI')
58 print('command NOSUCHAPI response code ' + str(req
.status_code
))
60 req
= requests
.get(url
=url
+ 'pmapi/NOSUCHCONTEXT/_metric')
61 print('context NOSUCHCONTEXT response code ' + str(req
.status_code
))
63 req
= requests
.get(url
=url
+ 'pmapi/0/_metric')
64 print('context 0 response code ' + str(req
.status_code
))
66 req
= requests
.get(url
=url
+ 'random_nonpmwebapi_url')
67 print('non-pmwebapi response code ' + str(req
.status_code
))
69 # ------------------------------------------------------------------------
71 def test_metric_enumeration(ctx
, prefix
):
72 ctxurl
= url
+ 'pmapi/' + str(ctx
) + '/'
73 if (ctx
== ctx_local
):
74 procargs
= ['pminfo', '-L', '-t']
77 procargs
= ['pminfo', '-h', args
.host
, '-t']
80 procargs
.append(prefix
)
81 dbg
= dbg
+ '-' + prefix
83 proc
= subprocess
.Popen(procargs
,
84 stdout
=subprocess
.PIPE
,
86 universal_newlines
=True)
88 f
= open('webapi-pmcd-' + dbg
, 'w')
90 line
= proc
.stdout
.readline()
93 if line
.strip() == '':
94 continue # blank line pminfo sometimes does that
95 if line
.find('Error: No PMCD agent') == -1 and \
96 line
.find('Not known to the PMDA') == -1 and \
97 line
.find('Error: Unknown or illegal') == -1:
98 num_metrics
= num_metrics
+ 1
100 f
.write('Total ' + str(num_metrics
) + ' metrics\n')
103 testprefix
='context #'+str(ctx
)+' '+dbg
+' metric '+prefix
+'.*'
104 print(testprefix
+ ' enumeration with pminfo #'+str(num_metrics
))
106 req
= requests
.get(url
=ctxurl
+ '_metric' + \
107 ('?prefix='+prefix
if prefix
!= '' else ''))
110 except Exception as e
:
111 print("ERROR: %s" % e
)
112 print(testprefix
+ 'error JSON-decoding ' + req
.text
)
113 print(testprefix
+ ' enumeration with pmwebinfo #'+str(len(resp
['metrics'])))
116 f
= open('webapi-webd-' + dbg
, 'w')
117 for metric
in resp
['metrics']:
118 web_metrics
= web_metrics
+ 1
119 f
.write(metric
['name'] + '\n')
120 f
.write('Total ' + str(web_metrics
) + ' metrics\n')
123 if (abs(len(resp
['metrics']) - num_metrics
) < 10): # allow some variation
124 print(testprefix
+ ' enumeration match count PASS')
126 print(testprefix
+ ' enumeration match count FAIL')
129 test_metric_enumeration(ctx_local
,'')
130 test_metric_enumeration(ctx_host
,'')
131 test_metric_enumeration(ctx_local
,'kernel')
132 test_metric_enumeration(ctx_host
,'kernel')
134 # ------------------------------------------------------------------------
136 # empty _fetch should get an error
137 req
= requests
.get(url
=url
+ 'pmapi/'+str(ctx_host
)+'/_fetch')
138 print('context #'+str(ctx_host
)+' response code ' + str(req
.status_code
))
139 def test_fetch(ctx
,metrics
):
140 # some fetches of real values
141 req
= requests
.get(url
=url
+ 'pmapi/'+str(ctx
)+'/_fetch?'+metrics
)
142 print('context #'+str(ctx_host
)+' fetch '+metrics
+' response code ' + str(req
.status_code
))
143 if (req
.status_code
!= 200):
147 print('timestamps fields:'+str(len(result
['timestamp']))+' len(values[]):'+str(len(result
['values'])))
148 for metric
in sorted(result
['values'], key
=lambda x
: x
['name']):
149 print(' metric:'+metric
['name'])
150 if (len(metric
['instances']) > 0):
151 print(' with instances')
153 print(' no instances')
154 # don't pretty-print instances/values, because they could fluctuate too much from test to test for _filter()
157 # use both contexts so neither times out
158 test_fetch(ctx_local
,'names=')
159 test_fetch(ctx_local
,'names=no.such.metric')
160 test_fetch(ctx_local
,'names=no.such.metric1,pmcd.client.whoami,no.such.metric2') # ignore the bad metrics; no pmcd.* with -L
161 test_fetch(ctx_host
,'names=no.such.metric3,pmcd.client.whoami,no.such.metric4') # ignore the bad metrics
162 test_fetch(ctx_host
,'names=sample.event.records') # events
163 test_fetch(ctx_host
,'pmids=121634826,0x740000b') # sample.long.one, sample.long.ten
164 test_fetch(ctx_local
,'names=keepalive')
165 test_fetch(ctx_host
,'names=sample.bogus_bin,sample.sysinfo,sample.byte_rate,sample.ulong.bin')
166 test_fetch(ctx_host
,'names=sample.longlong.bin,,,sample.ulonglong.million,sample.float.hundred')
167 test_fetch(ctx_host
,'names=sample.double.bin_ctr,sample.aggregate.null,sample.hordes.one')
168 test_fetch(ctx_host
,'names=sample.dodgey.value')
169 test_fetch(ctx_local
,'pmids=blargh')
171 # ------------------------------------------------------------------------
173 def test_metadata(ctx
,metrics
):
174 # some fetches of real values
175 req
= requests
.get(url
=url
+ 'pmapi/'+str(ctx
)+'/_metric?'+metrics
)
176 print('context #'+str(ctx_host
)+' metadata '+metrics
+' response code ' + str(req
.status_code
))
177 if (req
.status_code
!= 200):
181 print('len(metrics[]):'+str(len(result
['metrics'])))
182 for metric
in sorted(result
['metrics'], key
=lambda x
: x
['name']):
183 print(' metric:'+metric
['name']+' metadata:'+str(len(metric
.keys())))
184 # don't pretty-print metadata contents, because they could fluctuate too much from test to test for _filter()
187 # use both contexts so neither times out
188 test_metadata(ctx_local
,'prefix=no.such.metric')
189 test_metadata(ctx_local
,'prefix=no.such.metric1,pmcd.client.whoami,no.such.metric2') # ignore the bad metrics; no pmcd.* with -L
190 test_metadata(ctx_host
,'prefix=no.such.metric3,pmcd.client.whoami,no.such.metric4') # ignore the bad metrics
191 test_metadata(ctx_host
,'prefix=sample') # whole shebang
192 test_metadata(ctx_local
,'prefix=keepalive')
194 # ------------------------------------------------------------------------
196 def test_indom(ctx
,dom
):
197 # some fetches of real values
198 req
= requests
.get(url
=url
+ 'pmapi/'+str(ctx
)+'/_indom?'+dom
)
199 print('context #'+str(ctx_host
)+' indom '+dom
+' response code ' + str(req
.status_code
))
200 if (req
.status_code
!= 200):
204 print('indom:'+str(result
['indom']))
205 if (len(result
['instances']) > 0):
206 print(' with instances')
208 print(' no instances')
209 # don't pretty-print indom tuples, because they could fluctuate too much from test to test for _filter()
212 # use both contexts so neither times out
213 test_indom(ctx_local
,'indom=')
214 test_indom(ctx_host
,'indom=0x7400009') # sample.scramble.bin
215 test_indom(ctx_host
,'name=sample.many.int&instance=1,4')
216 test_indom(ctx_host
,'name=sample.many.int&instance=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21') # too many
217 test_indom(ctx_host
,'indom=0x7400008&iname=i-2,,,i-3') # sample.many.int
218 test_indom(ctx_host
,'name=pmcd.client.whoami')
219 test_indom(ctx_local
,'names=keepalive')
221 # ------------------------------------------------------------------------
223 def test_store(ctx
, suffix
):
224 # some stores of real values
225 req
= requests
.get(url
=url
+ 'pmapi/'+str(ctx
)+'/_store?'+suffix
)
226 print('context #'+str(ctx_host
)+' store '+suffix
+' response code ' + str(req
.status_code
))
227 if (req
.status_code
!= 200):
229 print('store: success')
231 def check_vals(ctx
, metric
):
232 # dump values after a store
233 req
= requests
.get(url
=url
+ 'pmapi/'+str(ctx
)+'/_fetch?names='+metric
)
234 if (req
.status_code
!= 200):
237 for metric
in sorted(result
['values'], key
=lambda x
: x
['name']):
238 for inst
in sorted(metric
['instances'], key
=lambda x
: x
['instance']):
239 print(' ' + metric
['name'] + '[' + str(inst
['value']) + ']')
241 # use both contexts so neither times out, verify values reflect stored change
242 test_store(ctx_local
, 'name=') # no metric
243 test_store(ctx_host
, 'name=sample.write_me') # no values
244 test_store(ctx_host
, 'name=sample.write_me&value=42')
245 test_store(ctx_local
, 'name=sample.write_me&value=bad')
247 test_store(ctx_host
, 'name=sample.long.write_me&value=42')
248 check_vals(ctx_host
, 'sample.long.write_me')
249 test_store(ctx_local
, 'name=sample.long.write_me&value=bad')
250 test_store(ctx_host
, 'name=sample.ulong.write_me&value=42')
251 check_vals(ctx_host
, 'sample.ulong.write_me')
252 test_store(ctx_local
, 'name=sample.ulong.write_me&value=bad')
253 test_store(ctx_host
, 'name=sample.float.write_me&value=42.0')
254 check_vals(ctx_host
, 'sample.float.write_me')
255 test_store(ctx_host
, 'name=sample.string.write_me&value=good')
256 check_vals(ctx_host
, 'sample.string.write_me')
257 test_store(ctx_local
, 'name=sample.double.write_me&value=bad')
258 test_store(ctx_local
, 'name=sample.aggregate.write_me&value=bad')
260 test_store(ctx_host
, 'name=sample.colour&value=10&instance=0')
261 check_vals(ctx_host
, 'sample.colour')
262 test_store(ctx_host
, 'name=sample.colour&value=20&instance=0,1')
263 check_vals(ctx_host
, 'sample.colour')
264 test_store(ctx_host
, 'name=sample.colour&value=30&iname=blue')
265 check_vals(ctx_host
, 'sample.colour')
266 test_store(ctx_host
, 'name=sample.colour&value=40&iname=red,blue')
267 check_vals(ctx_host
, 'sample.colour')
269 # ------------------------------------------------------------------------
271 print('sleeping briefly to expire contexts')
272 time
.sleep(5) # >> polltimeout=2
274 test_metadata(ctx_local
,'names=kernel.all.nprocs') # expect status 400ish
275 test_indom(ctx_host
,'names=kernel.all.nprocs')