initial code repo
[stor4nfv.git] / src / ceph / qa / workunits / rest / test.py
1 #!/usr/bin/python
2
3 from __future__ import print_function
4
5 import json
6 import os
7 import requests
8 import subprocess
9 import sys
10 import time
11 import uuid
12 import xml.etree.ElementTree
13
14 BASEURL = os.environ.get('BASEURL', 'http://localhost:5000/api/v0.1')
15
16
17 def fail(r, msg):
18     print('FAILURE: url ', r.url, file=sys.stderr)
19     print(msg, file=sys.stderr)
20     print('Response content: ', r.text, file=sys.stderr)
21     print('Headers: ', r.headers, file=sys.stderr)
22     sys.exit(1)
23
24
25 def expect(url, method, respcode, contenttype, extra_hdrs=None, data=None):
26     failmsg, r = expect_nofail(url, method, respcode, contenttype, extra_hdrs,
27                                data)
28     if failmsg:
29         fail(r, failmsg)
30     return r
31
32
33 def expect_nofail(url, method, respcode, contenttype, extra_hdrs=None,
34                  data=None):
35
36     fdict = {'get':requests.get, 'put':requests.put}
37     f = fdict[method.lower()]
38     r = f(BASEURL + '/' + url, headers=extra_hdrs, data=data)
39
40     print('{0} {1}: {2} {3}'.format(method, url, contenttype, r.status_code))
41
42     if r.status_code != respcode:
43         return 'expected {0}, got {1}'.format(respcode, r.status_code), r
44
45     r_contenttype = r.headers['content-type']
46
47     if contenttype in ['json', 'xml']:
48         contenttype = 'application/' + contenttype
49     elif contenttype:
50         contenttype = 'text/' + contenttype
51
52     if contenttype and r_contenttype != contenttype:
53         return 'expected {0}, got "{1}"'.format(contenttype, r_contenttype), r
54
55     if contenttype.startswith('application'):
56         if r_contenttype == 'application/json':
57             try:
58                 # older requests.py doesn't create r.myjson; create it myself
59                 r.myjson = json.loads(r.text)
60                 assert(r.myjson is not None)
61             except Exception as e:
62                 return 'Invalid JSON returned: "{0}"'.format(str(e)), r
63
64         if r_contenttype == 'application/xml':
65             try:
66                 # if it's there, squirrel it away for use in the caller
67                 r.tree = xml.etree.ElementTree.fromstring(r.text)
68             except Exception as e:
69                 return 'Invalid XML returned: "{0}"'.format(str(e)), r
70
71     return '', r
72
73
74 JSONHDR={'accept':'application/json'}
75 XMLHDR={'accept':'application/xml'}
76
77 if __name__ == '__main__':
78     expect('auth/export', 'GET', 200, 'plain')
79     expect('auth/export.json', 'GET', 200, 'json')
80     expect('auth/export.xml', 'GET', 200, 'xml')
81     expect('auth/export', 'GET', 200, 'json', JSONHDR)
82     expect('auth/export', 'GET', 200, 'xml', XMLHDR)
83
84     expect('auth/add?entity=client.xx&'
85            'caps=mon&caps=allow&caps=osd&caps=allow+*', 'PUT', 200, 'json',
86             JSONHDR)
87
88     r = expect('auth/export?entity=client.xx', 'GET', 200, 'plain')
89     # must use text/plain; default is application/x-www-form-urlencoded
90     expect('auth/add?entity=client.xx', 'PUT', 200, 'plain',
91            {'Content-Type':'text/plain'}, data=r.text)
92
93     r = expect('auth/list', 'GET', 200, 'plain')
94     assert('client.xx' in r.text)
95
96     r = expect('auth/list.json', 'GET', 200, 'json')
97     dictlist = r.myjson['output']['auth_dump']
98     xxdict = [d for d in dictlist if d['entity'] == 'client.xx'][0]
99     assert(xxdict)
100     assert('caps' in xxdict)
101     assert('mon' in xxdict['caps'])
102     assert('osd' in xxdict['caps'])
103
104     expect('auth/get-key?entity=client.xx', 'GET', 200, 'json', JSONHDR)
105     expect('auth/print-key?entity=client.xx', 'GET', 200, 'json', JSONHDR)
106     expect('auth/print_key?entity=client.xx', 'GET', 200, 'json', JSONHDR)
107
108     expect('auth/caps?entity=client.xx&caps=osd&caps=allow+rw', 'PUT', 200,
109            'json', JSONHDR)
110     r = expect('auth/list.json', 'GET', 200, 'json')
111     dictlist = r.myjson['output']['auth_dump']
112     xxdict = [d for d in dictlist if d['entity'] == 'client.xx'][0]
113     assert(xxdict)
114     assert('caps' in xxdict)
115     assert(not 'mon' in xxdict['caps'])
116     assert('osd' in xxdict['caps'])
117     assert(xxdict['caps']['osd'] == 'allow rw')
118
119     # export/import/export, compare
120     r = expect('auth/export', 'GET', 200, 'plain')
121     exp1 = r.text
122     assert('client.xx' in exp1)
123     r = expect('auth/import', 'PUT', 200, 'plain',
124                {'Content-Type':'text/plain'}, data=r.text)
125     r2 = expect('auth/export', 'GET', 200, 'plain')
126     assert(exp1 == r2.text)
127     expect('auth/del?entity=client.xx', 'PUT', 200, 'json', JSONHDR)
128
129     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
130     assert('epoch' in r.myjson['output'])
131
132     assert('GLOBAL' in expect('df', 'GET', 200, 'plain').text)
133     assert('DIRTY' in expect('df?detail=detail', 'GET', 200, 'plain').text)
134     # test param with no value (treated as param=param)
135     assert('DIRTY' in expect('df?detail', 'GET', 200, 'plain').text)
136
137     r = expect('df', 'GET', 200, 'json', JSONHDR)
138     assert('total_used_bytes' in r.myjson['output']['stats'])
139     r = expect('df', 'GET', 200, 'xml', XMLHDR)
140     assert(r.tree.find('output/stats/stats/total_used_bytes') is not None)
141
142     r = expect('df?detail', 'GET', 200, 'json', JSONHDR)
143     assert('rd_bytes' in r.myjson['output']['pools'][0]['stats'])
144     r = expect('df?detail', 'GET', 200, 'xml', XMLHDR)
145     assert(r.tree.find('output/stats/pools/pool/stats/rd_bytes') is not None)
146
147     expect('fsid', 'GET', 200, 'json', JSONHDR)
148     expect('health', 'GET', 200, 'json', JSONHDR)
149     expect('health?detail', 'GET', 200, 'json', JSONHDR)
150     expect('health?detail', 'GET', 200, 'plain')
151
152     # XXX no ceph -w equivalent yet
153
154     expect('mds/cluster_down', 'PUT', 200, '')
155     expect('mds/cluster_down', 'PUT', 200, '')
156     expect('mds/cluster_up', 'PUT', 200, '')
157     expect('mds/cluster_up', 'PUT', 200, '')
158
159     expect('mds/compat/rm_incompat?feature=4', 'PUT', 200, '')
160     expect('mds/compat/rm_incompat?feature=4', 'PUT', 200, '')
161
162     r = expect('mds/compat/show', 'GET', 200, 'json', JSONHDR)
163     assert('incompat' in r.myjson['output'])
164     r = expect('mds/compat/show', 'GET', 200, 'xml', XMLHDR)
165     assert(r.tree.find('output/mds_compat/incompat') is not None)
166
167     # EEXIST from CLI
168     expect('mds/deactivate?who=2', 'PUT', 400, '')
169
170     r = expect('mds/dump.xml', 'GET', 200, 'xml')
171     assert(r.tree.find('output/mdsmap/created') is not None)
172
173     expect('fs/flag/set?flag_name=enable_multiple&val=true', 'PUT', 200, '')
174     expect('osd/pool/create?pg_num=1&pool=my_cephfs_metadata', 'PUT', 200, '')
175     expect('osd/pool/create?pg_num=1&pool=my_cephfs_data', 'PUT', 200, '')
176     expect('fs/new?fs_name=mycephfs&metadata=my_cephfs_metadata&data=my_cephfs_data', 'PUT', 200, '')
177     expect('osd/pool/create?pool=data2&pg_num=10', 'PUT', 200, '')
178     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
179     pools = r.myjson['output']['pools']
180     poolnum = None
181     for p in pools:
182         if p['pool_name'] == 'data2':
183             poolnum = p['pool']
184             assert(p['pg_num'] == 10)
185             break
186     assert(poolnum is not None)
187     expect('mds/add_data_pool?pool={0}'.format(poolnum), 'PUT', 200, '')
188     expect('mds/remove_data_pool?pool={0}'.format(poolnum), 'PUT', 200, '')
189     expect('osd/pool/delete?pool=data2&pool2=data2'
190            '&sure=--yes-i-really-really-mean-it', 'PUT', 200, '')
191     expect('mds/set?var=allow_multimds&val=true&confirm=--yes-i-really-mean-it', 'PUT', 200, '')
192     expect('mds/set_max_mds?maxmds=4', 'PUT', 200, '')
193     expect('mds/set?var=max_mds&val=4', 'PUT', 200, '')
194     expect('mds/set?var=max_file_size&val=1048576', 'PUT', 200, '')
195     expect('mds/set?var=allow_new_snaps&val=true&confirm=--yes-i-really-mean-it', 'PUT', 200, '')
196     expect('mds/set?var=allow_new_snaps&val=0', 'PUT', 200, '')
197     expect('mds/set?var=inline_data&val=true&confirm=--yes-i-really-mean-it', 'PUT', 200, '')
198     expect('mds/set?var=inline_data&val=0', 'PUT', 200, '')
199     r = expect('mds/dump.json', 'GET', 200, 'json')
200     assert(r.myjson['output']['max_mds'] == 4)
201     expect('mds/set_max_mds?maxmds=3', 'PUT', 200, '')
202     r = expect('mds/stat.json', 'GET', 200, 'json')
203     expect('mds/set?var=max_mds&val=2', 'PUT', 200, '')
204     r = expect('mds/stat.json', 'GET', 200, 'json')
205     assert('epoch' in r.myjson['output']['fsmap'])
206     r = expect('mds/stat.xml', 'GET', 200, 'xml')
207     assert(r.tree.find('output/mds_stat/fsmap/epoch') is not None)
208
209     # more content tests below, just check format here
210     expect('mon/dump.json', 'GET', 200, 'json')
211     expect('mon/dump.xml', 'GET', 200, 'xml')
212
213     r = expect('mon/getmap', 'GET', 200, '')
214     assert(len(r.text) != 0)
215     r = expect('mon_status.json', 'GET', 200, 'json')
216     assert('name' in r.myjson['output'])
217     r = expect('mon_status.xml', 'GET', 200, 'xml')
218     assert(r.tree.find('output/mon_status/name') is not None)
219
220     bl = '192.168.0.1:0/1000'
221     expect('osd/blacklist?blacklistop=add&addr=' + bl, 'PUT', 200, '')
222     r = expect('osd/blacklist/ls.json', 'GET', 200, 'json')
223     assert([b for b in r.myjson['output'] if b['addr'] == bl])
224     expect('osd/blacklist?blacklistop=rm&addr=' + bl, 'PUT', 200, '')
225     r = expect('osd/blacklist/ls.json', 'GET', 200, 'json')
226     assert([b for b in r.myjson['output'] if b['addr'] == bl] == [])
227
228     expect('osd/crush/tunables?profile=legacy', 'PUT', 200, '')
229     expect('osd/crush/tunables?profile=bobtail', 'PUT', 200, '')
230
231     expect('osd/scrub?who=0', 'PUT', 200, '')
232     expect('osd/deep-scrub?who=0', 'PUT', 200, '')
233     expect('osd/repair?who=0', 'PUT', 200, '')
234
235     expect('osd/set?key=noup', 'PUT', 200, '')
236
237     expect('osd/down?ids=0', 'PUT', 200, '')
238     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
239     assert(r.myjson['output']['osds'][0]['osd'] == 0)
240     assert(r.myjson['output']['osds'][0]['up'] == 0)
241
242     expect('osd/unset?key=noup', 'PUT', 200, '')
243
244     for i in range(0,100):
245         r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
246         assert(r.myjson['output']['osds'][0]['osd'] == 0)
247         if r.myjson['output']['osds'][0]['up'] == 1:
248             break
249         else:
250             print("waiting for osd.0 to come back up", file=sys.stderr)
251             time.sleep(10)
252
253     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
254     assert(r.myjson['output']['osds'][0]['osd'] == 0)
255     assert(r.myjson['output']['osds'][0]['up'] == 1)
256
257     r = expect('osd/find?id=1', 'GET', 200, 'json', JSONHDR)
258     assert(r.myjson['output']['osd'] == 1)
259
260     expect('osd/out?ids=1', 'PUT', 200, '')
261     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
262     assert(r.myjson['output']['osds'][1]['osd'] == 1)
263     assert(r.myjson['output']['osds'][1]['in'] == 0)
264
265     expect('osd/in?ids=1', 'PUT', 200, '')
266     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
267     assert(r.myjson['output']['osds'][1]['osd'] == 1)
268     assert(r.myjson['output']['osds'][1]['in'] == 1)
269
270     r = expect('osd/find?id=0', 'GET', 200, 'json', JSONHDR)
271     assert(r.myjson['output']['osd'] == 0)
272
273     r = expect('osd/getmaxosd', 'GET', 200, 'xml', XMLHDR)
274     assert(r.tree.find('output/getmaxosd/max_osd') is not None)
275     r = expect('osd/getmaxosd', 'GET', 200, 'json', JSONHDR)
276     saved_maxosd = r.myjson['output']['max_osd']
277     expect('osd/setmaxosd?newmax=10', 'PUT', 200, '')
278     r = expect('osd/getmaxosd', 'GET', 200, 'json', JSONHDR)
279     assert(r.myjson['output']['max_osd'] == 10)
280     expect('osd/setmaxosd?newmax={0}'.format(saved_maxosd), 'PUT', 200, '')
281     r = expect('osd/getmaxosd', 'GET', 200, 'json', JSONHDR)
282     assert(r.myjson['output']['max_osd'] == saved_maxosd)
283
284     osd_uuid=uuid.uuid1()
285     r = expect('osd/create?uuid={0}'.format(osd_uuid), 'PUT', 200, 'json', JSONHDR)
286     assert('osdid' in r.myjson['output'])
287     osdid = r.myjson['output']['osdid']
288     expect('osd/lost?id={0}'.format(osdid), 'PUT', 400, '')
289     expect('osd/lost?id={0}&sure=--yes-i-really-mean-it'.format(osdid),
290            'PUT', 200, 'json', JSONHDR)
291     expect('osd/rm?ids={0}'.format(osdid), 'PUT', 200, '')
292     r = expect('osd/ls', 'GET', 200, 'json', JSONHDR)
293     assert(isinstance(r.myjson['output'], list))
294     r = expect('osd/ls', 'GET', 200, 'xml', XMLHDR)
295     assert(r.tree.find('output/osds/osd') is not None)
296
297     expect('osd/pause', 'PUT', 200, '')
298     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
299     assert('pauserd,pausewr' in r.myjson['output']['flags'])
300     expect('osd/unpause', 'PUT', 200, '')
301     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
302     assert('pauserd,pausewr' not in r.myjson['output']['flags'])
303
304     r = expect('osd/tree', 'GET', 200, 'json', JSONHDR)
305     assert('nodes' in r.myjson['output'])
306     r = expect('osd/tree', 'GET', 200, 'xml', XMLHDR)
307     assert(r.tree.find('output/tree/nodes') is not None)
308
309     expect('osd/pool/create?pool=data2&pg_num=10', 'PUT', 200, '')
310     r = expect('osd/lspools', 'GET', 200, 'json', JSONHDR)
311     assert([p for p in r.myjson['output'] if p['poolname'] == 'data2'])
312     expect('osd/pool/rename?srcpool=data2&destpool=data3', 'PUT', 200, '')
313     r = expect('osd/lspools', 'GET', 200, 'json', JSONHDR)
314     assert([p for p in r.myjson['output'] if p['poolname'] == 'data3'])
315     expect('osd/pool/mksnap?pool=data3&snap=datasnap', 'PUT', 200, '')
316     r = subprocess.call('rados -p data3 lssnap | grep -q datasnap', shell=True)
317     assert(r == 0)
318     expect('osd/pool/rmsnap?pool=data3&snap=datasnap', 'PUT', 200, '')
319     expect('osd/pool/delete?pool=data3', 'PUT', 400, '')
320     expect('osd/pool/delete?pool=data3&pool2=data3&sure=--yes-i-really-really-mean-it', 'PUT', 200, '')
321
322     r = expect('osd/stat', 'GET', 200, 'json', JSONHDR)
323     assert('num_up_osds' in r.myjson['output'])
324     r = expect('osd/stat', 'GET', 200, 'xml', XMLHDR)
325     assert(r.tree.find('output/osdmap/num_up_osds') is not None)
326
327     r = expect('osd/ls', 'GET', 200, 'json', JSONHDR)
328     for osdid in r.myjson['output']:
329         expect('tell/osd.{0}/version'.format(osdid), 'GET', 200, '')
330
331     expect('pg/debug?debugop=unfound_objects_exist', 'GET', 200, '')
332     expect('pg/debug?debugop=degraded_pgs_exist', 'GET', 200, '')
333     expect('pg/deep-scrub?pgid=1.0', 'PUT', 200, '')
334     r = expect('pg/dump', 'GET', 200, 'json', JSONHDR)
335     assert('pg_stats_sum' in r.myjson['output'])
336     r = expect('pg/dump', 'GET', 200, 'xml', XMLHDR)
337     assert(r.tree.find('output/pg_map/pg_stats_sum') is not None)
338
339     expect('pg/dump_json', 'GET', 200, 'json', JSONHDR)
340     expect('pg/dump_pools_json', 'GET', 200, 'json', JSONHDR)
341     expect('pg/dump_stuck?stuckops=inactive', 'GET', 200, '')
342     expect('pg/dump_stuck?stuckops=unclean', 'GET', 200, '')
343     expect('pg/dump_stuck?stuckops=stale', 'GET', 200, '')
344
345     r = expect('pg/getmap', 'GET', 200, '')
346     assert(len(r.text) != 0)
347
348     r = expect('pg/map?pgid=1.0', 'GET', 200, 'json', JSONHDR)
349     assert('acting' in r.myjson['output'])
350     assert(r.myjson['output']['pgid'] == '1.0')
351     r = expect('pg/map?pgid=1.0', 'GET', 200, 'xml', XMLHDR)
352     assert(r.tree.find('output/pg_map/acting') is not None)
353     assert(r.tree.find('output/pg_map/pgid').text == '1.0')
354
355     expect('pg/repair?pgid=1.0', 'PUT', 200, '')
356     expect('pg/scrub?pgid=1.0', 'PUT', 200, '')
357
358     expect('osd/set-full-ratio?ratio=0.90', 'PUT', 200, '')
359     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
360     assert(float(r.myjson['output']['full_ratio']) == 0.90)
361     expect('osd/set-full-ratio?ratio=0.95', 'PUT', 200, '')
362     expect('osd/set-backfillfull-ratio?ratio=0.88', 'PUT', 200, '')
363     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
364     assert(float(r.myjson['output']['backfillfull_ratio']) == 0.88)
365     expect('osd/set-backfillfull-ratio?ratio=0.90', 'PUT', 200, '')
366     expect('osd/set-nearfull-ratio?ratio=0.90', 'PUT', 200, '')
367     r = expect('osd/dump', 'GET', 200, 'json', JSONHDR)
368     assert(float(r.myjson['output']['nearfull_ratio']) == 0.90)
369     expect('osd/set-nearfull-ratio?ratio=0.85', 'PUT', 200, '')
370
371     r = expect('pg/stat', 'GET', 200, 'json', JSONHDR)
372     assert('num_pgs' in r.myjson['output'])
373     r = expect('pg/stat', 'GET', 200, 'xml', XMLHDR)
374     assert(r.tree.find('output/pg_summary/num_pgs') is not None)
375
376     expect('tell/1.0/query', 'GET', 200, 'json', JSONHDR)
377     expect('quorum?quorumcmd=enter', 'PUT', 200, 'json', JSONHDR)
378     expect('quorum?quorumcmd=enter', 'PUT', 200, 'xml', XMLHDR)
379     expect('quorum_status', 'GET', 200, 'json', JSONHDR)
380     expect('quorum_status', 'GET', 200, 'xml', XMLHDR)
381
382     # report's CRC needs to be handled
383     # r = expect('report', 'GET', 200, 'json', JSONHDR)
384     # assert('osd_stats' in r.myjson['output'])
385     # r = expect('report', 'GET', 200, 'xml', XMLHDR)
386     # assert(r.tree.find('output/report/osdmap') is not None)
387
388     r = expect('status', 'GET', 200, 'json', JSONHDR)
389     assert('osdmap' in r.myjson['output'])
390     r = expect('status', 'GET', 200, 'xml', XMLHDR)
391     assert(r.tree.find('output/status/osdmap') is not None)
392
393     r = expect('tell/osd.0/version', 'GET', 200, '')
394     assert('ceph version' in r.text)
395     expect('tell/osd.999/version', 'GET', 400, '')
396     expect('tell/osd.foo/version', 'GET', 400, '')
397
398     r = expect('tell/osd.0/dump_pg_recovery_stats', 'GET', 200, '')
399     assert('Started' in r.text)
400
401     expect('osd/reweight?id=0&weight=0.9', 'PUT', 200, '')
402     expect('osd/reweight?id=0&weight=-1', 'PUT', 400, '')
403     expect('osd/reweight?id=0&weight=1', 'PUT', 200, '')
404
405     for v in ['pg_num', 'pgp_num', 'size', 'min_size',
406               'crush_rule']:
407         r = expect('osd/pool/get.json?pool=rbd&var=' + v, 'GET', 200, 'json')
408         assert(v in r.myjson['output'])
409
410     r = expect('osd/pool/get.json?pool=rbd&var=size', 'GET', 200, 'json')
411     assert(r.myjson['output']['size'] >= 2)
412
413     expect('osd/pool/set?pool=rbd&var=size&val=3', 'PUT', 200, 'plain')
414     r = expect('osd/pool/get.json?pool=rbd&var=size', 'GET', 200, 'json')
415     assert(r.myjson['output']['size'] == 3)
416
417     expect('osd/pool/set?pool=rbd&var=size&val=2', 'PUT', 200, 'plain')
418     r = expect('osd/pool/get.json?pool=rbd&var=size', 'GET', 200, 'json')
419     assert(r.myjson['output']['size'] == 2)
420
421     r = expect('osd/pool/get.json?pool=rbd&var=crush_rule', 'GET', 200, 'json')
422     assert(r.myjson['output']['crush_rule'] == "replicated_rule")
423
424     print('OK')