diff options
author | Lingxian Kong <anlin.kong@gmail.com> | 2020-07-25 20:39:47 +1200 |
---|---|---|
committer | Lingxian Kong <anlin.kong@gmail.com> | 2020-07-27 09:28:17 +1200 |
commit | efb6a811bedd07819d95ff462195929dd1a33922 (patch) | |
tree | cb9ae96a76c7d149fe3e8edbc7e3ff1c10544b28 /trove/instance | |
parent | 39b0df0a6b87a6f2cb748cb81ac376088f34b88f (diff) | |
download | trove-efb6a811bedd07819d95ff462195929dd1a33922.tar.gz |
Using same config with primary for replicas
Change-Id: Icadc95ea54e4509dc148f8e84f2eaac5840509f3
Diffstat (limited to 'trove/instance')
-rw-r--r-- | trove/instance/models.py | 116 | ||||
-rw-r--r-- | trove/instance/service.py | 118 |
2 files changed, 130 insertions, 104 deletions
diff --git a/trove/instance/models.py b/trove/instance/models.py index 47b4c72c..daa8d3bf 100644 --- a/trove/instance/models.py +++ b/trove/instance/models.py @@ -1059,8 +1059,10 @@ class Instance(BuiltInstance): configuration_id=None, slave_of_id=None, cluster_config=None, replica_count=None, volume_type=None, modules=None, locality=None, region_name=None, access=None): - - region_name = region_name or CONF.service_credentials.region_name + nova_client = clients.create_nova_client(context) + cinder_client = clients.create_cinder_client(context) + datastore_cfg = CONF.get(datastore_version.manager) + volume_support = datastore_cfg.volume_support call_args = { 'name': name, @@ -1070,7 +1072,10 @@ class Instance(BuiltInstance): 'image_id': image_id, 'availability_zone': availability_zone, 'region_name': region_name, + 'locality': locality } + if cluster_config: + call_args['cluster_id'] = cluster_config.get("id", None) # All nova flavors are permitted for a datastore-version unless one # or more entries are found in datastore_version_metadata, @@ -1086,14 +1091,16 @@ class Instance(BuiltInstance): datastore=datastore.name, datastore_version=datastore_version.name, flavor_id=flavor_id) - - datastore_cfg = CONF.get(datastore_version.manager) - client = clients.create_nova_client(context) try: - flavor = client.flavors.get(flavor_id) + flavor = nova_client.flavors.get(flavor_id) except nova_exceptions.NotFound: raise exception.FlavorNotFound(uuid=flavor_id) + replica_source = None + if slave_of_id: + replica_source = DBInstance.find_by( + context, id=slave_of_id, deleted=False) + # If a different region is specified for the instance, ensure # that the flavor and image are the same in both regions if region_name and region_name != CONF.service_credentials.region_name: @@ -1101,13 +1108,23 @@ class Instance(BuiltInstance): datastore, datastore_version) deltas = {'instances': 1} - volume_support = datastore_cfg.volume_support if volume_support: - call_args['volume_type'] = volume_type + if replica_source: + try: + volume = cinder_client.volumes.get( + replica_source.volume_id) + except Exception as e: + LOG.error(f'Failed to get volume from Cinder, error: ' + f'{str(e)}') + raise exception.NotFound(uuid=replica_source.volume_id) + volume_type = volume.volume_type + volume_size = volume.size + dvm.validate_volume_type(context, volume_type, datastore.name, datastore_version.name) - call_args['volume_size'] = volume_size validate_volume_size(volume_size) + call_args['volume_type'] = volume_type + call_args['volume_size'] = volume_size deltas['volumes'] = volume_size # Instance volume should have enough space for the backup # Backup, and volume sizes are in GBs @@ -1147,60 +1164,36 @@ class Instance(BuiltInstance): datastore2=datastore.name) if slave_of_id: - Backup.verify_swift_auth_token(context) - - if databases or users: - raise exception.ReplicaCreateWithUsersDatabasesError() call_args['replica_of'] = slave_of_id call_args['replica_count'] = replica_count + replication_support = datastore_cfg.replication_strategy if not replication_support: raise exception.ReplicationNotSupported( datastore=datastore.name) - try: - # looking for replica source - replica_source = DBInstance.find_by( - context, - id=slave_of_id, - deleted=False) - if replica_source.slave_of_id: - raise exception.Forbidden( - _("Cannot create a replica of a replica %(id)s.") - % {'id': slave_of_id}) - if (CONF.verify_replica_volume_size - and replica_source.volume_size > volume_size): - raise exception.Forbidden( - _("Replica volume size should not be smaller than" - " master's, replica volume size: %(replica_size)s" - " and master volume size: %(master_size)s.") - % {'replica_size': volume_size, - 'master_size': replica_source.volume_size}) - # load the replica source status to check if - # source is available - load_simple_instance_server_status( + if (CONF.verify_replica_volume_size + and replica_source.volume_size > volume_size): + raise exception.Forbidden( + _("Replica volume size should not be smaller than" + " master's, replica volume size: %(replica_size)s" + " and master volume size: %(master_size)s.") + % {'replica_size': volume_size, + 'master_size': replica_source.volume_size}) + # load the replica source status to check if + # source is available + load_simple_instance_server_status( + context, + replica_source) + replica_source_instance = Instance( + context, replica_source, + None, + InstanceServiceStatus.find_by( context, - replica_source) - replica_source_instance = Instance( - context, replica_source, - None, - InstanceServiceStatus.find_by( - context, - instance_id=slave_of_id)) - replica_source_instance.validate_can_perform_action() - except exception.ModelNotFoundError: - LOG.exception( - "Cannot create a replica of %(id)s " - "as that instance could not be found.", - {'id': slave_of_id}) - raise exception.NotFound(uuid=slave_of_id) - elif replica_count and replica_count != 1: - raise exception.Forbidden(_( - "Replica count only valid when creating replicas. Cannot " - "create %(count)d instances.") % {'count': replica_count}) + instance_id=slave_of_id)) + replica_source_instance.validate_can_perform_action() + multi_replica = slave_of_id and replica_count and replica_count > 1 instance_count = replica_count if multi_replica else 1 - if locality: - call_args['locality'] = locality if not nics: nics = [] @@ -1211,8 +1204,6 @@ class Instance(BuiltInstance): for net_id in CONF.management_networks] if nics: call_args['nics'] = nics - if cluster_config: - call_args['cluster_id'] = cluster_config.get("id", None) if not modules: modules = [] @@ -1228,7 +1219,6 @@ class Instance(BuiltInstance): module_list = module_views.convert_modules_to_list(modules) def _create_resources(): - if cluster_config: cluster_id = cluster_config.get("id", None) shard_id = cluster_config.get("shard_id", None) @@ -1251,17 +1241,15 @@ class Instance(BuiltInstance): slave_of_id=slave_of_id, cluster_id=cluster_id, shard_id=shard_id, type=instance_type, region_id=region_name) - LOG.debug("Tenant %(tenant)s created new Trove instance " - "%(db)s in region %(region)s.", - {'tenant': context.project_id, 'db': db_info.id, - 'region': region_name}) - instance_id = db_info.id - cls.add_instance_modules(context, instance_id, modules) instance_name = name + LOG.debug(f"Creating new instance {instance_id}") ids.append(instance_id) names.append(instance_name) root_passwords.append(None) + + cls.add_instance_modules(context, instance_id, modules) + # change the name to be name + replica_number if more than one if multi_replica: replica_number = instance_index + 1 @@ -1272,9 +1260,9 @@ class Instance(BuiltInstance): # if a configuration group is associated with an instance, # generate an overrides dict to pass into the instance creation # method - config = Configuration(context, configuration_id) overrides = config.get_configuration_overrides() + service_status = InstanceServiceStatus.create( instance_id=instance_id, status=srvstatus.ServiceStatuses.NEW) diff --git a/trove/instance/service.py b/trove/instance/service.py index 17de296e..8542b024 100644 --- a/trove/instance/service.py +++ b/trove/instance/service.py @@ -32,7 +32,7 @@ from trove.common import pagination from trove.common import policy from trove.common import utils from trove.common import wsgi -from trove.datastore import models as datastore_models +from trove.datastore import models as ds_models from trove.extensions.mysql.common import populate_users from trove.extensions.mysql.common import populate_validated_databases from trove.instance import models, views @@ -341,24 +341,81 @@ class InstanceController(wsgi.Controller): raise exception.NetworkConflict() def create(self, req, body, tenant_id): - # TODO(hub-cap): turn this into middleware LOG.info("Creating a database instance for tenant '%s'", tenant_id) LOG.debug("req : '%s'\n\n", strutils.mask_password(req)) LOG.debug("body : '%s'\n\n", strutils.mask_password(body)) context = req.environ[wsgi.CONTEXT_KEY] policy.authorize_on_tenant(context, 'instance:create') - context.notification = notification.DBaaSInstanceCreate(context, - request=req) - datastore_args = body['instance'].get('datastore', {}) - datastore, datastore_version = ( - datastore_models.get_datastore_version(**datastore_args)) - image_id = datastore_version.image_id + context.notification = notification.DBaaSInstanceCreate( + context, request=req) + name = body['instance']['name'] - flavor_ref = body['instance']['flavorRef'] + slave_of_id = body['instance'].get('replica_of') + replica_count = body['instance'].get('replica_count') + flavor_ref = body['instance'].get('flavorRef') + datastore_args = body['instance'].get('datastore', {}) + volume_info = body['instance'].get('volume', {}) + availability_zone = body['instance'].get('availability_zone') + nics = body['instance'].get('nics', []) + locality = body['instance'].get('locality') + region_name = body['instance'].get( + 'region_name', CONF.service_credentials.region_name + ) + access = body['instance'].get('access', None) + + if slave_of_id: + if flavor_ref: + msg = 'Cannot specify flavor when creating replicas.' + raise exception.BadRequest(message=msg) + if datastore_args: + msg = 'Cannot specify datastore when creating replicas.' + raise exception.BadRequest(message=msg) + if volume_info: + msg = 'Cannot specify volume when creating replicas.' + raise exception.BadRequest(message=msg) + if locality: + msg = 'Cannot specify locality when creating replicas.' + raise exception.BadRequest(message=msg) + backup_model.verify_swift_auth_token(context) + else: + if replica_count and replica_count > 1: + msg = (f"Replica count only valid when creating replicas. " + f"Cannot create {replica_count} instances.") + raise exception.BadRequest(message=msg) + flavor_id = utils.get_id_from_href(flavor_ref) - configuration = self._configuration_parse(context, body) + if volume_info: + volume_size = int(volume_info.get('size')) + volume_type = volume_info.get('type') + else: + volume_size = None + volume_type = None + + if slave_of_id: + try: + replica_source = models.DBInstance.find_by( + context, id=slave_of_id, deleted=False) + flavor_id = replica_source.flavor_id + except exception.ModelNotFoundError: + LOG.error(f"Cannot create a replica of {slave_of_id} as that " + f"instance could not be found.") + raise exception.NotFound(uuid=slave_of_id) + if replica_source.slave_of_id: + raise exception.Forbidden( + f"Cannot create a replica of a replica {slave_of_id}") + + datastore_version = ds_models.DatastoreVersion.load_by_uuid( + replica_source.datastore_version_id) + datastore = ds_models.Datastore.load( + datastore_version.datastore_id) + else: + datastore, datastore_version = ds_models.get_datastore_version( + **datastore_args) + + image_id = datastore_version.image_id + databases = populate_validated_databases( body['instance'].get('databases', [])) database_names = [database.get('_name', '') for database in databases] @@ -368,7 +425,10 @@ class InstanceController(wsgi.Controller): database_names) except ValueError as ve: raise exception.BadRequest(message=ve) + if slave_of_id and (databases or users): + raise exception.ReplicaCreateWithUsersDatabasesError() + configuration = self._configuration_parse(context, body) modules = body['instance'].get('modules') # The following operations have their own API calls. @@ -388,34 +448,22 @@ class InstanceController(wsgi.Controller): policy.authorize_on_tenant( context, 'instance:extension:database:create') - if 'volume' in body['instance']: - volume_info = body['instance']['volume'] - volume_size = int(volume_info['size']) - volume_type = volume_info.get('type') - else: - volume_size = None - volume_type = None - if 'restorePoint' in body['instance']: backupRef = body['instance']['restorePoint']['backupRef'] backup_id = utils.get_id_from_href(backupRef) else: backup_id = None - availability_zone = body['instance'].get('availability_zone') - # Only 1 nic is allowed as defined in API jsonschema. - # Use list here just for backward compatibility. - nics = body['instance'].get('nics', []) + # Use list just for backward compatibility. if len(nics) > 0: - LOG.info('Checking user provided instance network %s', nics[0]) - self._check_nic(context, nics[0]) + nic = nics[0] + LOG.info('Checking user provided instance network %s', nic) + if slave_of_id and nic.get('ip_address'): + msg = "Cannot specify IP address when creating replicas." + raise exception.BadRequest(message=msg) + self._check_nic(context, nic) - slave_of_id = body['instance'].get('replica_of', - # also check for older name - body['instance'].get('slave_of')) - replica_count = body['instance'].get('replica_count') - locality = body['instance'].get('locality') if locality: locality_domain = ['affinity', 'anti-affinity'] locality_domain_msg = ("Invalid locality '%s'. " @@ -424,16 +472,6 @@ class InstanceController(wsgi.Controller): "', '".join(locality_domain))) if locality not in locality_domain: raise exception.BadRequest(message=locality_domain_msg) - if slave_of_id: - dupe_locality_msg = ( - 'Cannot specify locality when adding replicas to existing ' - 'master.') - raise exception.BadRequest(message=dupe_locality_msg) - - region_name = body['instance'].get( - 'region_name', CONF.service_credentials.region_name - ) - access = body['instance'].get('access', None) instance = models.Instance.create(context, name, flavor_id, image_id, databases, users, @@ -480,7 +518,7 @@ class InstanceController(wsgi.Controller): with StartNotification(context, instance_id=instance.id): instance.detach_configuration() if 'datastore_version' in kwargs: - datastore_version = datastore_models.DatastoreVersion.load( + datastore_version = ds_models.DatastoreVersion.load( instance.datastore, kwargs['datastore_version']) context.notification = ( notification.DBaaSInstanceUpgrade(context, request=req)) |