Replica report DETACH status when detaching
Detached replicas currently stay in the ACTIVE state while detaching although the instance may not be available throughout the whole process. Set the task status to DETACHING until the guest finishes. Update the scenario tests to expect this new instance state. Also improve the assertion to provide more granular feedback. Change-Id: Id43f382fa655bfa48feeb2521d7237d926883514 Closes-Bug: 1465600
This commit is contained in:
@@ -99,6 +99,7 @@ class InstanceStatus(object):
|
||||
RESTART_REQUIRED = "RESTART_REQUIRED"
|
||||
PROMOTE = "PROMOTE"
|
||||
EJECT = "EJECT"
|
||||
DETACH = "DETACH"
|
||||
|
||||
|
||||
def validate_volume_size(size):
|
||||
@@ -301,6 +302,8 @@ class SimpleInstance(object):
|
||||
return InstanceStatus.EJECT
|
||||
if InstanceTasks.LOGGING.action == action:
|
||||
return InstanceStatus.LOGGING
|
||||
if InstanceTasks.DETACHING.action == action:
|
||||
return InstanceStatus.DETACH
|
||||
|
||||
# Check for server status.
|
||||
if self.db_info.server_status in ["BUILD", "ERROR", "REBOOT",
|
||||
@@ -1002,6 +1005,9 @@ class Instance(BuiltInstance):
|
||||
if not self.slave_of_id:
|
||||
raise exception.BadRequest(_("Instance %s is not a replica.")
|
||||
% self.id)
|
||||
|
||||
self.update_db(task_status=InstanceTasks.DETACHING)
|
||||
|
||||
task_api.API(self.context).detach_replica(self.id)
|
||||
|
||||
def promote_to_replica_source(self):
|
||||
|
||||
@@ -80,6 +80,8 @@ class InstanceTasks(object):
|
||||
EJECTING = InstanceTask(0x09, 'EJECTING',
|
||||
'Ejecting the replica source.')
|
||||
LOGGING = InstanceTask(0x0a, 'LOGGING', 'Transferring guest logs.')
|
||||
DETACHING = InstanceTask(0x0b, 'DETACHING',
|
||||
'Detaching the instance from replica source.')
|
||||
|
||||
BUILDING_ERROR_DNS = InstanceTask(0x50, 'BUILDING', 'Build error: DNS.',
|
||||
is_error=True)
|
||||
|
||||
@@ -1178,6 +1178,9 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
|
||||
except (GuestError, GuestTimeout):
|
||||
LOG.exception(_("Failed to detach replica %s.") % self.id)
|
||||
raise
|
||||
finally:
|
||||
if not for_failover:
|
||||
self.reset_task_status()
|
||||
|
||||
def attach_replica(self, master):
|
||||
LOG.debug("Calling attach_replica on %s" % self.id)
|
||||
|
||||
@@ -220,7 +220,7 @@ class ReplicationRunner(TestRunner):
|
||||
self.test_helper.remove_data(data_set, host)
|
||||
|
||||
def run_detach_replica_from_source(self,
|
||||
expected_states=['ACTIVE'],
|
||||
expected_states=['DETACH', 'ACTIVE'],
|
||||
expected_http_code=202):
|
||||
self.assert_detach_replica_from_source(
|
||||
self.instance_info.id, self.replica_1_id,
|
||||
@@ -236,7 +236,7 @@ class ReplicationRunner(TestRunner):
|
||||
replica_id, expected_states, expected_http_code)
|
||||
|
||||
self._assert_is_master(master_id, other_replica_ids)
|
||||
self._assert_is_not_replica(replica_id, master_id)
|
||||
self._assert_is_not_replica(replica_id)
|
||||
|
||||
def assert_detach_replica(
|
||||
self, replica_id, expected_states, expected_http_code):
|
||||
@@ -245,13 +245,19 @@ class ReplicationRunner(TestRunner):
|
||||
self.assert_instance_action(
|
||||
replica_id, expected_states, expected_http_code)
|
||||
|
||||
def _assert_is_not_replica(self, instance_id, master_id):
|
||||
try:
|
||||
self._assert_is_replica(instance_id, master_id)
|
||||
self.fail("Non-replica '%s' is still replica of '%s'" %
|
||||
(instance_id, master_id))
|
||||
except AssertionError:
|
||||
pass
|
||||
def _assert_is_not_replica(self, instance_id):
|
||||
instance = self.get_instance(instance_id)
|
||||
self.assert_client_code(200)
|
||||
|
||||
if 'replica_of' not in instance._info:
|
||||
try:
|
||||
self._validate_replica(instance_id)
|
||||
self.fail("The instance is still configured as a replica "
|
||||
"after detached: %s" % instance_id)
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
self.fail("Unexpected replica_of ID.")
|
||||
|
||||
def run_delete_detached_replica(self,
|
||||
expected_last_state=['SHUTDOWN'],
|
||||
|
||||
@@ -780,16 +780,38 @@ class BuiltInstanceTasksTest(trove_testtools.TestCase):
|
||||
|
||||
@patch.object(BaseInstance, 'update_db')
|
||||
def test_detach_replica(self, mock_update_db):
|
||||
self.instance_task.detach_replica(Mock(), True)
|
||||
self.instance_task._guest.detach_replica.assert_called_with(True)
|
||||
mock_update_db.assert_called_with(slave_of_id=None)
|
||||
with patch.object(self.instance_task, 'reset_task_status') as tr_mock:
|
||||
self.instance_task.detach_replica(Mock(), True)
|
||||
self.instance_task._guest.detach_replica.assert_called_with(True)
|
||||
mock_update_db.assert_called_with(slave_of_id=None)
|
||||
tr_mock.assert_not_called()
|
||||
|
||||
with patch.object(self.instance_task, 'reset_task_status') as tr_mock:
|
||||
self.instance_task.detach_replica(Mock(), False)
|
||||
self.instance_task._guest.detach_replica.assert_called_with(False)
|
||||
mock_update_db.assert_called_with(slave_of_id=None)
|
||||
tr_mock.assert_called_once_with()
|
||||
|
||||
@patch.object(BaseInstance, 'update_db')
|
||||
@patch('trove.taskmanager.models.LOG')
|
||||
def test_error_detach_replica(self, mock_logging):
|
||||
with patch.object(self.instance_task._guest, 'detach_replica',
|
||||
side_effect=GuestError):
|
||||
self.assertRaises(GuestError, self.instance_task.detach_replica,
|
||||
Mock(), True)
|
||||
def test_error_detach_replica(self, mock_logging, mock_update_db):
|
||||
with patch.object(self.instance_task, 'reset_task_status') as tr_mock:
|
||||
with patch.object(self.instance_task._guest, 'detach_replica',
|
||||
side_effect=GuestError):
|
||||
self.assertRaises(
|
||||
GuestError, self.instance_task.detach_replica,
|
||||
Mock(), True)
|
||||
mock_update_db.assert_not_called()
|
||||
tr_mock.assert_not_called()
|
||||
|
||||
with patch.object(self.instance_task, 'reset_task_status') as tr_mock:
|
||||
with patch.object(self.instance_task._guest, 'detach_replica',
|
||||
side_effect=GuestError):
|
||||
self.assertRaises(
|
||||
GuestError, self.instance_task.detach_replica,
|
||||
Mock(), False)
|
||||
mock_update_db.assert_not_called()
|
||||
tr_mock.assert_called_once_with()
|
||||
|
||||
@patch.object(BaseInstance, 'update_db')
|
||||
def test_make_read_only(self, mock_update_db):
|
||||
|
||||
Reference in New Issue
Block a user