]> git.proxmox.com Git - ceph.git/blame - ceph/qa/tasks/radosgw_admin.py
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / qa / tasks / radosgw_admin.py
CommitLineData
7c673cae
FG
1"""
2Rgw admin testing against a running instance
3"""
4# The test cases in this file have been annotated for inventory.
5# To extract the inventory (in csv format) use the command:
6#
7# grep '^ *# TESTCASE' | sed 's/^ *# TESTCASE //'
8#
181888fb
FG
9# to run this standalone:
10# python qa/tasks/radosgw_admin.py [USER] HOSTNAME
11#
7c673cae 12
7c673cae
FG
13import json
14import logging
15import time
16import datetime
181888fb 17import sys
7c673cae 18
e306af50 19from io import BytesIO
f67539c2 20from queue import Queue
7c673cae
FG
21
22import boto.exception
23import boto.s3.connection
24import boto.s3.acl
25
26import httplib2
27
7c673cae 28
e306af50 29from tasks.util.rgw import rgwadmin, get_user_summary, get_user_successful_ops
7c673cae
FG
30
31log = logging.getLogger(__name__)
32
181888fb
FG
33def usage_acc_findentry2(entries, user, add=True):
34 for e in entries:
35 if e['user'] == user:
36 return e
37 if not add:
38 return None
39 e = {'user': user, 'buckets': []}
40 entries.append(e)
41 return e
42def usage_acc_findsum2(summaries, user, add=True):
43 for e in summaries:
44 if e['user'] == user:
45 return e
46 if not add:
47 return None
48 e = {'user': user, 'categories': [],
49 'total': {'bytes_received': 0,
50 'bytes_sent': 0, 'ops': 0, 'successful_ops': 0 }}
51 summaries.append(e)
52 return e
53def usage_acc_update2(x, out, b_in, err):
54 x['bytes_sent'] += b_in
55 x['bytes_received'] += out
56 x['ops'] += 1
57 if not err:
58 x['successful_ops'] += 1
59def usage_acc_validate_fields(r, x, x2, what):
60 q=[]
61 for field in ['bytes_sent', 'bytes_received', 'ops', 'successful_ops']:
62 try:
63 if x2[field] < x[field]:
64 q.append("field %s: %d < %d" % (field, x2[field], x[field]))
65 except Exception as ex:
66 r.append( "missing/bad field " + field + " in " + what + " " + str(ex))
67 return
68 if len(q) > 0:
69 r.append("incomplete counts in " + what + ": " + ", ".join(q))
70class usage_acc:
71 def __init__(self):
72 self.results = {'entries': [], 'summary': []}
73 def findentry(self, user):
74 return usage_acc_findentry2(self.results['entries'], user)
75 def findsum(self, user):
76 return usage_acc_findsum2(self.results['summary'], user)
77 def e2b(self, e, bucket, add=True):
78 for b in e['buckets']:
79 if b['bucket'] == bucket:
80 return b
81 if not add:
82 return None
83 b = {'bucket': bucket, 'categories': []}
84 e['buckets'].append(b)
85 return b
86 def c2x(self, c, cat, add=True):
87 for x in c:
88 if x['category'] == cat:
89 return x
90 if not add:
91 return None
92 x = {'bytes_received': 0, 'category': cat,
93 'bytes_sent': 0, 'ops': 0, 'successful_ops': 0 }
94 c.append(x)
95 return x
96 def update(self, c, cat, user, out, b_in, err):
97 x = self.c2x(c, cat)
98 usage_acc_update2(x, out, b_in, err)
e306af50 99 if not err and cat == 'create_bucket' and 'owner' not in x:
181888fb
FG
100 x['owner'] = user
101 def make_entry(self, cat, bucket, user, out, b_in, err):
102 if cat == 'create_bucket' and err:
103 return
104 e = self.findentry(user)
105 b = self.e2b(e, bucket)
106 self.update(b['categories'], cat, user, out, b_in, err)
107 s = self.findsum(user)
108 x = self.c2x(s['categories'], cat)
109 usage_acc_update2(x, out, b_in, err)
110 x = s['total']
111 usage_acc_update2(x, out, b_in, err)
112 def generate_make_entry(self):
113 return lambda cat,bucket,user,out,b_in,err: self.make_entry(cat, bucket, user, out, b_in, err)
114 def get_usage(self):
115 return self.results
116 def compare_results(self, results):
e306af50 117 if 'entries' not in results or 'summary' not in results:
181888fb
FG
118 return ['Missing entries or summary']
119 r = []
120 for e in self.results['entries']:
121 try:
122 e2 = usage_acc_findentry2(results['entries'], e['user'], False)
123 except Exception as ex:
124 r.append("malformed entry looking for user "
125 + e['user'] + " " + str(ex))
126 break
127 if e2 == None:
128 r.append("missing entry for user " + e['user'])
129 continue
130 for b in e['buckets']:
131 c = b['categories']
132 if b['bucket'] == 'nosuchbucket':
9f95a23c 133 print("got here")
181888fb
FG
134 try:
135 b2 = self.e2b(e2, b['bucket'], False)
136 if b2 != None:
137 c2 = b2['categories']
138 except Exception as ex:
139 r.append("malformed entry looking for bucket "
140 + b['bucket'] + " in user " + e['user'] + " " + str(ex))
141 break
142 if b2 == None:
143 r.append("can't find bucket " + b['bucket']
144 + " in user " + e['user'])
145 continue
146 for x in c:
147 try:
148 x2 = self.c2x(c2, x['category'], False)
149 except Exception as ex:
150 r.append("malformed entry looking for "
151 + x['category'] + " in bucket " + b['bucket']
152 + " user " + e['user'] + " " + str(ex))
153 break
154 usage_acc_validate_fields(r, x, x2, "entry: category "
155 + x['category'] + " bucket " + b['bucket']
156 + " in user " + e['user'])
157 for s in self.results['summary']:
158 c = s['categories']
159 try:
160 s2 = usage_acc_findsum2(results['summary'], s['user'], False)
161 except Exception as ex:
162 r.append("malformed summary looking for user " + e['user']
163 + " " + str(ex))
164 break
9f95a23c
TL
165 if s2 == None:
166 r.append("missing summary for user " + e['user'] + " " + str(ex))
167 continue
181888fb
FG
168 try:
169 c2 = s2['categories']
170 except Exception as ex:
171 r.append("malformed summary missing categories for user "
172 + e['user'] + " " + str(ex))
173 break
174 for x in c:
175 try:
176 x2 = self.c2x(c2, x['category'], False)
177 except Exception as ex:
178 r.append("malformed summary looking for "
179 + x['category'] + " user " + e['user'] + " " + str(ex))
180 break
181 usage_acc_validate_fields(r, x, x2, "summary: category "
182 + x['category'] + " in user " + e['user'])
183 x = s['total']
184 try:
185 x2 = s2['total']
186 except Exception as ex:
187 r.append("malformed summary looking for totals for user "
9f95a23c 188 + e['user'] + " " + str(ex))
181888fb
FG
189 break
190 usage_acc_validate_fields(r, x, x2, "summary: totals for user" + e['user'])
191 return r
192
193def ignore_this_entry(cat, bucket, user, out, b_in, err):
194 pass
195class requestlog_queue():
196 def __init__(self, add):
f67539c2 197 self.q = Queue(1000)
181888fb
FG
198 self.adder = add
199 def handle_request_data(self, request, response, error=False):
200 now = datetime.datetime.now()
9f95a23c
TL
201 if error:
202 pass
203 elif response.status < 200 or response.status >= 400:
204 error = True
92f5a8d4 205 self.q.put({'t': now, 'o': request, 'i': response, 'e': error})
181888fb
FG
206 def clear(self):
207 with self.q.mutex:
208 self.q.queue.clear()
209 def log_and_clear(self, cat, bucket, user, add_entry = None):
210 while not self.q.empty():
211 j = self.q.get()
9f95a23c 212 bytes_out = 0
92f5a8d4 213 if 'Content-Length' in j['o'].headers:
9f95a23c 214 bytes_out = int(j['o'].headers['Content-Length'])
181888fb 215 bytes_in = 0
f67539c2 216 msg = j['i'].msg
e306af50
TL
217 if 'content-length'in msg:
218 bytes_in = int(msg['content-length'])
181888fb 219 log.info('RL: %s %s %s bytes_out=%d bytes_in=%d failed=%r'
9f95a23c
TL
220 % (cat, bucket, user, bytes_out, bytes_in, j['e']))
221 if add_entry == None:
222 add_entry = self.adder
223 add_entry(cat, bucket, user, bytes_out, bytes_in, j['e'])
181888fb 224
7c673cae
FG
225def create_presigned_url(conn, method, bucket_name, key_name, expiration):
226 return conn.generate_url(expires_in=expiration,
227 method=method,
228 bucket=bucket_name,
229 key=key_name,
230 query_auth=True,
231 )
232
233def send_raw_http_request(conn, method, bucket_name, key_name, follow_redirects = False):
234 url = create_presigned_url(conn, method, bucket_name, key_name, 3600)
9f95a23c 235 print(url)
7c673cae
FG
236 h = httplib2.Http()
237 h.follow_redirects = follow_redirects
238 return h.request(url, method)
239
240
241def get_acl(key):
242 """
243 Helper function to get the xml acl from a key, ensuring that the xml
244 version tag is removed from the acl response
245 """
f67539c2 246 raw_acl = key.get_xml_acl().decode()
7c673cae
FG
247
248 def remove_version(string):
249 return string.split(
250 '<?xml version="1.0" encoding="UTF-8"?>'
251 )[-1]
252
253 def remove_newlines(string):
254 return string.strip('\n')
255
256 return remove_version(
257 remove_newlines(raw_acl)
258 )
259
260def task(ctx, config):
261 """
262 Test radosgw-admin functionality against a running rgw instance.
263 """
264 global log
265
7c673cae
FG
266 assert ctx.rgw.config, \
267 "radosgw_admin task needs a config passed from the rgw task"
268 config = ctx.rgw.config
269 log.debug('config is: %r', config)
270
271 clients_from_config = config.keys()
272
273 # choose first client as default
e306af50 274 client = next(iter(clients_from_config))
7c673cae 275
7c673cae
FG
276 # once the client is chosen, pull the host name and assigned port out of
277 # the role_endpoints that were assigned by the rgw task
11fdf7f2 278 endpoint = ctx.rgw.role_endpoints[client]
7c673cae 279
7c673cae
FG
280 ##
281 user1='foo'
282 user2='fud'
9f95a23c
TL
283 user3='bar'
284 user4='bud'
7c673cae
FG
285 subuser1='foo:foo1'
286 subuser2='foo:foo2'
287 display_name1='Foo'
288 display_name2='Fud'
9f95a23c 289 display_name3='Bar'
7c673cae 290 email='foo@foo.com'
7c673cae
FG
291 access_key='9te6NH5mcdcq0Tc5i8i1'
292 secret_key='Ny4IOauQoL18Gp2zM7lC1vLmoawgqcYP/YGcWfXu'
293 access_key2='p5YnriCv1nAtykxBrupQ'
294 secret_key2='Q8Tk6Q/27hfbFSYdSkPtUqhqx1GgzvpXa4WARozh'
9f95a23c
TL
295 access_key3='NX5QOQKC6BH2IDN8HC7A'
296 secret_key3='LnEsqNNqZIpkzauboDcLXLcYaWwLQ3Kop0zAnKIn'
7c673cae
FG
297 swift_secret1='gpS2G9RREMrnbqlp29PP2D36kgPR1tm72n5fPYfL'
298 swift_secret2='ri2VJQcKSYATOY6uaDUX7pxgkW+W1YmC6OCxPHwy'
299
300 bucket_name='myfoo'
301 bucket_name2='mybar'
302
303 # connect to rgw
304 connection = boto.s3.connection.S3Connection(
305 aws_access_key_id=access_key,
306 aws_secret_access_key=secret_key,
307 is_secure=False,
11fdf7f2
TL
308 port=endpoint.port,
309 host=endpoint.hostname,
7c673cae
FG
310 calling_format=boto.s3.connection.OrdinaryCallingFormat(),
311 )
312 connection2 = boto.s3.connection.S3Connection(
313 aws_access_key_id=access_key2,
314 aws_secret_access_key=secret_key2,
315 is_secure=False,
11fdf7f2
TL
316 port=endpoint.port,
317 host=endpoint.hostname,
7c673cae
FG
318 calling_format=boto.s3.connection.OrdinaryCallingFormat(),
319 )
9f95a23c
TL
320 connection3 = boto.s3.connection.S3Connection(
321 aws_access_key_id=access_key3,
322 aws_secret_access_key=secret_key3,
323 is_secure=False,
324 port=endpoint.port,
325 host=endpoint.hostname,
326 calling_format=boto.s3.connection.OrdinaryCallingFormat(),
327 )
7c673cae 328
181888fb
FG
329 acc = usage_acc()
330 rl = requestlog_queue(acc.generate_make_entry())
331 connection.set_request_hook(rl)
332 connection2.set_request_hook(rl)
9f95a23c 333 connection3.set_request_hook(rl)
181888fb 334
7c673cae
FG
335 # legend (test cases can be easily grep-ed out)
336 # TESTCASE 'testname','object','method','operation','assertion'
181888fb
FG
337
338 # TESTCASE 'usage-show0' 'usage' 'show' 'all usage' 'succeeds'
339 (err, summary0) = rgwadmin(ctx, client, ['usage', 'show'], check_status=True)
340
7c673cae
FG
341 # TESTCASE 'info-nosuch','user','info','non-existent user','fails'
342 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1])
343 assert err
344
345 # TESTCASE 'create-ok','user','create','w/all valid info','succeeds'
346 (err, out) = rgwadmin(ctx, client, [
347 'user', 'create',
348 '--uid', user1,
349 '--display-name', display_name1,
350 '--email', email,
351 '--access-key', access_key,
352 '--secret', secret_key,
353 '--max-buckets', '4'
354 ],
355 check_status=True)
356
357 # TESTCASE 'duplicate email','user','create','existing user email','fails'
358 (err, out) = rgwadmin(ctx, client, [
359 'user', 'create',
360 '--uid', user2,
361 '--display-name', display_name2,
362 '--email', email,
363 ])
364 assert err
365
366 # TESTCASE 'info-existing','user','info','existing user','returns correct info'
367 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1], check_status=True)
368 assert out['user_id'] == user1
369 assert out['email'] == email
370 assert out['display_name'] == display_name1
371 assert len(out['keys']) == 1
372 assert out['keys'][0]['access_key'] == access_key
373 assert out['keys'][0]['secret_key'] == secret_key
374 assert not out['suspended']
375
7c673cae
FG
376 # TESTCASE 'suspend-ok','user','suspend','active user','succeeds'
377 (err, out) = rgwadmin(ctx, client, ['user', 'suspend', '--uid', user1],
378 check_status=True)
379
380 # TESTCASE 'suspend-suspended','user','suspend','suspended user','succeeds w/advisory'
381 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1], check_status=True)
382 assert out['suspended']
383
384 # TESTCASE 're-enable','user','enable','suspended user','succeeds'
385 (err, out) = rgwadmin(ctx, client, ['user', 'enable', '--uid', user1], check_status=True)
386
387 # TESTCASE 'info-re-enabled','user','info','re-enabled user','no longer suspended'
388 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1], check_status=True)
389 assert not out['suspended']
390
391 # TESTCASE 'add-keys','key','create','w/valid info','succeeds'
392 (err, out) = rgwadmin(ctx, client, [
393 'key', 'create', '--uid', user1,
394 '--access-key', access_key2, '--secret', secret_key2,
395 ], check_status=True)
396
397 # TESTCASE 'info-new-key','user','info','after key addition','returns all keys'
398 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1],
399 check_status=True)
400 assert len(out['keys']) == 2
401 assert out['keys'][0]['access_key'] == access_key2 or out['keys'][1]['access_key'] == access_key2
402 assert out['keys'][0]['secret_key'] == secret_key2 or out['keys'][1]['secret_key'] == secret_key2
403
404 # TESTCASE 'rm-key','key','rm','newly added key','succeeds, key is removed'
405 (err, out) = rgwadmin(ctx, client, [
406 'key', 'rm', '--uid', user1,
407 '--access-key', access_key2,
408 ], check_status=True)
409 assert len(out['keys']) == 1
410 assert out['keys'][0]['access_key'] == access_key
411 assert out['keys'][0]['secret_key'] == secret_key
412
413 # TESTCASE 'add-swift-key','key','create','swift key','succeeds'
414 subuser_access = 'full'
415 subuser_perm = 'full-control'
416
417 (err, out) = rgwadmin(ctx, client, [
418 'subuser', 'create', '--subuser', subuser1,
419 '--access', subuser_access
420 ], check_status=True)
421
422 # TESTCASE 'add-swift-key','key','create','swift key','succeeds'
423 (err, out) = rgwadmin(ctx, client, [
424 'subuser', 'modify', '--subuser', subuser1,
425 '--secret', swift_secret1,
426 '--key-type', 'swift',
427 ], check_status=True)
428
429 # TESTCASE 'subuser-perm-mask', 'subuser', 'info', 'test subuser perm mask durability', 'succeeds'
430 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1])
431
432 assert out['subusers'][0]['permissions'] == subuser_perm
433
434 # TESTCASE 'info-swift-key','user','info','after key addition','returns all keys'
435 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1], check_status=True)
436 assert len(out['swift_keys']) == 1
437 assert out['swift_keys'][0]['user'] == subuser1
438 assert out['swift_keys'][0]['secret_key'] == swift_secret1
439
440 # TESTCASE 'add-swift-subuser','key','create','swift sub-user key','succeeds'
441 (err, out) = rgwadmin(ctx, client, [
442 'subuser', 'create', '--subuser', subuser2,
443 '--secret', swift_secret2,
444 '--key-type', 'swift',
445 ], check_status=True)
446
447 # TESTCASE 'info-swift-subuser','user','info','after key addition','returns all sub-users/keys'
448 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1], check_status=True)
449 assert len(out['swift_keys']) == 2
450 assert out['swift_keys'][0]['user'] == subuser2 or out['swift_keys'][1]['user'] == subuser2
451 assert out['swift_keys'][0]['secret_key'] == swift_secret2 or out['swift_keys'][1]['secret_key'] == swift_secret2
452
453 # TESTCASE 'rm-swift-key1','key','rm','subuser','succeeds, one key is removed'
454 (err, out) = rgwadmin(ctx, client, [
455 'key', 'rm', '--subuser', subuser1,
456 '--key-type', 'swift',
457 ], check_status=True)
458 assert len(out['swift_keys']) == 1
459
460 # TESTCASE 'rm-subuser','subuser','rm','subuser','success, subuser is removed'
461 (err, out) = rgwadmin(ctx, client, [
462 'subuser', 'rm', '--subuser', subuser1,
463 ], check_status=True)
464 assert len(out['subusers']) == 1
465
466 # TESTCASE 'rm-subuser-with-keys','subuser','rm','subuser','succeeds, second subser and key is removed'
467 (err, out) = rgwadmin(ctx, client, [
468 'subuser', 'rm', '--subuser', subuser2,
469 '--key-type', 'swift', '--purge-keys',
470 ], check_status=True)
471 assert len(out['swift_keys']) == 0
472 assert len(out['subusers']) == 0
473
474 # TESTCASE 'bucket-stats','bucket','stats','no session/buckets','succeeds, empty list'
475 (err, out) = rgwadmin(ctx, client, ['bucket', 'stats', '--uid', user1],
476 check_status=True)
477 assert len(out) == 0
478
7c673cae
FG
479 # TESTCASE 'bucket-stats2','bucket','stats','no buckets','succeeds, empty list'
480 (err, out) = rgwadmin(ctx, client, ['bucket', 'list', '--uid', user1], check_status=True)
481 assert len(out) == 0
482
483 # create a first bucket
484 bucket = connection.create_bucket(bucket_name)
485
181888fb
FG
486 rl.log_and_clear("create_bucket", bucket_name, user1)
487
7c673cae
FG
488 # TESTCASE 'bucket-list','bucket','list','one bucket','succeeds, expected list'
489 (err, out) = rgwadmin(ctx, client, ['bucket', 'list', '--uid', user1], check_status=True)
490 assert len(out) == 1
491 assert out[0] == bucket_name
492
181888fb
FG
493 bucket_list = connection.get_all_buckets()
494 assert len(bucket_list) == 1
495 assert bucket_list[0].name == bucket_name
496
497 rl.log_and_clear("list_buckets", '', user1)
498
7c673cae
FG
499 # TESTCASE 'bucket-list-all','bucket','list','all buckets','succeeds, expected list'
500 (err, out) = rgwadmin(ctx, client, ['bucket', 'list'], check_status=True)
501 assert len(out) >= 1
502 assert bucket_name in out;
503
504 # TESTCASE 'max-bucket-limit,'bucket','create','4 buckets','5th bucket fails due to max buckets == 4'
505 bucket2 = connection.create_bucket(bucket_name + '2')
181888fb 506 rl.log_and_clear("create_bucket", bucket_name + '2', user1)
7c673cae 507 bucket3 = connection.create_bucket(bucket_name + '3')
181888fb 508 rl.log_and_clear("create_bucket", bucket_name + '3', user1)
7c673cae 509 bucket4 = connection.create_bucket(bucket_name + '4')
181888fb 510 rl.log_and_clear("create_bucket", bucket_name + '4', user1)
7c673cae
FG
511 # the 5th should fail.
512 failed = False
513 try:
514 connection.create_bucket(bucket_name + '5')
515 except Exception:
516 failed = True
517 assert failed
181888fb 518 rl.log_and_clear("create_bucket", bucket_name + '5', user1)
7c673cae
FG
519
520 # delete the buckets
521 bucket2.delete()
181888fb 522 rl.log_and_clear("delete_bucket", bucket_name + '2', user1)
7c673cae 523 bucket3.delete()
181888fb 524 rl.log_and_clear("delete_bucket", bucket_name + '3', user1)
7c673cae 525 bucket4.delete()
181888fb 526 rl.log_and_clear("delete_bucket", bucket_name + '4', user1)
7c673cae
FG
527
528 # TESTCASE 'bucket-stats3','bucket','stats','new empty bucket','succeeds, empty list'
529 (err, out) = rgwadmin(ctx, client, [
530 'bucket', 'stats', '--bucket', bucket_name], check_status=True)
531 assert out['owner'] == user1
532 bucket_id = out['id']
533
534 # TESTCASE 'bucket-stats4','bucket','stats','new empty bucket','succeeds, expected bucket ID'
535 (err, out) = rgwadmin(ctx, client, ['bucket', 'stats', '--uid', user1], check_status=True)
536 assert len(out) == 1
537 assert out[0]['id'] == bucket_id # does it return the same ID twice in a row?
538
539 # use some space
540 key = boto.s3.key.Key(bucket)
541 key.set_contents_from_string('one')
181888fb 542 rl.log_and_clear("put_obj", bucket_name, user1)
7c673cae
FG
543
544 # TESTCASE 'bucket-stats5','bucket','stats','after creating key','succeeds, lists one non-empty object'
545 (err, out) = rgwadmin(ctx, client, [
546 'bucket', 'stats', '--bucket', bucket_name], check_status=True)
547 assert out['id'] == bucket_id
548 assert out['usage']['rgw.main']['num_objects'] == 1
549 assert out['usage']['rgw.main']['size_kb'] > 0
550
9f95a23c
TL
551 #validate we have a positive user stats now
552 (err, out) = rgwadmin(ctx, client,
553 ['user', 'stats','--uid', user1, '--sync-stats'],
554 check_status=True)
555 assert out['stats']['size'] > 0
556
7c673cae
FG
557 # reclaim it
558 key.delete()
181888fb 559 rl.log_and_clear("delete_obj", bucket_name, user1)
7c673cae
FG
560
561 # TESTCASE 'bucket unlink', 'bucket', 'unlink', 'unlink bucket from user', 'fails', 'access denied error'
562 (err, out) = rgwadmin(ctx, client,
563 ['bucket', 'unlink', '--uid', user1, '--bucket', bucket_name],
564 check_status=True)
565
566 # create a second user to link the bucket to
567 (err, out) = rgwadmin(ctx, client, [
568 'user', 'create',
569 '--uid', user2,
570 '--display-name', display_name2,
571 '--access-key', access_key2,
572 '--secret', secret_key2,
573 '--max-buckets', '1',
574 ],
575 check_status=True)
576
577 # try creating an object with the first user before the bucket is relinked
578 denied = False
579 key = boto.s3.key.Key(bucket)
580
581 try:
582 key.set_contents_from_string('two')
583 except boto.exception.S3ResponseError:
584 denied = True
585
586 assert not denied
181888fb 587 rl.log_and_clear("put_obj", bucket_name, user1)
7c673cae
FG
588
589 # delete the object
590 key.delete()
181888fb 591 rl.log_and_clear("delete_obj", bucket_name, user1)
7c673cae
FG
592
593 # link the bucket to another user
594 (err, out) = rgwadmin(ctx, client, ['metadata', 'get', 'bucket:{n}'.format(n=bucket_name)],
595 check_status=True)
596
597 bucket_data = out['data']
598 assert bucket_data['bucket']['name'] == bucket_name
599
600 bucket_id = bucket_data['bucket']['bucket_id']
601
602 # link the bucket to another user
603 (err, out) = rgwadmin(ctx, client, ['bucket', 'link', '--uid', user2, '--bucket', bucket_name, '--bucket-id', bucket_id],
604 check_status=True)
605
606 # try to remove user, should fail (has a linked bucket)
607 (err, out) = rgwadmin(ctx, client, ['user', 'rm', '--uid', user2])
608 assert err
609
610 # TESTCASE 'bucket unlink', 'bucket', 'unlink', 'unlink bucket from user', 'succeeds, bucket unlinked'
611 (err, out) = rgwadmin(ctx, client, ['bucket', 'unlink', '--uid', user2, '--bucket', bucket_name],
612 check_status=True)
613
614 # relink the bucket to the first user and delete the second user
615 (err, out) = rgwadmin(ctx, client,
616 ['bucket', 'link', '--uid', user1, '--bucket', bucket_name, '--bucket-id', bucket_id],
617 check_status=True)
618
619 (err, out) = rgwadmin(ctx, client, ['user', 'rm', '--uid', user2],
620 check_status=True)
621
9f95a23c
TL
622 #TESTCASE 'bucket link', 'bucket', 'tenanted user', 'succeeds'
623 tenant_name = "testx"
624 # create a tenanted user to link the bucket to
625 (err, out) = rgwadmin(ctx, client, [
626 'user', 'create',
627 '--tenant', tenant_name,
628 '--uid', 'tenanteduser',
629 '--display-name', 'tenanted-user',
630 '--access-key', access_key2,
631 '--secret', secret_key2,
632 '--max-buckets', '1',
633 ],
634 check_status=True)
635
636 # link the bucket to a tenanted user
637 (err, out) = rgwadmin(ctx, client, ['bucket', 'link', '--bucket', '/' + bucket_name, '--tenant', tenant_name, '--uid', 'tenanteduser'],
638 check_status=True)
639
640 # check if the bucket name has tenant/ prefix
641 (err, out) = rgwadmin(ctx, client, ['metadata', 'get', 'bucket:{n}'.format(n= tenant_name + '/' + bucket_name)],
642 check_status=True)
643
644 bucket_data = out['data']
645 assert bucket_data['bucket']['name'] == bucket_name
646 assert bucket_data['bucket']['tenant'] == tenant_name
647
648 # relink the bucket to the first user and delete the tenanted user
649 (err, out) = rgwadmin(ctx, client,
650 ['bucket', 'link', '--bucket', tenant_name + '/' + bucket_name, '--uid', user1],
651 check_status=True)
652
653 (err, out) = rgwadmin(ctx, client, ['user', 'rm', '--tenant', tenant_name, '--uid', 'tenanteduser'],
654 check_status=True)
655
7c673cae
FG
656 # TESTCASE 'object-rm', 'object', 'rm', 'remove object', 'succeeds, object is removed'
657
658 # upload an object
659 object_name = 'four'
660 key = boto.s3.key.Key(bucket, object_name)
661 key.set_contents_from_string(object_name)
181888fb
FG
662 rl.log_and_clear("put_obj", bucket_name, user1)
663
664 # fetch it too (for usage stats presently)
e306af50 665 s = key.get_contents_as_string(encoding='ascii')
181888fb
FG
666 rl.log_and_clear("get_obj", bucket_name, user1)
667 assert s == object_name
668 # list bucket too (for usage stats presently)
669 keys = list(bucket.list())
670 rl.log_and_clear("list_bucket", bucket_name, user1)
671 assert len(keys) == 1
672 assert keys[0].name == object_name
7c673cae
FG
673
674 # now delete it
675 (err, out) = rgwadmin(ctx, client,
676 ['object', 'rm', '--bucket', bucket_name, '--object', object_name],
677 check_status=True)
678
679 # TESTCASE 'bucket-stats6','bucket','stats','after deleting key','succeeds, lists one no objects'
680 (err, out) = rgwadmin(ctx, client, [
681 'bucket', 'stats', '--bucket', bucket_name],
682 check_status=True)
683 assert out['id'] == bucket_id
684 assert out['usage']['rgw.main']['num_objects'] == 0
685
686 # list log objects
687 # TESTCASE 'log-list','log','list','after activity','succeeds, lists one no objects'
688 (err, out) = rgwadmin(ctx, client, ['log', 'list'], check_status=True)
689 assert len(out) > 0
690
691 for obj in out:
692 # TESTCASE 'log-show','log','show','after activity','returns expected info'
693 if obj[:4] == 'meta' or obj[:4] == 'data' or obj[:18] == 'obj_delete_at_hint':
694 continue
695
696 (err, rgwlog) = rgwadmin(ctx, client, ['log', 'show', '--object', obj],
697 check_status=True)
698 assert len(rgwlog) > 0
699
700 # exempt bucket_name2 from checking as it was only used for multi-region tests
701 assert rgwlog['bucket'].find(bucket_name) == 0 or rgwlog['bucket'].find(bucket_name2) == 0
702 assert rgwlog['bucket'] != bucket_name or rgwlog['bucket_id'] == bucket_id
703 assert rgwlog['bucket_owner'] == user1 or rgwlog['bucket'] == bucket_name + '5' or rgwlog['bucket'] == bucket_name2
704 for entry in rgwlog['log_entries']:
705 log.debug('checking log entry: ', entry)
706 assert entry['bucket'] == rgwlog['bucket']
707 possible_buckets = [bucket_name + '5', bucket_name2]
708 user = entry['user']
709 assert user == user1 or user.endswith('system-user') or \
710 rgwlog['bucket'] in possible_buckets
711
712 # TESTCASE 'log-rm','log','rm','delete log objects','succeeds'
713 (err, out) = rgwadmin(ctx, client, ['log', 'rm', '--object', obj],
714 check_status=True)
715
716 # TODO: show log by bucket+date
717
7c673cae
FG
718 # TESTCASE 'user-suspend2','user','suspend','existing user','succeeds'
719 (err, out) = rgwadmin(ctx, client, ['user', 'suspend', '--uid', user1],
720 check_status=True)
721
722 # TESTCASE 'user-suspend3','user','suspend','suspended user','cannot write objects'
181888fb 723 denied = False
7c673cae
FG
724 try:
725 key = boto.s3.key.Key(bucket)
726 key.set_contents_from_string('five')
727 except boto.exception.S3ResponseError as e:
181888fb 728 denied = True
7c673cae
FG
729 assert e.status == 403
730
181888fb
FG
731 assert denied
732 rl.log_and_clear("put_obj", bucket_name, user1)
733
7c673cae
FG
734 # TESTCASE 'user-renable2','user','enable','suspended user','succeeds'
735 (err, out) = rgwadmin(ctx, client, ['user', 'enable', '--uid', user1],
736 check_status=True)
737
738 # TESTCASE 'user-renable3','user','enable','reenabled user','can write objects'
739 key = boto.s3.key.Key(bucket)
740 key.set_contents_from_string('six')
181888fb 741 rl.log_and_clear("put_obj", bucket_name, user1)
7c673cae
FG
742
743 # TESTCASE 'gc-list', 'gc', 'list', 'get list of objects ready for garbage collection'
744
745 # create an object large enough to be split into multiple parts
746 test_string = 'foo'*10000000
747
748 big_key = boto.s3.key.Key(bucket)
749 big_key.set_contents_from_string(test_string)
181888fb 750 rl.log_and_clear("put_obj", bucket_name, user1)
7c673cae
FG
751
752 # now delete the head
753 big_key.delete()
181888fb 754 rl.log_and_clear("delete_obj", bucket_name, user1)
7c673cae
FG
755
756 # wait a bit to give the garbage collector time to cycle
757 time.sleep(15)
758
759 (err, out) = rgwadmin(ctx, client, ['gc', 'list'])
760
761 assert len(out) > 0
762
763 # TESTCASE 'gc-process', 'gc', 'process', 'manually collect garbage'
764 (err, out) = rgwadmin(ctx, client, ['gc', 'process'], check_status=True)
765
766 #confirm
767 (err, out) = rgwadmin(ctx, client, ['gc', 'list'])
768
769 assert len(out) == 0
770
771 # TESTCASE 'rm-user-buckets','user','rm','existing user','fails, still has buckets'
772 (err, out) = rgwadmin(ctx, client, ['user', 'rm', '--uid', user1])
773 assert err
774
775 # delete should fail because ``key`` still exists
776 try:
777 bucket.delete()
778 except boto.exception.S3ResponseError as e:
779 assert e.status == 409
181888fb 780 rl.log_and_clear("delete_bucket", bucket_name, user1)
7c673cae
FG
781
782 key.delete()
181888fb 783 rl.log_and_clear("delete_obj", bucket_name, user1)
7c673cae 784 bucket.delete()
181888fb 785 rl.log_and_clear("delete_bucket", bucket_name, user1)
7c673cae
FG
786
787 # TESTCASE 'policy', 'bucket', 'policy', 'get bucket policy', 'returns S3 policy'
788 bucket = connection.create_bucket(bucket_name)
181888fb 789 rl.log_and_clear("create_bucket", bucket_name, user1)
7c673cae
FG
790
791 # create an object
792 key = boto.s3.key.Key(bucket)
793 key.set_contents_from_string('seven')
181888fb 794 rl.log_and_clear("put_obj", bucket_name, user1)
7c673cae
FG
795
796 # should be private already but guarantee it
797 key.set_acl('private')
181888fb 798 rl.log_and_clear("put_acls", bucket_name, user1)
7c673cae
FG
799
800 (err, out) = rgwadmin(ctx, client,
f67539c2 801 ['policy', '--bucket', bucket.name, '--object', key.key.decode()],
7c673cae
FG
802 check_status=True, format='xml')
803
804 acl = get_acl(key)
181888fb 805 rl.log_and_clear("get_acls", bucket_name, user1)
7c673cae
FG
806
807 assert acl == out.strip('\n')
808
809 # add another grantee by making the object public read
810 key.set_acl('public-read')
181888fb 811 rl.log_and_clear("put_acls", bucket_name, user1)
7c673cae
FG
812
813 (err, out) = rgwadmin(ctx, client,
f67539c2 814 ['policy', '--bucket', bucket.name, '--object', key.key.decode()],
7c673cae
FG
815 check_status=True, format='xml')
816
817 acl = get_acl(key)
181888fb 818 rl.log_and_clear("get_acls", bucket_name, user1)
7c673cae
FG
819
820 assert acl == out.strip('\n')
821
822 # TESTCASE 'rm-bucket', 'bucket', 'rm', 'bucket with objects', 'succeeds'
823 bucket = connection.create_bucket(bucket_name)
181888fb 824 rl.log_and_clear("create_bucket", bucket_name, user1)
7c673cae
FG
825 key_name = ['eight', 'nine', 'ten', 'eleven']
826 for i in range(4):
827 key = boto.s3.key.Key(bucket)
828 key.set_contents_from_string(key_name[i])
181888fb 829 rl.log_and_clear("put_obj", bucket_name, user1)
7c673cae
FG
830
831 (err, out) = rgwadmin(ctx, client,
832 ['bucket', 'rm', '--bucket', bucket_name, '--purge-objects'],
833 check_status=True)
834
835 # TESTCASE 'caps-add', 'caps', 'add', 'add user cap', 'succeeds'
836 caps='user=read'
837 (err, out) = rgwadmin(ctx, client, ['caps', 'add', '--uid', user1, '--caps', caps])
838
839 assert out['caps'][0]['perm'] == 'read'
840
841 # TESTCASE 'caps-rm', 'caps', 'rm', 'remove existing cap from user', 'succeeds'
842 (err, out) = rgwadmin(ctx, client, ['caps', 'rm', '--uid', user1, '--caps', caps])
843
844 assert not out['caps']
845
846 # TESTCASE 'rm-user','user','rm','existing user','fails, still has buckets'
847 bucket = connection.create_bucket(bucket_name)
181888fb 848 rl.log_and_clear("create_bucket", bucket_name, user1)
7c673cae
FG
849 key = boto.s3.key.Key(bucket)
850
851 (err, out) = rgwadmin(ctx, client, ['user', 'rm', '--uid', user1])
852 assert err
853
854 # TESTCASE 'rm-user2', 'user', 'rm', 'user with data', 'succeeds'
855 bucket = connection.create_bucket(bucket_name)
181888fb 856 rl.log_and_clear("create_bucket", bucket_name, user1)
7c673cae
FG
857 key = boto.s3.key.Key(bucket)
858 key.set_contents_from_string('twelve')
181888fb
FG
859 rl.log_and_clear("put_obj", bucket_name, user1)
860
861 time.sleep(35)
862
863 # need to wait for all usage data to get flushed, should take up to 30 seconds
864 timestamp = time.time()
865 while time.time() - timestamp <= (2 * 60): # wait up to 20 minutes
866 (err, out) = rgwadmin(ctx, client, ['usage', 'show', '--categories', 'delete_obj']) # one of the operations we did is delete_obj, should be present.
867 if get_user_successful_ops(out, user1) > 0:
868 break
869 time.sleep(1)
870
871 assert time.time() - timestamp <= (20 * 60)
872
873 # TESTCASE 'usage-show' 'usage' 'show' 'all usage' 'succeeds'
874 (err, out) = rgwadmin(ctx, client, ['usage', 'show'], check_status=True)
875 assert len(out['entries']) > 0
876 assert len(out['summary']) > 0
877
878 r = acc.compare_results(out)
879 if len(r) != 0:
880 sys.stderr.write(("\n".join(r))+"\n")
881 assert(len(r) == 0)
882
883 user_summary = get_user_summary(out, user1)
884
885 total = user_summary['total']
886 assert total['successful_ops'] > 0
887
888 # TESTCASE 'usage-show2' 'usage' 'show' 'user usage' 'succeeds'
889 (err, out) = rgwadmin(ctx, client, ['usage', 'show', '--uid', user1],
890 check_status=True)
891 assert len(out['entries']) > 0
892 assert len(out['summary']) > 0
893 user_summary = out['summary'][0]
894 for entry in user_summary['categories']:
895 assert entry['successful_ops'] > 0
896 assert user_summary['user'] == user1
897
898 # TESTCASE 'usage-show3' 'usage' 'show' 'user usage categories' 'succeeds'
899 test_categories = ['create_bucket', 'put_obj', 'delete_obj', 'delete_bucket']
900 for cat in test_categories:
901 (err, out) = rgwadmin(ctx, client, ['usage', 'show', '--uid', user1, '--categories', cat],
902 check_status=True)
903 assert len(out['summary']) > 0
904 user_summary = out['summary'][0]
905 assert user_summary['user'] == user1
906 assert len(user_summary['categories']) == 1
907 entry = user_summary['categories'][0]
908 assert entry['category'] == cat
909 assert entry['successful_ops'] > 0
910
9f95a23c
TL
911 # TESTCASE 'user-rename', 'user', 'rename', 'existing user', 'new user', 'succeeds'
912 # create a new user user3
913 (err, out) = rgwadmin(ctx, client, [
914 'user', 'create',
915 '--uid', user3,
916 '--display-name', display_name3,
917 '--access-key', access_key3,
918 '--secret', secret_key3,
919 '--max-buckets', '4'
920 ],
921 check_status=True)
922
923 # create a bucket
924 bucket = connection3.create_bucket(bucket_name + '6')
925
926 rl.log_and_clear("create_bucket", bucket_name + '6', user3)
927
928 # create object
929 object_name1 = 'thirteen'
930 key1 = boto.s3.key.Key(bucket, object_name1)
931 key1.set_contents_from_string(object_name1)
932 rl.log_and_clear("put_obj", bucket_name + '6', user3)
933
934 # rename user3
935 (err, out) = rgwadmin(ctx, client, ['user', 'rename', '--uid', user3, '--new-uid', user4], check_status=True)
936 assert out['user_id'] == user4
937 assert out['keys'][0]['access_key'] == access_key3
938 assert out['keys'][0]['secret_key'] == secret_key3
939
940 time.sleep(5)
941
942 # get bucket and object to test if user keys are preserved
943 bucket = connection3.get_bucket(bucket_name + '6')
e306af50 944 s = key1.get_contents_as_string(encoding='ascii')
9f95a23c
TL
945 rl.log_and_clear("get_obj", bucket_name + '6', user4)
946 assert s == object_name1
947
948 # TESTCASE 'user-rename', 'user', 'rename', 'existing user', 'another existing user', 'fails'
949 # create a new user user2
950 (err, out) = rgwadmin(ctx, client, [
951 'user', 'create',
952 '--uid', user2,
953 '--display-name', display_name2,
954 '--access-key', access_key2,
955 '--secret', secret_key2,
956 '--max-buckets', '4'
957 ],
958 check_status=True)
959
960 # create a bucket
961 bucket = connection2.create_bucket(bucket_name + '7')
962
963 rl.log_and_clear("create_bucket", bucket_name + '7', user2)
964
965 # create object
966 object_name2 = 'fourteen'
967 key2 = boto.s3.key.Key(bucket, object_name2)
968 key2.set_contents_from_string(object_name2)
969 rl.log_and_clear("put_obj", bucket_name + '7', user2)
970
971 (err, out) = rgwadmin(ctx, client, ['user', 'rename', '--uid', user4, '--new-uid', user2])
972 assert err
973
974 # test if user 2 and user4 can still access their bucket and objects after rename fails
975 bucket = connection3.get_bucket(bucket_name + '6')
e306af50 976 s = key1.get_contents_as_string(encoding='ascii')
9f95a23c
TL
977 rl.log_and_clear("get_obj", bucket_name + '6', user4)
978 assert s == object_name1
979
980 bucket = connection2.get_bucket(bucket_name + '7')
e306af50 981 s = key2.get_contents_as_string(encoding='ascii')
9f95a23c
TL
982 rl.log_and_clear("get_obj", bucket_name + '7', user2)
983 assert s == object_name2
984
985 (err, out) = rgwadmin(ctx, client,
986 ['user', 'rm', '--uid', user4, '--purge-data' ],
987 check_status=True)
988
989 (err, out) = rgwadmin(ctx, client,
990 ['user', 'rm', '--uid', user2, '--purge-data' ],
991 check_status=True)
992
993 time.sleep(5)
994
181888fb
FG
995 # should be all through with connection. (anything using connection
996 # should be BEFORE the usage stuff above.)
997 rl.log_and_clear("(before-close)", '-', '-', ignore_this_entry)
998 connection.close()
999 connection = None
1000
1001 # the usage flush interval is 30 seconds, wait that much an then some
1002 # to make sure everything has been flushed
1003 time.sleep(35)
1004
1005 # TESTCASE 'usage-trim' 'usage' 'trim' 'user usage' 'succeeds, usage removed'
1006 (err, out) = rgwadmin(ctx, client, ['usage', 'trim', '--uid', user1],
1007 check_status=True)
1008 (err, out) = rgwadmin(ctx, client, ['usage', 'show', '--uid', user1],
1009 check_status=True)
1010 assert len(out['entries']) == 0
1011 assert len(out['summary']) == 0
7c673cae
FG
1012
1013 (err, out) = rgwadmin(ctx, client,
1014 ['user', 'rm', '--uid', user1, '--purge-data' ],
1015 check_status=True)
1016
1017 # TESTCASE 'rm-user3','user','rm','deleted user','fails'
1018 (err, out) = rgwadmin(ctx, client, ['user', 'info', '--uid', user1])
1019 assert err
1020
1021 # TESTCASE 'zone-info', 'zone', 'get', 'get zone info', 'succeeds, has default placement rule'
1022 #
1023
31f18b77 1024 (err, out) = rgwadmin(ctx, client, ['zone', 'get','--rgw-zone','default'])
7c673cae
FG
1025 orig_placement_pools = len(out['placement_pools'])
1026
1027 # removed this test, it is not correct to assume that zone has default placement, it really
1028 # depends on how we set it up before
1029 #
1030 # assert len(out) > 0
1031 # assert len(out['placement_pools']) == 1
1032
1033 # default_rule = out['placement_pools'][0]
1034 # assert default_rule['key'] == 'default-placement'
1035
1036 rule={'key': 'new-placement', 'val': {'data_pool': '.rgw.buckets.2', 'index_pool': '.rgw.buckets.index.2'}}
1037
1038 out['placement_pools'].append(rule)
1039
1040 (err, out) = rgwadmin(ctx, client, ['zone', 'set'],
f67539c2 1041 stdin=BytesIO(json.dumps(out).encode()),
7c673cae
FG
1042 check_status=True)
1043
b32b8144 1044 (err, out) = rgwadmin(ctx, client, ['zone', 'get'])
7c673cae
FG
1045 assert len(out) > 0
1046 assert len(out['placement_pools']) == orig_placement_pools + 1
181888fb
FG
1047
1048 zonecmd = ['zone', 'placement', 'rm',
9f95a23c
TL
1049 '--rgw-zone', 'default',
1050 '--placement-id', 'new-placement']
181888fb
FG
1051
1052 (err, out) = rgwadmin(ctx, client, zonecmd, check_status=True)
1053
b32b8144
FG
1054 # TESTCASE 'zonegroup-info', 'zonegroup', 'get', 'get zonegroup info', 'succeeds'
1055 (err, out) = rgwadmin(ctx, client, ['zonegroup', 'get'], check_status=True)
1056
181888fb
FG
1057from teuthology.config import config
1058from teuthology.orchestra import cluster, remote
1059import argparse;
1060
1061def main():
1062 if len(sys.argv) == 3:
9f95a23c
TL
1063 user = sys.argv[1] + "@"
1064 host = sys.argv[2]
181888fb
FG
1065 elif len(sys.argv) == 2:
1066 user = ""
9f95a23c 1067 host = sys.argv[1]
181888fb
FG
1068 else:
1069 sys.stderr.write("usage: radosgw_admin.py [user] host\n")
9f95a23c 1070 exit(1)
181888fb
FG
1071 client0 = remote.Remote(user + host)
1072 ctx = config
1073 ctx.cluster=cluster.Cluster(remotes=[(client0,
e306af50 1074 [ 'ceph.client.rgw.%s' % (host), ]),])
181888fb
FG
1075 ctx.rgw = argparse.Namespace()
1076 endpoints = {}
1077 endpoints['ceph.client.rgw.%s' % host] = (host, 80)
1078 ctx.rgw.role_endpoints = endpoints
1079 ctx.rgw.realm = None
1080 ctx.rgw.regions = {'region0': { 'api name': 'api1',
e306af50
TL
1081 'is master': True, 'master zone': 'r0z0',
1082 'zones': ['r0z0', 'r0z1'] }}
181888fb
FG
1083 ctx.rgw.config = {'ceph.client.rgw.%s' % host: {'system user': {'name': '%s-system-user' % host}}}
1084 task(config, None)
1085 exit()
1086
1087if __name__ == '__main__':
1088 main()