Merge "Using same config with primary for replicas"

This commit is contained in:
Zuul 2020-07-27 04:08:07 +00:00 committed by Gerrit Code Review
commit be6f8565f5
11 changed files with 288 additions and 276 deletions

View File

@ -143,7 +143,7 @@ instance from the backup.
.. code-block:: console
$ openstack database instance create guest2 10 --size 2 --nic net-id=$network_id --backup BACKUP_ID
$ openstack database instance create guest2 --flavor 10 --size 2 --nic net-id=$network_id --backup BACKUP_ID
+-------------------+----------------------------------------------+
| Property | Value |
+-------------------+----------------------------------------------+

View File

@ -107,7 +107,7 @@ Create and access a database
.. code-block:: console
$ openstack database instance create mysql_instance_1 \
6 \
--flavor 6 \
--size 5 \
--nic net-id=8799cf10-01ef-40e2-b04e-06da7cfa5668 \
--databases test --users userA:password \

View File

@ -2,109 +2,164 @@
Set up database replication
===========================
You can create a replica of an existing database instance. When you make
subsequent changes to the original instance, the system automatically
applies those changes to the replica.
You can create replicas of an existing database instance(the primary) to
improve the performance and scale of read-intensive workloads. Read workloads
can be isolated to the replicas, while write workloads can be directed to the
primary. When you make subsequent changes to the primary, the system
automatically applies those changes to the replicas. Because replicas are
read-only, they don't directly reduce write-capacity burdens on the primary.
This feature isn't targeted at write-intensive workloads.
- Replicas are read-only.
- Not all the datastores support replication feature in Trove.
- When you create a replica, do not specify the ``--users`` or
``--databases`` options.
- A replica is created by using the same server configuration as the primary,
e.g. flavor, data volume, datastore, etc. After a replica is created, several
settings can be changed independently from the primary server, e.g. the data
volume size.
- You can choose a smaller volume or flavor for a replica than for the
original, but the replica's volume must be big enough to hold the
data snapshot from the original.
- Currently, There is no automated failover between primary and replicas.
This example shows you how to replicate a MySQL database instance.
- Trove can only create a new replica. Adding an already existing instance to
the replication group is not supported.
- Creating a replica of a replica is not supported.
- When deleting replication instances, replicas need to be removed before the
primary.
Set up replication
~~~~~~~~~~~~~~~~~~
------------------
#. **Get the instance ID**
#. Create a replica
Get the ID of the original instance you want to replicate:
First, make sure you have an instance (ID:
cebbf187-e223-46dd-8802-6dc04e895d0a) up and running in HEALTHY status,
create a replica:
.. code-block:: console
$ openstack database instance create test-mysql-replica-1 \
--nic net-id=$netid \
--replica_of cebbf187-e223-46dd-8802-6dc04e895d0a
#. Wait for the replica instance successfully created, verify status of the
replication servers.
.. code-block:: console
$ odbi list
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
| ID | Name | Datastore | Datastore Version | Status | Addresses | Flavor ID | Size | Region | Role |
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
| 71f30a72-4e47-4505-9e7f-ffd8933a331c | test-mysql-replica-1 | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.155', 'type': 'private'}] | d2 | 2 | RegionOne | replica |
| cebbf187-e223-46dd-8802-6dc04e895d0a | test-mysql | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.43', 'type': 'private'}] | d2 | 2 | RegionOne | primary |
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
#. Verify replication status.
Replication can be verified by making some modifications to the primary and
ensuring that the modifications also propagate back to the replica. We will
create a database called "newdb" on the primary and check it's automatically
created on the replica.
First, get the existing databases of primary and replica, they should be the
same:
.. code-block:: console
$ openstack database db list cebbf187-e223-46dd-8802-6dc04e895d0a # The primary
+--------+
| Name |
+--------+
| testdb |
+--------+
$ openstack database db list 71f30a72-4e47-4505-9e7f-ffd8933a331c # The replica
+--------+
| Name |
+--------+
| testdb |
+--------+
Create a new database on the primary:
.. code-block:: console
$ openstack database db create cebbf187-e223-46dd-8802-6dc04e895d0a newdb
Check the new database is also created on the replica:
.. code-block:: console
$ openstack database db list 71f30a72-4e47-4505-9e7f-ffd8933a331c
+--------+
| Name |
+--------+
| newdb |
| testdb |
+--------+
Failover
--------
Since replication is asynchronous, there is lag between the primary and the
replica. The amount of lag can be influenced by a number of factors like how
heavy the workload running on the primary server is and the latency between
data centers. In most cases, replica lag ranges between a few seconds to a
couple minutes.
#. Before performing failover, we will create one more replica:
.. code-block:: console
$ openstack database instance create test-mysql-replica-2 \
--nic net-id=$netid \
--replica_of cebbf187-e223-46dd-8802-6dc04e895d0a
Now we have 3 instances running in a replication group:
.. code-block:: console
$ odbi list
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
| ID | Name | Datastore | Datastore Version | Status | Addresses | Flavor ID | Size | Region | Role |
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
| 71f30a72-4e47-4505-9e7f-ffd8933a331c | test-mysql-replica-1 | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.155', 'type': 'private'}] | d2 | 2 | RegionOne | replica |
| a85ece86-9f62-4aa8-bb15-eba604cd2a01 | test-mysql-replica-2 | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.243', 'type': 'private'}] | d2 | 2 | RegionOne | replica |
| cebbf187-e223-46dd-8802-6dc04e895d0a | test-mysql | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.43', 'type': 'private'}] | d2 | 2 | RegionOne | primary |
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
#. Failover(promote) "test-mysql-replica-1" to primary.
.. code-block:: console
$ openstack database instance promote 71f30a72-4e47-4505-9e7f-ffd8933a331c
Wait for Trove setting up the new replication, the status of the 3 instances become "PROMOTE" then "HEALTHY".
.. code-block:: console
$ openstack database instance list
+-----------+------------+-----------+-------------------+--------+-----------+------+
| id | name | datastore | datastore_version | status | flavor_id | size |
+-----------+------------+-----------+-------------------+--------+-----------+------+
| 97b...ae6 | base_1 | mysql | mysql-5.5 | ACTIVE | 10 | 2 |
+-----------+------------+-----------+-------------------+--------+-----------+------+
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
| ID | Name | Datastore | Datastore Version | Status | Addresses | Flavor ID | Size | Region | Role |
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
| 71f30a72-4e47-4505-9e7f-ffd8933a331c | test-mysql-replica-1 | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.155', 'type': 'private'}] | d2 | 2 | RegionOne | primary |
| a85ece86-9f62-4aa8-bb15-eba604cd2a01 | test-mysql-replica-2 | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.243', 'type': 'private'}] | d2 | 2 | RegionOne | replica |
| cebbf187-e223-46dd-8802-6dc04e895d0a | test-mysql | mysql | 5.7.29 | HEALTHY | [{'address': '10.1.0.43', 'type': 'private'}] | d2 | 2 | RegionOne | replica |
+--------------------------------------+----------------------+-----------+-------------------+---------+------------------------------------------------+-----------+------+-----------+---------+
#. **Create the replica**
#. Point your application to the (former) replica.
Create a new instance that will be a replica of the original
instance. You do this by passing in the ``--replica_of`` option with
the :command:`openstack database instance create` command. This example creates a replica
called ``replica_1``. ``replica_1`` is a replica of the original instance,
``base_1``:
Each server has a unique connection string. Update your application to point
to the (former) replica instead of the primary.
.. code-block:: console
Other supported operations
--------------------------
$ openstack database instance create replica_1 6 --size=5 --nic net-id=$netid \
--datastore_version mysql-5.5 \
--datastore mysql --replica_of ID_OF_ORIGINAL_INSTANCE
* Remove a failed primary. This essentially is used to eject an already failed
primary in order to establish a new one between the replicas. Command:
``openstack database instance eject <primary_ID>``
#. **Verify replication status**
Pass in ``replica_1``'s instance ID with the :command:`openstack database instance show` command
to verify that the newly created ``replica_1`` instance is a replica
of the original ``base_1``. Note that the ``replica_of`` property is
set to the ID of ``base_1``.
.. code-block:: console
$ openstack database instance show INSTANCE_ID_OF_REPLICA_1
+-------------------+--------------------------------------+
| Property | Value |
+-------------------+--------------------------------------+
| created | 2014-09-16T11:16:49 |
| datastore | mysql |
| datastore_version | mysql-5.5 |
| flavor | 6 |
| id | 49c6eff6-ef91-4eff-91c0-efbda7e83c38 |
| name | replica_1 |
| replica_of | 97b4b853-80f6-414f-ba6f-c6f455a79ae6 |
| status | BUILD |
| updated | 2014-09-16T11:16:49 |
| volume | 5 |
+-------------------+--------------------------------------+
Now pass in ``base_1``'s instance ID with the :command:`openstack database instance show` command
to list the replica(s) associated with the original instance. Note
that the ``replicas`` property is set to the ID of ``replica_1``. If
there are multiple replicas, they appear as a comma-separated list.
.. code-block:: console
$ openstack database instance show INSTANCE_ID_OF_BASE_1
+-------------------+--------------------------------------+
| Property | Value |
+-------------------+--------------------------------------+
| created | 2014-09-16T11:04:56 |
| datastore | mysql |
| datastore_version | mysql-5.5 |
| flavor | 6 |
| id | 97b4b853-80f6-414f-ba6f-c6f455a79ae6 |
| ip | 172.16.200.2 |
| name | base_1 |
| replicas | 49c6eff6-ef91-4eff-91c0-efbda7e83c38 |
| status | ACTIVE |
| updated | 2014-09-16T11:05:06 |
| volume | 5 |
| volume_used | 0.11 |
+-------------------+--------------------------------------+
#. **Detach the replica**
If the original instance goes down, you can detach the replica. The
replica becomes a standalone database instance. You can then take the
new standalone instance and create a new replica of that instance.
You detach a replica using the :command:`openstack database instance detach replica` command:
.. code-block:: console
$ openstack database instance detach replica INSTANCE_ID_OF_REPLICA
* Change replica to a standalone database server. The detached replica becomes
a standalone server that accepts both reads and writes. The standalone server
can't be made into a replica again.. Command:
``openstack database instance detach <replica_ID>``

View File

@ -49,7 +49,7 @@ Upgrading datastore
.. code-block:: console
$ openstack database instance create test-mysql-upgrade \
d2 \
--flavor d2 \
--size 1 \
--nic net-id=$netid \
--datastore mysql --datastore_version 5.7.29 \

View File

@ -381,7 +381,7 @@ instance = {
"properties": {
"instance": {
"type": "object",
"required": ["name", "flavorRef"],
"required": ["name"],
"additionalProperties": True,
"properties": {
"name": non_empty_string,
@ -398,6 +398,12 @@ instance = {
"backupRef": uuid
}
},
"replica_of": uuid,
"replica_count": {
"type": "integer",
"minimum": 1,
"maximum": 3
},
"availability_zone": non_empty_string,
"datastore": {
"type": "object",

View File

@ -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(
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,
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(
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)

View File

@ -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))

View File

@ -359,8 +359,8 @@ class Manager(periodic_task.PeriodicTasks):
try:
for replica_index in range(0, len(ids)):
replica_number += 1
LOG.info("Creating replica %(num)d of %(count)d.",
{'num': replica_number, 'count': len(ids)})
LOG.info(f"Creating replica {replica_number} "
f"({ids[replica_index]}) of {len(ids)}.")
instance_tasks = FreshInstanceTasks.load(
context, ids[replica_index])

View File

@ -108,10 +108,6 @@ def instance_is_active(id):
def create_slave():
result = instance_info.dbaas.instances.create(
instance_info.name + "_slave",
instance_info.dbaas_flavor_href,
{'size': 2},
datastore=instance_info.dbaas_datastore,
datastore_version=instance_info.dbaas_datastore_version,
nics=instance_info.nics,
replica_of=instance_info.id)
assert_equal(200, instance_info.dbaas.last_http_code)
@ -141,20 +137,6 @@ def validate_master(master, slaves):
groups=[tests.DBAAS_API_REPLICATION],
enabled=CONFIG.swift_enabled)
class CreateReplicationSlave(object):
@test
def test_replica_provisioning_with_missing_replica_source(self):
assert_raises(exceptions.NotFound,
instance_info.dbaas.instances.create,
instance_info.name + "_slave",
instance_info.dbaas_flavor_href,
instance_info.volume,
datastore=instance_info.dbaas_datastore,
datastore_version=instance_info.dbaas_datastore_version,
nics=instance_info.nics,
replica_of="Missing replica source")
assert_equal(404, instance_info.dbaas.last_http_code)
@test
def test_create_db_on_master(self):
"""test_create_db_on_master"""

View File

@ -88,10 +88,7 @@ class ReplicationRunner(TestRunner):
client = self.auth_client
client.instances.create(
self.instance_info.name + '_' + replica_name,
self.instance_info.dbaas_flavor_href,
self.instance_info.volume, replica_of=master_id,
datastore=self.instance_info.dbaas_datastore,
datastore_version=self.instance_info.dbaas_datastore_version,
replica_of=master_id,
nics=self.instance_info.nics,
replica_count=replica_count)
self.assert_client_code(client, expected_http_code)
@ -154,10 +151,6 @@ class ReplicationRunner(TestRunner):
client = self.auth_client
self.non_affinity_repl_id = client.instances.create(
self.instance_info.name + '_non-affinity-repl',
self.instance_info.dbaas_flavor_href,
self.instance_info.volume,
datastore=self.instance_info.dbaas_datastore,
datastore_version=self.instance_info.dbaas_datastore_version,
nics=self.instance_info.nics,
replica_of=self.non_affinity_master_id,
replica_count=1).id

View File

@ -343,6 +343,7 @@ class TestReplication(trove_testtools.TestCase):
id=str(uuid.uuid4()),
name="TestMasterInstance",
datastore_version_id=self.datastore_version.id,
flavor_id=str(uuid.uuid4()),
volume_size=2)
self.master.set_task_status(InstanceTasks.NONE)
self.master.save()
@ -370,18 +371,6 @@ class TestReplication(trove_testtools.TestCase):
clients.create_nova_client = self.safe_nova_client
super(TestReplication, self).tearDown()
@patch('trove.instance.models.LOG')
def test_replica_of_not_active_master(self, mock_logging):
self.master.set_task_status(InstanceTasks.BUILDING)
self.master.save()
self.master_status.set_status(ServiceStatuses.BUILDING)
self.master_status.save()
self.assertRaises(exception.UnprocessableEntity,
Instance.create,
None, 'name', 1, "UUID", [], [], self.datastore,
self.datastore_version, 2,
None, slave_of_id=self.master.id)
@patch('trove.instance.models.LOG')
def test_replica_with_invalid_slave_of_id(self, mock_logging):
self.assertRaises(exception.NotFound,
@ -390,45 +379,6 @@ class TestReplication(trove_testtools.TestCase):
self.datastore_version, 2,
None, slave_of_id=str(uuid.uuid4()))
def test_create_replica_from_replica(self):
self.replica_datastore_version = Mock(
spec=datastore_models.DBDatastoreVersion)
self.replica_datastore_version.id = "UUID"
self.replica_datastore_version.manager = 'mysql'
self.replica_info = DBInstance(
InstanceTasks.NONE,
id="UUID",
name="TestInstance",
datastore_version_id=self.replica_datastore_version.id,
slave_of_id=self.master.id)
self.replica_info.save()
self.assertRaises(exception.Forbidden, Instance.create,
None, 'name', 2, "UUID", [], [], self.datastore,
self.datastore_version, 2,
None, slave_of_id=self.replica_info.id)
def test_create_replica_with_users(self):
self.users.append({"name": "testuser", "password": "123456"})
self.assertRaises(exception.ReplicaCreateWithUsersDatabasesError,
Instance.create, None, 'name', 2, "UUID", [],
self.users, self.datastore, self.datastore_version,
1, None, slave_of_id=self.master.id)
def test_create_replica_with_databases(self):
self.databases.append({"name": "testdb"})
self.assertRaises(exception.ReplicaCreateWithUsersDatabasesError,
Instance.create, None, 'name', 1, "UUID",
self.databases, [], self.datastore,
self.datastore_version, 2, None,
slave_of_id=self.master.id)
def test_replica_volume_size_smaller_than_master(self):
self.assertRaises(exception.Forbidden,
Instance.create,
None, 'name', 1, "UUID", [], [], self.datastore,
self.datastore_version, 1,
None, slave_of_id=self.master.id)
def trivial_key_function(id):
return id * id