Files
deb-python-os-win/os_win/tests/unit/utils/compute/test_clusterutils.py
Lucian Petrut 45b07177e9 Move unit tests to a separate folder
Until recently, os-win did not have any functional tests. As we're
now starting to add such tests, we need to clearly separate unit
tests.

Note that most OpenStack projects have already done this separation.

This change moves the unit tests to a separate folder.

Change-Id: I1649fae7095f9b4024e526358ac70a58189365bf
2016-10-17 14:25:45 +03:00

454 lines
19 KiB
Python

# Copyright 2016 Cloudbase Solutions Srl
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import ddt
import mock
from os_win import constants
from os_win import exceptions
from os_win.tests.unit import test_base
from os_win.utils.compute import _clusapi_utils
from os_win.utils.compute import clusterutils
@ddt.ddt
class ClusterUtilsTestCase(test_base.OsWinBaseTestCase):
"""Unit tests for the Hyper-V ClusterUtilsBase class."""
_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._clusterutils._clusapi_utils = mock.Mock()
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)
@mock.patch.object(clusterutils.ClusterUtils,
'_lookup_vm_group_check')
def test_add_vm_to_cluster(self, 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)
self.assertTrue(vm_group.PersistentState)
self.assertEqual(vm_group.AutoFailbackType,
self._clusterutils._FAILBACK_TRUE)
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)
exp_valid_transition_states = [constants.CLUSTER_GROUP_PENDING]
mock_migrate_vm.assert_called_once_with(
self._FAKE_VM_NAME, self._FAKE_HOST,
self._clusterutils._LIVE_MIGRATION_TYPE,
constants.CLUSTER_GROUP_ONLINE,
exp_valid_transition_states,
mock.sentinel.timeout)
@mock.patch.object(_clusapi_utils, 'DWORD')
@mock.patch.object(clusterutils.ClusterUtils,
'_wait_for_cluster_group_state')
@ddt.data(None, exceptions.ClusterException)
def test_migrate_vm(self, raised_exc, mock_wait_group, mock_dword):
mock_wait_group.side_effect = raised_exc
migrate_args = (self._FAKE_VM_NAME,
self._FAKE_HOST,
self._clusterutils._LIVE_MIGRATION_TYPE,
constants.CLUSTER_GROUP_ONLINE,
mock.sentinel.valid_transition_states,
mock.sentinel.timeout)
if raised_exc:
self.assertRaises(raised_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,
_clusapi_utils.CLUSPROP_SYNTAX_LIST_VALUE_DWORD,
mock_dword.return_value)
for prop_name in (_clusapi_utils.CLUSPROP_NAME_VM,
_clusapi_utils.CLUSPROP_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 = (
_clusapi_utils.CLUSAPI_GROUP_MOVE_RETURN_TO_SOURCE_NODE_ON_ERROR |
_clusapi_utils.CLUSAPI_GROUP_MOVE_QUEUE_ENABLED |
_clusapi_utils.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_wait_group.assert_called_once_with(
self._FAKE_VM_NAME, exp_clus_group_h,
constants.CLUSTER_GROUP_ONLINE,
self._FAKE_HOST,
mock.sentinel.valid_transition_states,
mock.sentinel.timeout)
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._utils, 'time')
def test_wait_for_clus_group_state_failed(self, mock_time):
desired_host = self._FAKE_HOST
desired_state = constants.CLUSTER_GROUP_ONLINE
valid_transition_states = [constants.CLUSTER_GROUP_PENDING]
group_states = [dict(owner_node=desired_host,
state=constants.CLUSTER_GROUP_PENDING),
dict(owner_node=desired_host,
state=constants.CLUSTER_GROUP_FAILED)]
self._clusapi.get_cluster_group_state.side_effect = group_states
# We don't want a timeout to be raised. We expect the tested
# function to force breaking the retry loop when the cluster
# group gets into a 'failed' state.
#
# As a precaution measure, we're still forcing a timeout at
# some point, to avoid an infinite loop if something goes wrong.
mock_time.time.side_effect = [0] * 10 + [100]
self.assertRaises(exceptions.InvalidClusterGroupState,
self._clusterutils._wait_for_cluster_group_state,
mock.sentinel.group_name,
mock.sentinel.group_handle,
desired_state,
desired_host,
valid_transition_states,
timeout=10)
self._clusapi.get_cluster_group_state.assert_has_calls(
[mock.call(mock.sentinel.group_handle)] * len(group_states))
@mock.patch.object(clusterutils._utils, 'time')
def test_wait_for_clus_group_state_success(self, mock_time):
desired_host = self._FAKE_HOST
desired_state = constants.CLUSTER_GROUP_ONLINE
group_state = dict(owner_node=desired_host.upper(),
state=desired_state)
self._clusapi.get_cluster_group_state.return_value = group_state
self._clusterutils._wait_for_cluster_group_state(
mock.sentinel.group_name,
mock.sentinel.group_handle,
desired_state,
desired_host,
[],
timeout=10)
self._clusapi.get_cluster_group_state.assert_called_once_with(
mock.sentinel.group_handle)
@mock.patch.object(clusterutils, 'tpool')
@mock.patch.object(clusterutils, 'patcher')
def test_monitor_vm_failover_no_vm(self, mock_patcher, mock_tpool):
self._clusterutils._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(fake_callback)
mock_tpool.execute.assert_called_once_with(
self._clusterutils._watcher,
self._clusterutils._WMI_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):
self._clusterutils._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(fake_callback)
mock_tpool.execute.assert_called_once_with(
self._clusterutils._watcher,
self._clusterutils._WMI_EVENT_TIMEOUT_MS)
fake_callback.assert_called_once_with(self._FAKE_VM_NAME,
self._FAKE_PREV_HOST,
self._FAKE_HOST)