From 206f37cd289f6640d751b2a74156261177a187e5 Mon Sep 17 00:00:00 2001 From: Tim Simpson Date: Wed, 6 Jun 2012 08:44:25 -0500 Subject: [PATCH] Raise exceptions on user or database if a resource with the same name is found. * Updated reference guest and guest API. --- reddwarf/common/exception.py | 11 ++++++ reddwarf/extensions/mysql/models.py | 52 +++++++++++++++++++++------- reddwarf/extensions/mysql/service.py | 2 ++ reddwarf/guestagent/api.py | 10 +++--- reddwarf/guestagent/dbaas.py | 24 ++++++++----- reddwarf/tests/fakes/guestagent.py | 16 ++++++--- 6 files changed, 84 insertions(+), 31 deletions(-) diff --git a/reddwarf/common/exception.py b/reddwarf/common/exception.py index 36f1ea960f..11d3f7a376 100644 --- a/reddwarf/common/exception.py +++ b/reddwarf/common/exception.py @@ -99,6 +99,16 @@ class MissingKey(BadRequest): message = _("Required element/key - %(key)s was not specified") +class DatabaseAlreadyExists(BadRequest): + + message = _('A database with the name "%(name)s" already exists.') + + +class UserAlreadyExists(BadRequest): + + message = _('A user with the name "%(name)s" already exists.') + + class UnprocessableEntity(ReddwarfError): message = _("Unable to process the contained request") @@ -133,3 +143,4 @@ class BadValue(ReddwarfError): class PollTimeOut(ReddwarfError): message = _("Polling request timed out.") + diff --git a/reddwarf/extensions/mysql/models.py b/reddwarf/extensions/mysql/models.py index ca4cec206b..3a3bcfa192 100644 --- a/reddwarf/extensions/mysql/models.py +++ b/reddwarf/extensions/mysql/models.py @@ -99,7 +99,15 @@ class User(object): def create(cls, context, instance_id, users): # Load InstanceServiceStatus to verify if it's running load_and_verify(context, instance_id) - create_guest_client(context, instance_id).create_user(users) + client = create_guest_client(context, instance_id) + for user in users: + user_name = user['_name'] + existing_users, _nadda = Users.load_with_client(client, limit=1, + marker=user_name, include_marker=True) + if len(existing_users) > 0 and \ + str(existing_users[0].name) == str(user_name): + raise exception.UserAlreadyExists(name=user_name) + return client.create_user(users) @classmethod def delete(cls, context, instance_id, username): @@ -155,18 +163,29 @@ class RootHistory(object): return history +def load_via_context(cls, context, instance_id): + """Creates guest and fetches pagination arguments from the context.""" + load_and_verify(context, instance_id) + limit = int(context.limit or cls.DEFAULT_LIMIT) + limit = cls.DEFAULT_LIMIT if limit > cls.DEFAULT_LIMIT else limit + client = create_guest_client(context, instance_id) + # The REST API standard dictates that we *NEVER* include the marker. + return cls.load_with_client(client=client, limit=limit, + marker=context.marker, include_marker=False) + + class Users(object): DEFAULT_LIMIT = int(config.Config.get('users_page_size', '20')) @classmethod def load(cls, context, instance_id): - load_and_verify(context, instance_id) - limit = int(context.limit or Users.DEFAULT_LIMIT) - limit = Users.DEFAULT_LIMIT if limit > Users.DEFAULT_LIMIT else limit - client = create_guest_client(context, instance_id) + return load_via_context(cls, context, instance_id) + + @classmethod + def load_with_client(cls, client, limit, marker, include_marker): user_list, next_marker = client.list_users(limit=limit, - marker=context.marker) + marker=marker, include_marker=include_marker) model_users = [] for user in user_list: mysql_user = guest_models.MySQLUser() @@ -194,7 +213,15 @@ class Schema(object): @classmethod def create(cls, context, instance_id, schemas): load_and_verify(context, instance_id) - create_guest_client(context, instance_id).create_database(schemas) + client = create_guest_client(context, instance_id) + for schema in schemas: + schema_name = schema['_name'] + existing_schema, _nadda = Schemas.load_with_client(client, limit=1, + marker=schema_name, include_marker=True) + if len(existing_schema) > 0 and \ + str(existing_schema[0].name) == str(schema_name): + raise exception.DatabaseAlreadyExists(name=schema_name) + return client.create_database(schemas) @classmethod def delete(cls, context, instance_id, schema): @@ -208,13 +235,12 @@ class Schemas(object): @classmethod def load(cls, context, instance_id): - load_and_verify(context, instance_id) - limit = int(context.limit or Schemas.DEFAULT_LIMIT) - if limit > Schemas.DEFAULT_LIMIT: - limit = Schemas.DEFAULT_LIMIT - client = create_guest_client(context, instance_id) + return load_via_context(cls, context, instance_id) + + @classmethod + def load_with_client(cls, client, limit, marker, include_marker): schemas, next_marker = client.list_databases(limit=limit, - marker=context.marker) + marker=marker, include_marker=include_marker) model_schemas = [] for schema in schemas: mysql_schema = guest_models.MySQLDatabase() diff --git a/reddwarf/extensions/mysql/service.py b/reddwarf/extensions/mysql/service.py index 3aedec9411..a528d9cb8e 100644 --- a/reddwarf/extensions/mysql/service.py +++ b/reddwarf/extensions/mysql/service.py @@ -39,6 +39,8 @@ class BaseController(wsgi.Controller): ], webob.exc.HTTPBadRequest: [ exception.BadRequest, + exception.DatabaseAlreadyExists, + exception.UserAlreadyExists ], webob.exc.HTTPNotFound: [ exception.NotFound, diff --git a/reddwarf/guestagent/api.py b/reddwarf/guestagent/api.py index 66de33977b..e09333d733 100644 --- a/reddwarf/guestagent/api.py +++ b/reddwarf/guestagent/api.py @@ -75,10 +75,11 @@ class API(object): LOG.debug(_("Creating Users for Instance %s"), self.id) self._cast("create_user", users=users) - def list_users(self, limit=None, marker=None): + def list_users(self, limit=None, marker=None, include_marker=False): """Make an asynchronous call to list database users""" LOG.debug(_("Listing Users for Instance %s"), self.id) - return self._call("list_users", limit=limit, marker=marker) + return self._call("list_users", limit=limit, marker=marker, + include_marker=include_marker) def delete_user(self, user): """Make an asynchronous call to delete an existing database user""" @@ -91,10 +92,11 @@ class API(object): LOG.debug(_("Creating databases for Instance %s"), self.id) self._cast("create_database", databases=databases) - def list_databases(self, limit=None, marker=None): + def list_databases(self, limit=None, marker=None, include_marker=False): """Make an asynchronous call to list databases""" LOG.debug(_("Listing databases for Instance %s"), self.id) - return self._call("list_databases", limit=limit, marker=marker) + return self._call("list_databases", limit=limit, marker=marker, + include_marker=include_marker) def delete_database(self, database): """Make an asynchronous call to delete an existing database diff --git a/reddwarf/guestagent/dbaas.py b/reddwarf/guestagent/dbaas.py index 80013e021c..7f5190fd8f 100644 --- a/reddwarf/guestagent/dbaas.py +++ b/reddwarf/guestagent/dbaas.py @@ -67,7 +67,10 @@ DBAAS_MYCNF = "/etc/dbaas/my.cnf/my.cnf.%dM" MYSQL_BASE_DIR = "/var/lib/mysql" CONFIG = config.Config - +INCLUDE_MARKER_OPERATORS = { + True: ">=", + False: ">" +} def generate_random_password(): return str(uuid.uuid4()) @@ -391,7 +394,7 @@ class MySqlAdmin(object): LOG.debug("result = " + str(result)) return result.rowcount != 0 - def list_databases(self, limit=None, marker=None): + def list_databases(self, limit=None, marker=None, include_marker=False): """List databases the user created on this mysql instance""" LOG.debug(_("---Listing Databases---")) databases = [] @@ -416,7 +419,8 @@ class MySqlAdmin(object): if limit: q.limit = limit + 1 if marker: - q.where.append("schema_name > '%s'" % marker) + q.where.append("schema_name %s '%s'" + % (INCLUDE_MARKER_OPERATORS[include_marker], marker)) t = text(str(q)) database_names = client.execute(t) next_marker = None @@ -436,7 +440,7 @@ class MySqlAdmin(object): next_marker = None return databases, next_marker - def list_users(self, limit=None, marker=None): + def list_users(self, limit=None, marker=None, include_marker=False): """List users that have access to the database""" LOG.debug(_("---Listing Users---")) users = [] @@ -449,7 +453,8 @@ class MySqlAdmin(object): q.where = ["host != 'localhost'"] q.order = ['User'] if marker: - q.where.append("User > '%s'" % marker) + q.where.append("User %s '%s'" + % (INCLUDE_MARKER_OPERATORS[include_marker], marker)) if limit: q.limit = limit + 1 t = text(str(q)) @@ -481,6 +486,7 @@ class MySqlAdmin(object): if result.rowcount <= limit: next_marker = None LOG.debug("users = " + str(users)) + return users, next_marker @@ -505,11 +511,11 @@ class DBaaSAgent(object): def delete_user(self, user): MySqlAdmin().delete_user(user) - def list_databases(self, limit=None, marker=None): - return MySqlAdmin().list_databases(limit, marker) + def list_databases(self, limit=None, marker=None, include_marker=False): + return MySqlAdmin().list_databases(limit, marker, include_marker) - def list_users(self, limit=None, marker=None): - return MySqlAdmin().list_users(limit, marker) + def list_users(self, limit=None, marker=None, include_marker=False): + return MySqlAdmin().list_users(limit, marker, include_marker) def enable_root(self): return MySqlAdmin().enable_root() diff --git a/reddwarf/tests/fakes/guestagent.py b/reddwarf/tests/fakes/guestagent.py index 872a513de6..29516ad7b0 100644 --- a/reddwarf/tests/fakes/guestagent.py +++ b/reddwarf/tests/fakes/guestagent.py @@ -63,12 +63,15 @@ class FakeGuest(object): def is_root_enabled(self): return self.root_was_enabled - def list_databases(self, limit=None, marker=None): + def list_databases(self, limit=None, marker=None, include_marker=False): dbs = [self.dbs[name] for name in self.dbs] names = [db['_name'] for db in dbs] if marker in names: - # Cut off everything left of and including the marker item. - dbs = dbs[names.index(marker) + 1:] + if not include_marker: + # Cut off everything left of and including the marker item. + dbs = dbs[names.index(marker) + 1:] + else: + dbs = dbs[names.index(marker):] next_marker = None if limit: if len(dbs) > limit: @@ -76,11 +79,14 @@ class FakeGuest(object): dbs = dbs[:limit] return dbs, next_marker - def list_users(self, limit=None, marker=None): + def list_users(self, limit=None, marker=None, include_marker=False): users = [self.users[name] for name in self.users] names = [user['_name'] for user in users] if marker in names: - users = users[names.index(marker) + 1:] + if not include_marker: + users = users[names.index(marker) + 1:] + else: + users = users[names.index(marker):] next_marker = None if limit: if len(users) > limit: