summaryrefslogtreecommitdiff
path: root/trove/instance
diff options
context:
space:
mode:
authorLingxian Kong <anlin.kong@gmail.com>2020-07-25 20:39:47 +1200
committerLingxian Kong <anlin.kong@gmail.com>2020-07-27 09:28:17 +1200
commitefb6a811bedd07819d95ff462195929dd1a33922 (patch)
treecb9ae96a76c7d149fe3e8edbc7e3ff1c10544b28 /trove/instance
parent39b0df0a6b87a6f2cb748cb81ac376088f34b88f (diff)
downloadtrove-efb6a811bedd07819d95ff462195929dd1a33922.tar.gz
Using same config with primary for replicas
Change-Id: Icadc95ea54e4509dc148f8e84f2eaac5840509f3
Diffstat (limited to 'trove/instance')
-rw-r--r--trove/instance/models.py116
-rw-r--r--trove/instance/service.py118
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))