コード例 #1
0
    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]])
コード例 #2
0
        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)
コード例 #3
0
    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()
コード例 #4
0
 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)
コード例 #5
0
ファイル: test_pathutils.py プロジェクト: ader1990/os-win
    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)
コード例 #6
0
    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)
コード例 #7
0
    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)
コード例 #8
0
    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)
コード例 #9
0
    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)
コード例 #10
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)
コード例 #11
0
    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]])
コード例 #12
0
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)