diff options
Diffstat (limited to 'nova/tests/unit/virt/vmwareapi')
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/__init__.py | 20 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/fake.py | 203 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/stubs.py | 7 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_configdrive.py | 3 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_driver_api.py | 65 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_ds_util.py | 2 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_imagecache.py | 2 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_images.py | 10 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_network_util.py | 10 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_session.py | 208 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_vif.py | 3 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_vim_util.py | 6 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_vm_util.py | 91 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_vmops.py | 47 | ||||
-rw-r--r-- | nova/tests/unit/virt/vmwareapi/test_volumeops.py | 192 |
15 files changed, 661 insertions, 208 deletions
diff --git a/nova/tests/unit/virt/vmwareapi/__init__.py b/nova/tests/unit/virt/vmwareapi/__init__.py index e69de29bb2..206b60cb8f 100644 --- a/nova/tests/unit/virt/vmwareapi/__init__.py +++ b/nova/tests/unit/virt/vmwareapi/__init__.py @@ -0,0 +1,20 @@ +# 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 unittest + +try: + import oslo_vmware # noqa: F401 +except ImportError: + raise unittest.SkipTest( + "The 'oslo.vmware' dependency is not installed." + ) diff --git a/nova/tests/unit/virt/vmwareapi/fake.py b/nova/tests/unit/virt/vmwareapi/fake.py index b98a287613..2c09afb8ec 100644 --- a/nova/tests/unit/virt/vmwareapi/fake.py +++ b/nova/tests/unit/virt/vmwareapi/fake.py @@ -23,11 +23,11 @@ import collections import sys from oslo_log import log as logging -from oslo_serialization import jsonutils from oslo_utils import units from oslo_utils import uuidutils from oslo_vmware import exceptions as vexc from oslo_vmware.objects import datastore as ds_obj +from oslo_vmware import vim_util from nova import exception from nova.virt.vmwareapi import constants @@ -76,23 +76,34 @@ def cleanup(): _db_content[c] = {} -def _create_object(table, table_obj): +def _create_object(table_obj): """Create an object in the db.""" - _db_content.setdefault(table, {}) - _db_content[table][table_obj.obj] = table_obj + _db_content.setdefault(table_obj.obj._type, {}) + update_object(table_obj) -def _get_object(obj_ref): +def get_object(obj_ref): """Get object for the give reference.""" - return _db_content[obj_ref.type][obj_ref] + return _db_content[obj_ref.type][obj_ref.value] -def _get_objects(obj_type): +def get_objects(obj_type): """Get objects of the type.""" - lst_objs = FakeRetrieveResult() - for key in _db_content[obj_type]: - lst_objs.add_object(_db_content[obj_type][key]) - return lst_objs + return _db_content[obj_type].values() + + +def get_first_object(obj_type): + """Get the first object of an object type""" + return next(iter(_db_content[obj_type].values())) + + +def get_first_object_ref(obj_type): + """Get the first reference of an object type""" + return get_first_object(obj_type).obj + + +def _no_objects_of_type(obj_type): + return not _db_content.get(obj_type) def _convert_to_array_of_mor(mors): @@ -135,21 +146,19 @@ class FakeRetrieveResult(object): if token is not None: self.token = token - def add_object(self, object): - self.objects.append(object) + def add_object(self, obj): + self.objects.append(obj) -def _get_object_refs(obj_type): - """Get object References of the type.""" - lst_objs = [] - for key in _db_content[obj_type]: - lst_objs.append(key) - return lst_objs +def get_object_refs(obj_type): + """Get iterator over object References of the type.""" + for obj in _db_content[obj_type].values(): + yield obj.obj -def _update_object(table, table_obj): +def update_object(table_obj): """Update objects of the type.""" - _db_content[table][table_obj.obj] = table_obj + _db_content[table_obj.obj._type][table_obj.obj.value] = table_obj class Prop(object): @@ -177,6 +186,14 @@ class ManagedObjectReference(object): self.type = name self._type = name + def __repr__(self): + return f'{self._type}:{self.value}' + + def __eq__(self, other): + return (other is not None and + vim_util.get_moref_value(other) == self.value and + vim_util.get_moref_type(other) == self.type) + class ObjectContent(object): """ObjectContent array holds dynamic properties.""" @@ -262,8 +279,11 @@ class ManagedObject(object): return prefix + "-" + str(self.__class__._counter) def __repr__(self): - return jsonutils.dumps({elem.name: elem.val - for elem in self.propSet}) + # We can't just dump the managed-object, because it may be circular + return "{}:{}({})".format(self.obj._type, self.obj.value, + ", ".join( + "{}={}".format(p.name, p.val if p.name == "name" else "<>") + for p in self.propSet)) class DataObject(object): @@ -593,8 +613,7 @@ class ResourcePool(ManagedObject): class DatastoreHostMount(DataObject): def __init__(self, value='host-100'): super(DatastoreHostMount, self).__init__() - host_ref = (_db_content["HostSystem"] - [list(_db_content["HostSystem"].keys())[0]].obj) + host_ref = get_first_object_ref("HostSystem") host_system = DataObject() host_system.ManagedObjectReference = [host_ref] host_system.value = value @@ -621,9 +640,15 @@ class ClusterComputeResource(ManagedObject): summary.effectiveCpu = 10000 self.set("summary", summary) + vm_list = DataObject() + vm_list.ManagedObjectReference = [] + self.set("vm", vm_list) + def _add_root_resource_pool(self, r_pool): if r_pool: self.set("resourcePool", r_pool) + pool = get_object(r_pool) + self.set("vm", pool.get("vm")) def _add_host(self, host_sys): if host_sys: @@ -659,7 +684,7 @@ class ClusterComputeResource(ManagedObject): # Compute the aggregate stats summary.numHosts = len(hosts.ManagedObjectReference) for host_ref in hosts.ManagedObjectReference: - host_sys = _get_object(host_ref) + host_sys = get_object(host_ref) connected = host_sys.get("connected") host_summary = host_sys.get("summary") summary.numCpuCores += host_summary.hardware.numCpuCores @@ -717,14 +742,17 @@ class HostSystem(ManagedObject): maintenance_mode=False): super(HostSystem, self).__init__("host") self.set("name", name) - if _db_content.get("HostNetworkSystem", None) is None: + if _no_objects_of_type("HostNetworkSystem"): create_host_network_system() - if not _get_object_refs('HostStorageSystem'): + + if _no_objects_of_type("HostStorageSystem"): create_host_storage_system() - host_net_key = list(_db_content["HostNetworkSystem"].keys())[0] - host_net_sys = _db_content["HostNetworkSystem"][host_net_key].obj - self.set("configManager.networkSystem", host_net_sys) - host_storage_sys_key = _get_object_refs('HostStorageSystem')[0] + + host_net_obj = get_first_object("HostNetworkSystem") + host_net_ref = host_net_obj.obj + self.set("configManager.networkSystem", host_net_ref) + + host_storage_sys_key = get_first_object_ref('HostStorageSystem') self.set("configManager.storageSystem", host_storage_sys_key) if not ds_ref: @@ -779,10 +807,9 @@ class HostSystem(ManagedObject): self.set("config.network.pnic", net_info_pnic) self.set("connected", connected) - if _db_content.get("Network", None) is None: + if _no_objects_of_type("Network"): create_network() - net_ref = _db_content["Network"][ - list(_db_content["Network"].keys())[0]].obj + net_ref = get_first_object_ref("Network") network_do = DataObject() network_do.ManagedObjectReference = [net_ref] self.set("network", network_do) @@ -792,9 +819,9 @@ class HostSystem(ManagedObject): vswitch_do.name = "vSwitch0" vswitch_do.portgroup = ["PortGroup-vmnet0"] - net_swicth = DataObject() - net_swicth.HostVirtualSwitch = [vswitch_do] - self.set("config.network.vswitch", net_swicth) + net_switch = DataObject() + net_switch.HostVirtualSwitch = [vswitch_do] + self.set("config.network.vswitch", net_switch) host_pg_do = DataObject() host_pg_do.key = "PortGroup-vmnet0" @@ -821,7 +848,7 @@ class HostSystem(ManagedObject): self.set("config.storageDevice.hostBusAdapter", host_bus_adapter_array) # Set the same on the storage system managed object - host_storage_sys = _get_object(host_storage_sys_key) + host_storage_sys = get_object(host_storage_sys_key) host_storage_sys.set('storageDeviceInfo.hostBusAdapter', host_bus_adapter_array) @@ -882,17 +909,15 @@ class Datacenter(ManagedObject): def __init__(self, name="ha-datacenter", ds_ref=None): super(Datacenter, self).__init__("dc") self.set("name", name) - if _db_content.get("Folder", None) is None: + if _no_objects_of_type("Folder"): create_folder() - folder_ref = _db_content["Folder"][ - list(_db_content["Folder"].keys())[0]].obj + folder_ref = get_first_object_ref("Folder") folder_do = DataObject() folder_do.ManagedObjectReference = [folder_ref] self.set("vmFolder", folder_ref) - if _db_content.get("Network", None) is None: + if _no_objects_of_type("Network"): create_network() - net_ref = _db_content["Network"][ - list(_db_content["Network"].keys())[0]].obj + net_ref = get_first_object_ref("Network") network_do = DataObject() network_do.ManagedObjectReference = [net_ref] self.set("network", network_do) @@ -927,54 +952,56 @@ class Task(ManagedObject): def create_host_network_system(): host_net_system = HostNetworkSystem() - _create_object("HostNetworkSystem", host_net_system) + _create_object(host_net_system) def create_host_storage_system(): host_storage_system = HostStorageSystem() - _create_object("HostStorageSystem", host_storage_system) + _create_object(host_storage_system) def create_host(ds_ref=None): host_system = HostSystem(ds_ref=ds_ref) - _create_object('HostSystem', host_system) + _create_object(host_system) def create_datacenter(name, ds_ref=None): data_center = Datacenter(name, ds_ref) - _create_object('Datacenter', data_center) + _create_object(data_center) def create_datastore(name, capacity, free): data_store = Datastore(name, capacity, free) - _create_object('Datastore', data_store) + _create_object(data_store) return data_store.obj def create_res_pool(): res_pool = ResourcePool() - _create_object('ResourcePool', res_pool) + _create_object(res_pool) return res_pool.obj def create_folder(): folder = Folder() - _create_object('Folder', folder) + _create_object(folder) return folder.obj def create_network(): network = Network() - _create_object('Network', network) + _create_object(network) def create_cluster(name, ds_ref): cluster = ClusterComputeResource(name=name) - cluster._add_host(_get_object_refs("HostSystem")[0]) - cluster._add_host(_get_object_refs("HostSystem")[1]) + for i, host in enumerate(get_object_refs("HostSystem")): + cluster._add_host(host) + if i >= 1: + break cluster._add_datastore(ds_ref) cluster._add_root_resource_pool(create_res_pool()) - _create_object('ClusterComputeResource', cluster) + _create_object(cluster) return cluster @@ -993,16 +1020,15 @@ def create_vm(uuid=None, name=None, devices = [] if vmPathName is None: - vm_path = ds_obj.DatastorePath( - list(_db_content['Datastore'].values())[0]) + vm_path = ds_obj.DatastorePath(get_first_object("Datastore")) else: vm_path = ds_obj.DatastorePath.parse(vmPathName) if res_pool_ref is None: - res_pool_ref = list(_db_content['ResourcePool'].keys())[0] + res_pool_ref = get_first_object_ref("ResourcePool") if host_ref is None: - host_ref = list(_db_content["HostSystem"].keys())[0] + host_ref = get_first_object_ref("HostSystem") # Fill in the default path to the vmx file if we were only given a # datastore. Note that if you create a VM with vmPathName '[foo]', when you @@ -1011,9 +1037,9 @@ def create_vm(uuid=None, name=None, if vm_path.rel_path == '': vm_path = vm_path.join(name, name + '.vmx') - for key, value in _db_content["Datastore"].items(): + for value in get_objects("Datastore"): if value.get('summary.name') == vm_path.datastore: - ds = key + ds = value.obj break else: ds = create_datastore(vm_path.datastore, 1024, 500) @@ -1030,9 +1056,9 @@ def create_vm(uuid=None, name=None, "instanceUuid": uuid, "version": version} vm = VirtualMachine(**vm_dict) - _create_object("VirtualMachine", vm) + _create_object(vm) - res_pool = _get_object(res_pool_ref) + res_pool = get_object(res_pool_ref) res_pool.vm.ManagedObjectReference.append(vm.obj) return vm.obj @@ -1040,7 +1066,7 @@ def create_vm(uuid=None, name=None, def create_task(task_name, state="running", result=None, error_fault=None): task = Task(task_name, state, result, error_fault) - _create_object("Task", task) + _create_object(task) return task @@ -1103,12 +1129,14 @@ def fake_fetch_image(context, instance, host, port, dc_name, ds_name, def _get_vm_mdo(vm_ref): """Gets the Virtual Machine with the ref from the db.""" - if _db_content.get("VirtualMachine", None) is None: + vms = _db_content.get("VirtualMachine") + if not vms: raise exception.NotFound("There is no VM registered") - if vm_ref not in _db_content.get("VirtualMachine"): + try: + return vms[vm_ref.value] + except KeyError: raise exception.NotFound("Virtual Machine with ref %s is not " - "there" % vm_ref) - return _db_content.get("VirtualMachine")[vm_ref] + "there" % vm_ref.value) def _merge_extraconfig(existing, changes): @@ -1354,11 +1382,10 @@ class FakeVim(object): def _find_all_by_uuid(self, *args, **kwargs): uuid = kwargs.get('uuid') vm_refs = [] - for vm_ref in _db_content.get("VirtualMachine"): - vm = _get_object(vm_ref) + for vm in get_objects("VirtualMachine"): vm_uuid = vm.get("summary.config.instanceUuid") if vm_uuid == uuid: - vm_refs.append(vm_ref) + vm_refs.append(vm.obj) return vm_refs def _delete_snapshot(self, method, *args, **kwargs): @@ -1412,7 +1439,7 @@ class FakeVim(object): vm_dict["extra_config"] = extraConfigs virtual_machine = VirtualMachine(**vm_dict) - _create_object("VirtualMachine", virtual_machine) + _create_object(virtual_machine) task_mdo = create_task(method, "success") return task_mdo.obj @@ -1420,7 +1447,7 @@ class FakeVim(object): """Unregisters a VM from the Host System.""" vm_ref = args[0] _get_vm_mdo(vm_ref) - del _db_content["VirtualMachine"][vm_ref] + del _db_content["VirtualMachine"][vm_ref.value] task_mdo = create_task(method, "success") return task_mdo.obj @@ -1491,13 +1518,7 @@ class FakeVim(object): def _set_power_state(self, method, vm_ref, pwr_state="poweredOn"): """Sets power state for the VM.""" - if _db_content.get("VirtualMachine", None) is None: - raise exception.NotFound("No Virtual Machine has been " - "registered yet") - if vm_ref not in _db_content.get("VirtualMachine"): - raise exception.NotFound("Virtual Machine with ref %s is not " - "there" % vm_ref) - vm_mdo = _db_content.get("VirtualMachine").get(vm_ref) + vm_mdo = _get_vm_mdo(vm_ref) vm_mdo.set("runtime.powerState", pwr_state) task_mdo = create_task(method, "success") return task_mdo.obj @@ -1526,7 +1547,7 @@ class FakeVim(object): # This means that we are retrieving props for all managed # data objects of the specified 'type' in the entire # inventory. This gets invoked by vim_util.get_objects. - mdo_refs = _db_content[spec_type] + mdo_refs = list(get_object_refs(spec_type)) elif obj_ref.type != spec_type: # This means that we are retrieving props for the managed # data objects in the parent object's 'path' property. @@ -1536,7 +1557,7 @@ class FakeVim(object): # path = 'datastore' # the above will retrieve all datastores in the given # cluster. - parent_mdo = _db_content[obj_ref.type][obj_ref] + parent_mdo = get_object(obj_ref) path = obj.selectSet[0].path mdo_refs = parent_mdo.get(path).ManagedObjectReference else: @@ -1545,12 +1566,13 @@ class FakeVim(object): # vim_util.get_properties_for_a_collection_of_objects. mdo_refs = [obj_ref] + mdo_list = _db_content[spec_type] for mdo_ref in mdo_refs: - mdo = _db_content[spec_type][mdo_ref] - prop_list = [] - for prop_name in properties: - prop = Prop(prop_name, mdo.get(prop_name)) - prop_list.append(prop) + mdo = mdo_list[mdo_ref.value] + prop_list = [ + Prop(prop_name, mdo.get(prop_name)) + for prop_name in properties + ] obj_content = ObjectContent(mdo.obj, prop_list) lst_ret_objs.add_object(obj_content) except Exception: @@ -1560,14 +1582,13 @@ class FakeVim(object): def _add_port_group(self, method, *args, **kwargs): """Adds a port group to the host system.""" - _host_sk = list(_db_content["HostSystem"].keys())[0] - host_mdo = _db_content["HostSystem"][_host_sk] + host_mdo = get_first_object("HostSystem") host_mdo._add_port_group(kwargs.get("portgrp")) def _add_iscsi_send_tgt(self, method, *args, **kwargs): """Adds a iscsi send target to the hba.""" send_targets = kwargs.get('targets') - host_storage_sys = _get_objects('HostStorageSystem').objects[0] + host_storage_sys = get_first_object('HostStorageSystem') iscsi_hba_array = host_storage_sys.get('storageDeviceInfo' '.hostBusAdapter') iscsi_hba = iscsi_hba_array.HostHostBusAdapter[0] diff --git a/nova/tests/unit/virt/vmwareapi/stubs.py b/nova/tests/unit/virt/vmwareapi/stubs.py index d0caaae43e..a0406bdac5 100644 --- a/nova/tests/unit/virt/vmwareapi/stubs.py +++ b/nova/tests/unit/virt/vmwareapi/stubs.py @@ -36,7 +36,7 @@ def fake_vim_prop(arg): return fake.get_fake_vim_object(arg) -def fake_is_vim_object(arg, module): +def fake_is_vim_object(module): """Stubs out the VMwareAPISession's is_vim_object method.""" return isinstance(module, fake.FakeVim) @@ -74,9 +74,10 @@ def set_stubs(test): fake.fake_upload_image) test.stub_out('nova.virt.vmwareapi.images.fetch_image', fake.fake_fetch_image) - test.stub_out('nova.virt.vmwareapi.driver.VMwareAPISession.vim', + test.stub_out('nova.virt.vmwareapi.session.VMwareAPISession.vim', fake_vim_prop) - test.stub_out('nova.virt.vmwareapi.driver.VMwareAPISession._is_vim_object', + test.stub_out('nova.virt.vmwareapi.session.VMwareAPISession.' + '_is_vim_object', fake_is_vim_object) test.stub_out('nova.network.neutron.API.update_instance_vnic_index', lambda *args, **kwargs: None) diff --git a/nova/tests/unit/virt/vmwareapi/test_configdrive.py b/nova/tests/unit/virt/vmwareapi/test_configdrive.py index de07444ddb..7e8b1c1b63 100644 --- a/nova/tests/unit/virt/vmwareapi/test_configdrive.py +++ b/nova/tests/unit/virt/vmwareapi/test_configdrive.py @@ -13,8 +13,9 @@ # License for the specific language governing permissions and limitations # under the License. +from unittest import mock + import fixtures -import mock from oslo_utils.fixture import uuidsentinel from nova import context diff --git a/nova/tests/unit/virt/vmwareapi/test_driver_api.py b/nova/tests/unit/virt/vmwareapi/test_driver_api.py index 5889fb8239..ac473c8c09 100644 --- a/nova/tests/unit/virt/vmwareapi/test_driver_api.py +++ b/nova/tests/unit/virt/vmwareapi/test_driver_api.py @@ -21,9 +21,9 @@ Test suite for VMwareAPI. import collections import datetime +from unittest import mock from eventlet import greenthread -import mock import os_resource_classes as orc from oslo_utils import fixture as utils_fixture from oslo_utils.fixture import uuidsentinel @@ -61,6 +61,7 @@ from nova.virt.vmwareapi import ds_util from nova.virt.vmwareapi import error_util from nova.virt.vmwareapi import imagecache from nova.virt.vmwareapi import images +from nova.virt.vmwareapi import session from nova.virt.vmwareapi import vif from nova.virt.vmwareapi import vim_util from nova.virt.vmwareapi import vm_util @@ -109,20 +110,11 @@ DEFAULT_FLAVOR_OBJS = [ ] -def _fake_create_session(inst): - session = vmwareapi_fake.DataObject() - session.key = 'fake_key' - session.userName = 'fake_username' - session._pbm_wsdl_loc = None - session._pbm = None - inst._session = session - - class VMwareDriverStartupTestCase(test.NoDBTestCase): def _start_driver_with_flags(self, expected_exception_type, startup_flags): self.flags(**startup_flags) with mock.patch( - 'nova.virt.vmwareapi.driver.VMwareAPISession.__init__'): + 'nova.virt.vmwareapi.session.VMwareAPISession.__init__'): e = self.assertRaises(Exception, driver.VMwareVCDriver, None) # noqa self.assertIs(type(e), expected_exception_type) @@ -154,36 +146,6 @@ class VMwareDriverStartupTestCase(test.NoDBTestCase): group='vmware')) -class VMwareSessionTestCase(test.NoDBTestCase): - - @mock.patch.object(driver.VMwareAPISession, '_is_vim_object', - return_value=False) - def test_call_method(self, mock_is_vim): - with test.nested( - mock.patch.object(driver.VMwareAPISession, '_create_session', - _fake_create_session), - mock.patch.object(driver.VMwareAPISession, 'invoke_api'), - ) as (fake_create, fake_invoke): - session = driver.VMwareAPISession() - session._vim = mock.Mock() - module = mock.Mock() - session._call_method(module, 'fira') - fake_invoke.assert_called_once_with(module, 'fira', session._vim) - - @mock.patch.object(driver.VMwareAPISession, '_is_vim_object', - return_value=True) - def test_call_method_vim(self, mock_is_vim): - with test.nested( - mock.patch.object(driver.VMwareAPISession, '_create_session', - _fake_create_session), - mock.patch.object(driver.VMwareAPISession, 'invoke_api'), - ) as (fake_create, fake_invoke): - session = driver.VMwareAPISession() - module = mock.Mock() - session._call_method(module, 'fira') - fake_invoke.assert_called_once_with(module, 'fira') - - class VMwareAPIVMTestCase(test.NoDBTestCase, test_diagnostics.DiagnosticsComparisonMixin): """Unit tests for Vmware API connection calls.""" @@ -337,7 +299,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, _fake_check_session) with mock.patch.object(greenthread, 'sleep'): - self.conn = driver.VMwareAPISession() + self.conn = session.VMwareAPISession() self.assertEqual(2, self.attempts) def _get_flavor_by_name(self, type): @@ -411,8 +373,8 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, def _get_vm_record(self): # Get record for VM - vms = vmwareapi_fake._get_objects("VirtualMachine") - for vm in vms.objects: + vms = vmwareapi_fake.get_objects("VirtualMachine") + for vm in vms: if vm.get('name') == vm_util._get_vm_name(self._display_name, self.uuid): return vm @@ -1307,7 +1269,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, def _snapshot_delete_vm_snapshot_exception(self, exception, call_count=1): self._create_vm() - fake_vm = vmwareapi_fake._get_objects("VirtualMachine").objects[0].obj + fake_vm = vmwareapi_fake.get_first_object_ref("VirtualMachine") snapshot_ref = vmwareapi_fake.ManagedObjectReference( value="Snapshot-123", name="VirtualMachineSnapshot") @@ -1801,8 +1763,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, get_vm_ref.assert_called_once_with(self.conn._session, self.instance) - get_volume_ref.assert_called_once_with( - connection_info['data']['volume']) + get_volume_ref.assert_called_once_with(connection_info['data']) self.assertTrue(get_vmdk_info.called) attach_disk_to_vm.assert_called_once_with(mock.sentinel.vm_ref, self.instance, adapter_type, disk_type, vmdk_path='fake-path') @@ -1878,8 +1839,8 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, def test_iscsi_rescan_hba(self): fake_target_portal = 'fake_target_host:port' - host_storage_sys = vmwareapi_fake._get_objects( - "HostStorageSystem").objects[0] + host_storage_sys = vmwareapi_fake.get_first_object( + "HostStorageSystem") iscsi_hba_array = host_storage_sys.get('storageDeviceInfo' '.hostBusAdapter') iscsi_hba = iscsi_hba_array.HostHostBusAdapter[0] @@ -1899,7 +1860,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, def test_iscsi_get_target(self): data = {'target_portal': 'fake_target_host:port', 'target_iqn': 'fake_target_iqn'} - host = vmwareapi_fake._get_objects('HostSystem').objects[0] + host = vmwareapi_fake.get_first_object('HostSystem') host._add_iscsi_target(data) vops = volumeops.VMwareVolumeOps(self.conn._session) result = vops._iscsi_get_target(data) @@ -2162,7 +2123,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, 'min_unit': 1, 'max_unit': 16, 'step_size': 1, - 'allocation_ratio': 16.0, + 'allocation_ratio': 4.0, }, orc.MEMORY_MB: { 'total': 2048, @@ -2170,7 +2131,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase, 'min_unit': 1, 'max_unit': 1024, 'step_size': 1, - 'allocation_ratio': 1.5, + 'allocation_ratio': 1.0, }, orc.DISK_GB: { 'total': 95, diff --git a/nova/tests/unit/virt/vmwareapi/test_ds_util.py b/nova/tests/unit/virt/vmwareapi/test_ds_util.py index 3b909642fb..1716027afb 100644 --- a/nova/tests/unit/virt/vmwareapi/test_ds_util.py +++ b/nova/tests/unit/virt/vmwareapi/test_ds_util.py @@ -14,8 +14,8 @@ from contextlib import contextmanager import re +from unittest import mock -import mock from oslo_utils import units from oslo_vmware import exceptions as vexc from oslo_vmware.objects import datastore as ds_obj diff --git a/nova/tests/unit/virt/vmwareapi/test_imagecache.py b/nova/tests/unit/virt/vmwareapi/test_imagecache.py index 485b1ea4cd..1116804d2f 100644 --- a/nova/tests/unit/virt/vmwareapi/test_imagecache.py +++ b/nova/tests/unit/virt/vmwareapi/test_imagecache.py @@ -13,8 +13,8 @@ # under the License. import datetime +from unittest import mock -import mock from oslo_config import cfg from oslo_utils import fixture as utils_fixture from oslo_utils.fixture import uuidsentinel diff --git a/nova/tests/unit/virt/vmwareapi/test_images.py b/nova/tests/unit/virt/vmwareapi/test_images.py index 7cfec00c97..20abc063a0 100644 --- a/nova/tests/unit/virt/vmwareapi/test_images.py +++ b/nova/tests/unit/virt/vmwareapi/test_images.py @@ -17,8 +17,8 @@ Test suite for images. import os import tarfile +from unittest import mock -import mock from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import units from oslo_vmware import rw_handles @@ -117,13 +117,11 @@ class VMwareImagesTestCase(test.NoDBTestCase): mock.patch.object(images.IMAGE_API, 'download'), mock.patch.object(images, 'image_transfer'), mock.patch.object(images, '_build_shadow_vm_config_spec'), - mock.patch.object(session, '_call_method'), mock.patch.object(vm_util, 'get_vmdk_info') ) as (mock_image_api_get, mock_image_api_download, mock_image_transfer, mock_build_shadow_vm_config_spec, - mock_call_method, mock_get_vmdk_info): image_data = {'id': 'fake-id', 'disk_format': 'vmdk', @@ -172,7 +170,7 @@ class VMwareImagesTestCase(test.NoDBTestCase): mock_write_handle) mock_get_vmdk_info.assert_called_once_with( session, mock.sentinel.vm_ref, 'fake-vm') - mock_call_method.assert_called_once_with( + session._call_method.assert_called_once_with( session.vim, "UnregisterVM", mock.sentinel.vm_ref) @mock.patch('oslo_vmware.rw_handles.ImageReadHandle') @@ -188,13 +186,11 @@ class VMwareImagesTestCase(test.NoDBTestCase): mock.patch.object(images.IMAGE_API, 'download'), mock.patch.object(images, 'image_transfer'), mock.patch.object(images, '_build_shadow_vm_config_spec'), - mock.patch.object(session, '_call_method'), mock.patch.object(vm_util, 'get_vmdk_info') ) as (mock_image_api_get, mock_image_api_download, mock_image_transfer, mock_build_shadow_vm_config_spec, - mock_call_method, mock_get_vmdk_info): image_data = {'id': 'fake-id', 'disk_format': 'vmdk', @@ -220,7 +216,7 @@ class VMwareImagesTestCase(test.NoDBTestCase): mock_image_transfer.assert_called_once_with(mock_read_handle, mock_write_handle) - mock_call_method.assert_called_once_with( + session._call_method.assert_called_once_with( session.vim, "UnregisterVM", mock.sentinel.vm_ref) mock_get_vmdk_info.assert_called_once_with( session, mock.sentinel.vm_ref, 'fake-vm') diff --git a/nova/tests/unit/virt/vmwareapi/test_network_util.py b/nova/tests/unit/virt/vmwareapi/test_network_util.py index 10f2583946..b3b5bb15ea 100644 --- a/nova/tests/unit/virt/vmwareapi/test_network_util.py +++ b/nova/tests/unit/virt/vmwareapi/test_network_util.py @@ -15,15 +15,15 @@ # under the License. import collections +from unittest import mock -import mock from oslo_vmware import vim_util from nova import test from nova.tests.unit.virt.vmwareapi import fake from nova.tests.unit.virt.vmwareapi import stubs -from nova.virt.vmwareapi import driver from nova.virt.vmwareapi import network_util +from nova.virt.vmwareapi import session ResultSet = collections.namedtuple('ResultSet', ['objects']) @@ -36,12 +36,12 @@ class GetNetworkWithTheNameTestCase(test.NoDBTestCase): def setUp(self): super(GetNetworkWithTheNameTestCase, self).setUp() fake.reset() - self.stub_out('nova.virt.vmwareapi.driver.VMwareAPISession.vim', + self.stub_out('nova.virt.vmwareapi.session.VMwareAPISession.vim', stubs.fake_vim_prop) - self.stub_out('nova.virt.vmwareapi.driver.' + self.stub_out('nova.virt.vmwareapi.session.' 'VMwareAPISession.is_vim_object', stubs.fake_is_vim_object) - self._session = driver.VMwareAPISession() + self._session = session.VMwareAPISession() def _build_cluster_networks(self, networks): """Returns a set of results for a cluster network lookup. diff --git a/nova/tests/unit/virt/vmwareapi/test_session.py b/nova/tests/unit/virt/vmwareapi/test_session.py new file mode 100644 index 0000000000..6088e1f5b2 --- /dev/null +++ b/nova/tests/unit/virt/vmwareapi/test_session.py @@ -0,0 +1,208 @@ +# Copyright (c) 2022 SAP SE +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# Copyright (c) 2012 VMware, Inc. +# Copyright (c) 2011 Citrix Systems, Inc. +# Copyright 2011 OpenStack Foundation +# +# 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. + + +""" +Test suite for VMwareAPI Session +""" + +from unittest import mock + +from oslo_vmware import exceptions as vexec + +from nova import test +from nova.tests.unit.virt.vmwareapi import fake as vmwareapi_fake +from nova.virt.vmwareapi import session + + +def _fake_create_session(inst): + _session = vmwareapi_fake.DataObject() + _session.key = 'fake_key' + _session.userName = 'fake_username' + _session._pbm_wsdl_loc = None + _session._pbm = None + inst._session = _session + + +def _fake_fetch_moref_impl(inst, _): + inst.moref = vmwareapi_fake.ManagedObjectReference( + value=mock.sentinel.moref2) + + +class FakeStableMoRefProxy(session.StableMoRefProxy): + def __init__(self, ref=None): + super(FakeStableMoRefProxy, self).__init__( + ref or vmwareapi_fake.ManagedObjectReference( + value=mock.sentinel.moref)) + + def fetch_moref(self, session): + pass + + def __repr__(self): + return "FakeStableMoRefProxy({!r})".format(self.moref) + + +class StableMoRefProxyTestCase(test.NoDBTestCase): + def test_proxy(self): + ref = FakeStableMoRefProxy() + self.assertEqual(mock.sentinel.moref, ref.value) + self.assertEqual("ManagedObject", ref._type) + + def test_proxy_classes(self): + # Necessary for suds serialisation + ref = FakeStableMoRefProxy() + self.assertEqual("ManagedObjectReference", ref.__class__.__name__) + + +class VMwareSessionTestCase(test.NoDBTestCase): + + @mock.patch.object(session.VMwareAPISession, '_is_vim_object', + return_value=False) + def test_call_method(self, mock_is_vim): + with test.nested( + mock.patch.object(session.VMwareAPISession, + '_create_session', + _fake_create_session), + mock.patch.object(session.VMwareAPISession, + 'invoke_api'), + ) as (fake_create, fake_invoke): + _session = session.VMwareAPISession() + _session._vim = mock.Mock() + module = mock.Mock() + _session._call_method(module, 'fira') + fake_invoke.assert_called_once_with(module, 'fira', _session._vim) + + @mock.patch.object(session.VMwareAPISession, '_is_vim_object', + return_value=True) + def test_call_method_vim(self, mock_is_vim): + with test.nested( + mock.patch.object(session.VMwareAPISession, + '_create_session', + _fake_create_session), + mock.patch.object(session.VMwareAPISession, + 'invoke_api'), + ) as (fake_create, fake_invoke): + _session = session.VMwareAPISession() + module = mock.Mock() + _session._call_method(module, 'fira') + fake_invoke.assert_called_once_with(module, 'fira') + + @mock.patch.object(session.VMwareAPISession, '_is_vim_object', + return_value=True) + def test_call_method_no_recovery(self, mock_is_vim): + with test.nested( + mock.patch.object(session.VMwareAPISession, '_create_session', + _fake_create_session), + mock.patch.object(session.VMwareAPISession, 'invoke_api'), + mock.patch.object(FakeStableMoRefProxy, 'fetch_moref'), + ) as (fake_create, fake_invoke, fake_fetch_moref): + _session = session.VMwareAPISession() + module = mock.Mock() + ref = FakeStableMoRefProxy() + + _session._call_method( + module, mock.sentinel.method_arg, ref, ref=ref) + + fake_invoke.assert_called_once_with( + module, mock.sentinel.method_arg, ref, ref=ref) + fake_fetch_moref.assert_not_called() + + @mock.patch.object(session.VMwareAPISession, '_is_vim_object', + return_value=True) + def test_call_method_recovery_arg_failed(self, mock_is_vim): + with test.nested( + mock.patch.object(session.VMwareAPISession, '_create_session', + _fake_create_session), + mock.patch.object(session.VMwareAPISession, 'invoke_api'), + mock.patch.object(FakeStableMoRefProxy, 'fetch_moref'), + ) as (fake_create, fake_invoke, fake_fetch_moref): + _session = session.VMwareAPISession() + module = mock.Mock() + ref = FakeStableMoRefProxy() + fake_invoke.side_effect = [vexec.ManagedObjectNotFoundException] + + self.assertRaises(vexec.ManagedObjectNotFoundException, + _session._call_method, module, mock.sentinel.method_arg, ref) + + fake_invoke.assert_called_once_with( + module, mock.sentinel.method_arg, ref) + fake_fetch_moref.assert_not_called() + + @mock.patch.object(session.VMwareAPISession, '_is_vim_object', + return_value=True) + def test_call_method_recovery_kwarg_failed(self, mock_is_vim): + with test.nested( + mock.patch.object(session.VMwareAPISession, '_create_session', + _fake_create_session), + mock.patch.object(session.VMwareAPISession, 'invoke_api'), + mock.patch.object(FakeStableMoRefProxy, 'fetch_moref'), + ) as (fake_create, fake_invoke, fake_fetch_moref): + _session = session.VMwareAPISession() + module = mock.Mock() + ref = FakeStableMoRefProxy() + fake_invoke.side_effect = [vexec.ManagedObjectNotFoundException] + + self.assertRaises(vexec.ManagedObjectNotFoundException, + _session._call_method, module, + mock.sentinel.method_arg, ref=ref) + + fake_invoke.assert_called_once_with( + module, mock.sentinel.method_arg, ref=ref) + fake_fetch_moref.assert_not_called() + + @mock.patch.object(session.VMwareAPISession, '_is_vim_object', + return_value=True) + def test_call_method_recovery_arg_success(self, mock_is_vim): + with test.nested( + mock.patch.object(session.VMwareAPISession, '_create_session', + _fake_create_session), + mock.patch.object(session.VMwareAPISession, 'invoke_api'), + mock.patch.object(FakeStableMoRefProxy, + 'fetch_moref', _fake_fetch_moref_impl), + ) as (fake_create, fake_invoke, fake_fetch_moref): + _session = session.VMwareAPISession() + module = mock.Mock() + ref = FakeStableMoRefProxy() + + fake_invoke.side_effect = [vexec.ManagedObjectNotFoundException( + details=dict(obj=mock.sentinel.moref), + ), None] + _session._call_method(module, mock.sentinel.method_arg, ref) + fake_invoke.assert_called_with( + module, mock.sentinel.method_arg, ref) + + @mock.patch.object(session.VMwareAPISession, '_is_vim_object', + return_value=True) + def test_call_method_recovery_kwarg_success(self, mock_is_vim): + with test.nested( + mock.patch.object(session.VMwareAPISession, '_create_session', + _fake_create_session), + mock.patch.object(session.VMwareAPISession, 'invoke_api'), + mock.patch.object(FakeStableMoRefProxy, + 'fetch_moref', _fake_fetch_moref_impl), + ) as (fake_create, fake_invoke, fake_fetch_moref): + _session = session.VMwareAPISession() + module = mock.Mock() + ref = FakeStableMoRefProxy() + + fake_invoke.side_effect = [vexec.ManagedObjectNotFoundException( + details=dict(obj=mock.sentinel.moref), + ), None] + _session._call_method(module, mock.sentinel.method_arg, ref=ref) + fake_invoke.assert_called_with( + module, mock.sentinel.method_arg, ref=ref) diff --git a/nova/tests/unit/virt/vmwareapi/test_vif.py b/nova/tests/unit/virt/vmwareapi/test_vif.py index b0fb9df47c..02d516fac7 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vif.py +++ b/nova/tests/unit/virt/vmwareapi/test_vif.py @@ -13,7 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + from oslo_vmware import vim_util from nova import exception diff --git a/nova/tests/unit/virt/vmwareapi/test_vim_util.py b/nova/tests/unit/virt/vmwareapi/test_vim_util.py index ebfa2010ee..b3057a99ac 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vim_util.py +++ b/nova/tests/unit/virt/vmwareapi/test_vim_util.py @@ -28,11 +28,11 @@ class VMwareVIMUtilTestCase(test.NoDBTestCase): def test_get_inner_objects(self): property = ['summary.name'] # Get the fake datastores directly from the cluster - cluster_refs = fake._get_object_refs('ClusterComputeResource') - cluster = fake._get_object(cluster_refs[0]) + cluster = fake.get_first_object('ClusterComputeResource') + cluster_ref = cluster.obj expected_ds = cluster.datastore.ManagedObjectReference # Get the fake datastores using inner objects utility method result = vim_util.get_inner_objects( - self.vim, cluster_refs[0], 'datastore', 'Datastore', property) + self.vim, cluster_ref, 'datastore', 'Datastore', property) datastores = [oc.obj for oc in result.objects] self.assertEqual(expected_ds, datastores) diff --git a/nova/tests/unit/virt/vmwareapi/test_vm_util.py b/nova/tests/unit/virt/vmwareapi/test_vm_util.py index ea30895a4d..82fa07a882 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vm_util.py +++ b/nova/tests/unit/virt/vmwareapi/test_vm_util.py @@ -15,14 +15,15 @@ # under the License. import collections +from unittest import mock -import mock from oslo_service import fixture as oslo_svc_fixture from oslo_utils import units from oslo_utils import uuidutils from oslo_vmware import exceptions as vexc from oslo_vmware.objects import datastore as ds_obj from oslo_vmware import pbm +from oslo_vmware import vim_util as vutil from nova import exception from nova.network import model as network_model @@ -31,7 +32,7 @@ from nova.tests.unit import fake_instance from nova.tests.unit.virt.vmwareapi import fake from nova.tests.unit.virt.vmwareapi import stubs from nova.virt.vmwareapi import constants -from nova.virt.vmwareapi import driver +from nova.virt.vmwareapi import session as vmware_session from nova.virt.vmwareapi import vm_util @@ -375,7 +376,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase): ide_controller = fake.VirtualIDEController() devices.append(scsi_controller) devices.append(ide_controller) - fake._update_object("VirtualMachine", vm) + fake.update_object(vm) # return the scsi type, not ide self.assertEqual(constants.DEFAULT_ADAPTER_TYPE, vm_util.get_scsi_adapter_type(devices)) @@ -387,7 +388,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase): ide_controller = fake.VirtualIDEController() devices.append(scsi_controller) devices.append(ide_controller) - fake._update_object("VirtualMachine", vm) + fake.update_object(vm) # the controller is not suitable since the device under this controller # has exceeded SCSI_MAX_CONNECT_NUMBER for i in range(0, constants.SCSI_MAX_CONNECT_NUMBER): @@ -1036,7 +1037,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase): found[0] = True mock_log_warn.side_effect = fake_log_warn - session = driver.VMwareAPISession() + session = vmware_session.VMwareAPISession() config_spec = vm_util.get_vm_create_spec( session.vim.client.factory, @@ -1987,23 +1988,85 @@ class VMwareVMUtilTestCase(test.NoDBTestCase): mock_get_name.assert_called_once_with(self._instance.display_name, self._instance.uuid) - -@mock.patch.object(driver.VMwareAPISession, 'vim', stubs.fake_vim_prop) + def test_create_fcd_id_obj(self): + fcd_id_obj = mock.Mock() + client_factory = mock.Mock() + client_factory.create.return_value = fcd_id_obj + fcd_id = mock.sentinel.fcd_id + ret = vm_util._create_fcd_id_obj(client_factory, fcd_id) + + self.assertEqual(fcd_id_obj, ret) + self.assertEqual(fcd_id, ret.id) + client_factory.create.assert_called_once_with('ns0:ID') + + @mock.patch.object(vm_util, '_create_fcd_id_obj') + @mock.patch.object(vutil, 'get_moref') + def test_attach_fcd(self, get_moref, create_fcd_id_obj): + disk_id = mock.sentinel.disk_id + create_fcd_id_obj.return_value = disk_id + + ds_ref = mock.sentinel.ds_ref + get_moref.return_value = ds_ref + + task = mock.sentinel.task + session = mock.Mock() + session._call_method.return_value = task + + vm_ref = mock.sentinel.vm_ref + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + controller_key = mock.sentinel.controller_key + unit_number = mock.sentinel.unit_number + vm_util.attach_fcd( + session, vm_ref, fcd_id, ds_ref_val, controller_key, unit_number) + + create_fcd_id_obj.assert_called_once_with( + session.vim.client.factory, fcd_id) + get_moref.assert_called_once_with(ds_ref_val, 'Datastore') + session._call_method.assert_called_once_with( + session.vim, "AttachDisk_Task", vm_ref, diskId=disk_id, + datastore=ds_ref, controllerKey=controller_key, + unitNumber=unit_number) + session._wait_for_task.assert_called_once_with(task) + + @mock.patch.object(vm_util, '_create_fcd_id_obj') + def test_detach_fcd(self, create_fcd_id_obj): + disk_id = mock.sentinel.disk_id + create_fcd_id_obj.return_value = disk_id + + task = mock.sentinel.task + session = mock.Mock() + session._call_method.return_value = task + + vm_ref = mock.sentinel.vm_ref + fcd_id = mock.sentinel.fcd_id + vm_util.detach_fcd(session, vm_ref, fcd_id) + + create_fcd_id_obj.assert_called_once_with( + session.vim.client.factory, fcd_id) + session._call_method.assert_called_once_with( + session.vim, "DetachDisk_Task", vm_ref, diskId=disk_id) + session._wait_for_task.assert_called_once_with(task) + + +@mock.patch.object(vmware_session.VMwareAPISession, 'vim', + stubs.fake_vim_prop) class VMwareVMUtilGetHostRefTestCase(test.NoDBTestCase): # N.B. Mocking on the class only mocks test_*(), but we need - # VMwareAPISession.vim to be mocked in both setUp and tests. Not mocking in - # setUp causes object initialisation to fail. Not mocking in tests results - # in vim calls not using FakeVim. - @mock.patch.object(driver.VMwareAPISession, 'vim', stubs.fake_vim_prop) + # session.VMwareAPISession.vim to be mocked in both setUp and tests. + # Not mocking in setUp causes object initialisation to fail. Not + # mocking in tests results in vim calls not using FakeVim. + @mock.patch.object(vmware_session.VMwareAPISession, 'vim', + stubs.fake_vim_prop) def setUp(self): super(VMwareVMUtilGetHostRefTestCase, self).setUp() fake.reset() vm_util.vm_refs_cache_reset() - self.session = driver.VMwareAPISession() + self.session = vmware_session.VMwareAPISession() # Create a fake VirtualMachine running on a known host - self.host_ref = list(fake._db_content['HostSystem'].keys())[0] + self.host_ref = fake.get_first_object_ref("HostSystem") self.vm_ref = fake.create_vm(host_ref=self.host_ref) @mock.patch.object(vm_util, 'get_vm_ref') @@ -2019,7 +2082,7 @@ class VMwareVMUtilGetHostRefTestCase(test.NoDBTestCase): def test_get_host_name_for_vm(self, mock_get_vm_ref): mock_get_vm_ref.return_value = self.vm_ref - host = fake._get_object(self.host_ref) + host = fake.get_object(self.host_ref) ret = vm_util.get_host_name_for_vm(self.session, 'fake-instance') diff --git a/nova/tests/unit/virt/vmwareapi/test_vmops.py b/nova/tests/unit/virt/vmwareapi/test_vmops.py index f84c113758..19990b8b32 100644 --- a/nova/tests/unit/virt/vmwareapi/test_vmops.py +++ b/nova/tests/unit/virt/vmwareapi/test_vmops.py @@ -14,8 +14,8 @@ # under the License. import time +from unittest import mock -import mock from oslo_serialization import jsonutils from oslo_utils.fixture import uuidsentinel as uuids from oslo_utils import units @@ -37,9 +37,9 @@ from nova.tests.unit.virt.vmwareapi import stubs from nova import version from nova.virt import hardware from nova.virt.vmwareapi import constants -from nova.virt.vmwareapi import driver from nova.virt.vmwareapi import ds_util from nova.virt.vmwareapi import images +from nova.virt.vmwareapi import session from nova.virt.vmwareapi import vif from nova.virt.vmwareapi import vim_util from nova.virt.vmwareapi import vm_util @@ -65,18 +65,20 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): self.flags(my_ip='', flat_injected=True) self._context = context.RequestContext('fake_user', 'fake_project') - self._session = driver.VMwareAPISession() + self._session = session.VMwareAPISession() self._virtapi = mock.Mock() self._image_id = uuids.image - fake_ds_ref = vmwareapi_fake.ManagedObjectReference(value='fake-ds') + fake_ds_ref = vmwareapi_fake.ManagedObjectReference( + name='Datastore', value='fake-ds') self._ds = ds_obj.Datastore( ref=fake_ds_ref, name='fake_ds', capacity=10 * units.Gi, freespace=10 * units.Gi) self._dc_info = ds_util.DcInfo( ref='fake_dc_ref', name='fake_dc', - vmFolder='fake_vm_folder') + vmFolder=vmwareapi_fake.ManagedObjectReference( + name='Folder', value='fake_vm_folder')) cluster = vmwareapi_fake.create_cluster('fake_cluster', fake_ds_ref) self._uuid = uuids.foo fake_info_cache = { @@ -166,7 +168,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): "projectid:fake_project\n" "projectname:None\n" "flavor:name:m1.micro\n" - "flavor:memory_mb:6\n" + "flavor:memory_mb:8\n" "flavor:vcpus:28\n" "flavor:ephemeral_gb:8128\n" "flavor:root_gb:496\n" @@ -297,7 +299,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): mock_save.assert_called_once_with() self.assertEqual(50, self._instance.progress) - @mock.patch.object(vm_util, 'get_vm_ref', return_value='fake_ref') + @mock.patch.object(vm_util, 'get_vm_ref', + return_value=vmwareapi_fake.ManagedObjectReference()) def test_get_info(self, mock_get_vm_ref): result = { 'summary.config.numCpu': 4, @@ -577,7 +580,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): vmware_tools_status="toolsOk", succeeds=False) - def test_clean_shutdown_no_vwaretools(self): + def test_clean_shutdown_no_vmwaretools(self): self._test_clean_shutdown(timeout=10, retry_interval=3, returns_on=1, @@ -1138,6 +1141,14 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): mock_attach_cdrom_to_vm.assert_called_once_with( vm_ref, self._instance, self._ds.ref, str(upload_iso_path)) + def test_prepare_for_spawn_invalid_ram(self): + instance = self._instance.obj_clone() + flavor = objects.Flavor(vcpus=1, memory_mb=6, ephemeral_gb=1, + swap=1024, extra_specs={}) + instance.flavor = flavor + self.assertRaises(exception.InstanceUnacceptable, + self._vmops.prepare_for_spawn, instance) + @mock.patch('nova.image.glance.API.get') @mock.patch.object(vmops.LOG, 'debug') @mock.patch.object(vmops.VMwareVMOps, '_fetch_image_if_missing') @@ -2051,7 +2062,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): extra_specs, self._metadata) - vm = vmwareapi_fake._get_object(vm_ref) + vm = vmwareapi_fake.get_object(vm_ref) # Test basic VM parameters self.assertEqual(self._instance.uuid, vm.name) @@ -2074,7 +2085,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): datastores = vm.datastore.ManagedObjectReference self.assertEqual(1, len(datastores)) - datastore = vmwareapi_fake._get_object(datastores[0]) + datastore = vmwareapi_fake.get_object(datastores[0]) self.assertEqual(self._ds.name, datastore.get('summary.name')) # Test that the VM's network is configured as specified @@ -2176,7 +2187,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): def _validate_flavor_extra_specs(self, flavor_extra_specs, expected): # Validate that the extra specs are parsed correctly flavor = objects.Flavor(name='my-flavor', - memory_mb=6, + memory_mb=8, vcpus=28, root_gb=496, ephemeral_gb=8128, @@ -2227,7 +2238,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): flavor_extra_specs = {'quota:cpu_limit': 7, 'quota:cpu_reservation': 6} flavor = objects.Flavor(name='my-flavor', - memory_mb=6, + memory_mb=8, vcpus=28, root_gb=496, ephemeral_gb=8128, @@ -2280,7 +2291,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): 'quota:cpu_reservation': 6, 'hw_video:ram_max_mb': 100} flavor = objects.Flavor(name='my-flavor', - memory_mb=6, + memory_mb=8, vcpus=28, root_gb=496, ephemeral_gb=8128, @@ -2692,7 +2703,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): def test_get_storage_policy_none(self): flavor = objects.Flavor(name='m1.small', - memory_mb=6, + memory_mb=8, vcpus=28, root_gb=496, ephemeral_gb=8128, @@ -2706,7 +2717,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): def test_get_storage_policy_extra_specs(self): extra_specs = {'vmware:storage_policy': 'flavor-policy'} flavor = objects.Flavor(name='m1.small', - memory_mb=6, + memory_mb=8, vcpus=28, root_gb=496, ephemeral_gb=8128, @@ -2781,7 +2792,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): def test_get_instance_metadata(self): flavor = objects.Flavor(id=7, name='m1.small', - memory_mb=6, + memory_mb=8, vcpus=28, root_gb=496, ephemeral_gb=8128, @@ -2796,7 +2807,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): "projectid:fake_project\n" "projectname:None\n" "flavor:name:m1.small\n" - "flavor:memory_mb:6\n" + "flavor:memory_mb:8\n" "flavor:vcpus:28\n" "flavor:ephemeral_gb:8128\n" "flavor:root_gb:496\n" @@ -2913,7 +2924,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase): def test_get_cores_per_socket(self): extra_specs = {'hw:cpu_sockets': 7} flavor = objects.Flavor(name='m1.small', - memory_mb=6, + memory_mb=8, vcpus=28, root_gb=496, ephemeral_gb=8128, diff --git a/nova/tests/unit/virt/vmwareapi/test_volumeops.py b/nova/tests/unit/virt/vmwareapi/test_volumeops.py index 0a051d62f5..003cbb9283 100644 --- a/nova/tests/unit/virt/vmwareapi/test_volumeops.py +++ b/nova/tests/unit/virt/vmwareapi/test_volumeops.py @@ -12,7 +12,9 @@ # License for the specific language governing permissions and limitations # under the License. -import mock +from unittest import mock + +import ddt from oslo_utils.fixture import uuidsentinel as uuids from oslo_vmware import exceptions as oslo_vmw_exceptions from oslo_vmware import vim_util as vutil @@ -26,11 +28,12 @@ from nova.tests.unit import fake_instance from nova.tests.unit.virt.vmwareapi import fake as vmwareapi_fake from nova.tests.unit.virt.vmwareapi import stubs from nova.virt.vmwareapi import constants -from nova.virt.vmwareapi import driver +from nova.virt.vmwareapi import session from nova.virt.vmwareapi import vm_util from nova.virt.vmwareapi import volumeops +@ddt.ddt class VMwareVolumeOpsTestCase(test.NoDBTestCase): def setUp(self): @@ -38,7 +41,7 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): super(VMwareVolumeOpsTestCase, self).setUp() vmwareapi_fake.reset() stubs.set_stubs(self) - self._session = driver.VMwareAPISession() + self._session = session.VMwareAPISession() self._context = context.RequestContext('fake_user', 'fake_project') self._volumeops = volumeops.VMwareVolumeOps(self._session) @@ -141,8 +144,7 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): get_vm_ref.assert_called_once_with(self._volumeops._session, instance) - get_volume_ref.assert_called_once_with( - connection_info['data']['volume']) + get_volume_ref.assert_called_once_with(connection_info['data']) self.assertTrue(get_vmdk_info.called) get_vm_state.assert_called_once_with(self._volumeops._session, instance) @@ -265,8 +267,7 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): get_vm_ref.assert_called_once_with(self._volumeops._session, instance) - get_volume_ref.assert_called_once_with( - connection_info['data']['volume']) + get_volume_ref.assert_called_once_with(connection_info['data']) get_vmdk_backed_disk_device.assert_called_once_with( mock.sentinel.vm_ref, connection_info['data']) adapter_type = vm_util.CONTROLLER_TO_ADAPTER_TYPE.get( @@ -315,8 +316,7 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): get_vm_ref.assert_called_once_with(self._volumeops._session, instance) - get_volume_ref.assert_called_once_with( - connection_info['data']['volume']) + get_volume_ref.assert_called_once_with(connection_info['data']) get_vmdk_backed_disk_device.assert_called_once_with( mock.sentinel.vm_ref, connection_info['data']) get_vm_state.assert_called_once_with(self._volumeops._session, @@ -406,6 +406,57 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): get_rdm_disk.assert_called_once_with(hardware_devices, disk_uuid) self.assertFalse(detach_disk_from_vm.called) + @mock.patch.object(vm_util, 'get_vm_ref') + @mock.patch.object(vm_util, 'get_vm_state') + @mock.patch.object(vm_util, 'detach_fcd') + def _test__detach_volume_fcd( + self, detach_fcd, get_vm_state, get_vm_ref, + adapter_type=constants.ADAPTER_TYPE_IDE, powered_off=True): + vm_ref = mock.sentinel.vm_ref + get_vm_ref.return_value = vm_ref + + if adapter_type == constants.ADAPTER_TYPE_IDE: + get_vm_state.return_value = ( + power_state.SHUTDOWN if powered_off else power_state.RUNNING) + + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + connection_info = {'data': {'id': fcd_id, + 'ds_ref_val': ds_ref_val, + 'adapter_type': adapter_type}} + instance = mock.sentinel.instance + + if adapter_type == constants.ADAPTER_TYPE_IDE and not powered_off: + self.assertRaises(exception.Invalid, + self._volumeops._detach_volume_fcd, + connection_info, + instance) + detach_fcd.assert_not_called() + else: + self._volumeops._detach_volume_fcd(connection_info, instance) + detach_fcd.assert_called_once_with( + self._volumeops._session, vm_ref, fcd_id) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_detach_volume_fcd_powered_off_instance(self, adapter_type): + self._test__detach_volume_fcd(adapter_type=adapter_type) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_detach_volume_fcd_powered_on_instance(self, adapter_type): + self._test__detach_volume_fcd(adapter_type=adapter_type, + powered_off=False) + + @mock.patch.object(volumeops.VMwareVolumeOps, '_detach_volume_fcd') + def test_detach_volume_fcd(self, detach_volume_fcd): + connection_info = {'driver_volume_type': constants.DISK_FORMAT_FCD} + instance = mock.sentinel.instance + self._volumeops.detach_volume(connection_info, instance) + detach_volume_fcd.assert_called_once_with(connection_info, instance) + def _test_attach_volume_vmdk(self, adapter_type=None): connection_info = {'driver_volume_type': constants.DISK_FORMAT_VMDK, 'serial': 'volume-fake-id', @@ -444,8 +495,7 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): get_vm_ref.assert_called_once_with(self._volumeops._session, self._instance) - get_volume_ref.assert_called_once_with( - connection_info['data']['volume']) + get_volume_ref.assert_called_once_with(connection_info['data']) self.assertTrue(get_vmdk_info.called) attach_disk_to_vm.assert_called_once_with( vm_ref, self._instance, adapter_type, @@ -498,6 +548,126 @@ class VMwareVolumeOpsTestCase(test.NoDBTestCase): constants.ADAPTER_TYPE_PARAVIRTUAL): self._test_attach_volume_vmdk(adapter_type) + @mock.patch.object(vm_util, 'allocate_controller_key_and_unit_number') + def test_get_controller_key_and_unit( + self, allocate_controller_key_and_unit_number): + key = mock.sentinel.key + unit = mock.sentinel.unit + allocate_controller_key_and_unit_number.return_value = ( + key, unit, None) + + with mock.patch.object(self._volumeops, '_session') as session: + devices = mock.sentinel.devices + session._call_method.return_value = devices + + vm_ref = mock.sentinel.vm_ref + adapter_type = mock.sentinel.adapter_type + ret = self._volumeops._get_controller_key_and_unit( + vm_ref, adapter_type) + self.assertEqual((key, unit, None), ret) + session._call_method.assert_called_once_with( + vutil, 'get_object_property', vm_ref, 'config.hardware.device') + allocate_controller_key_and_unit_number.assert_called_once_with( + session.vim.client.factory, devices, adapter_type) + + @mock.patch.object(volumeops.VMwareVolumeOps, + '_get_controller_key_and_unit') + @mock.patch.object(vm_util, 'reconfigure_vm') + @mock.patch.object(vm_util, 'attach_fcd') + def _test_attach_fcd( + self, attach_fcd, reconfigure_vm, get_controller_key_and_unit, + existing_controller=True): + key = mock.sentinel.key + unit = mock.sentinel.unit + spec = mock.sentinel.spec + if existing_controller: + get_controller_key_and_unit.return_value = (key, unit, None) + else: + get_controller_key_and_unit.side_effect = [(None, None, spec), + (key, unit, None)] + + with mock.patch.object(self._volumeops, '_session') as session: + config_spec = mock.Mock() + session.vim.client.factory.create.return_value = config_spec + + vm_ref = mock.sentinel.vm_ref + adapter_type = mock.sentinel.adapter_type + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + self._volumeops._attach_fcd( + vm_ref, adapter_type, fcd_id, ds_ref_val) + + attach_fcd.assert_called_once_with( + session, vm_ref, fcd_id, ds_ref_val, key, unit) + if existing_controller: + get_controller_key_and_unit.assert_called_once_with( + vm_ref, adapter_type) + reconfigure_vm.assert_not_called() + else: + exp_calls = [mock.call(vm_ref, adapter_type), + mock.call(vm_ref, adapter_type)] + get_controller_key_and_unit.assert_has_calls(exp_calls) + self.assertEqual([spec], config_spec.deviceChange) + reconfigure_vm.assert_called_once_with( + session, vm_ref, config_spec) + + def test_attach_fcd_using_existing_controller(self): + self._test_attach_fcd() + + def test_attach_fcd_using_new_controller(self): + self._test_attach_fcd(existing_controller=False) + + @mock.patch.object(vm_util, 'get_vm_ref') + @mock.patch.object(vm_util, 'get_vm_state') + @mock.patch.object(volumeops.VMwareVolumeOps, '_attach_fcd') + def _test__attach_volume_fcd( + self, attach_fcd, get_vm_state, get_vm_ref, + adapter_type=constants.ADAPTER_TYPE_IDE, powered_off=True): + vm_ref = mock.sentinel.vm_ref + get_vm_ref.return_value = vm_ref + + if adapter_type == constants.ADAPTER_TYPE_IDE: + get_vm_state.return_value = ( + power_state.SHUTDOWN if powered_off else power_state.RUNNING) + + fcd_id = mock.sentinel.fcd_id + ds_ref_val = mock.sentinel.ds_ref_val + connection_info = {'data': {'id': fcd_id, + 'ds_ref_val': ds_ref_val, + 'adapter_type': adapter_type}} + instance = mock.sentinel.instance + + if adapter_type == constants.ADAPTER_TYPE_IDE and not powered_off: + self.assertRaises(exception.Invalid, + self._volumeops._attach_volume_fcd, + connection_info, + instance) + attach_fcd.assert_not_called() + else: + self._volumeops._attach_volume_fcd(connection_info, instance) + attach_fcd.assert_called_once_with( + vm_ref, adapter_type, fcd_id, ds_ref_val) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_attach_volume_fcd_powered_off_instance(self, adapter_type): + self._test__attach_volume_fcd(adapter_type=adapter_type) + + @ddt.data( + constants.ADAPTER_TYPE_BUSLOGIC, constants.ADAPTER_TYPE_IDE, + constants.ADAPTER_TYPE_LSILOGICSAS, constants.ADAPTER_TYPE_PARAVIRTUAL) + def test_attach_volume_fcd_powered_on_instance(self, adapter_type): + self._test__attach_volume_fcd(adapter_type=adapter_type, + powered_off=False) + + @mock.patch.object(volumeops.VMwareVolumeOps, '_attach_volume_fcd') + def test_attach_volume_fcd(self, attach_volume_fcd): + connection_info = {'driver_volume_type': constants.DISK_FORMAT_FCD} + instance = mock.sentinel.instance + self._volumeops.attach_volume(connection_info, instance) + attach_volume_fcd.assert_called_once_with(connection_info, instance) + def test_attach_volume_iscsi(self): for adapter_type in (None, constants.DEFAULT_ADAPTER_TYPE, constants.ADAPTER_TYPE_BUSLOGIC, |