Hyper-V: adds os-win library

Adds os-win to requirements.txt.
Replaces the current usage of *Utils classes in the
Hyper-V Driver with the equivalent *Utils classes
from os-win.

Adds decorators to the HyperVDriver methods that prevent
os-win specific exceptions to leak outside the driver.

Depends-On: Id5cd1dce195b38611f4f8c74857087620048b13f

Co-Authored-By: Lucian Petrut <lpetrut@cloudbasesolutions.com>

Partially Implements: blueprint add-os-win-library

Change-Id: I04509843210dcedf98a0cd9e08fa07865c8a76de
This commit is contained in:
Claudiu Belu 2015-12-04 16:49:54 +02:00
parent e155510be7
commit 2b0832ee2b
32 changed files with 293 additions and 566 deletions

View File

@ -17,17 +17,21 @@
A Hyper-V Nova Compute driver.
"""
import functools
import platform
import sys
from nova import exception
from nova.virt import driver
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from oslo_log import log as logging
from oslo_utils import excutils
import six
from hyperv.i18n import _, _LE
from hyperv.nova import eventhandler
from hyperv.nova import hostops
from hyperv.nova import hostutils
from hyperv.nova import imagecache
from hyperv.nova import livemigrationops
from hyperv.nova import migrationops
@ -40,6 +44,55 @@ from hyperv.nova import volumeops
LOG = logging.getLogger(__name__)
def convert_exceptions(function, exception_map):
expected_exceptions = tuple(exception_map.keys())
@functools.wraps(function)
def wrapper(*args, **kwargs):
try:
return function(*args, **kwargs)
except expected_exceptions as ex:
raised_exception = exception_map.get(type(ex))
if not raised_exception:
# exception might be a subclass of an expected exception.
for expected in expected_exceptions:
if isinstance(ex, expected):
raised_exception = exception_map[expected]
break
exc_info = sys.exc_info()
# NOTE(claudiub): Python 3 raises the exception object given as
# the second argument in six.reraise.
# The original message will be maintained by passing the original
# exception.
exc = raised_exception(exc_info[1])
six.reraise(raised_exception, exc, exc_info[2])
return wrapper
def decorate_all_methods(decorator, *args, **kwargs):
def decorate(cls):
for attr in cls.__dict__:
class_member = getattr(cls, attr)
if callable(class_member):
setattr(cls, attr, decorator(class_member, *args, **kwargs))
return cls
return decorate
exception_conversion_map = {
# expected_exception: converted_exception
os_win_exc.OSWinException: exception.NovaException,
os_win_exc.HyperVVMNotFoundException: exception.InstanceNotFound,
}
# NOTE(claudiub): the purpose of the decorator below is to prevent any
# os_win exceptions (subclasses of OSWinException) to leak outside of the
# HyperVDriver.
@decorate_all_methods(convert_exceptions, exception_conversion_map)
class HyperVDriver(driver.ComputeDriver):
capabilities = {
"has_imagecache": True,
@ -65,7 +118,7 @@ class HyperVDriver(driver.ComputeDriver):
self._imagecache = imagecache.ImageCache()
def _check_minimum_windows_version(self):
if not hostutils.HostUtils().check_min_windows_version(6, 2):
if not utilsfactory.get_hostutils().check_min_windows_version(6, 2):
# the version is of Windows is older than Windows Server 2012 R2.
# Log an error, lettingusers know that this version is not
# supported any longer.

View File

@ -20,15 +20,15 @@ import sys
if sys.platform == 'win32':
import wmi
from nova import exception
from nova.i18n import _LW
from nova.virt import event as virtevent
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
from hyperv.nova import constants
from hyperv.nova import serialconsoleops
from hyperv.nova import utilsfactory
LOG = logging.getLogger(__name__)
@ -122,7 +122,7 @@ class InstanceEventHandler(object):
"will be ignored."),
instance_name)
return instance_uuid
except exception.InstanceNotFound:
except os_win_exc.HyperVVMNotFoundException:
# The instance has been deleted.
pass

View File

@ -38,6 +38,7 @@ from oslo_utils import units
from hyperv.i18n import _, _LE, _LI
from hyperv.nova import constants
from hyperv.nova import pathutils
from hyperv.nova import utilsfactory
from hyperv.nova import vmops
@ -58,7 +59,7 @@ LOG = logging.getLogger(__name__)
class HostOps(object):
def __init__(self):
self._hostutils = utilsfactory.get_hostutils()
self._pathutils = utilsfactory.get_pathutils()
self._pathutils = pathutils.PathUtils()
self._vmutils = utilsfactory.get_vmutils()
self._vmops = vmops.VMOps()
self._api = api.API()

View File

@ -22,6 +22,7 @@ from nova import exception
from nova import utils
from nova.virt import imagecache
from nova.virt import images
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
@ -29,7 +30,7 @@ from oslo_utils import units
from oslo_utils import uuidutils
from hyperv.i18n import _
from hyperv.nova import utilsfactory
from hyperv.nova import pathutils
LOG = logging.getLogger(__name__)
@ -54,7 +55,7 @@ def synchronize_with_path(f):
class ImageCache(imagecache.ImageCacheManager):
def __init__(self):
super(ImageCache, self).__init__()
self._pathutils = utilsfactory.get_pathutils()
self._pathutils = pathutils.PathUtils()
self._vhdutils = utilsfactory.get_vhdutils()
self.used_images = []
self.unexplained_images = []
@ -67,8 +68,7 @@ class ImageCache(imagecache.ImageCacheManager):
return instance.root_gb
def _resize_and_cache_vhd(self, instance, vhd_path):
vhd_info = self._vhdutils.get_vhd_info(vhd_path)
vhd_size = vhd_info['MaxInternalSize']
vhd_size = self._vhdutils.get_vhd_size(vhd_path)['VirtualSize']
root_vhd_size_gb = self._get_root_vhd_size_gb(instance)
root_vhd_size = root_vhd_size_gb * units.Gi
@ -166,7 +166,7 @@ class ImageCache(imagecache.ImageCacheManager):
def _verify_rescue_image(self, instance, rescue_image_id,
rescue_image_path):
rescue_image_info = self._vhdutils.get_vhd_info(rescue_image_path)
rescue_image_size = rescue_image_info['MaxInternalSize']
rescue_image_size = rescue_image_info['VirtualSize']
flavor_disk_size = instance.root_gb * units.Gi
if rescue_image_size > flavor_disk_size:

View File

@ -18,6 +18,7 @@ Management class for live migration VM operations.
"""
import functools
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
@ -25,8 +26,8 @@ from oslo_utils import excutils
from hyperv.i18n import _
from hyperv.nova import block_device_manager
from hyperv.nova import imagecache
from hyperv.nova import pathutils
from hyperv.nova import serialconsoleops
from hyperv.nova import utilsfactory
from hyperv.nova import vmops
from hyperv.nova import volumeops
@ -54,7 +55,7 @@ class LiveMigrationOps(object):
else:
self._livemigrutils = None
self._pathutils = utilsfactory.get_pathutils()
self._pathutils = pathutils.PathUtils()
self._vmops = vmops.VMOps()
self._volumeops = volumeops.VolumeOps()
self._serial_console_ops = serialconsoleops.SerialConsoleOps()

View File

@ -21,6 +21,7 @@ import os
from nova import exception
from nova.virt import configdrive
from nova.virt import driver
from os_win import utilsfactory
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import units
@ -29,7 +30,7 @@ from hyperv.i18n import _, _LE
from hyperv.nova import block_device_manager
from hyperv.nova import constants
from hyperv.nova import imagecache
from hyperv.nova import utilsfactory
from hyperv.nova import pathutils
from hyperv.nova import vmops
from hyperv.nova import volumeops
@ -41,7 +42,7 @@ class MigrationOps(object):
self._hostutils = utilsfactory.get_hostutils()
self._vmutils = utilsfactory.get_vmutils()
self._vhdutils = utilsfactory.get_vhdutils()
self._pathutils = utilsfactory.get_pathutils()
self._pathutils = pathutils.PathUtils()
self._volumeops = volumeops.VolumeOps()
self._vmops = vmops.VMOps()
self._imagecache = imagecache.ImageCache()
@ -228,11 +229,9 @@ class MigrationOps(object):
self._vhdutils.reconnect_parent_vhd(diff_vhd_path,
base_vhd_copy_path)
LOG.debug("Merging base disk %(base_vhd_copy_path)s and "
"diff disk %(diff_vhd_path)s",
{'base_vhd_copy_path': base_vhd_copy_path,
'diff_vhd_path': diff_vhd_path})
self._vhdutils.merge_vhd(diff_vhd_path, base_vhd_copy_path)
LOG.debug("Merging differential disk %s into its parent.",
diff_vhd_path)
self._vhdutils.merge_vhd(diff_vhd_path)
# Replace the differential VHD with the merged one
self._pathutils.rename(base_vhd_copy_path, diff_vhd_path)
@ -242,7 +241,7 @@ class MigrationOps(object):
self._pathutils.remove(base_vhd_copy_path)
def _check_resize_vhd(self, vhd_path, vhd_info, new_size):
curr_size = vhd_info['MaxInternalSize']
curr_size = vhd_info['VirtualSize']
if new_size < curr_size:
raise exception.CannotResizeDisk(
reason=_("Cannot resize the root disk to a smaller size. "

View File

@ -14,21 +14,15 @@
# under the License.
import os
import shutil
import sys
import time
if sys.platform == 'win32':
import wmi
from nova import exception
from nova import utils
from os_win.utils import pathutils
from oslo_config import cfg
from oslo_log import log as logging
from hyperv.i18n import _
from hyperv.nova import constants
from hyperv.nova import vmutils
LOG = logging.getLogger(__name__)
@ -47,89 +41,13 @@ CONF.register_opts(hyperv_opts, 'hyperv')
CONF.import_opt('instances_path', 'nova.compute.manager')
ERROR_INVALID_NAME = 123
ERROR_DIR_IS_NOT_EMPTY = 145
class PathUtils(object):
def __init__(self):
self._set_smb_conn()
@property
def _smb_conn(self):
if self._smb_conn_attr:
return self._smb_conn_attr
raise vmutils.HyperVException(_("The SMB WMI namespace is not "
"available on this OS version."))
def _set_smb_conn(self):
# The following namespace is not available prior to Windows
# Server 2012. utilsfactory is not used in order to avoid a
# circular dependency.
try:
self._smb_conn_attr = wmi.WMI(
moniker=r"root\Microsoft\Windows\SMB")
except wmi.x_wmi:
self._smb_conn_attr = None
def open(self, path, mode):
"""Wrapper on __builtin__.open used to simplify unit testing."""
from six.moves import builtins
return builtins.open(path, mode)
def exists(self, path):
return os.path.exists(path)
def makedirs(self, path):
os.makedirs(path)
def remove(self, path):
os.remove(path)
def rename(self, src, dest):
os.rename(src, dest)
def copyfile(self, src, dest):
self.copy(src, dest)
def copy(self, src, dest):
# With large files this is 2x-3x faster than shutil.copy(src, dest),
# especially when copying to a UNC target.
# shutil.copyfileobj(...) with a proper buffer is better than
# shutil.copy(...) but still 20% slower than a shell copy.
# It can be replaced with Win32 API calls to avoid the process
# spawning overhead.
LOG.debug('Copying file from %s to %s', src, dest)
output, ret = utils.execute('cmd.exe', '/C', 'copy', '/Y', src, dest)
if ret:
raise IOError(_('The file copy from %(src)s to %(dest)s failed')
% {'src': src, 'dest': dest})
def move_folder_files(self, src_dir, dest_dir):
"""Moves the files of the given src_dir to dest_dir.
It will ignore any nested folders.
:param src_dir: Given folder from which to move files.
:param dest_dir: Folder to which to move files.
"""
for fname in os.listdir(src_dir):
src = os.path.join(src_dir, fname)
# ignore subdirs.
if os.path.isfile(src):
self.rename(src, os.path.join(dest_dir, fname))
def rmtree(self, path):
# This will be removed once support for Windows Server 2008R2 is
# stopped
for i in range(5):
try:
shutil.rmtree(path)
return
except WindowsError as e:
if e.winerror == ERROR_DIR_IS_NOT_EMPTY:
time.sleep(1)
else:
raise e
# NOTE(claudiub): part of the pre-existing PathUtils is nova-specific and
# it does not belong in the os-win library. In order to ensure the same
# functionality with the least amount of changes necessary, adding as a mixin
# the os_win.pathutils.PathUtils class into this PathUtils.
class PathUtils(pathutils.PathUtils):
def get_instances_dir(self, remote_server=None):
local_instance_path = os.path.normpath(CONF.instances_path)
@ -145,25 +63,15 @@ class PathUtils(object):
else:
return local_instance_path
def _check_create_dir(self, path):
if not self.exists(path):
LOG.debug('Creating directory: %s', path)
self.makedirs(path)
def _check_remove_dir(self, path):
if self.exists(path):
LOG.debug('Removing directory: %s', path)
self.rmtree(path)
def _get_instances_sub_dir(self, dir_name, remote_server=None,
create_dir=True, remove_dir=False):
instances_path = self.get_instances_dir(remote_server)
path = os.path.join(instances_path, dir_name)
try:
if remove_dir:
self._check_remove_dir(path)
self.check_remove_dir(path)
if create_dir:
self._check_create_dir(path)
self.check_create_dir(path)
return path
except WindowsError as ex:
if ex.winerror == ERROR_INVALID_NAME:
@ -261,55 +169,6 @@ class PathUtils(object):
if self.exists(local_log_path):
self.copy(local_log_path, remote_log_path)
def check_smb_mapping(self, smbfs_share):
mappings = self._smb_conn.Msft_SmbMapping(RemotePath=smbfs_share)
if not mappings:
return False
if os.path.exists(smbfs_share):
LOG.debug('Share already mounted: %s', smbfs_share)
return True
else:
LOG.debug('Share exists but is unavailable: %s ', smbfs_share)
self.unmount_smb_share(smbfs_share, force=True)
return False
def mount_smb_share(self, smbfs_share, username=None, password=None):
try:
LOG.debug('Mounting share: %s', smbfs_share)
self._smb_conn.Msft_SmbMapping.Create(RemotePath=smbfs_share,
UserName=username,
Password=password)
except wmi.x_wmi as exc:
err_msg = (_(
'Unable to mount SMBFS share: %(smbfs_share)s '
'WMI exception: %(wmi_exc)s') % {'smbfs_share': smbfs_share,
'wmi_exc': exc})
raise vmutils.HyperVException(err_msg)
def unmount_smb_share(self, smbfs_share, force=False):
mappings = self._smb_conn.Msft_SmbMapping(RemotePath=smbfs_share)
if not mappings:
LOG.debug('Share %s is not mounted. Skipping unmount.',
smbfs_share)
for mapping in mappings:
# Due to a bug in the WMI module, getting the output of
# methods returning None will raise an AttributeError
try:
mapping.Remove(Force=force)
except AttributeError:
pass
except wmi.x_wmi:
# If this fails, a 'Generic Failure' exception is raised.
# This happens even if we unforcefully unmount an in-use
# share, for which reason we'll simply ignore it in this
# case.
if force:
raise vmutils.HyperVException(
_("Could not unmount share: %s") % smbfs_share)
def lookup_image_basepath(self, image_name):
# Note: it is possible that the path doesn't exist
base_dir = self.get_base_vhd_dir()

View File

@ -14,10 +14,10 @@
# under the License.
from nova.console import type as ctype
from os_win import utilsfactory
from oslo_log import log as logging
from hyperv.nova import hostops
from hyperv.nova import utilsfactory
LOG = logging.getLogger(__name__)

View File

@ -18,15 +18,15 @@ from nova.console import serial as serial_console
from nova.console import type as ctype
from nova import exception
from nova.i18n import _, _LI # noqa
from os_win.utils.io import ioutils
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
import six
from hyperv.nova import constants
from hyperv.nova import ioutils
from hyperv.nova import namedpipe
from hyperv.nova import pathutils
from hyperv.nova import serialproxy
from hyperv.nova import utilsfactory
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@ -38,7 +38,7 @@ class SerialConsoleHandler(object):
"""Handles serial console ops related to a given instance."""
def __init__(self, instance_name):
self._vmutils = utilsfactory.get_vmutils()
self._pathutils = utilsfactory.get_pathutils()
self._pathutils = pathutils.PathUtils()
self._instance_name = instance_name
self._log_path = self._pathutils.get_vm_console_log_paths(
@ -128,7 +128,7 @@ class SerialConsoleHandler(object):
if enable_logging:
kwargs['log_file'] = self._log_path
handler = namedpipe.NamedPipeHandler(pipe_path, **kwargs)
handler = utilsfactory.get_named_pipe_handler(pipe_path, **kwargs)
return handler
def _get_vm_serial_port_mapping(self):

View File

@ -19,12 +19,13 @@ import os
from nova import exception
from nova.i18n import _LI, _LE # noqa
from nova import utils
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
import six
from hyperv.nova import pathutils
from hyperv.nova import serialconsolehandler
from hyperv.nova import utilsfactory
CONF = cfg.CONF
@ -46,7 +47,7 @@ def instance_synchronized(func):
class SerialConsoleOps(object):
def __init__(self):
self._vmutils = utilsfactory.get_vmutils()
self._pathutils = utilsfactory.get_pathutils()
self._pathutils = pathutils.PathUtils()
@instance_synchronized
def start_console_handler(self, instance_name):

View File

@ -19,13 +19,16 @@ Management class for VM snapshot operations.
import os
from nova.compute import task_states
from nova import exception
from nova.image import glance
from nova import utils
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
from hyperv.i18n import _LW
from hyperv.nova import utilsfactory
from hyperv.nova import pathutils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@ -33,7 +36,7 @@ LOG = logging.getLogger(__name__)
class SnapshotOps(object):
def __init__(self):
self._pathutils = utilsfactory.get_pathutils()
self._pathutils = pathutils.PathUtils()
self._vmutils = utilsfactory.get_vmutils()
self._vhdutils = utilsfactory.get_vhdutils()
@ -54,7 +57,11 @@ class SnapshotOps(object):
def instance_synchronized_snapshot():
self._snapshot(context, instance, image_id, update_task_state)
instance_synchronized_snapshot()
try:
instance_synchronized_snapshot()
except os_win_exc.HyperVVMNotFoundException:
# the instance might dissapear before starting the operation.
raise exception.InstanceNotFound(instance_id=instance.uuid)
def _snapshot(self, context, instance, image_id, update_task_state):
"""Create snapshot from a running VM instance."""
@ -103,11 +110,9 @@ class SnapshotOps(object):
self._vhdutils.reconnect_parent_vhd(dest_vhd_path,
dest_base_disk_path)
LOG.debug("Merging base disk %(dest_base_disk_path)s and "
"diff disk %(dest_vhd_path)s",
{'dest_base_disk_path': dest_base_disk_path,
'dest_vhd_path': dest_vhd_path})
self._vhdutils.merge_vhd(dest_vhd_path, dest_base_disk_path)
LOG.debug("Merging diff disk %s into its parent.",
dest_vhd_path)
self._vhdutils.merge_vhd(dest_vhd_path)
image_vhd_path = dest_base_disk_path
LOG.debug("Updating Glance image %(image_id)s with content from "

View File

@ -23,16 +23,10 @@ from hyperv.nova import hostutils
from hyperv.nova import vmutils
from hyperv.nova import volumeutils
hyper_opts = [
cfg.BoolOpt('force_volumeutils_v1',
default=False,
help='Force V1 volume utility class'),
]
CONF = cfg.CONF
CONF.register_opts(hyper_opts, 'hyperv')
LOG = logging.getLogger(__name__)
CONF.import_group('hyperv', 'os_win.utilsfactory')
utils = hostutils.HostUtils()

View File

@ -18,11 +18,11 @@ import abc
from nova.i18n import _
from nova.network import model as network_model
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
from hyperv.nova import ovsutils
from hyperv.nova import utilsfactory
hyperv_opts = [
@ -62,22 +62,11 @@ class HyperVNovaNetworkVIFDriver(HyperVBaseVIFDriver):
"""Nova network VIF driver."""
def __init__(self):
self._vmutils = utilsfactory.get_vmutils()
self._netutils = utilsfactory.get_networkutils()
def plug(self, instance, vif):
vswitch_path = self._netutils.get_external_vswitch(
CONF.hyperv.vswitch_name)
vm_name = instance.name
LOG.debug('Creating vswitch port for instance: %s', vm_name)
if self._netutils.vswitch_port_needed():
vswitch_data = self._netutils.create_vswitch_port(vswitch_path,
vm_name)
else:
vswitch_data = vswitch_path
self._vmutils.set_nic_connection(vm_name, vif['id'], vswitch_data)
self._netutils.connect_vnic_to_vswitch(CONF.hyperv.vswitch_name,
vif['id'])
class HyperVOVSVIFDriver(HyperVNovaNetworkVIFDriver):

View File

@ -29,6 +29,8 @@ from nova import objects
from nova import utils
from nova.virt import configdrive
from nova.virt import hardware
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_log import log as logging
@ -43,10 +45,10 @@ from hyperv.i18n import _, _LI, _LE, _LW
from hyperv.nova import block_device_manager
from hyperv.nova import constants
from hyperv.nova import imagecache
from hyperv.nova import pathutils
from hyperv.nova import serialconsoleops
from hyperv.nova import utilsfactory
from hyperv.nova import utilsfactory as old_utilsfactory
from hyperv.nova import vif as vif_utils
from hyperv.nova import vmutils
from hyperv.nova import volumeops
LOG = logging.getLogger(__name__)
@ -126,10 +128,10 @@ class VMOps(object):
_ROOT_DISK_CTRL_ADDR = 0
def __init__(self):
self._vmutils = utilsfactory.get_vmutils()
self._vmutils = old_utilsfactory.get_vmutils()
self._vhdutils = utilsfactory.get_vhdutils()
self._pathutils = utilsfactory.get_pathutils()
self._hostutils = utilsfactory.get_hostutils()
self._pathutils = pathutils.PathUtils()
self._serial_console_ops = serialconsoleops.SerialConsoleOps()
self._volumeops = volumeops.VolumeOps()
self._imagecache = imagecache.ImageCache()
@ -191,7 +193,7 @@ class VMOps(object):
base_vhd_path = self._imagecache.get_cached_image(context, instance,
rescue_image_id)
base_vhd_info = self._vhdutils.get_vhd_info(base_vhd_path)
base_vhd_size = base_vhd_info['MaxInternalSize']
base_vhd_size = base_vhd_info['VirtualSize']
format_ext = base_vhd_path.split('.')[-1]
root_vhd_path = self._pathutils.get_root_vhd_path(instance.name,
@ -270,8 +272,7 @@ class VMOps(object):
def _create_ephemeral_disk(self, instance_name, eph_info):
self._vhdutils.create_dynamic_vhd(eph_info['path'],
eph_info['size'] * units.Gi,
eph_info['format'])
eph_info['size'] * units.Gi)
def set_boot_order(self, vm_gen, block_device_info, instance_name):
boot_order = self._block_device_manager.get_boot_order(
@ -682,7 +683,7 @@ class VMOps(object):
LOG.info(_LI("Soft shutdown succeeded."),
instance=instance)
return True
except vmutils.HyperVException as e:
except os_win_exc.HyperVException as e:
# Exception is raised when trying to shutdown the instance
# while it is still booting.
LOG.debug("Soft shutdown failed: %s", e, instance=instance)
@ -737,7 +738,7 @@ class VMOps(object):
self._set_vm_state(instance,
constants.HYPERV_VM_STATE_DISABLED)
except exception.InstanceNotFound:
except os_win_exc.HyperVVMNotFoundException:
# The manager can call the stop API after receiving instance
# power off events. If this is triggered when the instance
# is being deleted, it might attempt to power off an unexisting

View File

@ -26,6 +26,7 @@ if sys.platform == 'win32':
import wmi
from nova import exception
from os_win import exceptions as os_win_exc
from oslo_config import cfg
from oslo_log import log as logging
from oslo_service import loopingcall
@ -40,28 +41,9 @@ from hyperv.nova import hostutils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
# TODO(alexpilotti): Move the exceptions to a separate module
# TODO(alexpilotti): Add more domain exceptions
class HyperVException(exception.NovaException):
def __init__(self, message=None):
super(HyperVException, self).__init__(message)
# TODO(alexpilotti): Add a storage exception base class
class VHDResizeException(HyperVException):
def __init__(self, message=None):
super(HyperVException, self).__init__(message)
class HyperVAuthorizationException(HyperVException):
def __init__(self, message=None):
super(HyperVException, self).__init__(message)
class UnsupportedConfigDriveFormatException(HyperVException):
def __init__(self, message=None):
super(HyperVException, self).__init__(message)
# NOTE(claudiub): in order to make sure the same exceptions are being raised.
HyperVException = os_win_exc.HyperVException
HyperVAuthorizationException = os_win_exc.HyperVAuthorizationException
class VMUtils(object):

View File

@ -22,9 +22,12 @@ import os
import re
import time
from nova import block_device
from nova import exception
from nova import utils
from nova.virt import driver
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
@ -34,8 +37,6 @@ from six.moves import range
from hyperv.i18n import _, _LE, _LW
from hyperv.nova import constants
from hyperv.nova import utilsfactory
from hyperv.nova import vmutils
LOG = logging.getLogger(__name__)
@ -72,7 +73,7 @@ class VolumeOps(object):
def __init__(self):
self._vmutils = utilsfactory.get_vmutils()
self._volutils = utilsfactory.get_volumeutils()
self._volutils = utilsfactory.get_iscsi_initiator_utils()
self._initiator = None
self._default_root_device = 'vda'
self.volume_drivers = {'smbfs': SMBFSVolumeDriver(),
@ -121,8 +122,8 @@ class VolumeOps(object):
root_device = block_device_info.get('root_device_name')
if not root_device:
root_device = self._default_root_device
return self._volutils.volume_in_mapping(root_device,
block_device_info)
return block_device.volume_in_mapping(root_device,
block_device_info)
def fix_instance_volume_disk_paths(self, instance_name, block_device_info):
mapping = driver.block_device_info_get_mapping(block_device_info)
@ -212,7 +213,7 @@ class VolumeOps(object):
class ISCSIVolumeDriver(object):
def __init__(self):
self._vmutils = utilsfactory.get_vmutils()
self._volutils = utilsfactory.get_volumeutils()
self._volutils = utilsfactory.get_iscsi_initiator_utils()
def login_storage_target(self, connection_info):
data = connection_info['data']
@ -413,9 +414,8 @@ def export_path_synchronized(f):
class SMBFSVolumeDriver(object):
def __init__(self):
self._pathutils = utilsfactory.get_pathutils()
self._smbutils = utilsfactory.get_smbutils()
self._vmutils = utilsfactory.get_vmutils()
self._volutils = utilsfactory.get_volumeutils()
self._username_regex = re.compile(r'user(?:name)?=([^, ]+)')
self._password_regex = re.compile(r'pass(?:word)?=([^, ]+)')
@ -443,7 +443,7 @@ class SMBFSVolumeDriver(object):
disk_path,
ctrller_path,
slot)
except vmutils.HyperVException as exn:
except os_win_exc.HyperVException as exn:
LOG.exception(_LE('Attach volume failed to %(instance_name)s: '
'%(exn)s'), {'instance_name': instance_name,
'exn': exn})
@ -486,12 +486,12 @@ class SMBFSVolumeDriver(object):
def ensure_share_mounted(self, connection_info):
export_path = self._get_export_path(connection_info)
if not self._pathutils.check_smb_mapping(export_path):
if not self._smbutils.check_smb_mapping(export_path):
opts_str = connection_info['data'].get('options', '')
username, password = self._parse_credentials(opts_str)
self._pathutils.mount_smb_share(export_path,
username=username,
password=password)
self._smbutils.mount_smb_share(export_path,
username=username,
password=password)
def _parse_credentials(self, opts_str):
match = self._username_regex.findall(opts_str)
@ -516,7 +516,7 @@ class SMBFSVolumeDriver(object):
# an instance.
@utils.synchronized(export_path)
def unmount_synchronized():
self._pathutils.unmount_smb_share(export_path)
self._smbutils.unmount_smb_share(export_path)
unmount_synchronized()
def set_disk_qos_specs(self, connection_info, instance_name,

View File

@ -15,9 +15,10 @@
# under the License.
import mock
from os_win import utilsfactory
from six.moves import builtins
from hyperv.nova import utilsfactory
from hyperv.nova import utilsfactory as old_utilsfactory
from hyperv.tests import test
@ -29,15 +30,16 @@ class HyperVBaseTestCase(test.NoDBTestCase):
wmi_patcher = mock.patch.object(builtins, 'wmi', create=True,
new=self._mock_wmi)
platform_patcher = mock.patch('sys.platform', 'win32')
hostutils_patcher = mock.patch.object(utilsfactory, 'utils')
hostutils_patcher = mock.patch.multiple(utilsfactory,
_get_class=mock.DEFAULT)
utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class')
old_utilsfactory_patcher = mock.patch.object(old_utilsfactory,
'_get_class')
platform_patcher.start()
wmi_patcher.start()
hostutils_patcher.start()
utilsfactory_patcher.start()
old_utilsfactory_patcher.start()
self.addCleanup(wmi_patcher.stop)
self.addCleanup(platform_patcher.stop)
self.addCleanup(hostutils_patcher.stop)
self.addCleanup(utilsfactory_patcher.stop)
self.addCleanup(old_utilsfactory_patcher.stop)

View File

@ -21,7 +21,9 @@ import platform
import mock
from nova import exception
from nova import safe_utils
from nova.virt import driver as base_driver
from os_win import exceptions as os_win_exc
from hyperv.nova import driver
from hyperv.tests.unit import test_base
@ -47,16 +49,45 @@ class HyperVDriverTestCase(test_base.HyperVBaseTestCase):
self.driver._serialconsoleops = mock.MagicMock()
self.driver._imagecache = mock.MagicMock()
@mock.patch.object(driver.hostutils.HostUtils, 'check_min_windows_version')
def test_check_minimum_windows_version(self, mock_check_min_win_version):
mock_check_min_win_version.return_value = False
@mock.patch.object(driver.utilsfactory, 'get_hostutils')
def test_check_minimum_windows_version(self, mock_get_hostutils):
mock_hostutils = mock_get_hostutils.return_value
mock_hostutils.check_min_windows_version.return_value = False
self.assertRaises(exception.HypervisorTooOld,
self.driver._check_minimum_windows_version)
def test_public_api_signatures(self):
self.assertPublicAPISignatures(base_driver.ComputeDriver(None),
self.driver)
# NOTE(claudiub): wrapped functions do not keep the same signature in
# Python 2.7, which causes this test to fail. Instead, we should
# compare the public API signatures of the unwrapped methods.
for attr in driver.HyperVDriver.__dict__:
class_member = getattr(driver.HyperVDriver, attr)
if callable(class_member):
mocked_method = mock.patch.object(
driver.HyperVDriver, attr,
safe_utils.get_wrapped_function(class_member))
mocked_method.start()
self.addCleanup(mocked_method.stop)
self.assertPublicAPISignatures(base_driver.ComputeDriver,
driver.HyperVDriver)
def test_converted_exception(self):
self.driver._vmops.get_info.side_effect = (
os_win_exc.OSWinException)
self.assertRaises(exception.NovaException,
self.driver.get_info, mock.sentinel.instance)
self.driver._vmops.get_info.side_effect = os_win_exc.HyperVException
self.assertRaises(exception.NovaException,
self.driver.get_info, mock.sentinel.instance)
self.driver._vmops.get_info.side_effect = (
os_win_exc.HyperVVMNotFoundException(vm_name='foofoo'))
self.assertRaises(exception.InstanceNotFound,
self.driver.get_info, mock.sentinel.instance)
def test_need_legacy_block_device_info(self):
self.assertFalse(self.driver.need_legacy_block_device_info)

View File

@ -16,11 +16,11 @@
import eventlet
import mock
from nova import exception
from os_win import exceptions as os_win_exc
from os_win import utilsfactory
from hyperv.nova import constants
from hyperv.nova import eventhandler
from hyperv.nova import utilsfactory
from hyperv.tests.unit import test_base
@ -139,7 +139,8 @@ class EventHandlerTestCase(test_base.HyperVBaseTestCase):
side_effect = (mock.sentinel.instance_uuid
if not missing_uuid else None, )
else:
side_effect = exception.InstanceNotFound('fake_instance_uuid')
side_effect = os_win_exc.HyperVVMNotFoundException(
vm_name=mock.sentinel.instance_name)
mock_get_uuid = self._event_handler._vmutils.get_instance_uuid
mock_get_uuid.side_effect = side_effect

View File

@ -47,6 +47,7 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
self._hostops = hostops.HostOps()
self._hostops._api = mock.MagicMock()
self._hostops._vmops = mock.MagicMock()
self._hostops._pathutils = mock.MagicMock()
def test_get_cpu_info(self):
mock_processors = mock.MagicMock()

View File

@ -50,8 +50,8 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(imagecache.ImageCache, '_get_root_vhd_size_gb')
def test_resize_and_cache_vhd_smaller(self, mock_get_vhd_size_gb):
self.imagecache._vhdutils.get_vhd_info.return_value = {
'MaxInternalSize': (self.FAKE_VHD_SIZE_GB + 1) * units.Gi
self.imagecache._vhdutils.get_vhd_size.return_value = {
'VirtualSize': (self.FAKE_VHD_SIZE_GB + 1) * units.Gi
}
mock_get_vhd_size_gb.return_value = self.FAKE_VHD_SIZE_GB
mock_internal_vhd_size = (
@ -63,7 +63,7 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
mock.sentinel.instance,
mock.sentinel.vhd_path)
self.imagecache._vhdutils.get_vhd_info.assert_called_once_with(
self.imagecache._vhdutils.get_vhd_size.assert_called_once_with(
mock.sentinel.vhd_path)
mock_get_vhd_size_gb.assert_called_once_with(mock.sentinel.instance)
mock_internal_vhd_size.assert_called_once_with(
@ -162,7 +162,7 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
fake_rescue_image_id = 'fake_rescue_image_id'
self.imagecache._vhdutils.get_vhd_info.return_value = {
'MaxInternalSize': self.instance.root_gb + 1}
'VirtualSize': self.instance.root_gb + 1}
(expected_path,
expected_vhd_path) = self._prepare_get_cached_image(
rescue_image_id=fake_rescue_image_id,

View File

@ -14,10 +14,10 @@
# under the License.
import mock
from os_win import exceptions as os_win_exc
from oslo_config import cfg
from hyperv.nova import livemigrationops
from hyperv.nova import vmutils
from hyperv.tests import fake_instance
from hyperv.tests.unit import test_base
@ -32,6 +32,7 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
self.context = 'fake_context'
self._livemigrops = livemigrationops.LiveMigrationOps()
self._livemigrops._block_dev_man = mock.MagicMock()
self._livemigrops._pathutils = mock.MagicMock()
@mock.patch('hyperv.nova.serialconsoleops.SerialConsoleOps.'
'stop_console_handler')
@ -44,8 +45,8 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
fake_dest = mock.sentinel.DESTINATION
self._livemigrops._livemigrutils.live_migrate_vm.side_effect = [
side_effect]
if side_effect is vmutils.HyperVException:
self.assertRaises(vmutils.HyperVException,
if side_effect is os_win_exc.HyperVException:
self.assertRaises(os_win_exc.HyperVException,
self._livemigrops.live_migration,
self.context, mock_instance, fake_dest,
mock_post, mock_recover, False, None)
@ -73,7 +74,7 @@ class LiveMigrationOpsTestCase(test_base.HyperVBaseTestCase):
self._test_live_migration(side_effect=None)
def test_live_migration_exception(self):
self._test_live_migration(side_effect=vmutils.HyperVException)
self._test_live_migration(side_effect=os_win_exc.HyperVException)
def test_live_migration_wrong_os_version(self):
self._livemigrops._livemigrutils = None

View File

@ -16,11 +16,11 @@ import os
import mock
from nova import exception
from os_win import exceptions as os_win_exc
from oslo_utils import units
from hyperv.nova import constants
from hyperv.nova import migrationops
from hyperv.nova import vmutils
from hyperv.tests import fake_instance
from hyperv.tests.unit import test_base
@ -315,7 +315,7 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
recon_parent_vhd.assert_called_once_with(fake_diff_vhd_path,
base_vhd_copy_path)
self._migrationops._vhdutils.merge_vhd.assert_called_once_with(
fake_diff_vhd_path, base_vhd_copy_path)
fake_diff_vhd_path)
self._migrationops._pathutils.rename.assert_called_once_with(
base_vhd_copy_path, fake_diff_vhd_path)
@ -327,10 +327,10 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
os.path.basename(fake_base_vhd_path))
self._migrationops._vhdutils.reconnect_parent_vhd.side_effect = (
vmutils.HyperVException)
os_win_exc.HyperVException)
self._migrationops._pathutils.exists.return_value = True
self.assertRaises(vmutils.HyperVException,
self.assertRaises(os_win_exc.HyperVException,
self._migrationops._merge_base_vhd,
fake_diff_vhd_path, fake_base_vhd_path)
self._migrationops._pathutils.exists.assert_called_once_with(
@ -341,7 +341,7 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(migrationops.MigrationOps, '_resize_vhd')
def test_check_resize_vhd(self, mock_resize_vhd):
self._migrationops._check_resize_vhd(
vhd_path=mock.sentinel.vhd_path, vhd_info={'MaxInternalSize': 1},
vhd_path=mock.sentinel.vhd_path, vhd_info={'VirtualSize': 1},
new_size=2)
mock_resize_vhd.assert_called_once_with(mock.sentinel.vhd_path, 2)
@ -349,7 +349,7 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
self.assertRaises(exception.CannotResizeDisk,
self._migrationops._check_resize_vhd,
mock.sentinel.vhd_path,
{'MaxInternalSize': 1}, 0)
{'VirtualSize': 1}, 0)
@mock.patch.object(migrationops.MigrationOps, '_merge_base_vhd')
def test_resize_vhd(self, mock_merge_base_vhd):

View File

@ -21,7 +21,6 @@ from six.moves import builtins
from hyperv.nova import constants
from hyperv.nova import pathutils
from hyperv.nova import vmutils
from hyperv.tests.unit import test_base
@ -36,45 +35,6 @@ class PathUtilsTestCase(test_base.HyperVBaseTestCase):
self._pathutils = pathutils.PathUtils()
self._pathutils._smb_conn_attr = mock.MagicMock()
@mock.patch.object(pathutils, 'wmi', create=True)
def _test_smb_conn(self, mock_wmi, smb_available=True):
mock_wmi.x_wmi = Exception
mock_wmi.WMI.side_effect = None if smb_available else Exception
self._pathutils._set_smb_conn()
if smb_available:
expected_conn = mock_wmi.WMI.return_value
self.assertEqual(expected_conn, self._pathutils._smb_conn)
else:
self.assertRaises(vmutils.HyperVException,
getattr,
self._pathutils, '_smb_conn')
def test_smb_conn_available(self):
self._test_smb_conn()
def test_smb_conn_unavailable(self):
self._test_smb_conn(smb_available=False)
@mock.patch.object(pathutils.PathUtils, 'rename')
@mock.patch.object(os.path, 'isfile')
@mock.patch.object(os, 'listdir')
def test_move_folder_files(self, mock_listdir, mock_isfile, mock_rename):
src_dir = 'src'
dest_dir = 'dest'
fname = 'tmp_file.txt'
subdir = 'tmp_folder'
src_fname = os.path.join(src_dir, fname)
dest_fname = os.path.join(dest_dir, fname)
# making sure src_subdir is not moved.
mock_listdir.return_value = [fname, subdir]
mock_isfile.side_effect = [True, False]
self._pathutils.move_folder_files(src_dir, dest_dir)
mock_rename.assert_called_once_with(src_fname, dest_fname)
def _mock_lookup_configdrive_path(self, ext, rescue=False):
self._pathutils.get_instance_dir = mock.MagicMock(
return_value=self.fake_instance_dir)
@ -113,81 +73,6 @@ class PathUtilsTestCase(test_base.HyperVBaseTestCase):
self.fake_instance_name)
self.assertIsNone(configdrive_path)
@mock.patch.object(pathutils.PathUtils, 'unmount_smb_share')
@mock.patch('os.path.exists')
def _test_check_smb_mapping(self, mock_exists, mock_unmount_smb_share,
existing_mappings=True, share_available=False):
mock_exists.return_value = share_available
fake_mappings = (
[mock.sentinel.smb_mapping] if existing_mappings else [])
self._pathutils._smb_conn.Msft_SmbMapping.return_value = (
fake_mappings)
ret_val = self._pathutils.check_smb_mapping(
mock.sentinel.share_path)
self.assertEqual(existing_mappings and share_available, ret_val)
if existing_mappings and not share_available:
mock_unmount_smb_share.assert_called_once_with(
mock.sentinel.share_path, force=True)
def test_check_mapping(self):
self._test_check_smb_mapping()
def test_remake_unavailable_mapping(self):
self._test_check_smb_mapping(existing_mappings=True,
share_available=False)
def test_available_mapping(self):
self._test_check_smb_mapping(existing_mappings=True,
share_available=True)
def test_mount_smb_share(self):
fake_create = self._pathutils._smb_conn.Msft_SmbMapping.Create
self._pathutils.mount_smb_share(mock.sentinel.share_path,
mock.sentinel.username,
mock.sentinel.password)
fake_create.assert_called_once_with(
RemotePath=mock.sentinel.share_path,
UserName=mock.sentinel.username,
Password=mock.sentinel.password)
def _test_unmount_smb_share(self, force=False):
fake_mapping = mock.Mock()
smb_mapping_class = self._pathutils._smb_conn.Msft_SmbMapping
smb_mapping_class.return_value = [fake_mapping]
self._pathutils.unmount_smb_share(mock.sentinel.share_path,
force)
smb_mapping_class.assert_called_once_with(
RemotePath=mock.sentinel.share_path)
fake_mapping.Remove.assert_called_once_with(Force=force)
def test_soft_unmount_smb_share(self):
self._test_unmount_smb_share()
def test_force_unmount_smb_share(self):
self._test_unmount_smb_share(force=True)
@mock.patch('shutil.rmtree')
def test_rmtree(self, mock_rmtree):
class WindowsError(Exception):
def __init__(self, winerror=None):
self.winerror = winerror
mock_rmtree.side_effect = [WindowsError(
pathutils.ERROR_DIR_IS_NOT_EMPTY), True]
fake_windows_error = WindowsError
with mock.patch.object(builtins, 'WindowsError',
fake_windows_error, create=True):
self._pathutils.rmtree(mock.sentinel.FAKE_PATH)
mock_rmtree.assert_has_calls([mock.call(mock.sentinel.FAKE_PATH),
mock.call(mock.sentinel.FAKE_PATH)])
@mock.patch('os.path.join')
def test_get_instances_sub_dir(self, fake_path_join):
@ -197,7 +82,7 @@ class PathUtilsTestCase(test_base.HyperVBaseTestCase):
fake_dir_name = "fake_dir_name"
fake_windows_error = WindowsError
self._pathutils._check_create_dir = mock.MagicMock(
self._pathutils.check_create_dir = mock.MagicMock(
side_effect=WindowsError(pathutils.ERROR_INVALID_NAME))
with mock.patch.object(builtins, 'WindowsError',
fake_windows_error, create=True):

View File

@ -18,20 +18,19 @@ import mock
from nova import exception
from hyperv.nova import constants
from hyperv.nova import ioutils
from hyperv.nova import namedpipe
from hyperv.nova import serialconsolehandler
from hyperv.nova import serialproxy
from hyperv.nova import utilsfactory
from hyperv.tests.unit import test_base
class SerialConsoleHandlerTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(utilsfactory, 'get_pathutils')
def setUp(self, mock_get_pathutils):
_FAKE_INSTANCE_NAME = 'fake_instance_name'
def setUp(self):
super(SerialConsoleHandlerTestCase, self).setUp()
self._consolehandler = serialconsolehandler.SerialConsoleHandler(
mock.sentinel.instance_name)
self._FAKE_INSTANCE_NAME)
self._consolehandler._log_path = mock.sentinel.log_path
self._consolehandler._pathutils = mock.Mock()
@ -88,7 +87,7 @@ class SerialConsoleHandlerTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(serialproxy, 'SerialProxy')
@mock.patch('nova.console.serial.acquire_port')
@mock.patch.object(serialconsolehandler.threading, 'Event')
@mock.patch.object(ioutils, 'IOQueue')
@mock.patch.object(serialconsolehandler.ioutils, 'IOQueue')
def test_setup_serial_proxy_handler(self, mock_io_queue, mock_event,
mock_acquire_port,
mock_serial_proxy_class):
@ -105,7 +104,7 @@ class SerialConsoleHandlerTestCase(test_base.HyperVBaseTestCase):
self._consolehandler._setup_serial_proxy_handler()
mock_serial_proxy_class.assert_called_once_with(
mock.sentinel.instance_name,
self._FAKE_INSTANCE_NAME,
mock.sentinel.host, mock.sentinel.port,
mock_input_queue,
mock_output_queue,
@ -161,8 +160,9 @@ class SerialConsoleHandlerTestCase(test_base.HyperVBaseTestCase):
enable_logging=False)]
mock_get_handler.assert_has_calls(expected_calls, any_order=True)
@mock.patch.object(namedpipe, 'NamedPipeHandler')
def _test_get_named_pipe_handler(self, mock_pipe_handler_class,
@mock.patch.object(serialconsolehandler.utilsfactory,
'get_named_pipe_handler')
def _test_get_named_pipe_handler(self, mock_get_pipe_handler,
pipe_type=None, enable_logging=False):
expected_args = {}
@ -182,8 +182,8 @@ class SerialConsoleHandlerTestCase(test_base.HyperVBaseTestCase):
ret_val = self._consolehandler._get_named_pipe_handler(
mock.sentinel.pipe_path, pipe_type, enable_logging)
self.assertEqual(mock_pipe_handler_class.return_value, ret_val)
mock_pipe_handler_class.assert_called_once_with(
self.assertEqual(mock_get_pipe_handler.return_value, ret_val)
mock_get_pipe_handler.assert_called_once_with(
mock.sentinel.pipe_path,
**expected_args)

View File

@ -28,6 +28,7 @@ class SerialConsoleOpsTestCase(test_base.HyperVBaseTestCase):
super(SerialConsoleOpsTestCase, self).setUp()
serialconsoleops._console_handlers = {}
self._serialops = serialconsoleops.SerialConsoleOps()
self._serialops._pathutils = mock.MagicMock()
def _setup_console_handler_mock(self):
mock_console_handler = mock.Mock()

View File

@ -17,6 +17,8 @@ import os
import mock
from nova.compute import task_states
from nova import exception
from os_win import exceptions as os_win_exc
from hyperv.nova import snapshotops
from hyperv.tests import fake_instance
@ -96,7 +98,7 @@ class SnapshotOpsTestCase(test_base.HyperVBaseTestCase):
mock_reconnect.assert_called_once_with(dest_vhd_path,
base_dest_disk_path)
self._snapshotops._vhdutils.merge_vhd.assert_called_once_with(
dest_vhd_path, base_dest_disk_path)
dest_vhd_path)
mock_save_glance_image.assert_called_once_with(
self.context, mock.sentinel.IMAGE_ID, base_dest_disk_path)
else:
@ -119,3 +121,18 @@ class SnapshotOpsTestCase(test_base.HyperVBaseTestCase):
def test_snapshot_no_base_disk(self):
self._test_snapshot(base_disk_path=None)
@mock.patch.object(snapshotops.SnapshotOps, '_snapshot')
def test_snapshot_instance_not_found(self, mock_snapshot):
mock_instance = fake_instance.fake_instance_obj(self.context)
mock_snapshot.side_effect = os_win_exc.HyperVVMNotFoundException(
vm_name=mock_instance.name)
self.assertRaises(exception.InstanceNotFound,
self._snapshotops.snapshot,
self.context, mock_instance, mock.sentinel.image_id,
mock.sentinel.update_task_state)
mock_snapshot.assert_called_once_with(self.context, mock_instance,
mock.sentinel.image_id,
mock.sentinel.update_task_state)

View File

@ -21,21 +21,21 @@ import mock
from hyperv.nova import utilsfactory
from hyperv.nova import vmutils
from hyperv.nova import volumeutilsv2
from hyperv.nova import vmutilsv2
from hyperv.tests import test
class TestHyperVUtilsFactory(test.NoDBTestCase):
def test_get_class(self):
expected_instance = volumeutilsv2.VolumeUtilsV2()
utilsfactory.utils = mock.MagicMock()
utilsfactory.utils.get_windows_version.return_value = '6.2'
instance = utilsfactory._get_class('volumeutils')
@mock.patch.object(utilsfactory.utils, 'get_windows_version')
def test_get_class(self, mock_get_windows_version):
expected_instance = vmutilsv2.VMUtilsV2()
mock_get_windows_version.return_value = '6.2'
instance = utilsfactory._get_class('vmutils')
self.assertEqual(type(expected_instance), type(instance))
def test_get_class_not_found(self):
utilsfactory.utils = mock.MagicMock()
utilsfactory.utils.get_windows_version.return_value = '5.2'
@mock.patch.object(utilsfactory.utils, 'get_windows_version')
def test_get_class_not_found(self, mock_get_windows_version):
mock_get_windows_version.return_value = '5.2'
self.assertRaises(vmutils.HyperVException, utilsfactory._get_class,
'hostutils')
'vmutils')

View File

@ -1,4 +1,5 @@
# Copyright 2014 Cloudbase Solutions SRL
# Copyright 2015 Cloudbase Solutions Srl
#
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -13,123 +14,22 @@
# License for the specific language governing permissions and limitations
# under the License.
"""
Unit tests for the Hyper-V vif module.
"""
import mock
from nova.network import model as network_model
from nova.tests.unit.objects import test_virtual_interface
from oslo_config import cfg
from hyperv.nova import vif
from hyperv.tests import fake_instance
from hyperv.tests.unit import test_base
CONF = cfg.CONF
class GetVIFDriverTestCase(test_base.HyperVBaseTestCase):
def _test_get_vif_driver(self, expected_driver, vif_type,
network_class='nova.network.api.API',
expected_exception=None):
self.flags(network_api_class=network_class)
if expected_exception:
self.assertRaises(expected_exception,
vif.get_vif_driver,
vif_type)
else:
actual_class = type(vif.get_vif_driver(vif_type))
self.assertEqual(expected_driver, actual_class)
def test_get_vif_driver_neutron(self):
self._test_get_vif_driver(
expected_driver=vif.HyperVNeutronVIFDriver,
vif_type=network_model.VIF_TYPE_OTHER,
network_class='nova.network.neutronv2.api.API')
def test_get_vif_driver_nova(self):
self._test_get_vif_driver(
expected_driver=vif.HyperVNovaNetworkVIFDriver,
vif_type=network_model.VIF_TYPE_OTHER,
network_class='nova.network.api.API')
def test_get_vif_driver_ovs(self):
self._test_get_vif_driver(expected_driver=vif.HyperVOVSVIFDriver,
vif_type=network_model.VIF_TYPE_OVS)
def test_get_vif_driver_invalid_class(self):
self._test_get_vif_driver(
expected_driver=None,
vif_type=network_model.VIF_TYPE_OTHER,
network_class='fake.driver',
expected_exception=TypeError)
class HyperVOVSVIFDriverTestCase(test_base.HyperVBaseTestCase):
class HyperVNovaNetworkVIFDriverTestCase(test_base.HyperVBaseTestCase):
def setUp(self):
super(HyperVOVSVIFDriverTestCase, self).setUp()
self.context = 'fake-context'
self.instance = fake_instance.fake_instance_obj(self.context)
self._vif = vif.HyperVOVSVIFDriver()
self._vif._vmutils = mock.MagicMock()
self._vif._netutils = mock.MagicMock()
self._fake_vif = dict(test_virtual_interface.fake_vif,
network={'bridge': 'fake_bridge'})
super(HyperVNovaNetworkVIFDriverTestCase, self).setUp()
self.vif_driver = vif.HyperVNovaNetworkVIFDriver()
def test_plug(self):
mock_get_external_vswitch = self._vif._netutils.get_external_vswitch
mock_set_nic_connection = self._vif._vmutils.set_nic_connection
self._vif._netutils.vswitch_port_needed.return_value = False
self.flags(vswitch_name=mock.sentinel.vswitch_name, group='hyperv')
fake_vif = {'id': mock.sentinel.fake_id}
self._vif.plug(self.instance, self._fake_vif)
mock_set_nic_connection.assert_called_once_with(
self.instance.name,
self._fake_vif['id'],
mock_get_external_vswitch())
@mock.patch('nova.utils.execute')
@mock.patch('hyperv.nova.ovsutils.check_bridge_has_dev')
def _test_post_start(self, mock_check_bridge_has_dev, mock_execute, calls,
bridge_has_dev=True):
mock_check_bridge_has_dev.return_value = bridge_has_dev
self._vif.post_start(self.instance, self._fake_vif)
mock_execute.assert_has_calls(calls)
def test_post_start_no_dev(self, bridge_has_dev=False):
calls = [
mock.call('ovs-vsctl', '--timeout=120', '--', '--if-exists',
'del-port', self._fake_vif['id'],
'--', 'add-port',
self._fake_vif['network']['bridge'],
self._fake_vif['id'], '--', 'set', 'Interface',
self._fake_vif['id'],
'external-ids:iface-id=%s' % self._fake_vif['id'],
'external-ids:iface-status=active',
'external-ids:attached-mac=%s' %
self._fake_vif['address'],
'external-ids:vm-uuid=%s' % self.instance.uuid)
]
self._test_post_start(calls=calls, bridge_has_dev=bridge_has_dev)
def test_post_start(self):
self._test_post_start(calls=[])
@mock.patch('nova.utils.execute')
def test_unplug(self, mock_execute):
calls = [
mock.call(
'ovs-vsctl', '--timeout=120', '--',
'--if-exists', 'del-port',
self._fake_vif['network']['bridge'],
self._fake_vif['id'])
]
self._vif.unplug(self.instance, self._fake_vif)
mock_execute.assert_has_calls(calls)
self.vif_driver.plug(mock.sentinel.instance, fake_vif)
netutils = self.vif_driver._netutils
netutils.connect_vnic_to_vswitch.assert_called_once_with(
mock.sentinel.vswitch_name, mock.sentinel.fake_id)

View File

@ -22,6 +22,7 @@ from nova.objects import flavor as flavor_obj
from nova.tests.unit.objects import test_flavor
from nova.tests.unit.objects import test_virtual_interface
from nova.virt import hardware
from os_win import exceptions as os_win_exc
from oslo_concurrency import processutils
from oslo_config import cfg
from oslo_utils import fileutils
@ -31,7 +32,6 @@ import six
from hyperv.nova import block_device_manager
from hyperv.nova import constants
from hyperv.nova import vmops
from hyperv.nova import vmutils
from hyperv.nova import volumeops
from hyperv.tests import fake_instance
from hyperv.tests.unit import test_base
@ -134,7 +134,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock_instance = fake_instance.fake_instance_obj(self.context)
mock_instance.root_gb = self.FAKE_SIZE
self.flags(use_cow_images=use_cow_images)
self._vmops._vhdutils.get_vhd_info.return_value = {'MaxInternalSize':
self._vmops._vhdutils.get_vhd_info.return_value = {'VirtualSize':
vhd_size * units.Gi}
self._vmops._vhdutils.get_vhd_format.return_value = vhd_format
root_vhd_internal_size = mock_instance.root_gb * units.Gi
@ -348,7 +348,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock_ephemeral_info)
mock_create_dynamic_vhd.assert_called_once_with('fake_eph_path',
10 * units.Gi, 'vhd')
10 * units.Gi)
@mock.patch.object(block_device_manager.BlockDeviceInfoManager,
'get_boot_order')
@ -406,8 +406,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
self.context, mock_instance, mock_image_meta,
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
mock.sentinel.INFO, block_device_info)
elif fail is vmutils.HyperVException:
self.assertRaises(vmutils.HyperVException, self._vmops.spawn,
elif fail is os_win_exc.HyperVException:
self.assertRaises(os_win_exc.HyperVException, self._vmops.spawn,
self.context, mock_instance, mock_image_meta,
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
mock.sentinel.INFO, block_device_info)
@ -464,7 +464,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
self._test_spawn(exists=False, root_device_info=root_device_info,
block_device_info=block_device_info,
configdrive_required=True,
fail=vmutils.HyperVException)
fail=os_win_exc.HyperVException)
def test_spawn_not_required(self):
root_device_info = mock.sentinel.ROOT_DEV_INFO
@ -475,8 +475,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
def test_spawn_no_admin_permissions(self):
self._vmops._vmutils.check_admin_permissions.side_effect = (
vmutils.HyperVException)
self.assertRaises(vmutils.HyperVException,
os_win_exc.HyperVException)
self.assertRaises(os_win_exc.HyperVException,
self._vmops.spawn,
self.context, mock.DEFAULT, mock.DEFAULT,
[mock.sentinel.FILE], mock.sentinel.PASSWORD,
@ -532,7 +532,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
mock_instance.flavor = flavor
if remotefx is True and vm_gen == constants.VM_GEN_2:
self.assertRaises(vmutils.HyperVException,
self.assertRaises(os_win_exc.HyperVException,
self._vmops.create_instance,
instance=mock_instance,
network_info=[fake_network_info],
@ -923,10 +923,11 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch('hyperv.nova.vmops.VMOps.power_off')
def test_destroy_exception(self, mock_power_off):
mock_instance = fake_instance.fake_instance_obj(self.context)
self._vmops._vmutils.destroy_vm.side_effect = vmutils.HyperVException
self._vmops._vmutils.destroy_vm.side_effect = (
os_win_exc.HyperVException)
self._vmops._vmutils.vm_exists.return_value = True
self.assertRaises(vmutils.HyperVException,
self.assertRaises(os_win_exc.HyperVException,
self._vmops.destroy, mock_instance)
def test_reboot_hard(self):
@ -949,10 +950,11 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch("hyperv.nova.vmops.VMOps._soft_shutdown")
def test_reboot_soft_exception(self, mock_soft_shutdown, mock_power_on):
mock_soft_shutdown.return_value = True
mock_power_on.side_effect = vmutils.HyperVException("Expected failure")
mock_power_on.side_effect = os_win_exc.HyperVException(
"Expected failure")
instance = fake_instance.fake_instance_obj(self.context)
self.assertRaises(vmutils.HyperVException, self._vmops.reboot,
self.assertRaises(os_win_exc.HyperVException, self._vmops.reboot,
instance, {}, vmops.REBOOT_TYPE_SOFT)
mock_soft_shutdown.assert_called_once_with(instance)
@ -983,7 +985,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
instance = fake_instance.fake_instance_obj(self.context)
mock_shutdown_vm = self._vmops._vmutils.soft_shutdown_vm
mock_shutdown_vm.side_effect = vmutils.HyperVException(
mock_shutdown_vm.side_effect = os_win_exc.HyperVException(
"Expected failure.")
result = self._vmops._soft_shutdown(instance, self._FAKE_TIMEOUT)
@ -1071,8 +1073,8 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch("hyperv.nova.vmops.VMOps._soft_shutdown")
def test_power_off_unexisting_instance(self, mock_soft_shutdown):
mock_soft_shutdown.side_effect = (
exception.InstanceNotFound('fake_instance_uuid'))
mock_soft_shutdown.side_effect = os_win_exc.HyperVVMNotFoundException(
vm_name=mock.sentinel.vm_name)
self._test_power_off(timeout=1, set_state_expected=False)
@mock.patch("hyperv.nova.vmops.VMOps._set_vm_state")
@ -1144,8 +1146,10 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
def test_set_vm_state_exception(self):
mock_instance = fake_instance.fake_instance_obj(self.context)
self._vmops._vmutils.set_vm_state.side_effect = vmutils.HyperVException
self.assertRaises(vmutils.HyperVException, self._vmops._set_vm_state,
self._vmops._vmutils.set_vm_state.side_effect = (
os_win_exc.HyperVException)
self.assertRaises(os_win_exc.HyperVException,
self._vmops._set_vm_state,
mock_instance, mock.sentinel.STATE)
def test_get_vm_state(self):
@ -1682,7 +1686,7 @@ class VMOpsTestCase(test_base.HyperVBaseTestCase):
else:
expected_result = image_prop_secure_boot == 'required'
if fake_vm_gen != constants.VM_GEN_2 and expected_result:
self.assertRaises(vmutils.HyperVException,
self.assertRaises(os_win_exc.HyperVException,
self._vmops._requires_secure_boot,
mock_instance, mock_image_meta)
else:

View File

@ -17,6 +17,7 @@
import os
import mock
from os_win import exceptions as os_win_exc
from oslo_config import cfg
from nova import exception
@ -24,8 +25,6 @@ from nova.tests.unit import fake_block_device
from oslo_utils import units
from hyperv.nova import constants
from hyperv.nova import pathutils
from hyperv.nova import vmutils
from hyperv.nova import volumeops
from hyperv.tests.unit import test_base
@ -145,16 +144,15 @@ class VolumeOpsTestCase(test_base.HyperVBaseTestCase):
fake_volume_driver.disconnect_volumes.assert_called_once_with(
block_device_mapping)
def test_ebs_root_in_block_devices(self):
@mock.patch('nova.block_device.volume_in_mapping')
def test_ebs_root_in_block_devices(self, mock_vol_in_mapping):
block_device_info = get_fake_block_dev_info()
response = self._volumeops.ebs_root_in_block_devices(block_device_info)
self._volumeops._volutils.volume_in_mapping.assert_called_once_with(
mock_vol_in_mapping.assert_called_once_with(
self._volumeops._default_root_device, block_device_info)
self.assertEqual(
self._volumeops._volutils.volume_in_mapping.return_value,
response)
self.assertEqual(mock_vol_in_mapping.return_value, response)
def test_get_volume_connector(self):
mock_instance = mock.DEFAULT
@ -316,9 +314,9 @@ class ISCSIVolumeDriverTestCase(test_base.HyperVBaseTestCase):
mock_logout_storage_target,
mock_get_mounted_disk):
connection_info = get_fake_connection_info()
mock_get_mounted_disk.side_effect = vmutils.HyperVException
mock_get_mounted_disk.side_effect = os_win_exc.HyperVException
self.assertRaises(vmutils.HyperVException,
self.assertRaises(os_win_exc.HyperVException,
self._volume_driver.attach_volume, connection_info,
mock.sentinel.instance_name)
mock_logout_storage_target.assert_called_with(mock.sentinel.fake_iqn)
@ -491,7 +489,8 @@ class SMBFSVolumeDriverTestCase(test_base.HyperVBaseTestCase):
super(SMBFSVolumeDriverTestCase, self).setUp()
self._volume_driver = volumeops.SMBFSVolumeDriver()
self._volume_driver._vmutils = mock.MagicMock()
self._volume_driver._pathutils = mock.MagicMock()
self._volume_driver._smbutils = mock.MagicMock()
self._volume_driver._volutils = mock.MagicMock()
@mock.patch.object(volumeops.SMBFSVolumeDriver, 'ensure_share_mounted')
@mock.patch.object(volumeops.SMBFSVolumeDriver, '_get_disk_path')
@ -543,15 +542,14 @@ class SMBFSVolumeDriverTestCase(test_base.HyperVBaseTestCase):
def test_attach_non_existing_image(self, mock_get_disk_path,
mock_ensure_share_mounted):
self._volume_driver._vmutils.attach_drive.side_effect = (
vmutils.HyperVException())
os_win_exc.HyperVException)
self.assertRaises(exception.VolumeAttachFailed,
self._volume_driver.attach_volume,
self._FAKE_CONNECTION_INFO,
mock.sentinel.instance_name)
@mock.patch.object(volumeops.SMBFSVolumeDriver, '_get_disk_path')
@mock.patch.object(pathutils.PathUtils, 'unmount_smb_share')
def test_detach_volume(self, mock_unmount_smb_share, mock_get_disk_path):
def test_detach_volume(self, mock_get_disk_path):
mock_get_disk_path.return_value = (
mock.sentinel.disk_path)
@ -586,9 +584,9 @@ class SMBFSVolumeDriverTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(volumeops.SMBFSVolumeDriver, '_parse_credentials')
def _test_ensure_mounted(self, mock_parse_credentials, is_mounted=False):
mock_mount_smb_share = self._volume_driver._pathutils.mount_smb_share
mock_mount_smb_share = self._volume_driver._smbutils.mount_smb_share
mock_check_smb_mapping = (
self._volume_driver._pathutils.check_smb_mapping)
self._volume_driver._smbutils.check_smb_mapping)
mock_check_smb_mapping.return_value = is_mounted
mock_parse_credentials.return_value = (
self._FAKE_USERNAME, self._FAKE_PASSWORD)
@ -613,7 +611,7 @@ class SMBFSVolumeDriverTestCase(test_base.HyperVBaseTestCase):
def test_disconnect_volumes(self):
mock_unmount_smb_share = (
self._volume_driver._pathutils.unmount_smb_share)
self._volume_driver._smbutils.unmount_smb_share)
block_device_mapping = [
{'connection_info': self._FAKE_CONNECTION_INFO}]
self._volume_driver.disconnect_volumes(block_device_mapping)

View File

@ -5,6 +5,7 @@
pbr>=1.6
Babel>=1.3
os-win>=0.0.6 # Apache-2.0
oslo.config>=2.3.0 # Apache-2.0
oslo.log>=1.8.0 # Apache-2.0
oslo.serialization>=1.4.0 # Apache-2.0