def test_retry_decorator(self, mock_time): err_code = 1 max_retry_count = 5 max_sleep_time = 2 timeout = max_retry_count + 1 mock_time.time.side_effect = range(timeout) raised_exc = exceptions.Win32Exception(message='fake_exc', error_code=err_code) side_effect = [raised_exc] * max_retry_count side_effect.append(mock.sentinel.ret_val) (fake_func, fake_func_side_effect) = self._get_fake_func_with_retry_decorator( error_codes=err_code, exceptions=exceptions.Win32Exception, max_retry_count=max_retry_count, max_sleep_time=max_sleep_time, timeout=timeout, side_effect=side_effect) ret_val = fake_func(mock.sentinel.arg, kwarg=mock.sentinel.kwarg) self.assertEqual(mock.sentinel.ret_val, ret_val) fake_func_side_effect.assert_has_calls( [mock.call(mock.sentinel.arg, kwarg=mock.sentinel.kwarg)] * (max_retry_count + 1)) self.assertEqual(max_retry_count + 1, mock_time.time.call_count) mock_time.sleep.assert_has_calls( [mock.call(sleep_time) for sleep_time in [1, 2, 2, 2, 1]])
def func_side_effect(fake_arg, retry_context): self.assertEqual(mock.sentinel.arg, fake_arg) self.assertEqual(retry_context, dict(prevent_retry=False)) retry_context['prevent_retry'] = True raise exceptions.Win32Exception(message='fake_exc', error_code=1)
def test_is_virtual_disk_file_attached(self, mock_get_vhd_info, mock_exists, exists=True, open_fails=False): mock_exists.return_value = exists if open_fails: mock_get_vhd_info.side_effect = exceptions.Win32Exception( message="fake exc") else: mock_get_vhd_info.return_value = { 'IsLoaded': mock.sentinel.attached } fallback = self._disk_utils.is_virtual_disk_file_attached fallback.return_value = True ret_val = self._vhdutils.is_virtual_disk_file_attached( mock.sentinel.vhd_path) exp_ret_val = True if exists else False self.assertEqual(exp_ret_val, ret_val) if exists: mock_get_vhd_info.assert_called_once_with( mock.sentinel.vhd_path, [w_const.GET_VIRTUAL_DISK_INFO_IS_LOADED]) else: mock_get_vhd_info.assert_not_called() if exists and open_fails: fallback.assert_called_once_with(mock.sentinel.vhd_path) else: fallback.assert_not_called()
def test_copy_exc(self, mock_isdir): mock_isdir.return_value = False self._mock_run.side_effect = exceptions.Win32Exception( func_name='mock_copy', error_code='fake_error_code', error_message='fake_error_msg') self.assertRaises(IOError, self._pathutils.copy, mock.sentinel.src, mock.sentinel.dest)
def test_get_file_id_exc(self): self._mock_run.side_effect = exceptions.Win32Exception( message="fake exc") self.assertRaises(exceptions.Win32Exception, self._pathutils.get_file_id, mock.sentinel.path) self._io_utils.close_handle.assert_called_once_with( self._io_utils.open.return_value)
def test_ensure_buff_func_unexpected_exception(self): fake_exc = exceptions.Win32Exception(message='fake_message', error_code=1) func_side_effect = mock.Mock(side_effect=fake_exc) fake_func = self._get_fake_iscsi_utils_getter_func( func_side_effect=func_side_effect, decorator_args={}) self.assertRaises(exceptions.Win32Exception, fake_func, self._initiator)
def _test_retry_decorator_exceeded(self, mock_time, expected_try_count, mock_time_side_eff=None, timeout=None, max_retry_count=None): raised_exc = exceptions.Win32Exception(message='fake_exc') mock_time.time.side_effect = mock_time_side_eff (fake_func, fake_func_side_effect) = self._get_fake_func_with_retry_decorator( exceptions=exceptions.Win32Exception, timeout=timeout, side_effect=raised_exc) self.assertRaises(exceptions.Win32Exception, fake_func) fake_func_side_effect.assert_has_calls( [mock.call()] * expected_try_count)
def test_detach_virtual_disk_exc(self, is_attached, mock_is_attached, mock_open, mock_exists): # We'll try another approach before erroring out if the image cannot # be opened (e.g. attached on a different host). mock_exists.return_value = True mock_is_attached.return_value = is_attached mock_open.side_effect = exceptions.Win32Exception(message='fake exc') if is_attached: self.assertRaises(exceptions.Win32Exception, self._vhdutils.detach_virtual_disk, mock.sentinel.vhd_path) else: self._vhdutils.detach_virtual_disk(mock.sentinel.vhd_path) mock_is_attached.assert_called_once_with(mock.sentinel.vhd_path)
def _test_ensure_buff_decorator(self, mock_get_items, required_buff_sz=None, returned_element_count=None, parse_output=False): insufficient_buff_exc = exceptions.Win32Exception( message='fake_err_msg', error_code=iscsi_utils.ERROR_INSUFFICIENT_BUFFER) func_requests_buff_sz = required_buff_sz is not None struct_type = ctypes.c_uint decorator_args = dict(struct_type=struct_type, parse_output=parse_output, func_requests_buff_sz=func_requests_buff_sz) func_side_effect = mock.Mock(side_effect=(insufficient_buff_exc, None)) fake_func = self._get_fake_iscsi_utils_getter_func( returned_element_count=returned_element_count, required_buff_sz=required_buff_sz, func_side_effect=func_side_effect, decorator_args=decorator_args) ret_val = fake_func(self._initiator, fake_arg=mock.sentinel.arg) if parse_output: self.assertEqual(mock_get_items.return_value, ret_val) else: self.assertEqual(mock.sentinel.ret_val, ret_val) # We expect our decorated method to be called exactly two times. first_call_args_dict = func_side_effect.call_args_list[0][1] self.assertIsInstance(first_call_args_dict['buff'], ctypes.c_ubyte * 0) self.assertEqual(first_call_args_dict['buff_size_val'], 0) self.assertEqual(first_call_args_dict['element_count_val'], 0) if required_buff_sz: expected_buff_sz = required_buff_sz else: expected_buff_sz = ctypes.sizeof( struct_type) * returned_element_count second_call_args_dict = func_side_effect.call_args_list[1][1] self.assertIsInstance(second_call_args_dict['buff'], ctypes.c_ubyte * expected_buff_sz) self.assertEqual(second_call_args_dict['buff_size_val'], required_buff_sz or 0) self.assertEqual(second_call_args_dict['element_count_val'], returned_element_count or 0)
def _test_retry_decorator_no_retry(self, mock_sleep, expected_exceptions=(), expected_error_codes=()): err_code = 1 raised_exc = exceptions.Win32Exception(message='fake_exc', error_code=err_code) fake_func, fake_func_side_effect = ( self._get_fake_func_with_retry_decorator( error_codes=expected_error_codes, exceptions=expected_exceptions, side_effect=raised_exc)) self.assertRaises(exceptions.Win32Exception, fake_func, mock.sentinel.arg, fake_kwarg=mock.sentinel.kwarg) self.assertFalse(mock_sleep.called) fake_func_side_effect.assert_called_once_with( mock.sentinel.arg, fake_kwarg=mock.sentinel.kwarg)
def test_retry_decorator(self, mock_sleep): err_code = 1 max_retry_count = 5 max_sleep_time = 4 raised_exc = exceptions.Win32Exception(message='fake_exc', error_code=err_code) side_effect = [raised_exc] * max_retry_count side_effect.append(mock.sentinel.ret_val) fake_func = self._get_fake_func_with_retry_decorator( error_codes=err_code, exceptions=exceptions.Win32Exception, max_retry_count=max_retry_count, max_sleep_time=max_sleep_time, side_effect=side_effect)[0] ret_val = fake_func() self.assertEqual(mock.sentinel.ret_val, ret_val) mock_sleep.assert_has_calls([mock.call(sleep_time) for sleep_time in [1, 2, 3, 4, 4]])
class ClusterUtilsTestCase(test_base.OsWinBaseTestCase): """Unit tests for the Hyper-V ClusterUtilsBase class.""" _autospec_classes = [ clusterutils._clusapi_utils.ClusApiUtils, ] _FAKE_RES_NAME = "fake_res_name" _FAKE_HOST = "fake_host" _FAKE_PREV_HOST = "fake_prev_host" _FAKE_VM_NAME = 'instance-00000001' _FAKE_RESOURCEGROUP_NAME = 'Virtual Machine %s' % _FAKE_VM_NAME def setUp(self): super(ClusterUtilsTestCase, self).setUp() self._clusterutils = clusterutils.ClusterUtils() self._clusterutils._conn_cluster = mock.MagicMock() self._clusterutils._cluster = mock.MagicMock() self._clusapi = self._clusterutils._clusapi_utils def test_init_hyperv_conn(self): fake_cluster_name = "fake_cluster" mock_cluster = mock.MagicMock() mock_cluster.path_.return_value = r"\\%s\root" % fake_cluster_name mock_conn = mock.MagicMock() mock_conn.MSCluster_Cluster.return_value = [mock_cluster] self._clusterutils._get_wmi_conn = mock.MagicMock() self._clusterutils._get_wmi_conn.return_value = mock_conn self._clusterutils._init_hyperv_conn("fake_host") def test_init_hyperv_conn_exception(self): self._clusterutils._get_wmi_conn = mock.MagicMock() self._clusterutils._get_wmi_conn.side_effect = AttributeError self.assertRaises(exceptions.HyperVClusterException, self._clusterutils._init_hyperv_conn, "fake_host") @mock.patch.object(clusterutils.ClusterUtils, '_get_cluster_nodes') def test_check_cluster_state_not_enough_nodes(self, mock_get_nodes): self.assertRaises(exceptions.HyperVClusterException, self._clusterutils.check_cluster_state) def test_get_node_name(self): self._clusterutils._this_node = mock.sentinel.fake_node_name self.assertEqual(mock.sentinel.fake_node_name, self._clusterutils.get_node_name()) def test_get_cluster_nodes(self): fake_node1 = mock.MagicMock(Dependent=mock.sentinel.cluster_node1) fake_node2 = mock.MagicMock(Dependent=mock.sentinel.cluster_node2) node_list = [fake_node1, fake_node2] expected = [mock.sentinel.cluster_node1, mock.sentinel.cluster_node2] fake_class = self._clusterutils._conn_cluster.MSCluster_ClusterToNode fake_class.return_value = node_list self.assertEqual(expected, self._clusterutils._get_cluster_nodes()) def test_get_vm_groups(self): vm_gr1 = mock.MagicMock(GroupType=self._clusterutils._VM_GROUP_TYPE) vm_gr2 = mock.MagicMock() vm_gr3 = mock.MagicMock(GroupType=self._clusterutils._VM_GROUP_TYPE) fake_assoc1 = mock.MagicMock(PartComponent=vm_gr1) fake_assoc2 = mock.MagicMock(PartComponent=vm_gr2) fake_assoc3 = mock.MagicMock(PartComponent=vm_gr3) assoc_list = [fake_assoc1, fake_assoc2, fake_assoc3] fake_conn = self._clusterutils._conn_cluster fake_conn.MSCluster_ClusterToResourceGroup.return_value = assoc_list res = list(self._clusterutils._get_vm_groups()) self.assertIn(vm_gr1, res) self.assertNotIn(vm_gr2, res) self.assertIn(vm_gr3, res) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm_group') def test_lookup_vm_group_check(self, mock_lookup_vm_group): mock_lookup_vm_group.return_value = mock.sentinel.fake_vm ret = self._clusterutils._lookup_vm_group_check(self._FAKE_VM_NAME) self.assertEqual(mock.sentinel.fake_vm, ret) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm_group') def test_lookup_vm_group_check_no_vm(self, mock_lookup_vm_group): mock_lookup_vm_group.return_value = None self.assertRaises(exceptions.HyperVVMNotFoundException, self._clusterutils._lookup_vm_group_check, self._FAKE_VM_NAME) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_res') def test_lookup_vm_group(self, mock_lookup_res): self._clusterutils._lookup_vm_group(self._FAKE_VM_NAME) mock_lookup_res.assert_called_once_with( self._clusterutils._conn_cluster.MSCluster_ResourceGroup, self._FAKE_VM_NAME) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm') def test_lookup_vm_check(self, mock_lookup_vm): mock_lookup_vm.return_value = mock.sentinel.fake_vm ret = self._clusterutils._lookup_vm_check(self._FAKE_VM_NAME) self.assertEqual(mock.sentinel.fake_vm, ret) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm') def test_lookup_vm_check_no_vm(self, mock_lookup_vm): mock_lookup_vm.return_value = None self.assertRaises(exceptions.HyperVVMNotFoundException, self._clusterutils._lookup_vm_check, self._FAKE_VM_NAME) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_res') def test_lookup_vm(self, mock_lookup_res): self._clusterutils._lookup_vm(self._FAKE_VM_NAME) mock_lookup_res.assert_called_once_with( self._clusterutils._conn_cluster.MSCluster_Resource, self._clusterutils._VM_BASE_NAME % self._FAKE_VM_NAME) def test_lookup_res_no_res(self): res_list = [] resource_source = mock.MagicMock() resource_source.return_value = res_list self.assertIsNone( self._clusterutils._lookup_res(resource_source, self._FAKE_RES_NAME)) resource_source.assert_called_once_with(Name=self._FAKE_RES_NAME) def test_lookup_res_duplicate_res(self): res_list = [mock.sentinel.r1, mock.sentinel.r1] resource_source = mock.MagicMock() resource_source.return_value = res_list self.assertRaises(exceptions.HyperVClusterException, self._clusterutils._lookup_res, resource_source, self._FAKE_RES_NAME) resource_source.assert_called_once_with(Name=self._FAKE_RES_NAME) def test_lookup_res(self): res_list = [mock.sentinel.r1] resource_source = mock.MagicMock() resource_source.return_value = res_list self.assertEqual( mock.sentinel.r1, self._clusterutils._lookup_res(resource_source, self._FAKE_RES_NAME)) resource_source.assert_called_once_with(Name=self._FAKE_RES_NAME) @mock.patch.object(clusterutils.ClusterUtils, '_get_cluster_nodes') def test_get_cluster_node_names(self, mock_get_cluster_nodes): cluster_nodes = [mock.Mock(Name='node1'), mock.Mock(Name='node2')] mock_get_cluster_nodes.return_value = cluster_nodes ret = self._clusterutils.get_cluster_node_names() self.assertItemsEqual(['node1', 'node2'], ret) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm_group_check') def test_get_vm_host(self, mock_lookup_vm_group_check): owner_node = "fake_owner_node" vm = mock.Mock(OwnerNode=owner_node) mock_lookup_vm_group_check.return_value = vm self.assertEqual(owner_node, self._clusterutils.get_vm_host(self._FAKE_VM_NAME)) @mock.patch.object(clusterutils.ClusterUtils, '_get_vm_groups') def test_list_instances(self, mock_get_vm_groups): mock_get_vm_groups.return_value = [ mock.Mock(Name='vm1'), mock.Mock(Name='vm2') ] ret = self._clusterutils.list_instances() self.assertItemsEqual(['vm1', 'vm2'], ret) @mock.patch.object(clusterutils.ClusterUtils, '_get_vm_groups') def test_list_instance_uuids(self, mock_get_vm_groups): mock_get_vm_groups.return_value = [ mock.Mock(Id='uuid1'), mock.Mock(Id='uuid2') ] ret = self._clusterutils.list_instance_uuids() self.assertItemsEqual(['uuid1', 'uuid2'], ret) @ddt.data(True, False) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm_group_check') def test_add_vm_to_cluster(self, auto_failback, mock_lookup_vm_group_check): self._clusterutils._cluster.AddVirtualMachine = mock.MagicMock() vm_group = mock.Mock() mock_lookup_vm_group_check.return_value = vm_group self._clusterutils.add_vm_to_cluster(self._FAKE_VM_NAME, mock.sentinel.max_failover_count, mock.sentinel.failover_period, auto_failback) self.assertEqual(mock.sentinel.max_failover_count, vm_group.FailoverThreshold) self.assertEqual(mock.sentinel.failover_period, vm_group.FailoverPeriod) self.assertTrue(vm_group.PersistentState) self.assertEqual(vm_group.AutoFailbackType, int(auto_failback)) self.assertEqual(vm_group.FailbackWindowStart, self._clusterutils._FAILBACK_WINDOW_MIN) self.assertEqual(vm_group.FailbackWindowEnd, self._clusterutils._FAILBACK_WINDOW_MAX) vm_group.put.assert_called_once_with() @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm_check') def test_bring_online(self, mock_lookup_vm_check): vm = mock.MagicMock() mock_lookup_vm_check.return_value = vm self._clusterutils.bring_online(self._FAKE_VM_NAME) vm.BringOnline.assert_called_once_with() @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm') def test_take_offline(self, mock_lookup_vm): vm = mock.MagicMock() mock_lookup_vm.return_value = vm self._clusterutils.take_offline(self._FAKE_VM_NAME) vm.TakeOffline.assert_called_once_with() @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm_group') def test_delete(self, mock_lookup_vm_group): vm = mock.MagicMock() mock_lookup_vm_group.return_value = vm self._clusterutils.delete(self._FAKE_VM_NAME) vm.DestroyGroup.assert_called_once_with( self._clusterutils._DESTROY_GROUP) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm') def test_vm_exists_true(self, mock_lookup_vm): vm = mock.MagicMock() mock_lookup_vm.return_value = vm self.assertTrue(self._clusterutils.vm_exists(self._FAKE_VM_NAME)) @mock.patch.object(clusterutils.ClusterUtils, '_lookup_vm') def test_vm_exists_false(self, mock_lookup_vm): mock_lookup_vm.return_value = None self.assertFalse(self._clusterutils.vm_exists(self._FAKE_VM_NAME)) @mock.patch.object(clusterutils.ClusterUtils, '_migrate_vm') def test_live_migrate_vm(self, mock_migrate_vm): self._clusterutils.live_migrate_vm(self._FAKE_VM_NAME, self._FAKE_HOST, mock.sentinel.timeout) mock_migrate_vm.assert_called_once_with( self._FAKE_VM_NAME, self._FAKE_HOST, self._clusterutils._LIVE_MIGRATION_TYPE, constants.CLUSTER_GROUP_ONLINE, mock.sentinel.timeout) @mock.patch.object(wintypes, 'DWORD') @mock.patch.object(clusterutils.ClusterUtils, '_wait_for_cluster_group_migration') @mock.patch.object(clusterutils.ClusterUtils, '_validate_migration') @mock.patch.object(clusterutils, '_ClusterGroupStateChangeListener') @ddt.data(None, exceptions.ClusterException) def test_migrate_vm(self, wait_unexpected_exc, mock_listener_cls, mock_validate_migr, mock_wait_group, mock_dword): mock_wait_group.side_effect = wait_unexpected_exc migrate_args = (self._FAKE_VM_NAME, self._FAKE_HOST, self._clusterutils._LIVE_MIGRATION_TYPE, constants.CLUSTER_GROUP_ONLINE, mock.sentinel.timeout) if wait_unexpected_exc: self.assertRaises(wait_unexpected_exc, self._clusterutils._migrate_vm, *migrate_args) else: self._clusterutils._migrate_vm(*migrate_args) mock_dword.assert_called_once_with( self._clusterutils._LIVE_MIGRATION_TYPE) self._clusapi.get_property_list_entry.assert_has_calls([ mock.call(prop_name, w_const.CLUSPROP_SYNTAX_LIST_VALUE_DWORD, mock_dword.return_value) for prop_name in (w_const.CLUS_RESTYPE_NAME_VM, w_const.CLUS_RESTYPE_NAME_VM_CONFIG) ]) expected_prop_entries = [ self._clusapi.get_property_list_entry.return_value ] * 2 self._clusapi.get_property_list.assert_called_once_with( expected_prop_entries) expected_migrate_flags = ( w_const.CLUSAPI_GROUP_MOVE_RETURN_TO_SOURCE_NODE_ON_ERROR | w_const.CLUSAPI_GROUP_MOVE_QUEUE_ENABLED | w_const.CLUSAPI_GROUP_MOVE_HIGH_PRIORITY_START) exp_clus_h = self._clusapi.open_cluster.return_value exp_clus_node_h = self._clusapi.open_cluster_node.return_value exp_clus_group_h = self._clusapi.open_cluster_group.return_value self._clusapi.open_cluster.assert_called_once_with() self._clusapi.open_cluster_group.assert_called_once_with( exp_clus_h, self._FAKE_VM_NAME) self._clusapi.open_cluster_node.assert_called_once_with( exp_clus_h, self._FAKE_HOST) self._clusapi.move_cluster_group.assert_called_once_with( exp_clus_group_h, exp_clus_node_h, expected_migrate_flags, self._clusapi.get_property_list.return_value) mock_listener_cls.assert_called_once_with(exp_clus_h, self._FAKE_VM_NAME) mock_listener = mock_listener_cls.return_value mock_wait_group.assert_called_once_with( mock_listener.__enter__.return_value, self._FAKE_VM_NAME, exp_clus_group_h, constants.CLUSTER_GROUP_ONLINE, mock.sentinel.timeout) if not wait_unexpected_exc: mock_validate_migr.assert_called_once_with( exp_clus_group_h, self._FAKE_VM_NAME, constants.CLUSTER_GROUP_ONLINE, self._FAKE_HOST) self._clusapi.close_cluster_group.assert_called_once_with( exp_clus_group_h) self._clusapi.close_cluster_node.assert_called_once_with( exp_clus_node_h) self._clusapi.close_cluster.assert_called_once_with(exp_clus_h) @mock.patch.object(clusterutils.ClusterUtils, '_cancel_cluster_group_migration') @mock.patch.object(clusterutils.ClusterUtils, '_wait_for_cluster_group_migration') @mock.patch.object(clusterutils.ClusterUtils, '_validate_migration') @mock.patch.object(clusterutils, '_ClusterGroupStateChangeListener') @ddt.data(True, False) def test_migrate_vm_timeout(self, finished_after_cancel, mock_listener_cls, mock_validate_migr, mock_wait_group, mock_cancel_migr): timeout_exc = exceptions.ClusterGroupMigrationTimeOut( group_name=self._FAKE_VM_NAME, time_elapsed=10) mock_wait_group.side_effect = timeout_exc mock_listener = mock_listener_cls.return_value.__enter__.return_value mock_validate_migr.side_effect = ( (None, ) if finished_after_cancel else exceptions.ClusterGroupMigrationFailed( group_name=self._FAKE_VM_NAME, expected_state=mock.sentinel.expected_state, expected_node=self._FAKE_HOST, group_state=mock.sentinel.expected_state, owner_node=mock.sentinel.other_host)) migrate_args = (self._FAKE_VM_NAME, self._FAKE_HOST, self._clusterutils._LIVE_MIGRATION_TYPE, mock.sentinel.exp_state, mock.sentinel.timeout) if finished_after_cancel: self._clusterutils._migrate_vm(*migrate_args) else: self.assertRaises(exceptions.ClusterGroupMigrationTimeOut, self._clusterutils._migrate_vm, *migrate_args) exp_clus_group_h = self._clusapi.open_cluster_group.return_value mock_cancel_migr.assert_called_once_with(mock_listener, self._FAKE_VM_NAME, exp_clus_group_h, mock.sentinel.exp_state, mock.sentinel.timeout) mock_validate_migr.assert_called_once_with(exp_clus_group_h, self._FAKE_VM_NAME, mock.sentinel.exp_state, self._FAKE_HOST) @ddt.data({}, { 'expected_state': constants.CLUSTER_GROUP_OFFLINE, 'is_valid': False }, { 'expected_node': 'some_other_node', 'is_valid': False }) @ddt.unpack def test_validate_migration(self, expected_node=_FAKE_HOST, expected_state=constants.CLUSTER_GROUP_ONLINE, is_valid=True): group_state = dict(owner_node=self._FAKE_HOST.upper(), state=constants.CLUSTER_GROUP_ONLINE) self._clusapi.get_cluster_group_state.return_value = group_state if is_valid: self._clusterutils._validate_migration(mock.sentinel.group_handle, self._FAKE_VM_NAME, expected_state, expected_node) else: self.assertRaises(exceptions.ClusterGroupMigrationFailed, self._clusterutils._validate_migration, mock.sentinel.group_handle, self._FAKE_VM_NAME, expected_state, expected_node) self._clusapi.get_cluster_group_state.assert_called_once_with( mock.sentinel.group_handle) @mock.patch.object(clusterutils.ClusterUtils, '_cancel_cluster_group_migration') @mock.patch.object(clusterutils, '_ClusterGroupStateChangeListener') def test_cancel_cluster_group_migration_public(self, mock_listener_cls, mock_cancel_migr): exp_clus_h = self._clusapi.open_cluster.return_value exp_clus_group_h = self._clusapi.open_cluster_group.return_value mock_listener = mock_listener_cls.return_value mock_listener.__enter__.return_value = mock_listener self._clusterutils.cancel_cluster_group_migration( mock.sentinel.group_name, mock.sentinel.expected_state, mock.sentinel.timeout) self._clusapi.open_cluster.assert_called_once_with() self._clusapi.open_cluster_group.assert_called_once_with( exp_clus_h, mock.sentinel.group_name) mock_listener.__enter__.assert_called_once_with() mock_listener_cls.assert_called_once_with(exp_clus_h, mock.sentinel.group_name) mock_cancel_migr.assert_called_once_with(mock_listener, mock.sentinel.group_name, exp_clus_group_h, mock.sentinel.expected_state, mock.sentinel.timeout) self._clusapi.close_cluster.assert_called_once_with(exp_clus_h) self._clusapi.close_cluster_group.assert_called_once_with( exp_clus_group_h) @mock.patch.object(clusterutils.ClusterUtils, '_get_cluster_group_state') @mock.patch.object(clusterutils.ClusterUtils, '_is_migration_pending') @mock.patch.object(clusterutils.ClusterUtils, '_wait_for_cluster_group_migration') @ddt.data({}, {'cancel_exception': test_base.TestingException()}, { 'cancel_exception': exceptions.Win32Exception(error_code=w_const.INVALID_HANDLE_VALUE, func_name=mock.sentinel.func_name, error_message=mock.sentinel.error_message) }, { 'cancel_exception': exceptions.Win32Exception(error_code=w_const.ERROR_INVALID_STATE, func_name=mock.sentinel.func_name, error_message=mock.sentinel.error_message), 'invalid_state_for_cancel': True }, { 'cancel_exception': exceptions.Win32Exception(error_code=w_const.ERROR_INVALID_STATE, func_name=mock.sentinel.func_name, error_message=mock.sentinel.error_message), 'invalid_state_for_cancel': True, 'cancel_still_pending': True }, {'cancel_still_pending': True}, { 'cancel_still_pending': True, 'cancel_wait_exception': test_base.TestingException() }) @ddt.unpack def test_cancel_cluster_group_migration(self, mock_wait_migr, mock_is_migr_pending, mock_get_gr_state, cancel_still_pending=False, cancel_exception=None, invalid_state_for_cancel=False, cancel_wait_exception=None): expected_exception = None if cancel_wait_exception: expected_exception = exceptions.JobTerminateFailed() if (cancel_exception and (not invalid_state_for_cancel or cancel_still_pending)): expected_exception = cancel_exception mock_is_migr_pending.return_value = cancel_still_pending mock_get_gr_state.return_value = dict( state=mock.sentinel.state, status_info=mock.sentinel.status_info) self._clusapi.cancel_cluster_group_operation.side_effect = ( cancel_exception or (not cancel_still_pending, )) mock_wait_migr.side_effect = cancel_wait_exception cancel_args = (mock.sentinel.listener, mock.sentinel.group_name, mock.sentinel.group_handle, mock.sentinel.expected_state, mock.sentinel.timeout) if expected_exception: self.assertRaises( expected_exception.__class__, self._clusterutils._cancel_cluster_group_migration, *cancel_args) else: self._clusterutils._cancel_cluster_group_migration(*cancel_args) self._clusapi.cancel_cluster_group_operation.assert_called_once_with( mock.sentinel.group_handle) if isinstance(cancel_exception, exceptions.Win32Exception): mock_get_gr_state.assert_called_once_with( mock.sentinel.group_handle) mock_is_migr_pending.assert_called_once_with( mock.sentinel.state, mock.sentinel.status_info, mock.sentinel.expected_state) if cancel_still_pending and not cancel_exception: mock_wait_migr.assert_called_once_with( mock.sentinel.listener, mock.sentinel.group_name, mock.sentinel.group_handle, mock.sentinel.expected_state, timeout=mock.sentinel.timeout) def test_is_migration_pending(self): self.assertTrue( self._clusterutils._is_migration_pending( group_state=constants.CLUSTER_GROUP_OFFLINE, group_status_info=0, expected_state=constants.CLUSTER_GROUP_ONLINE)) self.assertTrue( self._clusterutils._is_migration_pending( group_state=constants.CLUSTER_GROUP_ONLINE, group_status_info=w_const. CLUSGRP_STATUS_WAITING_IN_QUEUE_FOR_MOVE | 1, # noqa expected_state=constants.CLUSTER_GROUP_ONLINE)) self.assertFalse( self._clusterutils._is_migration_pending( group_state=constants.CLUSTER_GROUP_OFFLINE, group_status_info=0, expected_state=constants.CLUSTER_GROUP_OFFLINE)) @mock.patch.object(clusterutils.ClusterUtils, '_is_migration_pending') @mock.patch.object(clusterutils.ClusterUtils, '_get_cluster_group_state') @mock.patch.object(clusterutils, 'time') def test_wait_for_clus_group_migr_timeout(self, mock_time, mock_get_gr_state, mock_is_migr_pending): exp_wait_iterations = 3 mock_listener = mock.Mock() mock_time.time.side_effect = range(exp_wait_iterations + 2) timeout = 10 state_info = dict(state=mock.sentinel.current_state, status_info=mock.sentinel.status_info) events = [ dict(status_info=mock.sentinel.migr_queued), dict(state=mock.sentinel.pending_state), queue.Empty ] mock_get_gr_state.return_value = state_info mock_is_migr_pending.return_value = True mock_listener.get.side_effect = events self.assertRaises(exceptions.ClusterGroupMigrationTimeOut, self._clusterutils._wait_for_cluster_group_migration, mock_listener, mock.sentinel.group_name, mock.sentinel.group_handle, mock.sentinel.expected_state, timeout=timeout) mock_get_gr_state.assert_called_once_with(mock.sentinel.group_handle) exp_wait_times = [ timeout - elapsed - 1 for elapsed in range(exp_wait_iterations) ] mock_listener.get.assert_has_calls( [mock.call(wait_time) for wait_time in exp_wait_times]) mock_is_migr_pending.assert_has_calls([ mock.call(mock.sentinel.current_state, mock.sentinel.status_info, mock.sentinel.expected_state), mock.call(mock.sentinel.current_state, mock.sentinel.migr_queued, mock.sentinel.expected_state), mock.call(mock.sentinel.pending_state, mock.sentinel.migr_queued, mock.sentinel.expected_state) ]) @mock.patch.object(clusterutils.ClusterUtils, '_is_migration_pending') @mock.patch.object(clusterutils.ClusterUtils, '_get_cluster_group_state') def test_wait_for_clus_group_migr_success(self, mock_get_gr_state, mock_is_migr_pending): mock_listener = mock.Mock() state_info = dict(state=mock.sentinel.current_state, status_info=mock.sentinel.status_info) mock_get_gr_state.return_value = state_info mock_is_migr_pending.side_effect = [True, False] mock_listener.get.return_value = {} self._clusterutils._wait_for_cluster_group_migration( mock_listener, mock.sentinel.group_name, mock.sentinel.group_handle, mock.sentinel.expected_state, timeout=None) mock_listener.get.assert_called_once_with(None) @mock.patch.object(clusterutils.ClusterUtils, '_get_cluster_group_state') @mock.patch.object(clusterutils.ClusterUtils, '_is_migration_queued') def test_get_cluster_group_state_info(self, mock_is_migr_queued, mock_get_gr_state): exp_clus_h = self._clusapi.open_cluster.return_value exp_clus_group_h = self._clusapi.open_cluster_group.return_value mock_get_gr_state.return_value = dict( state=mock.sentinel.state, status_info=mock.sentinel.status_info, owner_node=mock.sentinel.owner_node) sts_info = self._clusterutils.get_cluster_group_state_info( mock.sentinel.group_name) exp_sts_info = dict(state=mock.sentinel.state, owner_node=mock.sentinel.owner_node, migration_queued=mock_is_migr_queued.return_value) self.assertEqual(exp_sts_info, sts_info) self._clusapi.open_cluster.assert_called_once_with() self._clusapi.open_cluster_group.assert_called_once_with( exp_clus_h, mock.sentinel.group_name) mock_get_gr_state.assert_called_once_with(exp_clus_group_h) mock_is_migr_queued.assert_called_once_with(mock.sentinel.status_info) self._clusapi.close_cluster.assert_called_once_with(exp_clus_h) self._clusapi.close_cluster_group.assert_called_once_with( exp_clus_group_h) @mock.patch('ctypes.byref') def test_get_cluster_group_state(self, mock_byref): mock_byref.side_effect = lambda x: ('byref', x) state_info = dict(state=mock.sentinel.state, owner_node=mock.sentinel.owner_node) self._clusapi.get_cluster_group_state.return_value = state_info self._clusapi.cluster_group_control.return_value = ( mock.sentinel.buff, mock.sentinel.buff_sz) self._clusapi.get_cluster_group_status_info.return_value = ( mock.sentinel.status_info) exp_state_info = state_info.copy() exp_state_info['status_info'] = mock.sentinel.status_info ret_val = self._clusterutils._get_cluster_group_state( mock.sentinel.group_handle) self.assertEqual(exp_state_info, ret_val) self._clusapi.get_cluster_group_state.assert_called_once_with( mock.sentinel.group_handle) self._clusapi.cluster_group_control.assert_called_once_with( mock.sentinel.group_handle, w_const.CLUSCTL_GROUP_GET_RO_COMMON_PROPERTIES) self._clusapi.get_cluster_group_status_info.assert_called_once_with( mock_byref(mock.sentinel.buff), mock.sentinel.buff_sz) @mock.patch.object(clusterutils, 'tpool') @mock.patch.object(clusterutils, 'patcher') def test_monitor_vm_failover_no_vm(self, mock_patcher, mock_tpool): mock_watcher = mock.MagicMock() fake_prev = mock.MagicMock(OwnerNode=self._FAKE_PREV_HOST) fake_wmi_object = mock.MagicMock(OwnerNode=self._FAKE_HOST, Name='Virtual Machine', previous=fake_prev) mock_tpool.execute.return_value = fake_wmi_object fake_callback = mock.MagicMock() self._clusterutils._monitor_vm_failover(mock_watcher, fake_callback, mock.sentinel.event_timeout_ms) mock_tpool.execute.assert_called_once_with( mock_watcher, mock.sentinel.event_timeout_ms) fake_callback.assert_not_called() @mock.patch.object(clusterutils, 'tpool') @mock.patch.object(clusterutils, 'patcher') def test_monitor_vm_failover(self, mock_patcher, mock_tpool): mock_watcher = mock.MagicMock() fake_prev = mock.MagicMock(OwnerNode=self._FAKE_PREV_HOST) fake_wmi_object = mock.MagicMock(OwnerNode=self._FAKE_HOST, Name=self._FAKE_RESOURCEGROUP_NAME, previous=fake_prev) mock_tpool.execute.return_value = fake_wmi_object fake_callback = mock.MagicMock() self._clusterutils._monitor_vm_failover(mock_watcher, fake_callback) mock_tpool.execute.assert_called_once_with( mock_watcher, self._clusterutils._WMI_EVENT_TIMEOUT_MS) fake_callback.assert_called_once_with(self._FAKE_VM_NAME, self._FAKE_PREV_HOST, self._FAKE_HOST) @mock.patch.object(clusterutils.ClusterUtils, '_get_failover_watcher') @mock.patch.object(clusterutils.ClusterUtils, '_monitor_vm_failover') @mock.patch.object(clusterutils, 'time') def test_get_vm_owner_change_listener(self, mock_time, mock_monitor, mock_get_watcher): mock_monitor.side_effect = [ None, exceptions.OSWinException, KeyboardInterrupt ] listener = self._clusterutils.get_vm_owner_change_listener() self.assertRaises(KeyboardInterrupt, listener, mock.sentinel.callback) mock_monitor.assert_has_calls([ mock.call(mock_get_watcher.return_value, mock.sentinel.callback, constants.DEFAULT_WMI_EVENT_TIMEOUT_MS) ] * 3) mock_time.sleep.assert_called_once_with( constants.DEFAULT_WMI_EVENT_TIMEOUT_MS / 1000)