]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: add multi-region and zone support
authorJosh Durgin <josh.durgin@inktank.com>
Mon, 22 Jul 2013 17:38:28 +0000 (10:38 -0700)
committerJosh Durgin <josh.durgin@inktank.com>
Wed, 24 Jul 2013 16:59:50 +0000 (09:59 -0700)
Take client<->zone/region and the associated pools from ceph.conf, so
we don't have to invent a new format to specify it.

General region info is added to a new configuration section in the rgw
task. Each client is assumed to be a different zone, and a system user
is created with the key specified in the yaml, so it can be passed to
later task configuration as well. This isn't strictly necessary, but
avoids having to lookup this info in later tasks through something
like radosgw-admin.

Ports are allocated automatically because there's no obvious mapping
from host to client in the task configuration. Later tests can get the
endpoints desired by reading the region map.

Signed-off-by: Josh Durgin <josh.durgin@inktank.com>
teuthology/task/apache.conf.template
teuthology/task/rgw.py
teuthology/task_util/rgw.py

index 87e92728a90667398da2e652728e0c82f1184e65..8b21d650dc4716e1d4cf27283f5d72f09d0c69ed 100644 (file)
   LoadModule log_config_module {mod_path}/mod_log_config.so
 </IfModule>
 
-Listen 7280
-ServerName rgwtest.example.com
+Listen {port}
+ServerName {host}
 
 ServerRoot {testdir}/apache
-ErrorLog {testdir}/archive/apache/error.log
+ErrorLog {testdir}/archive/apache.{client}/error.log
 LogFormat "%h l %u %t \"%r\" %>s %b \"{{Referer}}i\" \"%{{User-agent}}i\"" combined
-CustomLog {testdir}/archive/apache/access.log combined
-PidFile {testdir}/apache/tmp/apache.pid
-DocumentRoot {testdir}/apache/htdocs
-FastCgiIPCDir {testdir}/apache/tmp/fastcgi_sock
-FastCgiExternalServer {testdir}/apache/htdocs/rgw.fcgi -socket rgw_sock
+CustomLog {testdir}/archive/apache.{client}/access.log combined
+PidFile {testdir}/apache/tmp.{client}/apache.pid
+DocumentRoot {testdir}/apache/htdocs.{client}
+FastCgiIPCDir {testdir}/apache/tmp.{client}/fastcgi_sock
+FastCgiExternalServer {testdir}/apache/htdocs.{client}/rgw.fcgi -socket rgw_sock
 RewriteEngine On
 
 RewriteRule ^/([a-zA-Z0-9-_.]*)([/]?.*) /rgw.fcgi?page=$1&params=$2&%{{QUERY_STRING}} [E=HTTP_AUTHORIZATION:%{{HTTP:Authorization}},L]
@@ -32,7 +32,7 @@ SetEnv RGW_LOG_LEVEL 20
 SetEnv RGW_SHOULD_LOG yes
 SetEnv RGW_PRINT_CONTINUE {print_continue}
 
-<Directory {testdir}/apache/htdocs>
+<Directory {testdir}/apache/htdocs.{client}>
   Options +ExecCGI
   AllowOverride All
   SetHandler fastcgi-script
index 860e0f9c961041aafb44802cee0f943badb50ff2..35f5c63635a7d15b046ef6ac6865d94062eee897 100644 (file)
@@ -5,7 +5,6 @@ import os
 
 from cStringIO import StringIO
 
-import ceph_manager
 from ..orchestra import run
 from teuthology import misc as teuthology
 from teuthology import contextutil
@@ -23,11 +22,14 @@ def create_dirs(ctx, config):
             args=[
                 'mkdir',
                 '-p',
-                '{tdir}/apache/htdocs'.format(tdir=testdir),
-                '{tdir}/apache/tmp'.format(tdir=testdir),
+                '{tdir}/apache/htdocs.{client}'.format(tdir=testdir,
+                                                       client=client),
+                '{tdir}/apache/tmp.{client}'.format(tdir=testdir,
+                                                    client=client),
                 run.Raw('&&'),
                 'mkdir',
-                '{tdir}/archive/apache'.format(tdir=testdir),
+                '{tdir}/archive/apache.{client}'.format(tdir=testdir,
+                                                        client=client),
                 ],
             )
     try:
@@ -39,20 +41,25 @@ def create_dirs(ctx, config):
                 args=[
                     'rm',
                     '-rf',
-                    '{tdir}/apache/tmp'.format(tdir=testdir),
+                    '{tdir}/apache/tmp.{client}'.format(tdir=testdir,
+                                                        client=client),
                     run.Raw('&&'),
                     'rmdir',
-                    '{tdir}/apache/htdocs'.format(tdir=testdir),
-                    run.Raw('&&'),
-                    'rmdir',
-                    '{tdir}/apache'.format(tdir=testdir),
+                    '{tdir}/apache/htdocs.{client}'.format(tdir=testdir,
+                                                           client=client),
                     ],
                 )
+        ctx.cluster.only(client).run(
+            'rmdir',
+            '{tdir}/apache.{client}'.format(tdir=testdir,
+                                            client=client),
+            )
 
 
 @contextlib.contextmanager
-def ship_config(ctx, config):
+def ship_config(ctx, config, role_endpoints):
     assert isinstance(config, dict)
+    assert isinstance(role_endpoints, dict)
     testdir = teuthology.get_testdir(ctx)
     log.info('Shipping apache config and rgw.fcgi...')
     src = os.path.join(os.path.dirname(__file__), 'apache.conf.template')
@@ -65,30 +72,37 @@ def ship_config(ctx, config):
         else:
             mod_path = '/usr/lib64/httpd/modules'
             print_continue = 'off'
+        host, port = role_endpoints[client]
         with file(src, 'rb') as f:
             conf = f.read().format(
                 testdir=testdir,
                 mod_path=mod_path,
                 print_continue=print_continue,
+                host=host,
+                port=port,
+                client=client,
                 )
             teuthology.write_file(
                 remote=remote,
-                path='{tdir}/apache/apache.conf'.format(tdir=testdir),
+                path='{tdir}/apache/apache.{client}.conf'.format(tdir=testdir,
+                                                                 client=client),
                 data=conf,
                 )
         teuthology.write_file(
             remote=remote,
-            path='{tdir}/apache/htdocs/rgw.fcgi'.format(tdir=testdir),
+            path='{tdir}/apache/htdocs.{client}/rgw.fcgi'.format(tdir=testdir,
+                                                                 client=client),
             data="""#!/bin/sh
 ulimit -c unlimited
-exec radosgw -f
-""".format(tdir=testdir)
+exec radosgw -f -n {client}
+""".format(tdir=testdir, client=client)
             )
         remote.run(
             args=[
                 'chmod',
                 'a=rx',
-                '{tdir}/apache/htdocs/rgw.fcgi'.format(tdir=testdir),
+                '{tdir}/apache/htdocs.{client}/rgw.fcgi'.format(tdir=testdir,
+                                                                client=client),
                 ],
             )
     try:
@@ -100,11 +114,13 @@ exec radosgw -f
                 args=[
                     'rm',
                     '-f',
-                    '{tdir}/apache/apache.conf'.format(tdir=testdir),
+                    '{tdir}/apache/apache.{client}.conf'.format(tdir=testdir,
+                                                                client=client),
                     run.Raw('&&'),
                     'rm',
                     '-f',
-                    '{tdir}/apache/htdocs/rgw.fcgi'.format(tdir=testdir),
+                    '{tdir}/apache/htdocs.{client}/rgw.fcgi'.format(tdir=testdir,
+                                                                    client=client),
                     ],
                 )
 
@@ -124,27 +140,30 @@ def start_rgw(ctx, config):
         log.info('client {client} is id {id}'.format(client=client, id=id_))
         run_cmd=[
             'sudo',
-                '{tdir}/adjust-ulimits'.format(tdir=testdir),
-                'ceph-coverage',
-                '{tdir}/archive/coverage'.format(tdir=testdir),
-                '{tdir}/daemon-helper'.format(tdir=testdir),
-                'term',
+            '{tdir}/adjust-ulimits'.format(tdir=testdir),
+            'ceph-coverage',
+            '{tdir}/archive/coverage'.format(tdir=testdir),
+            '{tdir}/daemon-helper'.format(tdir=testdir),
+            'term',
             ]
         run_cmd_tail=[
-                'radosgw',
-                # authenticate as the client this is co-located with
-                '--name', '{client}'.format(client=client),
-                # use the keyring for the client we're running as
-                '-k', '/etc/ceph/ceph.{client}.keyring'.format(client=client),
-                '--log-file', '/var/log/ceph/rgw.log',
-                '--rgw_ops_log_socket_path', '{tdir}/rgw.opslog.sock'.format(tdir=testdir),
-                '{tdir}/apache/apache.conf'.format(tdir=testdir),
-                '--foreground',
-                run.Raw('|'),
-                'sudo',
-                'tee',
-                '/var/log/ceph/rgw.stdout'.format(tdir=testdir),
-                run.Raw('2>&1'),
+            'radosgw',
+            '-n', client,
+            '-k', '/etc/ceph/ceph.{client}.keyring'.format(client=client),
+            '--log-file',
+            '/var/log/ceph/rgw.{client}.log'.format(client=client),
+            '--rgw_ops_log_socket_path',
+            '{tdir}/rgw.opslog.{client}.sock'.format(tdir=testdir,
+                                                     client=client),
+            '{tdir}/apache/apache.{client}.conf'.format(tdir=testdir,
+                                                        client=client),
+            '--foreground',
+            run.Raw('|'),
+            'sudo',
+            'tee',
+            '/var/log/ceph/rgw.{client}.stdout'.format(tdir=testdir,
+                                                       client=client),
+            run.Raw('2>&1'),
             ]
 
         run_cmd.extend(
@@ -174,7 +193,8 @@ def start_rgw(ctx, config):
                 args=[
                     'rm',
                     '-f',
-                    '{tdir}/rgw.opslog.sock'.format(tdir=testdir),
+                    '{tdir}/rgw.opslog.{client}.sock'.format(tdir=testdir,
+                                                             client=client),
                     ],
                 )
 
@@ -199,7 +219,8 @@ def start_apache(ctx, config):
                 apache_name,
                 '-X',
                 '-f',
-                '{tdir}/apache/apache.conf'.format(tdir=testdir),
+                '{tdir}/apache/apache.{client}.conf'.format(tdir=testdir,
+                                                            client=client),
                 ],
             logger=log.getChild(client),
             stdin=run.PIPE,
@@ -216,370 +237,138 @@ def start_apache(ctx, config):
 
         run.wait(apaches.itervalues())
 
-# pulls the configured zone info out and also 
-# inserts some sensible defaults. 
-def extract_zone_info(zone_info):
-
-    # now send the zone settings across the wire
-    system_user = zone_info['user']
-    system_access_key = zone_info['access_key']
-    system_secret_key = zone_info['secret_key']
-    zone_suffix = zone_info['zone_suffix']
-
-    # new dict to hold the data
-    zone_dict = {}
-    zone_dict['domain_root'] = '.rgw.root' + zone_suffix
-    zone_dict['control_pool'] = '.rgw.control' + zone_suffix
-    zone_dict['gc_pool'] = '.rgw.gc' + zone_suffix
-    zone_dict['log_pool'] = '.log' + zone_suffix
-    zone_dict['intent_log_pool'] = '.intent-log' + zone_suffix
-    zone_dict['usage_log_pool'] = '.usage' + zone_suffix
-    zone_dict['user_keys_pool'] = '.users' + zone_suffix
-    zone_dict['user_email_pool'] = '.users.email' + zone_suffix
-    zone_dict['user_swift_pool'] = '.users.swift' + zone_suffix
-    zone_dict['user_uid_pool'] = '.users.id' + zone_suffix
-
-    system_user_dict = {}
-    system_user_dict['user'] = system_user
-    system_user_dict['access_key'] = system_access_key
-    system_user_dict['secret_key'] = system_secret_key
-
-    zone_dict['system_key'] = system_user_dict
-
-    return zone_dict
-
-# pulls the configured region info out and also 
-# inserts some sensible defaults. 
-def extract_region_info(region_info, host_name):
-    # create a new, empty dict to populate
-    region_dict = {}
-    # use specifed region name or default <client name>_'region'
-    if region_info.has_key('name'):
-        region_dict['name'] = region_info['name']
-    else: 
-        region_dict['name'] = '{client}_region'.format(client=client)
-
-    # use specified api name or default to 'default'
-    if region_info.has_key('api_name'):
-        region_dict['api_name'] = region_info['api_name']
-    else: 
-        region_dict['api_name'] = 'default'
-
-    # Going to assume this is specified for now
-    if region_info.has_key('is_master'):
-        region_dict['is_master'] = region_info['is_master']
-    else: 
-        pass
-        #region_dict['is_master'] = 'false'
-
-    # use specified api name or default to 'default'
-    if region_info.has_key('api_name'):
-        region_dict['api_name'] = region_info['api_name']
-    else: 
-        region_dict['api_name'] = 'default'
-
-    # add in sensible defaults for the endpoints field 
-    if region_info.has_key('endpoints'):
-        region_dict['endpoints'] = region_info['endpoints']
-    else: 
-        region_dict['endpoints'] = []
-        region_dict['endpoints'].append('http://{client}:80/'.format(client=host_name))
-
-    # use specified master zone or default to the first specified zone 
-    # (if there's more than one)
-    if region_info.has_key('master_zone'):
-        region_dict['master_zone'] = region_info['master_zone']
-    else: 
-        region_dict['master_zone'] = 'default'
-
-    region_dict['zones'] = []
-    zones = region_info['zones'].split(',')
-    for zone in zones:
-        # build up a zone 
-        name, log_meta, log_data = zone.split(':')
-        log.info('zone: {zone} meta:{meta} data:{data}'.format(zone=name, 
-                                            meta=log_meta, data=log_data))
-        new_zone_dict = {}
-        new_zone_dict['name'] = name
-        new_zone_dict['endpoints'] = []
-        new_zone_dict['endpoints'].append('http://{client}:80/'.format(client=host_name))
-        new_zone_dict['log_meta'] = log_meta
-        new_zone_dict['log_data'] = log_data
-        region_dict['zones'].append(new_zone_dict) 
-
-    # just using defaults for now, revisit this later to allow 
-    # the configs to specify placement_targets and default_placement policies
-    region_dict['placement_targets'] = []
-    default_placement_dict = {}
-    default_placement_dict['name'] = 'default-placement'
-    default_placement_dict['tags'] = []
-    region_dict['placement_targets'].append(default_placement_dict)
-
-    region_dict['default_placement'] = 'default-placement'
-
-    return region_dict
+def extract_zone_info(ctx, client, client_config):
+    user_info = client_config['system user']
+    system_user = user_info['name']
+    system_access_key = user_info['access key']
+    system_secret_key = user_info['secret key']
+
+    ceph_config = ctx.ceph.conf.get('global', {})
+    ceph_config.update(ctx.ceph.conf.get('client', {}))
+    ceph_config.update(ctx.ceph.conf.get(client, {}))
+    for key in ['rgw zone', 'rgw region', 'rgw zone root pool']:
+        assert key in ceph_config, \
+               'ceph conf must contain {key} for {client}'.format(key=key,
+                                                                  client=client)
+    region = ceph_config['rgw region']
+    zone = ceph_config['rgw zone']
+    zone_info = dict(
+        domain_root=ceph_config['rgw zone root pool'],
+        )
+    for key in ['control_pool', 'gc_pool', 'log_pool', 'intent_log_pool',
+                'usage_log_pool', 'user_keys_pool', 'user_email_pool',
+                'user_swift_pool', 'user_uid_pool']:
+        zone_info[key] = '.' + region + '.' + zone + '.' + key
+
+    zone_info['system_key'] = dict(
+        user=system_user,
+        access_key=system_access_key,
+        secret_key=system_secret_key,
+        )
+    return region, zone, zone_info
+
+def extract_region_info(region, region_info):
+    assert isinstance(region_info['zones'], list) and region_info['zones'], \
+           'zones must be a non-empty list'
+    return dict(
+        name=region,
+        api_name=region_info.get('api name', region),
+        is_master=region_info.get('is master', False),
+        master_zone=region_info.get('master zone', region_info['zones'][0]),
+        placement_targets=region_info.get('placement targets', []),
+        default_placement=region_info.get('default placement', ''),
+        )
+
+def assign_ports(ctx, config):
+    port = 7280
+    role_endpoints = {}
+    for remote, roles_for_host in ctx.cluster.remotes.iteritems():
+        for role in roles_for_host:
+            if role in config:
+                role_endpoints[role] = (remote.name.split('@')[1], port)
+                port += 1
+
+    return role_endpoints
+
+def fill_in_endpoints(region_info, role_zones, role_endpoints):
+    for role, (host, port) in role_endpoints.iteritems():
+        region, zone, _ = role_zones[role]
+        host, port = role_endpoints[role]
+        endpoint = 'http://{host}:{port}/'.format(host=host, port=port)
+        region_conf = region_info[region]
+        region_conf.setdefault('endpoints', [])
+        region_conf['endpoints'].append(endpoint)
+        region_conf.setdefault('zones', [])
+        region_conf['zones'].append(dict(name=zone, endpoints=[endpoint]))
 
 @contextlib.contextmanager
-def configure_regions_and_zones(ctx, config):
+def configure_regions_and_zones(ctx, config, regions, role_endpoints):
+    if not regions:
+        yield
+        return
+
     log.info('Configuring regions and zones...')
 
-    client_list = []
-    region_files = []
-    zone_files = []
+    log.debug('config is %r', config)
+    log.debug('regions are %r', regions)
+    log.debug('role_endpoints = %r', role_endpoints)
+    role_zones = dict([(client, extract_zone_info(ctx, client, c_config))
+                       for client, c_config in config.iteritems()])
+    log.debug('roles_zones = %r', role_zones)
+    region_info = dict([(region, extract_region_info(region, r_config))
+                        for region, r_config in regions.iteritems()])
 
-    # iterate through all the clients, creating files with the region zone info and shipping those
-    # to all hosts. Then, pull out the zone info and stash it in client_list
+    fill_in_endpoints(region_info, role_zones, role_endpoints)
     for client in config.iterkeys():
-        (remote,) = ctx.cluster.only(client).remotes.iterkeys()
-
-        client_config = config.get(client)
-        if client_config is None:
-            client_config = {}
-
-        # extract the dns name from remote
-        user_name, host_name = str(remote).split('@')
-
-        log.info("rgw %s config is %s dns is %s", client, client_config, host_name)
-        
-        if 'region_info' in client_config:
-            region_dict = extract_region_info(client_config['region_info'], host_name)
-            region_name = region_dict['name']
-
-            log.info('constructed region info: {data}'.format(data=region_dict))
-            
-            file_name = region_dict['name'] + '.input'
-            testdir = teuthology.get_testdir(ctx)
-            region_file_path = os.path.join(testdir, file_name)
-
-            tmpFile = StringIO()
-
-            # use json.dumps as a pretty printer so that the generated file
-            # is easier to read
-            tmpFile.write('{data}'.format(data=json.dumps(region_dict, sort_keys=True,
-                                          indent=4)))
-
-            # ship this region file to all clients
-            for region_client in config.iterkeys():
-                (region_remote,) = ctx.cluster.only(region_client).remotes.iterkeys()
-
-                log.info('sending region file {region_file} to {host}'.format( \
-                                            region_file=region_file_path,
-                                            host=str(region_remote)))
-
+        for region, info in region_info.iteritems():
+            region_json = json.dumps(info)
+            log.debug('region info is: %s', region_json)
+            rgwadmin(ctx, client,
+                     cmd=['-n', client, 'region', 'set'],
+                     stdin=StringIO(region_json),
+                     check_status=True)
+            if info['is_master']:
+                rgwadmin(ctx, client,
+                         cmd=['-n', client,
+                              'region', 'default',
+                              '--rgw-region', region],
+                         check_status=True)
+        for role, (_, zone, info) in role_zones.iteritems():
+            rgwadmin(ctx, client,
+                     cmd=['-n', client, 'zone', 'set', '--rgw-zone', zone],
+                     stdin=StringIO(json.dumps(info)),
+                     check_status=True)
+
+    first_mon = teuthology.get_first_mon(ctx, config)
+    (mon,) = ctx.cluster.only(first_mon).remotes.iterkeys()
+    # removing these objects from .rgw.root and the per-zone root pools
+    # may or may not matter
+    rados(ctx, mon,
+          cmd=['-p', '.rgw.root', 'rm', 'region_info.default'])
+    rados(ctx, mon,
+          cmd=['-p', '.rgw.root', 'rm', 'zone_info.default'])
 
-                tmpFile.seek(0)
-
-                teuthology.write_file(
-                  remote=region_remote,
-                  path=region_file_path,
-                  data=tmpFile,
-                )
-
-            region_files.append(region_file_path)
-
-
-            # now work on the zone info
-            if 'zone_info' in client_config:
-                zone_info = client_config['zone_info']
-                zone_dict = extract_zone_info(zone_info)
-    
-                zone_suffix = zone_info['zone_suffix']
-                zone_name = zone_info['name']
-    
-                file_name = 'zone' + zone_suffix + '.input'
-                testdir = teuthology.get_testdir(ctx)
-                zone_file_path = os.path.join(testdir, file_name)
-                log.info('Sending zone file {file_out} to host {host}'.format(file_out=zone_file_path, \
-                                                                   host=host_name))
-        
-                tmpFile = StringIO()
-        
-                tmpFile.write('{data}'.format(data=json.dumps(zone_dict,  
-                                              sort_keys=True, indent=4)))
-    
-                # zone info only gets sent to the particular client
-                tmpFile.seek(0)
-                teuthology.write_file(
-                  remote=remote,
-                  path=zone_file_path,
-                  data=tmpFile,
-                )
-        
-                zone_files.append(zone_file_path)
-    
-                # add an entry that is client, remote, region, zone, suffix, zone file path
-                # this data is used later 
-                client_entry = []
-                client_entry.append(client)
-                client_entry.append(remote)
-                client_entry.append(region_name)
-                client_entry.append(zone_name)
-                client_entry.append(zone_suffix)
-                client_entry.append(zone_file_path)
-                client_list.append(client_entry)
-    
-            else:  # if 'zone_info' in client_config:
-                log.info('no zone info found for client {client}'.format(client=client))
-
-        else: # if 'region_info' in client_config:
-            log.info('no region info found for client {client}'.format(client=client))
-
-
-    # now apply the region info on all clients
-    for region_file_path in region_files:
-        for client_entry in client_list: 
-
-            client = client_entry[0]
-            remote = client_entry[1]
-            region_name = client_entry[2]
-            zone_name = client_entry[3]
-            suffix = client_entry[4]
-            zone_file_path = client_entry[5]
-
-            log.info('cl: {client_name} rn: {region} zn: {zone} rf: {rf} suf:{suffix} zfp:{zfp}'.format(\
-                 client_name=str(remote), region=region_name, zone=zone_name,
-                 rf=region_file_path, suffix=suffix, zfp=zone_file_path))
-
-            # now use this file to set the region info on the host
-            (err, out) = rgwadmin(ctx, remote, ['region', 'set', '--infile', 
-                              region_file_path, '--name', 
-                              '{client}'.format(client=client),
-                              '--rgw-region',
-                              '{region}'.format(region=region_name),
-                              '--rgw-zone',
-                              '{zone}'.format(zone=zone_name)])
-            assert not err
-
-    # go through the clients and set the zone info
-    for client_entry in client_list: 
-        client = client_entry[0]
-        remote = client_entry[1]
-        region_name = client_entry[2]
-        zone_name = client_entry[3]
-        suffix = client_entry[4]
-        zone_file_path = client_entry[5]
-
-
-        (err, out) = rgwadmin(ctx, remote, ['zone', 'set', 
-                        '--rgw-zone={zone}'.format(zone=zone_name), 
-                        '--infile', zone_file_path,
-                        '--name', '{client}'.format(client=client),
-                        '--rgw-region',
-                        '{region}'.format(region=region_name),
-                        ])
-        assert not err
-
-    # set this to the default region
-    for client_entry in client_list: 
-
-        client = client_entry[0]
-        remote = client_entry[1]
-        region_name = client_entry[2]
-        zone_name = client_entry[3]
-        suffix = client_entry[4]
-        zone_file_path = client_entry[5]
-        log.info('rf: {rf}'.format(rf=region_file_path))
-        (err, out) = rgwadmin(ctx, remote, ['region', 'default', 
-                            '--name', '{client}'.format(client=client),
-                            '--rgw-region',
-                            '{region}'.format(region=region_name),
-                            #'--rgw-zone',
-                            #'{zone}'.format(zone=zone_name)
-                            ])
-        if out:
-            log.info('JB out: {data}'.format(data=out()))
-        if err:
-            log.info('err: {err} out: {out}'.format(err=err, out=out))
-
-        assert not err
-
-    # only do this if there's at least one zone specified
-    if len(client_list) > 0:
-        # need the mon to make rados calls
-        first_mon = teuthology.get_first_mon(ctx, config)
-        (mon,) = ctx.cluster.only(first_mon).remotes.iterkeys()
-
-        # delete the pools for the default
-        err = rados(ctx, mon, ['-p', '.rgw.root', 'rm', 'region_info.default'])
-        log.info('err is %d' % err)
-
-        err = rados(ctx, mon, ['-p', '.rgw.root', 'rm', 'zone_info.default'])
-        log.info('err is %d' % err)
-        
-    # delete the pools for the suffixs
-    for client_entry in client_list: 
-
-        client = client_entry[0]
-        remote = client_entry[1]
-        region_name = client_entry[2]
-        zone_name = client_entry[3]
-        suffix = client_entry[4]
-        zone_file_path = client_entry[5]
-
-        err = rados(ctx, mon, ['-p', '.rgw.root' + suffix, 
-                               'rm', 'region_info.default'])
-        log.info('err is %d' % err)
-            
-        err = rados(ctx, mon, ['-p', '.rgw.root' + suffix, 
-                               'rm', 'zone_info.default'])
-        log.info('err is %d' % err)
-
-    # update the region map
-    for client_entry in client_list: 
-
-        client = client_entry[0]
-        remote = client_entry[1]
-        region_name = client_entry[2]
-        zone_name = client_entry[3]
-        suffix = client_entry[4]
-        zone_file_path = client_entry[5]
-
-        (err, out) = rgwadmin(ctx, remote, ['region-map', 'update',
-                                            '--name', 
-                                            '{client}'.format(client=client),
-                                            '--rgw-region',
-                                            '{region}'.format(region=region_name),
-                                            '--rgw-zone',
-                                            '{zone}'.format(zone=zone_name)])
-        assert not err
-
-    try:
-        yield
-    finally:
-        log.info('Cleaning up regions and zones....')
-
-        for client_entry in client_list: 
-
-            client = client_entry[0]
-            remote = client_entry[1]
-            region_name = client_entry[2]
-            zone_name = client_entry[3]
-            suffix = client_entry[4]
-            zone_file_path = client_entry[5]
-
-            for region_file in region_files:
-                log.info('deleting {file_name} from host {host}'.format( \
-                         file_name=region_file, 
-                         host=str(remote)))
-                remote.run(
-                    args=[
-                        'rm',
-                        '-f',
-                        '{file_path}'.format(file_path=region_file),
-                    ],
-                )
-
-            for zone_file in zone_files:
-                log.info('deleting {file_name} from host {host}'.format( \
-                         file_name=zone_file, 
-                         host=str(remote)))
-                remote.run(
-                    args=[
-                        'rm',
-                        '-f',
-                        '{file_path}'.format(file_path=zone_file),
-                    ],
-                )
+    for client in config.iterkeys():
+        rgwadmin(ctx, client, cmd=['-n', client, 'regionmap', 'update'])
+        for role, (_, zone, zone_info) in role_zones.iteritems():
+            rados(ctx, mon,
+                  cmd=['-p', zone_info['domain_root'],
+                       'rm', 'region_info.default'])
+            rados(ctx, mon,
+                  cmd=['-p', zone_info['domain_root'],
+                       'rm', 'zone_info.default'])
+            rgwadmin(ctx, client,
+                     cmd=[
+                         '-n', client,
+                         'user', 'create',
+                         '--uid', zone_info['system_key']['user'],
+                         '--access-key', zone_info['system_key']['access_key'],
+                         '--secret-key', zone_info['system_key']['secret_key'],
+                         '--display-name', zone_info['system_key']['user'],
+                         ],
+                     check_status=True,
+                     )
+    yield
 
 @contextlib.contextmanager
 def task(ctx, config):
@@ -626,6 +415,58 @@ def task(ctx, config):
               global:
                 rgw print continue: false
         - rgw: [client.0]
+
+    To run rgws for multiple regions or zones, describe the regions
+    and their zones in a regions section. The endpoints will be
+    generated by this task. Each client must have a region, zone,
+    and pools assigned in ceph.conf::
+
+        tasks:
+        - install:
+        - ceph:
+            conf:
+              client.0:
+                rgw region: foo
+                rgw zone: foo-1
+                rgw region root pool: foo.rgw.root
+                rgw zone root pool: foo.rgw.root
+              client.1:
+                rgw region: bar
+                rgw zone: bar-master
+                rgw region root pool: bar.rgw.root
+                rgw zone root pool: bar.rgw.root
+              client.2:
+                rgw region: bar
+                rgw zone: bar-secondary
+                rgw region root pool: bar.rgw.root
+                rgw zone root pool: bar-secondary.rgw.root
+        - rgw:
+            regions:
+              foo:
+                api name: api_name # default: region name
+                is master: true    # default: false
+                master zone: foo-1 # default: first zone
+                zones: [foo-1]
+                placement targets: [target1, target2] # default: []
+                default placement: target2            # default: ''
+              bar:
+                api name: bar-api
+                zones: [bar-master, bar-secondary]
+            client.0:
+              system user:
+                name: foo-system
+                access key: X2IYPSTY1072DDY1SJMC
+                secret key: YIMHICpPvT+MhLTbSsiBJ1jQF15IFvJA8tgwJEcm
+            client.1:
+              system user:
+                name: bar1
+                access key: Y2IYPSTY1072DDY1SJMC
+                secret key: XIMHICpPvT+MhLTbSsiBJ1jQF15IFvJA8tgwJEcm
+            client.2:
+              system user:
+                name: bar2
+                access key: Z2IYPSTY1072DDY1SJMC
+                secret key: ZIMHICpPvT+MhLTbSsiBJ1jQF15IFvJA8tgwJEcm
     """
     if config is None:
         config = dict(('client.{id}'.format(id=id_), None)
@@ -633,24 +474,25 @@ def task(ctx, config):
     elif isinstance(config, list):
         config = dict((name, None) for name in config)
 
-    #overrides = ctx.config.get('overrides', {})
-    #teuthology.deep_merge(config, overrides.get('rgw', {}))
+    regions = {}
+    if 'regions' in config:
+        # separate region info so only clients are keys in config
+        regions = config['regions']
+        del config['regions']
 
-    log.info('jb, config is: %s' % config)
-    log.info('jb, config2 is: %s' % dict(conf=config.get('conf', {})))
-
-    for _, roles_for_host in ctx.cluster.remotes.iteritems():
-        running_rgw = False
-        for role in roles_for_host:
-            if role in config.iterkeys():
-                assert not running_rgw, "Only one client per host can run rgw."
-                running_rgw = True
+    role_endpoints = assign_ports(ctx, config)
 
     with contextutil.nested(
         lambda: create_dirs(ctx=ctx, config=config),
-        lambda: ship_config(ctx=ctx, config=config),
+        lambda: configure_regions_and_zones(
+            ctx=ctx,
+            config=config,
+            regions=regions,
+            role_endpoints=role_endpoints,
+            ),
+        lambda: ship_config(ctx=ctx, config=config,
+                            role_endpoints=role_endpoints),
         lambda: start_rgw(ctx=ctx, config=config),
         lambda: start_apache(ctx=ctx, config=config),
-        lambda: configure_regions_and_zones(ctx=ctx, config=config),
         ):
         yield
index 5a41f7e4149446b58122b0fddc0a50e3cbe01b8f..43bbdcf9ff99049a2b136b17d2350b73ddedd1f3 100644 (file)
@@ -6,7 +6,7 @@ from teuthology import misc as teuthology
 
 log = logging.getLogger(__name__)
 
-def rgwadmin(ctx, client, cmd, stdin=StringIO()):
+def rgwadmin(ctx, client, cmd, stdin=StringIO(), check_status=False):
     log.info('radosgw-admin: %s' % cmd)
     testdir = teuthology.get_testdir(ctx)
     pre = [
@@ -21,7 +21,7 @@ def rgwadmin(ctx, client, cmd, stdin=StringIO()):
     (remote,) = ctx.cluster.only(client).remotes.iterkeys()
     proc = remote.run(
         args=pre,
-        check_status=False,
+        check_status=check_status,
         stdout=StringIO(),
         stderr=StringIO(),
         stdin=stdin,