qa: update 660 to generate index.html, fixing pcp-testsuite runs
[pcp.git] / qa / src / test_webapi.python
blob1a762f6196028895e8d366c57254caf470076c38
1 #!/usr/bin/pcp python
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
14 # for more details.
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')
33 resp = req.json()
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')
38 resp = req.json()
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']
75 dbg = 'local'
76 else:
77 procargs = ['pminfo', '-h', args.host, '-t']
78 dbg = 'host'
79 if (prefix != ''):
80 procargs.append(prefix)
81 dbg = dbg + '-' + prefix
83 proc = subprocess.Popen(procargs,
84 stdout=subprocess.PIPE,
85 stderr=devnull,
86 universal_newlines=True)
87 num_metrics = 0
88 f = open('webapi-pmcd-' + dbg, 'w')
89 while True:
90 line = proc.stdout.readline()
91 if (line == ''):
92 break # eof
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
99 f.write(line)
100 f.write('Total ' + str(num_metrics) + ' metrics\n')
101 f.close()
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 ''))
108 try:
109 resp = req.json()
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'])))
115 web_metrics = 0
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')
121 f.close()
123 if (abs(len(resp['metrics']) - num_metrics) < 10): # allow some variation
124 print(testprefix + ' enumeration match count PASS')
125 else:
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):
144 return
145 # print req.content
146 result = req.json()
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')
152 else:
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):
178 return
179 # print req.content
180 result = req.json()
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):
201 return
202 # print req.content
203 result = req.json()
204 print('indom:'+str(result['indom']))
205 if (len(result['instances']) > 0):
206 print(' with instances')
207 else:
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):
228 return
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):
235 return
236 result = req.json()
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')