summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Van Dijk <mvandijk@tesora.com>2015-03-07 02:49:30 -0500
committerMatthew Van Dijk <mvandijk@tesora.com>2015-03-27 14:04:28 +0000
commit32387f8ffd3ba6e45064d675d2b18cb666397894 (patch)
tree9925ccc5aaa9dd39bbdf8e2b8624df93b6708bf4
parent417719c5a7089ffd1e784a9147ea48500be9129f (diff)
downloadtrove-32387f8ffd3ba6e45064d675d2b18cb666397894.tar.gz
Fix replica source state validation
Creating a replica did not check that the specified source was available. It is necessary that the source's state is active and that it is not busy. This is done by verifying the state and status of the master. The new checks will throw a HTTP 422 UnprocessableEntity in cases when the status is not ACTIVE or source is busy performing a task Changes: - adding validation step that checks if master exists and in proper state. - added appropriate API and unit tests Co-authored by: Denis Makogon <dmakogon@mirantis.com> DocImpact Change-Id: Ib4e37ff0036998bc53058e400054ed93fcc2e144 Closes-Bug: #1357704
-rw-r--r--trove/instance/models.py12
-rw-r--r--trove/tests/api/replication.py10
-rw-r--r--trove/tests/unittests/instance/test_instance_models.py85
3 files changed, 85 insertions, 22 deletions
diff --git a/trove/instance/models.py b/trove/instance/models.py
index 3c436102..367e7b67 100644
--- a/trove/instance/models.py
+++ b/trove/instance/models.py
@@ -718,6 +718,18 @@ class Instance(BuiltInstance):
raise exception.Forbidden(
_("Cannot create a replica of a replica %(id)s.")
% {'id': slave_of_id})
+ # 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 "
diff --git a/trove/tests/api/replication.py b/trove/tests/api/replication.py
index 0047c248..7e685e3d 100644
--- a/trove/tests/api/replication.py
+++ b/trove/tests/api/replication.py
@@ -68,6 +68,16 @@ def slave_is_running(running=True):
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,
+ slave_of="Missing replica source")
+ assert_equal(404, instance_info.dbaas.last_http_code)
+
+ @test
def test_create_db_on_master(self):
databases = [{'name': existing_db_on_master}]
# Ensure that the auth_token in the dbaas client is not stale
diff --git a/trove/tests/unittests/instance/test_instance_models.py b/trove/tests/unittests/instance/test_instance_models.py
index 8c80dd7c..1183afe9 100644
--- a/trove/tests/unittests/instance/test_instance_models.py
+++ b/trove/tests/unittests/instance/test_instance_models.py
@@ -18,7 +18,6 @@ from trove.common import cfg
from trove.common import exception
from trove.backup import models as backup_models
from trove.datastore import models as datastore_models
-from trove.datastore.models import DBDatastoreVersion
from trove.common.instance import ServiceStatuses
from trove.instance.models import filter_ips
from trove.instance.models import InstanceServiceStatus
@@ -216,35 +215,77 @@ class TestReplication(TestCase):
def setUp(self):
util.init_db()
- self.replica_datastore_version = Mock(spec=DBDatastoreVersion)
- self.replica_datastore_version.id = "UUID"
- self.replica_datastore_version.manager = 'mysql'
- self.root_info = DBInstance(
- InstanceTasks.NONE,
- id="Another_instance",
- name="TestInstance",
- datastore_version_id=self.replica_datastore_version.id)
- self.root_info.save()
- self.replica_info = DBInstance(
+
+ self.datastore = datastore_models.DBDatastore.create(
+ id=str(uuid.uuid4()),
+ name='name',
+ default_version_id=str(uuid.uuid4()))
+
+ self.datastore_version = datastore_models.DBDatastoreVersion.create(
+ id=self.datastore.default_version_id,
+ name='name',
+ image_id=str(uuid.uuid4()),
+ packages=str(uuid.uuid4()),
+ datastore_id=self.datastore.id,
+ manager='mysql',
+ active=1)
+
+ self.master = DBInstance(
InstanceTasks.NONE,
- id="UUID",
- name="TestInstance",
- datastore_version_id=self.replica_datastore_version.id,
- slave_of_id="Another_instance")
- self.replica_info.save()
- self.safe_nova = models.create_nova_client
- models.create_nova_client = nova.fake_create_nova_client
+ id=str(uuid.uuid4()),
+ name="TestMasterInstance",
+ datastore_version_id=self.datastore_version.id)
+ self.master.set_task_status(InstanceTasks.NONE)
+ self.master.save()
+ self.master_status = InstanceServiceStatus(
+ ServiceStatuses.RUNNING,
+ id=str(uuid.uuid4()),
+ instance_id=self.master.id)
+ self.master_status.save()
+ self.safe_nova_client = models.create_nova_client
+ models.create_nova_client = nova.fake_create_nova_client
super(TestReplication, self).setUp()
def tearDown(self):
- models.create_nova_client = self.safe_nova
- self.replica_info.delete()
- self.root_info.delete()
+ self.master.delete()
+ self.master_status.delete()
+ self.datastore.delete()
+ self.datastore_version.delete()
+ models.create_nova_client = self.safe_nova_client
super(TestReplication, self).tearDown()
+ def test_replica_of_not_active_master(self):
+ 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", [], [], None,
+ self.datastore_version, 1,
+ None, slave_of_id=self.master.id)
+
+ def test_replica_with_invalid_slave_of_id(self):
+ self.assertRaises(exception.NotFound,
+ Instance.create,
+ None, 'name', 1, "UUID", [], [], None,
+ self.datastore_version, 1,
+ 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", [], [], None,
- self.replica_datastore_version, 1,
+ self.datastore_version, 1,
None, slave_of_id=self.replica_info.id)