]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgwam: rename file
authorYehuda Sadeh <yehuda@redhat.com>
Mon, 10 May 2021 23:43:46 +0000 (16:43 -0700)
committerYehuda Sadeh <yehuda@redhat.com>
Wed, 24 Nov 2021 20:54:29 +0000 (12:54 -0800)
Signed-off-by: Yehuda Sadeh <yehuda@redhat.com>
src/rgw/rgw_admin_multi.py [deleted file]
src/rgw/rgwam.py [new file with mode: 0644]

diff --git a/src/rgw/rgw_admin_multi.py b/src/rgw/rgw_admin_multi.py
deleted file mode 100644 (file)
index b1c71a0..0000000
+++ /dev/null
@@ -1,637 +0,0 @@
-import subprocess
-import random
-import string
-import json
-import argparse
-import sys
-import socket
-import base64
-
-from urllib.parse import urlparse
-
-DEFAULT_PORT = 8000
-
-def bool_str(x):
-    return 'true' if x else 'false'
-
-def rand_alphanum_lower(l):
-    return ''.join(random.choices(string.ascii_lowercase + string.digits, k=l))
-
-def get_endpoints(endpoints, period = None):
-    if endpoints:
-        return endpoints
-
-    hostname = socket.getfqdn()
-
-    port = DEFAULT_PORT
-
-    while True:
-        ep = 'http://%s:%d' % (hostname, port)
-        if not period or not period.endpoint_exists(ep):
-            return ep
-        port += 1
-
-class JSONObj:
-    def to_json(self):
-        return json.dumps(self, default=lambda o: o.__dict__, indent=4)
-
-class RealmAccessConf(JSONObj):
-    def __init__(self, endpoint, uid, access_key, secret):
-        self.endpoint = endpoint
-        self.uid = uid
-        self.access_key = access_key
-        self.secret = secret
-
-class RGWZone(JSONObj):
-    def __init__(self, zone_dict):
-        self.id = zone_dict['id']
-        self.name = zone_dict['name']
-        self.endpoints = zone_dict['endpoints']
-
-class RGWZoneGroup(JSONObj):
-    def __init__(self, zg_dict):
-        self.id = zg_dict['id']
-        self.name = zg_dict['name']
-        self.api_name = zg_dict['api_name']
-        self.is_master = zg_dict['is_master']
-        self.endpoints = zg_dict['endpoints']
-
-        self.zones_by_id = {}
-        self.zones_by_name = {}
-        self.all_endpoints = []
-
-        for zone in zg_dict['zones']:
-            z = RGWZone(zone)
-            self.zones_by_id[zone['id']] = z
-            self.zones_by_name[zone['name']] = z
-            self.all_endpoints += z.endpoints
-
-    def endpoint_exists(self, endpoint):
-        for ep in self.all_endpoints:
-            if ep == endpoint:
-                return True
-        return False
-
-class RGWPeriod(JSONObj):
-    def __init__(self, period_dict):
-        self.id = period_dict['id']
-        self.epoch = period_dict['epoch']
-        pm = period_dict['period_map']
-        self.zonegroups_by_id = {}
-        self.zonegroups_by_name = {}
-
-        for zg in pm['zonegroups']:
-            self.zonegroups_by_id[zg['id']] = RGWZoneGroup(zg)
-            self.zonegroups_by_name[zg['name']] = RGWZoneGroup(zg)
-
-    def endpoint_exists(self, endpoint):
-        for _, zg in self.zonegroups_by_id.items():
-            if zg.endpoint_exists(endpoint):
-                return True
-        return False
-    
-    def find_zonegroup(self, zonegroup):
-        for _, zg in self.zonegroups_by_id.items():
-            if not zonegroup or zg.name == zonegroup:
-                return zg.name
-        return None
-
-class RGWAccessKey(JSONObj):
-    def __init__(self, d):
-        self.uid = d['user']
-        self.access_key = d['access_key']
-        self.secret_key = d['secret_key']
-
-class RGWUser(JSONObj):
-    def __init__(self, d):
-        self.uid = d['user_id']
-        self.display_name = d['display_name']
-        self.email = d['email']
-
-        self.keys = []
-
-        for k in d['keys']:
-            self.keys.append(RGWAccessKey(k))
-
-        is_system = d.get('system') or 'false'
-        self.system = (is_system == 'true')
-
-
-
-class RGWAMException(BaseException):
-    def __init__(self, message):
-        self.message = message
-
-
-class RGWAdminCmd:
-    def __init__(self):
-        self.cmd_prefix = [ 'radosgw-admin' ]
-
-    def run(self, cmd):
-        run_cmd = self.cmd_prefix + cmd
-        result = subprocess.run(run_cmd, stdout=subprocess.PIPE)
-        return (result.returncode, result.stdout)
-
-class RGWCmd:
-    def __init__(self):
-        self.cmd_prefix = [ 'radosgw' ]
-
-    def run(self, cmd):
-        run_cmd = self.cmd_prefix + cmd
-        result = subprocess.run(run_cmd, stdout=subprocess.PIPE)
-        return (result.returncode, result.stdout)
-
-class RealmOp(RGWAdminCmd):
-    def __init__(self):
-        RGWAdminCmd.__init__(self)
-        
-    def create(self, name = None, is_default = True):
-        self.name = name
-        if not self.name:
-            self.name = 'realm-' + rand_alphanum_lower(8)
-
-        params = [ 'realm',
-                   'create',
-                   '--rgw-realm', self.name ]
-
-        if is_default:
-            params += [ '--default' ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        return self.info
-
-    def pull(self, url, access_key, secret, set_default = False):
-        params = [ 'realm',
-                   'pull',
-                   '--url', url,
-                   '--access-key', access_key,
-                   '--secret', secret ]
-
-        if set_default:
-            params += [ '--default' ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        self.name = self.info['name']
-
-        return self.info
-
-class ZonegroupOp(RGWAdminCmd):
-    def __init__(self):
-        RGWAdminCmd.__init__(self)
-        
-    def create(self, realm, name = None, endpoints = None, is_master = True, is_default = True):
-        self.name = name
-        if not self.name:
-            self.name = 'zg-' + rand_alphanum_lower(8)
-
-        params = [ 'zonegroup',
-                   'create',
-                   '--rgw-realm', realm,
-                   '--rgw-zonegroup', self.name,
-                   '--endpoints', endpoints ]
-
-        if is_master:
-            params += [ '--master' ]
-
-        if is_default:
-            params += [ '--default' ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        return self.info
-
-class ZoneOp(RGWAdminCmd):
-    def __init__(self):
-        RGWAdminCmd.__init__(self)
-        
-    def create(self, realm, zonegroup, name = None, endpoints = None, is_master = True, is_default = True,
-               access_key = None, secret = None):
-        self.name = name
-        if not self.name:
-            self.name = 'zg-' + rand_alphanum_lower(8)
-
-        params = [ 'zone',
-                   'create',
-                   '--rgw-realm', realm,
-                   '--rgw-zonegroup', zonegroup,
-                   '--rgw-zone', self.name,
-                   '--endpoints', endpoints ]
-
-        if is_master:
-            params += [ '--master' ]
-
-        if is_default:
-            params += [ '--default' ]
-
-        if access_key:
-            params += [ '--access-key', access_key ]
-
-        if secret:
-            params += [ '--secret', secret ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        return self.info
-
-    def modify(self, endpoints = None, is_master = None, is_default = None, access_key = None, secret = None):
-        params = [ 'zone',
-                   'modify' ]
-
-        if endpoints:
-            params += [ '--endpoints', endpoints ]
-
-        if is_master is not None:
-            params += [ '--master', bool_str(is_master) ]
-
-        if is_default is not None:
-            params += [ '--default', bool_str(is_default) ]
-
-        if access_key:
-            params += [ '--access-key', access_key ]
-
-        if secret:
-            params += [ '--secret', secret ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        return self.info
-
-class PeriodOp(RGWAdminCmd):
-    def __init__(self):
-        RGWAdminCmd.__init__(self)
-        
-    def update(self, realm, commit = True):
-
-        params = [ 'period',
-                   'update',
-                   '--rgw-realm', realm ]
-
-        if commit:
-            params += [ '--commit' ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        return self.info
-
-    def get(self, realm = None):
-        params = [ 'period',
-                   'get' ]
-
-        if realm:
-            params += [ '--rgw-realm', realm ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        return self.info
-
-class UserOp(RGWAdminCmd):
-    def __init__(self):
-        RGWAdminCmd.__init__(self)
-        
-    def create(self, uid = None, uid_prefix = None, display_name = None, email = None, is_system = False):
-        self.uid = uid
-        if not self.uid:
-            prefix = uid_prefix or 'user'
-            self.uid = prefix + '-' + rand_alphanum_lower(6)
-
-        self.display_name = display_name
-        if not self.display_name:
-            self.display_name = self.uid
-
-        params = [ 'user',
-                   'create',
-                   '--uid', self.uid,
-                   '--display-name', self.display_name ]
-
-        if email:
-            params += [ '--email', email ]
-
-        if is_system:
-            params += [ '--system' ]
-
-        retcode, stdout = RGWAdminCmd.run(self, params)
-        if retcode != 0:
-            return None
-
-        self.info = json.loads(stdout)
-
-        return self.info
-
-class RGWAM:
-    def __init__(self):
-        pass
-
-    def realm_bootstrap(self, realm, zonegroup, zone, endpoints, sys_uid, uid):
-        endpoints = get_endpoints(endpoints)
-
-        realm_info = RealmOp().create(realm)
-        if not realm_info:
-            return
-
-        realm_name = realm_info['name']
-        realm_id = realm_info['id']
-        print('Created realm %s (%s)' % (realm_name, realm_id))
-
-        zg_info = ZonegroupOp().create(realm_name, zonegroup, endpoints, True, True)
-        if not zg_info:
-            return
-
-        zg_name = zg_info['name']
-        zg_id = zg_info['id']
-        print('Created zonegroup %s (%s)' % (zg_name, zg_id))
-
-        zone_info = ZoneOp().create(realm_name, zg_name, zone, endpoints, True, True)
-        if not zone_info:
-            return
-
-        zone_name = zone_info['name']
-        zone_id = zone_info['id']
-        print('Created zone %s (%s)' % (zone_name, zone_id))
-
-        period_info = PeriodOp().update(realm_name, True)
-        if not period_info:
-            return
-
-        period = RGWPeriod(period_info)
-
-        print('Period: ' + period.id)
-
-        sys_user_info = UserOp().create(uid = sys_uid, uid_prefix = 'user-sys', is_system = True)
-
-        sys_user = RGWUser(sys_user_info)
-
-        print('Created system user: %s' % sys_user.uid)
-
-        sys_access_key = ''
-        sys_secret = ''
-
-        if len(sys_user.keys) > 0:
-            sys_access_key = sys_user.keys[0].access_key
-            sys_secret = sys_user.keys[0].secret_key
-
-        zone_info = ZoneOp().modify(endpoints, None, None, sys_access_key, sys_secret)
-        if not zone_info:
-            return
-
-        user_info = UserOp().create(uid = uid, is_system = False)
-
-        user = RGWUser(user_info)
-
-        print('Created regular user: %s' % user.uid)
-
-        eps = endpoints.split(',')
-        ep = ''
-        if len(eps) > 0:
-            ep = eps[0]
-            o = urlparse(ep)
-            self.run_radosgw(port = o.port)
-
-        
-        realm_access = RealmAccessConf(ep, sys_user.uid, sys_access_key, sys_secret)
-
-        print(realm_access.to_json())
-
-        realm_access_b = realm_access.to_json().encode('utf-8')
-        print('Realm Access Conf (b64): %s' % base64.b64encode(realm_access_b).decode('utf-8'))
-
-
-    def zone_create(self, realm_access_b64, zonegroup = None, zone = None, endpoints = None):
-        if not realm_access_b64:
-            print('ERROR: missing realm access config')
-            return
-
-        realm_access_b = base64.b64decode(realm_access_b64)
-        realm_access_s = realm_access_b.decode('utf-8')
-
-        realm_access = json.loads(realm_access_s)
-
-        access_key = realm_access['access_key']
-        secret = realm_access['secret']
-
-        realm_info = RealmOp().pull(realm_access['endpoint'], access_key, secret, set_default = True)
-        if not realm_info:
-            return
-
-        realm_name = realm_info['name']
-        realm_id = realm_info['id']
-        print('Pulled realm %s (%s)' % (realm_name, realm_id))
-
-        period_info = PeriodOp().get()
-
-        period = RGWPeriod(period_info)
-
-        print('Period: ' + period.id)
-        endpoints = get_endpoints(endpoints, period)
-
-        zg_name = period.find_zonegroup(zonegroup)
-        if not zg_name:
-            print("ERROR: zonegroup %s not found" % (zonegroup or '<none>'))
-            return
-
-        zone_info = ZoneOp().create(realm_name, zg_name, zone, endpoints, False, True,
-                access_key, secret)
-        if not zone_info:
-            return
-
-        zone_name = zone_info['name']
-        zone_id = zone_info['id']
-        print('Created zone %s (%s)' % (zone_name, zone_id))
-
-        period_info = PeriodOp().update(realm_name, True)
-        if not period_info:
-            return
-
-        period = RGWPeriod(period_info)
-
-        print(period.to_json())
-
-        eps = endpoints.split(',')
-        ep = ''
-        if len(eps) > 0:
-            ep = eps[0]
-            o = urlparse(ep)
-            self.run_radosgw(port = o.port)
-
-    def run_radosgw(self, port = None, log_file = None, debug_ms = None, debug_rgw = None):
-
-        fe_cfg = 'beast'
-        if port:
-            fe_cfg += ' port=%s' % port
-
-
-        params = [ '--rgw-frontends', fe_cfg ]
-
-        if log_file:
-            params += [ '--log-file', log_file ]
-
-        if debug_ms:
-            params += [ '--debug-ms', debug_ms ]
-
-        if debug_rgw:
-            params += [ '--debug-rgw', debug_rgw ]
-
-        RGWCmd().run(params)
-
-
-class RealmCommand:
-    def __init__(self, args):
-        self.args = args
-
-    def parse(self):
-        parser = argparse.ArgumentParser(
-            description='S3 control tool',
-            usage='''rgwam realm <subcommand>
-
-The subcommands are:
-   bootstrap                     Manipulate bucket versioning
-''')
-        parser.add_argument('subcommand', help='Subcommand to run')
-        # parse_args defaults to [1:] for args, but you need to
-        # exclude the rest of the args too, or validation will fail
-        args = parser.parse_args(self.args[0:1])
-        if not hasattr(self, args.subcommand):
-            print('Unrecognized subcommand:', args.subcommand)
-            parser.print_help()
-            exit(1)
-        # use dispatch pattern to invoke method with same name
-        return getattr(self, args.subcommand)
-
-    def bootstrap(self):
-        parser = argparse.ArgumentParser(
-            description='Bootstrap new realm',
-            usage='rgwam realm bootstrap [<args>]')
-        parser.add_argument('--realm')
-        parser.add_argument('--zonegroup')
-        parser.add_argument('--zone')
-        parser.add_argument('--endpoints')
-        parser.add_argument('--sys-uid')
-        parser.add_argument('--uid')
-
-        args = parser.parse_args(self.args[1:])
-
-        RGWAM().realm_bootstrap(args.realm, args.zonegroup, args.zone, args.endpoints,
-                                args.sys_uid, args.uid)
-
-
-class ZoneCommand:
-    def __init__(self, args):
-        self.args = args
-
-    def parse(self):
-        parser = argparse.ArgumentParser(
-            description='S3 control tool',
-            usage='''rgwam zone <subcommand>
-
-The subcommands are:
-   run                     run radosgw daemon in current zone
-''')
-        parser.add_argument('subcommand', help='Subcommand to run')
-        # parse_args defaults to [1:] for args, but you need to
-        # exclude the rest of the args too, or validation will fail
-        args = parser.parse_args(self.args[0:1])
-        if not hasattr(self, args.subcommand):
-            print('Unrecognized subcommand:', args.subcommand)
-            parser.print_help()
-            exit(1)
-        # use dispatch pattern to invoke method with same name
-        return getattr(self, args.subcommand)
-
-    def run(self):
-        parser = argparse.ArgumentParser(
-            description='Run radosgw daemon',
-            usage='rgwam zone run [<args>]')
-        parser.add_argument('--port')
-        parser.add_argument('--log-file')
-        parser.add_argument('--debug-ms')
-        parser.add_argument('--debug-rgw')
-
-        args = parser.parse_args(self.args[1:])
-
-        RGWAM().run_radosgw(port = args.port)
-
-    def create(self):
-        parser = argparse.ArgumentParser(
-            description='Create new zone to join existing realm',
-            usage='rgwam zone create [<args>]')
-        parser.add_argument('--realm-access')
-        parser.add_argument('--zone')
-        parser.add_argument('--zonegroup')
-        parser.add_argument('--endpoints')
-
-        args = parser.parse_args(self.args[1:])
-
-        RGWAM().zone_create(args.realm_access, args.zonegroup, args.zone, args.endpoints)
-
-
-class TopLevelCommand:
-
-    def _parse(self):
-        parser = argparse.ArgumentParser(
-            description='RGW assist for multisite tool',
-            usage='''rgwam <command> [<args>]
-
-The commands are:
-   realm bootstrap               Bootstrap realm
-   zone run                      Run radosgw in current zone
-''')
-        parser.add_argument('command', help='Subcommand to run')
-        # parse_args defaults to [1:] for args, but you need to
-        # exclude the rest of the args too, or validation will fail
-        args = parser.parse_args(sys.argv[1:2])
-        if not hasattr(self, args.command) or args.command[0] == '_':
-            print('Unrecognized command:', args.command)
-            parser.print_help()
-            exit(1)
-        # use dispatch pattern to invoke method with same name
-        return getattr(self, args.command)
-
-    def realm(self):
-        cmd = RealmCommand(sys.argv[2:]).parse()
-        cmd()
-
-    def zone(self):
-        cmd = ZoneCommand(sys.argv[2:]).parse()
-        cmd()
-
-
-def main():
-    cmd = TopLevelCommand()._parse()
-    try:
-        cmd()
-    except RGWAMException as e:
-        print('ERROR: ' + e.message)
-
-
-if __name__ == '__main__':
-    main()
-
diff --git a/src/rgw/rgwam.py b/src/rgw/rgwam.py
new file mode 100644 (file)
index 0000000..b1c71a0
--- /dev/null
@@ -0,0 +1,637 @@
+import subprocess
+import random
+import string
+import json
+import argparse
+import sys
+import socket
+import base64
+
+from urllib.parse import urlparse
+
+DEFAULT_PORT = 8000
+
+def bool_str(x):
+    return 'true' if x else 'false'
+
+def rand_alphanum_lower(l):
+    return ''.join(random.choices(string.ascii_lowercase + string.digits, k=l))
+
+def get_endpoints(endpoints, period = None):
+    if endpoints:
+        return endpoints
+
+    hostname = socket.getfqdn()
+
+    port = DEFAULT_PORT
+
+    while True:
+        ep = 'http://%s:%d' % (hostname, port)
+        if not period or not period.endpoint_exists(ep):
+            return ep
+        port += 1
+
+class JSONObj:
+    def to_json(self):
+        return json.dumps(self, default=lambda o: o.__dict__, indent=4)
+
+class RealmAccessConf(JSONObj):
+    def __init__(self, endpoint, uid, access_key, secret):
+        self.endpoint = endpoint
+        self.uid = uid
+        self.access_key = access_key
+        self.secret = secret
+
+class RGWZone(JSONObj):
+    def __init__(self, zone_dict):
+        self.id = zone_dict['id']
+        self.name = zone_dict['name']
+        self.endpoints = zone_dict['endpoints']
+
+class RGWZoneGroup(JSONObj):
+    def __init__(self, zg_dict):
+        self.id = zg_dict['id']
+        self.name = zg_dict['name']
+        self.api_name = zg_dict['api_name']
+        self.is_master = zg_dict['is_master']
+        self.endpoints = zg_dict['endpoints']
+
+        self.zones_by_id = {}
+        self.zones_by_name = {}
+        self.all_endpoints = []
+
+        for zone in zg_dict['zones']:
+            z = RGWZone(zone)
+            self.zones_by_id[zone['id']] = z
+            self.zones_by_name[zone['name']] = z
+            self.all_endpoints += z.endpoints
+
+    def endpoint_exists(self, endpoint):
+        for ep in self.all_endpoints:
+            if ep == endpoint:
+                return True
+        return False
+
+class RGWPeriod(JSONObj):
+    def __init__(self, period_dict):
+        self.id = period_dict['id']
+        self.epoch = period_dict['epoch']
+        pm = period_dict['period_map']
+        self.zonegroups_by_id = {}
+        self.zonegroups_by_name = {}
+
+        for zg in pm['zonegroups']:
+            self.zonegroups_by_id[zg['id']] = RGWZoneGroup(zg)
+            self.zonegroups_by_name[zg['name']] = RGWZoneGroup(zg)
+
+    def endpoint_exists(self, endpoint):
+        for _, zg in self.zonegroups_by_id.items():
+            if zg.endpoint_exists(endpoint):
+                return True
+        return False
+    
+    def find_zonegroup(self, zonegroup):
+        for _, zg in self.zonegroups_by_id.items():
+            if not zonegroup or zg.name == zonegroup:
+                return zg.name
+        return None
+
+class RGWAccessKey(JSONObj):
+    def __init__(self, d):
+        self.uid = d['user']
+        self.access_key = d['access_key']
+        self.secret_key = d['secret_key']
+
+class RGWUser(JSONObj):
+    def __init__(self, d):
+        self.uid = d['user_id']
+        self.display_name = d['display_name']
+        self.email = d['email']
+
+        self.keys = []
+
+        for k in d['keys']:
+            self.keys.append(RGWAccessKey(k))
+
+        is_system = d.get('system') or 'false'
+        self.system = (is_system == 'true')
+
+
+
+class RGWAMException(BaseException):
+    def __init__(self, message):
+        self.message = message
+
+
+class RGWAdminCmd:
+    def __init__(self):
+        self.cmd_prefix = [ 'radosgw-admin' ]
+
+    def run(self, cmd):
+        run_cmd = self.cmd_prefix + cmd
+        result = subprocess.run(run_cmd, stdout=subprocess.PIPE)
+        return (result.returncode, result.stdout)
+
+class RGWCmd:
+    def __init__(self):
+        self.cmd_prefix = [ 'radosgw' ]
+
+    def run(self, cmd):
+        run_cmd = self.cmd_prefix + cmd
+        result = subprocess.run(run_cmd, stdout=subprocess.PIPE)
+        return (result.returncode, result.stdout)
+
+class RealmOp(RGWAdminCmd):
+    def __init__(self):
+        RGWAdminCmd.__init__(self)
+        
+    def create(self, name = None, is_default = True):
+        self.name = name
+        if not self.name:
+            self.name = 'realm-' + rand_alphanum_lower(8)
+
+        params = [ 'realm',
+                   'create',
+                   '--rgw-realm', self.name ]
+
+        if is_default:
+            params += [ '--default' ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        return self.info
+
+    def pull(self, url, access_key, secret, set_default = False):
+        params = [ 'realm',
+                   'pull',
+                   '--url', url,
+                   '--access-key', access_key,
+                   '--secret', secret ]
+
+        if set_default:
+            params += [ '--default' ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        self.name = self.info['name']
+
+        return self.info
+
+class ZonegroupOp(RGWAdminCmd):
+    def __init__(self):
+        RGWAdminCmd.__init__(self)
+        
+    def create(self, realm, name = None, endpoints = None, is_master = True, is_default = True):
+        self.name = name
+        if not self.name:
+            self.name = 'zg-' + rand_alphanum_lower(8)
+
+        params = [ 'zonegroup',
+                   'create',
+                   '--rgw-realm', realm,
+                   '--rgw-zonegroup', self.name,
+                   '--endpoints', endpoints ]
+
+        if is_master:
+            params += [ '--master' ]
+
+        if is_default:
+            params += [ '--default' ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        return self.info
+
+class ZoneOp(RGWAdminCmd):
+    def __init__(self):
+        RGWAdminCmd.__init__(self)
+        
+    def create(self, realm, zonegroup, name = None, endpoints = None, is_master = True, is_default = True,
+               access_key = None, secret = None):
+        self.name = name
+        if not self.name:
+            self.name = 'zg-' + rand_alphanum_lower(8)
+
+        params = [ 'zone',
+                   'create',
+                   '--rgw-realm', realm,
+                   '--rgw-zonegroup', zonegroup,
+                   '--rgw-zone', self.name,
+                   '--endpoints', endpoints ]
+
+        if is_master:
+            params += [ '--master' ]
+
+        if is_default:
+            params += [ '--default' ]
+
+        if access_key:
+            params += [ '--access-key', access_key ]
+
+        if secret:
+            params += [ '--secret', secret ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        return self.info
+
+    def modify(self, endpoints = None, is_master = None, is_default = None, access_key = None, secret = None):
+        params = [ 'zone',
+                   'modify' ]
+
+        if endpoints:
+            params += [ '--endpoints', endpoints ]
+
+        if is_master is not None:
+            params += [ '--master', bool_str(is_master) ]
+
+        if is_default is not None:
+            params += [ '--default', bool_str(is_default) ]
+
+        if access_key:
+            params += [ '--access-key', access_key ]
+
+        if secret:
+            params += [ '--secret', secret ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        return self.info
+
+class PeriodOp(RGWAdminCmd):
+    def __init__(self):
+        RGWAdminCmd.__init__(self)
+        
+    def update(self, realm, commit = True):
+
+        params = [ 'period',
+                   'update',
+                   '--rgw-realm', realm ]
+
+        if commit:
+            params += [ '--commit' ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        return self.info
+
+    def get(self, realm = None):
+        params = [ 'period',
+                   'get' ]
+
+        if realm:
+            params += [ '--rgw-realm', realm ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        return self.info
+
+class UserOp(RGWAdminCmd):
+    def __init__(self):
+        RGWAdminCmd.__init__(self)
+        
+    def create(self, uid = None, uid_prefix = None, display_name = None, email = None, is_system = False):
+        self.uid = uid
+        if not self.uid:
+            prefix = uid_prefix or 'user'
+            self.uid = prefix + '-' + rand_alphanum_lower(6)
+
+        self.display_name = display_name
+        if not self.display_name:
+            self.display_name = self.uid
+
+        params = [ 'user',
+                   'create',
+                   '--uid', self.uid,
+                   '--display-name', self.display_name ]
+
+        if email:
+            params += [ '--email', email ]
+
+        if is_system:
+            params += [ '--system' ]
+
+        retcode, stdout = RGWAdminCmd.run(self, params)
+        if retcode != 0:
+            return None
+
+        self.info = json.loads(stdout)
+
+        return self.info
+
+class RGWAM:
+    def __init__(self):
+        pass
+
+    def realm_bootstrap(self, realm, zonegroup, zone, endpoints, sys_uid, uid):
+        endpoints = get_endpoints(endpoints)
+
+        realm_info = RealmOp().create(realm)
+        if not realm_info:
+            return
+
+        realm_name = realm_info['name']
+        realm_id = realm_info['id']
+        print('Created realm %s (%s)' % (realm_name, realm_id))
+
+        zg_info = ZonegroupOp().create(realm_name, zonegroup, endpoints, True, True)
+        if not zg_info:
+            return
+
+        zg_name = zg_info['name']
+        zg_id = zg_info['id']
+        print('Created zonegroup %s (%s)' % (zg_name, zg_id))
+
+        zone_info = ZoneOp().create(realm_name, zg_name, zone, endpoints, True, True)
+        if not zone_info:
+            return
+
+        zone_name = zone_info['name']
+        zone_id = zone_info['id']
+        print('Created zone %s (%s)' % (zone_name, zone_id))
+
+        period_info = PeriodOp().update(realm_name, True)
+        if not period_info:
+            return
+
+        period = RGWPeriod(period_info)
+
+        print('Period: ' + period.id)
+
+        sys_user_info = UserOp().create(uid = sys_uid, uid_prefix = 'user-sys', is_system = True)
+
+        sys_user = RGWUser(sys_user_info)
+
+        print('Created system user: %s' % sys_user.uid)
+
+        sys_access_key = ''
+        sys_secret = ''
+
+        if len(sys_user.keys) > 0:
+            sys_access_key = sys_user.keys[0].access_key
+            sys_secret = sys_user.keys[0].secret_key
+
+        zone_info = ZoneOp().modify(endpoints, None, None, sys_access_key, sys_secret)
+        if not zone_info:
+            return
+
+        user_info = UserOp().create(uid = uid, is_system = False)
+
+        user = RGWUser(user_info)
+
+        print('Created regular user: %s' % user.uid)
+
+        eps = endpoints.split(',')
+        ep = ''
+        if len(eps) > 0:
+            ep = eps[0]
+            o = urlparse(ep)
+            self.run_radosgw(port = o.port)
+
+        
+        realm_access = RealmAccessConf(ep, sys_user.uid, sys_access_key, sys_secret)
+
+        print(realm_access.to_json())
+
+        realm_access_b = realm_access.to_json().encode('utf-8')
+        print('Realm Access Conf (b64): %s' % base64.b64encode(realm_access_b).decode('utf-8'))
+
+
+    def zone_create(self, realm_access_b64, zonegroup = None, zone = None, endpoints = None):
+        if not realm_access_b64:
+            print('ERROR: missing realm access config')
+            return
+
+        realm_access_b = base64.b64decode(realm_access_b64)
+        realm_access_s = realm_access_b.decode('utf-8')
+
+        realm_access = json.loads(realm_access_s)
+
+        access_key = realm_access['access_key']
+        secret = realm_access['secret']
+
+        realm_info = RealmOp().pull(realm_access['endpoint'], access_key, secret, set_default = True)
+        if not realm_info:
+            return
+
+        realm_name = realm_info['name']
+        realm_id = realm_info['id']
+        print('Pulled realm %s (%s)' % (realm_name, realm_id))
+
+        period_info = PeriodOp().get()
+
+        period = RGWPeriod(period_info)
+
+        print('Period: ' + period.id)
+        endpoints = get_endpoints(endpoints, period)
+
+        zg_name = period.find_zonegroup(zonegroup)
+        if not zg_name:
+            print("ERROR: zonegroup %s not found" % (zonegroup or '<none>'))
+            return
+
+        zone_info = ZoneOp().create(realm_name, zg_name, zone, endpoints, False, True,
+                access_key, secret)
+        if not zone_info:
+            return
+
+        zone_name = zone_info['name']
+        zone_id = zone_info['id']
+        print('Created zone %s (%s)' % (zone_name, zone_id))
+
+        period_info = PeriodOp().update(realm_name, True)
+        if not period_info:
+            return
+
+        period = RGWPeriod(period_info)
+
+        print(period.to_json())
+
+        eps = endpoints.split(',')
+        ep = ''
+        if len(eps) > 0:
+            ep = eps[0]
+            o = urlparse(ep)
+            self.run_radosgw(port = o.port)
+
+    def run_radosgw(self, port = None, log_file = None, debug_ms = None, debug_rgw = None):
+
+        fe_cfg = 'beast'
+        if port:
+            fe_cfg += ' port=%s' % port
+
+
+        params = [ '--rgw-frontends', fe_cfg ]
+
+        if log_file:
+            params += [ '--log-file', log_file ]
+
+        if debug_ms:
+            params += [ '--debug-ms', debug_ms ]
+
+        if debug_rgw:
+            params += [ '--debug-rgw', debug_rgw ]
+
+        RGWCmd().run(params)
+
+
+class RealmCommand:
+    def __init__(self, args):
+        self.args = args
+
+    def parse(self):
+        parser = argparse.ArgumentParser(
+            description='S3 control tool',
+            usage='''rgwam realm <subcommand>
+
+The subcommands are:
+   bootstrap                     Manipulate bucket versioning
+''')
+        parser.add_argument('subcommand', help='Subcommand to run')
+        # parse_args defaults to [1:] for args, but you need to
+        # exclude the rest of the args too, or validation will fail
+        args = parser.parse_args(self.args[0:1])
+        if not hasattr(self, args.subcommand):
+            print('Unrecognized subcommand:', args.subcommand)
+            parser.print_help()
+            exit(1)
+        # use dispatch pattern to invoke method with same name
+        return getattr(self, args.subcommand)
+
+    def bootstrap(self):
+        parser = argparse.ArgumentParser(
+            description='Bootstrap new realm',
+            usage='rgwam realm bootstrap [<args>]')
+        parser.add_argument('--realm')
+        parser.add_argument('--zonegroup')
+        parser.add_argument('--zone')
+        parser.add_argument('--endpoints')
+        parser.add_argument('--sys-uid')
+        parser.add_argument('--uid')
+
+        args = parser.parse_args(self.args[1:])
+
+        RGWAM().realm_bootstrap(args.realm, args.zonegroup, args.zone, args.endpoints,
+                                args.sys_uid, args.uid)
+
+
+class ZoneCommand:
+    def __init__(self, args):
+        self.args = args
+
+    def parse(self):
+        parser = argparse.ArgumentParser(
+            description='S3 control tool',
+            usage='''rgwam zone <subcommand>
+
+The subcommands are:
+   run                     run radosgw daemon in current zone
+''')
+        parser.add_argument('subcommand', help='Subcommand to run')
+        # parse_args defaults to [1:] for args, but you need to
+        # exclude the rest of the args too, or validation will fail
+        args = parser.parse_args(self.args[0:1])
+        if not hasattr(self, args.subcommand):
+            print('Unrecognized subcommand:', args.subcommand)
+            parser.print_help()
+            exit(1)
+        # use dispatch pattern to invoke method with same name
+        return getattr(self, args.subcommand)
+
+    def run(self):
+        parser = argparse.ArgumentParser(
+            description='Run radosgw daemon',
+            usage='rgwam zone run [<args>]')
+        parser.add_argument('--port')
+        parser.add_argument('--log-file')
+        parser.add_argument('--debug-ms')
+        parser.add_argument('--debug-rgw')
+
+        args = parser.parse_args(self.args[1:])
+
+        RGWAM().run_radosgw(port = args.port)
+
+    def create(self):
+        parser = argparse.ArgumentParser(
+            description='Create new zone to join existing realm',
+            usage='rgwam zone create [<args>]')
+        parser.add_argument('--realm-access')
+        parser.add_argument('--zone')
+        parser.add_argument('--zonegroup')
+        parser.add_argument('--endpoints')
+
+        args = parser.parse_args(self.args[1:])
+
+        RGWAM().zone_create(args.realm_access, args.zonegroup, args.zone, args.endpoints)
+
+
+class TopLevelCommand:
+
+    def _parse(self):
+        parser = argparse.ArgumentParser(
+            description='RGW assist for multisite tool',
+            usage='''rgwam <command> [<args>]
+
+The commands are:
+   realm bootstrap               Bootstrap realm
+   zone run                      Run radosgw in current zone
+''')
+        parser.add_argument('command', help='Subcommand to run')
+        # parse_args defaults to [1:] for args, but you need to
+        # exclude the rest of the args too, or validation will fail
+        args = parser.parse_args(sys.argv[1:2])
+        if not hasattr(self, args.command) or args.command[0] == '_':
+            print('Unrecognized command:', args.command)
+            parser.print_help()
+            exit(1)
+        # use dispatch pattern to invoke method with same name
+        return getattr(self, args.command)
+
+    def realm(self):
+        cmd = RealmCommand(sys.argv[2:]).parse()
+        cmd()
+
+    def zone(self):
+        cmd = ZoneCommand(sys.argv[2:]).parse()
+        cmd()
+
+
+def main():
+    cmd = TopLevelCommand()._parse()
+    try:
+        cmd()
+    except RGWAMException as e:
+        print('ERROR: ' + e.message)
+
+
+if __name__ == '__main__':
+    main()
+