1 from abc import ABCMeta, abstractmethod
2 from cStringIO import StringIO
5 from conn import get_gateway_connection
8 """ interface to run commands against a distinct ceph cluster """
9 __metaclass__ = ABCMeta
12 def admin(self, args = None, **kwargs):
13 """ execute a radosgw-admin command """
17 """ interface to control a single radosgw instance """
18 __metaclass__ = ABCMeta
20 def __init__(self, host = None, port = None, cluster = None, zone = None, proto = 'http', connection = None):
23 self.cluster = cluster
26 self.connection = connection
29 def start(self, args = []):
30 """ start the gateway with the given args """
35 """ stop the gateway """
39 return '%s://%s:%d' % (self.proto, self.host, self.port)
42 """ interface for system objects, represented in json format and
43 manipulated with radosgw-admin commands """
44 __metaclass__ = ABCMeta
46 def __init__(self, data = None, uuid = None):
50 self.load_from_json(data)
53 def build_command(self, command):
54 """ return the command line for the given command, including arguments
55 to specify this object """
59 def load_from_json(self, data):
60 """ update internal state based on json data """
63 def command(self, cluster, cmd, args = None, **kwargs):
64 """ run the given command and return the output and retcode """
65 args = self.build_command(cmd) + (args or [])
66 return cluster.admin(args, **kwargs)
68 def json_command(self, cluster, cmd, args = None, **kwargs):
69 """ run the given command, parse the output and return the resulting
71 s, r = self.command(cluster, cmd, args or [], **kwargs)
73 output = s.decode('utf-8')
74 output = output[output.find('{'):] # trim extra output before json
75 data = json.loads(output)
76 self.load_from_json(data)
80 # mixins for supported commands
82 def create(self, cluster, args = None, **kwargs):
83 """ create the object with the given arguments """
84 return self.json_command(cluster, 'create', args, **kwargs)
87 def delete(self, cluster, args = None, **kwargs):
88 """ delete the object """
89 # not json_command() because delete has no output
90 _, r = self.command(cluster, 'delete', args, **kwargs)
96 def get(self, cluster, args = None, **kwargs):
97 """ read the object from storage """
98 kwargs['read_only'] = True
99 return self.json_command(cluster, 'get', args, **kwargs)
102 def set(self, cluster, data, args = None, **kwargs):
103 """ set the object by json """
104 kwargs['stdin'] = StringIO(json.dumps(data))
105 return self.json_command(cluster, 'set', args, **kwargs)
107 class Modify(object):
108 def modify(self, cluster, args = None, **kwargs):
109 """ modify the object with the given arguments """
110 return self.json_command(cluster, 'modify', args, **kwargs)
112 class CreateDelete(Create, Delete): pass
113 class GetSet(Get, Set): pass
115 class Zone(SystemObject, SystemObject.CreateDelete, SystemObject.GetSet, SystemObject.Modify):
116 def __init__(self, name, zonegroup = None, cluster = None, data = None, zone_id = None, gateways = None):
118 self.zonegroup = zonegroup
119 self.cluster = cluster
120 self.gateways = gateways or []
121 super(Zone, self).__init__(data, zone_id)
124 """ command-line argument to specify this zone """
125 return ['--rgw-zone', self.name]
128 """ command-line arguments to specify this zone/zonegroup/realm """
129 args = self.zone_arg()
131 args += self.zonegroup.zonegroup_args()
134 def build_command(self, command):
135 """ build a command line for the given command and args """
136 return ['zone', command] + self.zone_args()
138 def load_from_json(self, data):
139 """ load the zone from json """
141 self.name = data['name']
143 def start(self, args = None):
144 """ start all gateways """
145 for g in self.gateways:
149 """ stop all gateways """
150 for g in self.gateways:
154 return self.zonegroup.period if self.zonegroup else None
157 return self.zonegroup.realm() if self.zonegroup else None
159 def is_read_only(self):
163 raise NotImplementedError
165 def has_buckets(self):
168 def get_conn(self, credentials):
169 return ZoneConn(self, credentials) # not implemented, but can be used
171 class ZoneConn(object):
172 def __init__(self, zone, credentials):
174 self.name = zone.name
175 """ connect to the zone's first gateway """
176 if isinstance(credentials, list):
177 self.credentials = credentials[0]
179 self.credentials = credentials
181 if self.zone.gateways is not None:
182 self.conn = get_gateway_connection(self.zone.gateways[0], self.credentials)
184 def get_connection(self):
187 def get_bucket(self, bucket_name, credentials):
188 raise NotImplementedError
190 def check_bucket_eq(self, zone, bucket_name):
191 raise NotImplementedError
193 class ZoneGroup(SystemObject, SystemObject.CreateDelete, SystemObject.GetSet, SystemObject.Modify):
194 def __init__(self, name, period = None, data = None, zonegroup_id = None, zones = None, master_zone = None):
197 self.zones = zones or []
198 self.master_zone = master_zone
199 super(ZoneGroup, self).__init__(data, zonegroup_id)
202 self.zones_by_type = {}
205 self.ro_zones.append(z)
207 self.rw_zones.append(z)
209 def zonegroup_arg(self):
210 """ command-line argument to specify this zonegroup """
211 return ['--rgw-zonegroup', self.name]
213 def zonegroup_args(self):
214 """ command-line arguments to specify this zonegroup/realm """
215 args = self.zonegroup_arg()
218 args += realm.realm_arg()
221 def build_command(self, command):
222 """ build a command line for the given command and args """
223 return ['zonegroup', command] + self.zonegroup_args()
225 def zone_by_id(self, zone_id):
226 """ return the matching zone by id """
227 for zone in self.zones:
228 if zone.id == zone_id:
232 def load_from_json(self, data):
233 """ load the zonegroup from json """
235 self.name = data['name']
236 master_id = data['master_zone']
237 if not self.master_zone or master_id != self.master_zone.id:
238 self.master_zone = self.zone_by_id(master_id)
240 def add(self, cluster, zone, args = None, **kwargs):
241 """ add an existing zone to the zonegroup """
242 args = zone.zone_arg() + (args or [])
243 data, r = self.json_command(cluster, 'add', args, **kwargs)
245 zone.zonegroup = self
246 self.zones.append(zone)
249 def remove(self, cluster, zone, args = None, **kwargs):
250 """ remove an existing zone from the zonegroup """
251 args = zone.zone_arg() + (args or [])
252 data, r = self.json_command(cluster, 'remove', args, **kwargs)
254 zone.zonegroup = None
255 self.zones.remove(zone)
259 return self.period.realm if self.period else None
261 class Period(SystemObject, SystemObject.Get):
262 def __init__(self, realm = None, data = None, period_id = None, zonegroups = None, master_zonegroup = None):
264 self.zonegroups = zonegroups or []
265 self.master_zonegroup = master_zonegroup
266 super(Period, self).__init__(data, period_id)
268 def zonegroup_by_id(self, zonegroup_id):
269 """ return the matching zonegroup by id """
270 for zonegroup in self.zonegroups:
271 if zonegroup.id == zonegroup_id:
275 def build_command(self, command):
276 """ build a command line for the given command and args """
277 return ['period', command]
279 def load_from_json(self, data):
280 """ load the period from json """
282 master_id = data['master_zonegroup']
283 if not self.master_zonegroup or master_id != self.master_zonegroup.id:
284 self.master_zonegroup = self.zonegroup_by_id(master_id)
286 def update(self, zone, args = None, **kwargs):
287 """ run 'radosgw-admin period update' on the given zone """
289 args = zone.zone_args() + (args or [])
290 if kwargs.pop('commit', False):
291 args.append('--commit')
292 return self.json_command(zone.cluster, 'update', args, **kwargs)
294 def commit(self, zone, args = None, **kwargs):
295 """ run 'radosgw-admin period commit' on the given zone """
297 args = zone.zone_args() + (args or [])
298 return self.json_command(zone.cluster, 'commit', args, **kwargs)
300 class Realm(SystemObject, SystemObject.CreateDelete, SystemObject.GetSet):
301 def __init__(self, name, period = None, data = None, realm_id = None):
303 self.current_period = period
304 super(Realm, self).__init__(data, realm_id)
307 """ return the command-line arguments that specify this realm """
308 return ['--rgw-realm', self.name]
310 def build_command(self, command):
311 """ build a command line for the given command and args """
312 return ['realm', command] + self.realm_arg()
314 def load_from_json(self, data):
315 """ load the realm from json """
318 def pull(self, cluster, gateway, credentials, args = [], **kwargs):
319 """ pull an existing realm from the given gateway """
320 args += ['--url', gateway.endpoint()]
321 args += credentials.credential_args()
322 return self.json_command(cluster, 'pull', args, **kwargs)
324 def master_zonegroup(self):
325 """ return the current period's master zonegroup """
326 if self.current_period is None:
328 return self.current_period.master_zonegroup
330 def meta_master_zone(self):
331 """ return the current period's metadata master zone """
332 zonegroup = self.master_zonegroup()
333 if zonegroup is None:
335 return zonegroup.master_zone
338 def __init__(self, access_key, secret):
339 self.access_key = access_key
342 def credential_args(self):
343 return ['--access-key', self.access_key, '--secret', self.secret]
345 class User(SystemObject):
346 def __init__(self, uid, data = None, name = None, credentials = None):
348 self.credentials = credentials or []
349 super(User, self).__init__(data, uid)
352 """ command-line argument to specify this user """
353 return ['--uid', self.id]
355 def build_command(self, command):
356 """ build a command line for the given command and args """
357 return ['user', command] + self.user_arg()
359 def load_from_json(self, data):
360 """ load the user from json """
361 self.id = data['user_id']
362 self.name = data['display_name']
363 self.credentials = [Credentials(k['access_key'], k['secret_key']) for k in data['keys']]
365 def create(self, zone, args = None, **kwargs):
366 """ create the user with the given arguments """
368 args = zone.zone_args() + (args or [])
369 return self.json_command(zone.cluster, 'create', args, **kwargs)
371 def info(self, zone, args = None, **kwargs):
372 """ read the user from storage """
374 args = zone.zone_args() + (args or [])
375 kwargs['read_only'] = True
376 return self.json_command(zone.cluster, 'info', args, **kwargs)
378 def delete(self, zone, args = None, **kwargs):
379 """ delete the user """
381 args = zone.zone_args() + (args or [])
382 return self.command(zone.cluster, 'delete', args, **kwargs)