2 Run a series of rgw admin commands through the rest interface.
4 The test cases in this file have been annotated for inventory.
5 To extract the inventory (in csv format) use the command:
7 grep '^ *# TESTCASE' | sed 's/^ *# TESTCASE //'
10 from cStringIO import StringIO
15 import boto.s3.connection
21 from boto.connection import AWSAuthConnection
22 from teuthology import misc as teuthology
23 from util.rgw import get_user_summary, get_user_successful_ops
25 log = logging.getLogger(__name__)
27 def rgwadmin(ctx, client, cmd):
29 Perform rgw admin command
32 :param cmd: command to execute.
33 :return: command exit status, json result.
35 log.info('radosgw-admin: %s' % cmd)
36 testdir = teuthology.get_testdir(ctx)
40 '{tdir}/archive/coverage'.format(tdir=testdir),
46 (remote,) = ctx.cluster.only(client).remotes.iterkeys()
54 out = proc.stdout.getvalue()
56 if not r and out != '':
59 log.info(' json result: %s' % j)
62 log.info(' raw result: %s' % j)
66 def rgwadmin_rest(connection, cmd, params=None, headers=None, raw=False):
68 perform a rest command
70 log.info('radosgw-admin-rest: %s %s' % (cmd, params))
71 put_cmds = ['create', 'link', 'add']
72 post_cmds = ['unlink', 'modify']
73 delete_cmds = ['trim', 'rm', 'process']
74 get_cmds = ['check', 'info', 'show', 'list']
76 bucket_sub_resources = ['object', 'policy', 'index']
77 user_sub_resources = ['subuser', 'key', 'caps']
78 zone_sub_resources = ['pool', 'log', 'garbage']
80 def get_cmd_method_and_handler(cmd):
82 Get the rest command and handler from information in cmd and
83 from the imported requests object.
85 if cmd[1] in put_cmds:
86 return 'PUT', requests.put
87 elif cmd[1] in delete_cmds:
88 return 'DELETE', requests.delete
89 elif cmd[1] in post_cmds:
90 return 'POST', requests.post
91 elif cmd[1] in get_cmds:
92 return 'GET', requests.get
94 def get_resource(cmd):
96 Get the name of the resource from information in cmd.
98 if cmd[0] == 'bucket' or cmd[0] in bucket_sub_resources:
99 if cmd[0] == 'bucket':
102 return 'bucket', cmd[0]
103 elif cmd[0] == 'user' or cmd[0] in user_sub_resources:
107 return 'user', cmd[0]
108 elif cmd[0] == 'usage':
110 elif cmd[0] == 'zone' or cmd[0] in zone_sub_resources:
114 return 'zone', cmd[0]
116 def build_admin_request(conn, method, resource = '', headers=None, data='',
117 query_args=None, params=None):
119 Build an administative request adapted from the build_request()
120 method of boto.connection
123 path = conn.calling_format.build_path_base('admin', resource)
124 auth_path = conn.calling_format.build_auth_path('admin', resource)
125 host = conn.calling_format.build_host(conn.server_name(), 'admin')
127 path += '?' + query_args
128 boto.log.debug('path=%s' % path)
129 auth_path += '?' + query_args
130 boto.log.debug('auth_path=%s' % auth_path)
131 return AWSAuthConnection.build_base_http_request(conn, method, path,
132 auth_path, params, headers, data, host)
134 method, handler = get_cmd_method_and_handler(cmd)
135 resource, query_args = get_resource(cmd)
136 request = build_admin_request(connection, method, resource,
137 query_args=query_args, headers=headers)
139 url = '{protocol}://{host}{path}'.format(protocol=request.protocol,
140 host=request.host, path=request.path)
142 request.authorize(connection=connection)
143 result = handler(url, params=params, headers=request.headers)
146 log.info(' text result: %s' % result.txt)
147 return result.status_code, result.txt
149 log.info(' json result: %s' % result.json())
150 return result.status_code, result.json()
153 def task(ctx, config):
155 Test radosgw-admin functionality through the RESTful interface
157 assert config is None or isinstance(config, list) \
158 or isinstance(config, dict), \
159 "task s3tests only supports a list or dictionary for configuration"
160 all_clients = ['client.{id}'.format(id=id_)
161 for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')]
164 if isinstance(config, list):
165 config = dict.fromkeys(config)
166 clients = config.keys()
168 # just use the first client...
173 admin_display_name = 'Ms. Admin User'
174 admin_access_key = 'MH1WC2XQ1S8UISFDZC8W'
175 admin_secret_key = 'dQyrTPA0s248YeN5bBv4ukvKU0kh54LWWywkrpoG'
176 admin_caps = 'users=read, write; usage=read, write; buckets=read, write; zone=read, write'
180 subuser1 = 'foo:foo1'
181 subuser2 = 'foo:foo2'
182 display_name1 = 'Foo'
183 display_name2 = 'Fud'
184 email = 'foo@foo.com'
185 access_key = '9te6NH5mcdcq0Tc5i8i1'
186 secret_key = 'Ny4IOauQoL18Gp2zM7lC1vLmoawgqcYP/YGcWfXu'
187 access_key2 = 'p5YnriCv1nAtykxBrupQ'
188 secret_key2 = 'Q8Tk6Q/27hfbFSYdSkPtUqhqx1GgzvpXa4WARozh'
189 swift_secret1 = 'gpS2G9RREMrnbqlp29PP2D36kgPR1tm72n5fPYfL'
190 swift_secret2 = 'ri2VJQcKSYATOY6uaDUX7pxgkW+W1YmC6OCxPHwy'
192 bucket_name = 'myfoo'
194 # legend (test cases can be easily grep-ed out)
195 # TESTCASE 'testname','object','method','operation','assertion'
196 # TESTCASE 'create-admin-user','user','create','administrative user','succeeds'
197 (err, out) = rgwadmin(ctx, client, [
200 '--display-name', admin_display_name,
201 '--access-key', admin_access_key,
202 '--secret', admin_secret_key,
203 '--max-buckets', '0',
210 (remote,) = ctx.cluster.only(client).remotes.iterkeys()
211 remote_host = remote.name.split('@')[1]
212 admin_conn = boto.s3.connection.S3Connection(
213 aws_access_key_id=admin_access_key,
214 aws_secret_access_key=admin_secret_key,
218 calling_format=boto.s3.connection.OrdinaryCallingFormat(),
221 # TESTCASE 'info-nosuch','user','info','non-existent user','fails'
222 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {"uid": user1})
225 # TESTCASE 'create-ok','user','create','w/all valid info','succeeds'
226 (ret, out) = rgwadmin_rest(admin_conn,
229 'display-name' : display_name1,
231 'access-key' : access_key,
232 'secret-key' : secret_key,
238 # TESTCASE 'info-existing','user','info','existing user','returns correct info'
239 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
241 assert out['user_id'] == user1
242 assert out['email'] == email
243 assert out['display_name'] == display_name1
244 assert len(out['keys']) == 1
245 assert out['keys'][0]['access_key'] == access_key
246 assert out['keys'][0]['secret_key'] == secret_key
247 assert not out['suspended']
249 # TESTCASE 'suspend-ok','user','suspend','active user','succeeds'
250 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'modify'], {'uid' : user1, 'suspended' : True})
253 # TESTCASE 'suspend-suspended','user','suspend','suspended user','succeeds w/advisory'
254 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
256 assert out['suspended']
258 # TESTCASE 're-enable','user','enable','suspended user','succeeds'
259 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'modify'], {'uid' : user1, 'suspended' : 'false'})
262 # TESTCASE 'info-re-enabled','user','info','re-enabled user','no longer suspended'
263 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
265 assert not out['suspended']
267 # TESTCASE 'add-keys','key','create','w/valid info','succeeds'
268 (ret, out) = rgwadmin_rest(admin_conn,
271 'access-key' : access_key2,
272 'secret-key' : secret_key2
278 # TESTCASE 'info-new-key','user','info','after key addition','returns all keys'
279 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
281 assert len(out['keys']) == 2
282 assert out['keys'][0]['access_key'] == access_key2 or out['keys'][1]['access_key'] == access_key2
283 assert out['keys'][0]['secret_key'] == secret_key2 or out['keys'][1]['secret_key'] == secret_key2
285 # TESTCASE 'rm-key','key','rm','newly added key','succeeds, key is removed'
286 (ret, out) = rgwadmin_rest(admin_conn,
289 'access-key' : access_key2
294 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
296 assert len(out['keys']) == 1
297 assert out['keys'][0]['access_key'] == access_key
298 assert out['keys'][0]['secret_key'] == secret_key
300 # TESTCASE 'add-swift-key','key','create','swift key','succeeds'
301 (ret, out) = rgwadmin_rest(admin_conn,
302 ['subuser', 'create'],
303 {'subuser' : subuser1,
304 'secret-key' : swift_secret1,
310 # TESTCASE 'info-swift-key','user','info','after key addition','returns all keys'
311 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
313 assert len(out['swift_keys']) == 1
314 assert out['swift_keys'][0]['user'] == subuser1
315 assert out['swift_keys'][0]['secret_key'] == swift_secret1
317 # TESTCASE 'add-swift-subuser','key','create','swift sub-user key','succeeds'
318 (ret, out) = rgwadmin_rest(admin_conn,
319 ['subuser', 'create'],
320 {'subuser' : subuser2,
321 'secret-key' : swift_secret2,
327 # TESTCASE 'info-swift-subuser','user','info','after key addition','returns all sub-users/keys'
328 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
330 assert len(out['swift_keys']) == 2
331 assert out['swift_keys'][0]['user'] == subuser2 or out['swift_keys'][1]['user'] == subuser2
332 assert out['swift_keys'][0]['secret_key'] == swift_secret2 or out['swift_keys'][1]['secret_key'] == swift_secret2
334 # TESTCASE 'rm-swift-key1','key','rm','subuser','succeeds, one key is removed'
335 (ret, out) = rgwadmin_rest(admin_conn,
337 {'subuser' : subuser1,
343 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
344 assert len(out['swift_keys']) == 1
346 # TESTCASE 'rm-subuser','subuser','rm','subuser','success, subuser is removed'
347 (ret, out) = rgwadmin_rest(admin_conn,
349 {'subuser' : subuser1
354 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
355 assert len(out['subusers']) == 1
357 # TESTCASE 'rm-subuser-with-keys','subuser','rm','subuser','succeeds, second subser and key is removed'
358 (ret, out) = rgwadmin_rest(admin_conn,
360 {'subuser' : subuser2,
361 'key-type' : 'swift',
367 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})
368 assert len(out['swift_keys']) == 0
369 assert len(out['subusers']) == 0
371 # TESTCASE 'bucket-stats','bucket','info','no session/buckets','succeeds, empty list'
372 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'info'], {'uid' : user1})
377 connection = boto.s3.connection.S3Connection(
378 aws_access_key_id=access_key,
379 aws_secret_access_key=secret_key,
383 calling_format=boto.s3.connection.OrdinaryCallingFormat(),
386 # TESTCASE 'bucket-stats2','bucket','stats','no buckets','succeeds, empty list'
387 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'info'], {'uid' : user1, 'stats' : True})
391 # create a first bucket
392 bucket = connection.create_bucket(bucket_name)
394 # TESTCASE 'bucket-list','bucket','list','one bucket','succeeds, expected list'
395 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'info'], {'uid' : user1})
398 assert out[0] == bucket_name
400 # TESTCASE 'bucket-stats3','bucket','stats','new empty bucket','succeeds, empty list'
401 (ret, out) = rgwadmin_rest(admin_conn,
402 ['bucket', 'info'], {'bucket' : bucket_name, 'stats' : True})
405 assert out['owner'] == user1
406 bucket_id = out['id']
408 # TESTCASE 'bucket-stats4','bucket','stats','new empty bucket','succeeds, expected bucket ID'
409 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'info'], {'uid' : user1, 'stats' : True})
412 assert out[0]['id'] == bucket_id # does it return the same ID twice in a row?
415 key = boto.s3.key.Key(bucket)
416 key.set_contents_from_string('one')
418 # TESTCASE 'bucket-stats5','bucket','stats','after creating key','succeeds, lists one non-empty object'
419 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'info'], {'bucket' : bucket_name, 'stats' : True})
421 assert out['id'] == bucket_id
422 assert out['usage']['rgw.main']['num_objects'] == 1
423 assert out['usage']['rgw.main']['size_kb'] > 0
428 # TESTCASE 'bucket unlink', 'bucket', 'unlink', 'unlink bucket from user', 'fails', 'access denied error'
429 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'unlink'], {'uid' : user1, 'bucket' : bucket_name})
433 # create a second user to link the bucket to
434 (ret, out) = rgwadmin_rest(admin_conn,
437 'display-name' : display_name2,
438 'access-key' : access_key2,
439 'secret-key' : secret_key2,
445 # try creating an object with the first user before the bucket is relinked
447 key = boto.s3.key.Key(bucket)
450 key.set_contents_from_string('two')
451 except boto.exception.S3ResponseError:
459 # link the bucket to another user
460 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'link'], {'uid' : user2, 'bucket' : bucket_name})
464 # try creating an object with the first user which should cause an error
465 key = boto.s3.key.Key(bucket)
468 key.set_contents_from_string('three')
469 except boto.exception.S3ResponseError:
474 # relink the bucket to the first user and delete the second user
475 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'link'], {'uid' : user1, 'bucket' : bucket_name})
478 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'rm'], {'uid' : user2})
481 # TESTCASE 'object-rm', 'object', 'rm', 'remove object', 'succeeds, object is removed'
485 key = boto.s3.key.Key(bucket, object_name)
486 key.set_contents_from_string(object_name)
489 (ret, out) = rgwadmin_rest(admin_conn, ['object', 'rm'], {'bucket' : bucket_name, 'object' : object_name})
492 # TESTCASE 'bucket-stats6','bucket','stats','after deleting key','succeeds, lists one no objects'
493 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'info'], {'bucket' : bucket_name, 'stats' : True})
495 assert out['id'] == bucket_id
496 assert out['usage']['rgw.main']['num_objects'] == 0
498 # create a bucket for deletion stats
499 useless_bucket = connection.create_bucket('useless_bucket')
500 useless_key = useless_bucket.new_key('useless_key')
501 useless_key.set_contents_from_string('useless string')
505 useless_bucket.delete()
507 # wait for the statistics to flush
510 # need to wait for all usage data to get flushed, should take up to 30 seconds
511 timestamp = time.time()
512 while time.time() - timestamp <= (20 * 60): # wait up to 20 minutes
513 (ret, out) = rgwadmin_rest(admin_conn, ['usage', 'show'], {'categories' : 'delete_obj'}) # last operation we did is delete obj, wait for it to flush
515 if get_user_successful_ops(out, user1) > 0:
519 assert time.time() - timestamp <= (20 * 60)
521 # TESTCASE 'usage-show' 'usage' 'show' 'all usage' 'succeeds'
522 (ret, out) = rgwadmin_rest(admin_conn, ['usage', 'show'])
524 assert len(out['entries']) > 0
525 assert len(out['summary']) > 0
526 user_summary = get_user_summary(out, user1)
527 total = user_summary['total']
528 assert total['successful_ops'] > 0
530 # TESTCASE 'usage-show2' 'usage' 'show' 'user usage' 'succeeds'
531 (ret, out) = rgwadmin_rest(admin_conn, ['usage', 'show'], {'uid' : user1})
533 assert len(out['entries']) > 0
534 assert len(out['summary']) > 0
535 user_summary = out['summary'][0]
536 for entry in user_summary['categories']:
537 assert entry['successful_ops'] > 0
538 assert user_summary['user'] == user1
540 # TESTCASE 'usage-show3' 'usage' 'show' 'user usage categories' 'succeeds'
541 test_categories = ['create_bucket', 'put_obj', 'delete_obj', 'delete_bucket']
542 for cat in test_categories:
543 (ret, out) = rgwadmin_rest(admin_conn, ['usage', 'show'], {'uid' : user1, 'categories' : cat})
545 assert len(out['summary']) > 0
546 user_summary = out['summary'][0]
547 assert user_summary['user'] == user1
548 assert len(user_summary['categories']) == 1
549 entry = user_summary['categories'][0]
550 assert entry['category'] == cat
551 assert entry['successful_ops'] > 0
553 # TESTCASE 'usage-trim' 'usage' 'trim' 'user usage' 'succeeds, usage removed'
554 (ret, out) = rgwadmin_rest(admin_conn, ['usage', 'trim'], {'uid' : user1})
556 (ret, out) = rgwadmin_rest(admin_conn, ['usage', 'show'], {'uid' : user1})
558 assert len(out['entries']) == 0
559 assert len(out['summary']) == 0
561 # TESTCASE 'user-suspend2','user','suspend','existing user','succeeds'
562 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'modify'], {'uid' : user1, 'suspended' : True})
565 # TESTCASE 'user-suspend3','user','suspend','suspended user','cannot write objects'
567 key = boto.s3.key.Key(bucket)
568 key.set_contents_from_string('five')
569 except boto.exception.S3ResponseError as e:
570 assert e.status == 403
572 # TESTCASE 'user-renable2','user','enable','suspended user','succeeds'
573 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'modify'], {'uid' : user1, 'suspended' : 'false'})
576 # TESTCASE 'user-renable3','user','enable','reenabled user','can write objects'
577 key = boto.s3.key.Key(bucket)
578 key.set_contents_from_string('six')
580 # TESTCASE 'garbage-list', 'garbage', 'list', 'get list of objects ready for garbage collection'
582 # create an object large enough to be split into multiple parts
583 test_string = 'foo'*10000000
585 big_key = boto.s3.key.Key(bucket)
586 big_key.set_contents_from_string(test_string)
588 # now delete the head
591 # TESTCASE 'rm-user-buckets','user','rm','existing user','fails, still has buckets'
592 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'rm'], {'uid' : user1})
595 # delete should fail because ``key`` still exists
598 except boto.exception.S3ResponseError as e:
599 assert e.status == 409
604 # TESTCASE 'policy', 'bucket', 'policy', 'get bucket policy', 'returns S3 policy'
605 bucket = connection.create_bucket(bucket_name)
608 key = boto.s3.key.Key(bucket)
609 key.set_contents_from_string('seven')
611 # should be private already but guarantee it
612 key.set_acl('private')
614 (ret, out) = rgwadmin_rest(admin_conn, ['policy', 'show'], {'bucket' : bucket.name, 'object' : key.key})
617 acl = key.get_xml_acl()
618 assert acl == out.strip('\n')
620 # add another grantee by making the object public read
621 key.set_acl('public-read')
623 (ret, out) = rgwadmin_rest(admin_conn, ['policy', 'show'], {'bucket' : bucket.name, 'object' : key.key})
626 acl = key.get_xml_acl()
627 assert acl == out.strip('\n')
629 # TESTCASE 'rm-bucket', 'bucket', 'rm', 'bucket with objects', 'succeeds'
630 bucket = connection.create_bucket(bucket_name)
631 key_name = ['eight', 'nine', 'ten', 'eleven']
633 key = boto.s3.key.Key(bucket)
634 key.set_contents_from_string(key_name[i])
636 (ret, out) = rgwadmin_rest(admin_conn, ['bucket', 'rm'], {'bucket' : bucket_name, 'purge-objects' : True})
639 # TESTCASE 'caps-add', 'caps', 'add', 'add user cap', 'succeeds'
641 (ret, out) = rgwadmin_rest(admin_conn, ['caps', 'add'], {'uid' : user1, 'user-caps' : caps})
643 assert out[0]['perm'] == 'read'
645 # TESTCASE 'caps-rm', 'caps', 'rm', 'remove existing cap from user', 'succeeds'
646 (ret, out) = rgwadmin_rest(admin_conn, ['caps', 'rm'], {'uid' : user1, 'user-caps' : caps})
650 # TESTCASE 'rm-user','user','rm','existing user','fails, still has buckets'
651 bucket = connection.create_bucket(bucket_name)
652 key = boto.s3.key.Key(bucket)
654 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'rm'], {'uid' : user1})
657 # TESTCASE 'rm-user2', 'user', 'rm', user with data', 'succeeds'
658 bucket = connection.create_bucket(bucket_name)
659 key = boto.s3.key.Key(bucket)
660 key.set_contents_from_string('twelve')
662 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'rm'], {'uid' : user1, 'purge-data' : True})
665 # TESTCASE 'rm-user3','user','info','deleted user','fails'
666 (ret, out) = rgwadmin_rest(admin_conn, ['user', 'info'], {'uid' : user1})