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. A Hyper-V Nova Compute driver.
""" """
import functools
import platform import platform
import sys
from nova import exception from nova import exception
from nova.virt import driver 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_log import log as logging
from oslo_utils import excutils from oslo_utils import excutils
import six
from hyperv.i18n import _, _LE from hyperv.i18n import _, _LE
from hyperv.nova import eventhandler from hyperv.nova import eventhandler
from hyperv.nova import hostops from hyperv.nova import hostops
from hyperv.nova import hostutils
from hyperv.nova import imagecache from hyperv.nova import imagecache
from hyperv.nova import livemigrationops from hyperv.nova import livemigrationops
from hyperv.nova import migrationops from hyperv.nova import migrationops
@ -40,6 +44,55 @@ from hyperv.nova import volumeops
LOG = logging.getLogger(__name__) 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): class HyperVDriver(driver.ComputeDriver):
capabilities = { capabilities = {
"has_imagecache": True, "has_imagecache": True,
@ -65,7 +118,7 @@ class HyperVDriver(driver.ComputeDriver):
self._imagecache = imagecache.ImageCache() self._imagecache = imagecache.ImageCache()
def _check_minimum_windows_version(self): 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. # the version is of Windows is older than Windows Server 2012 R2.
# Log an error, lettingusers know that this version is not # Log an error, lettingusers know that this version is not
# supported any longer. # supported any longer.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,21 +14,15 @@
# under the License. # under the License.
import os import os
import shutil
import sys
import time import time
if sys.platform == 'win32':
import wmi
from nova import exception from nova import exception
from nova import utils from os_win.utils import pathutils
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from hyperv.i18n import _ from hyperv.i18n import _
from hyperv.nova import constants from hyperv.nova import constants
from hyperv.nova import vmutils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -47,89 +41,13 @@ CONF.register_opts(hyperv_opts, 'hyperv')
CONF.import_opt('instances_path', 'nova.compute.manager') CONF.import_opt('instances_path', 'nova.compute.manager')
ERROR_INVALID_NAME = 123 ERROR_INVALID_NAME = 123
ERROR_DIR_IS_NOT_EMPTY = 145
class PathUtils(object): # NOTE(claudiub): part of the pre-existing PathUtils is nova-specific and
def __init__(self): # it does not belong in the os-win library. In order to ensure the same
self._set_smb_conn() # functionality with the least amount of changes necessary, adding as a mixin
# the os_win.pathutils.PathUtils class into this PathUtils.
@property class PathUtils(pathutils.PathUtils):
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
def get_instances_dir(self, remote_server=None): def get_instances_dir(self, remote_server=None):
local_instance_path = os.path.normpath(CONF.instances_path) local_instance_path = os.path.normpath(CONF.instances_path)
@ -145,25 +63,15 @@ class PathUtils(object):
else: else:
return local_instance_path 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, def _get_instances_sub_dir(self, dir_name, remote_server=None,
create_dir=True, remove_dir=False): create_dir=True, remove_dir=False):
instances_path = self.get_instances_dir(remote_server) instances_path = self.get_instances_dir(remote_server)
path = os.path.join(instances_path, dir_name) path = os.path.join(instances_path, dir_name)
try: try:
if remove_dir: if remove_dir:
self._check_remove_dir(path) self.check_remove_dir(path)
if create_dir: if create_dir:
self._check_create_dir(path) self.check_create_dir(path)
return path return path
except WindowsError as ex: except WindowsError as ex:
if ex.winerror == ERROR_INVALID_NAME: if ex.winerror == ERROR_INVALID_NAME:
@ -261,55 +169,6 @@ class PathUtils(object):
if self.exists(local_log_path): if self.exists(local_log_path):
self.copy(local_log_path, remote_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): def lookup_image_basepath(self, image_name):
# Note: it is possible that the path doesn't exist # Note: it is possible that the path doesn't exist
base_dir = self.get_base_vhd_dir() base_dir = self.get_base_vhd_dir()

View File

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

View File

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

View File

@ -19,13 +19,16 @@ Management class for VM snapshot operations.
import os import os
from nova.compute import task_states from nova.compute import task_states
from nova import exception
from nova.image import glance from nova.image import glance
from nova import utils 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_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from hyperv.i18n import _LW from hyperv.i18n import _LW
from hyperv.nova import utilsfactory from hyperv.nova import pathutils
CONF = cfg.CONF CONF = cfg.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -33,7 +36,7 @@ LOG = logging.getLogger(__name__)
class SnapshotOps(object): class SnapshotOps(object):
def __init__(self): def __init__(self):
self._pathutils = utilsfactory.get_pathutils() self._pathutils = pathutils.PathUtils()
self._vmutils = utilsfactory.get_vmutils() self._vmutils = utilsfactory.get_vmutils()
self._vhdutils = utilsfactory.get_vhdutils() self._vhdutils = utilsfactory.get_vhdutils()
@ -54,7 +57,11 @@ class SnapshotOps(object):
def instance_synchronized_snapshot(): def instance_synchronized_snapshot():
self._snapshot(context, instance, image_id, update_task_state) 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): def _snapshot(self, context, instance, image_id, update_task_state):
"""Create snapshot from a running VM instance.""" """Create snapshot from a running VM instance."""
@ -103,11 +110,9 @@ class SnapshotOps(object):
self._vhdutils.reconnect_parent_vhd(dest_vhd_path, self._vhdutils.reconnect_parent_vhd(dest_vhd_path,
dest_base_disk_path) dest_base_disk_path)
LOG.debug("Merging base disk %(dest_base_disk_path)s and " LOG.debug("Merging diff disk %s into its parent.",
"diff disk %(dest_vhd_path)s", dest_vhd_path)
{'dest_base_disk_path': dest_base_disk_path, self._vhdutils.merge_vhd(dest_vhd_path)
'dest_vhd_path': dest_vhd_path})
self._vhdutils.merge_vhd(dest_vhd_path, dest_base_disk_path)
image_vhd_path = dest_base_disk_path image_vhd_path = dest_base_disk_path
LOG.debug("Updating Glance image %(image_id)s with content from " 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 vmutils
from hyperv.nova import volumeutils from hyperv.nova import volumeutils
hyper_opts = [
cfg.BoolOpt('force_volumeutils_v1',
default=False,
help='Force V1 volume utility class'),
]
CONF = cfg.CONF CONF = cfg.CONF
CONF.register_opts(hyper_opts, 'hyperv')
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
CONF.import_group('hyperv', 'os_win.utilsfactory')
utils = hostutils.HostUtils() utils = hostutils.HostUtils()

View File

@ -18,11 +18,11 @@ import abc
from nova.i18n import _ from nova.i18n import _
from nova.network import model as network_model from nova.network import model as network_model
from os_win import utilsfactory
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from hyperv.nova import ovsutils from hyperv.nova import ovsutils
from hyperv.nova import utilsfactory
hyperv_opts = [ hyperv_opts = [
@ -62,22 +62,11 @@ class HyperVNovaNetworkVIFDriver(HyperVBaseVIFDriver):
"""Nova network VIF driver.""" """Nova network VIF driver."""
def __init__(self): def __init__(self):
self._vmutils = utilsfactory.get_vmutils()
self._netutils = utilsfactory.get_networkutils() self._netutils = utilsfactory.get_networkutils()
def plug(self, instance, vif): def plug(self, instance, vif):
vswitch_path = self._netutils.get_external_vswitch( self._netutils.connect_vnic_to_vswitch(CONF.hyperv.vswitch_name,
CONF.hyperv.vswitch_name) vif['id'])
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)
class HyperVOVSVIFDriver(HyperVNovaNetworkVIFDriver): class HyperVOVSVIFDriver(HyperVNovaNetworkVIFDriver):

View File

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

View File

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

View File

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

View File

@ -15,9 +15,10 @@
# under the License. # under the License.
import mock import mock
from os_win import utilsfactory
from six.moves import builtins from six.moves import builtins
from hyperv.nova import utilsfactory from hyperv.nova import utilsfactory as old_utilsfactory
from hyperv.tests import test from hyperv.tests import test
@ -29,15 +30,16 @@ class HyperVBaseTestCase(test.NoDBTestCase):
wmi_patcher = mock.patch.object(builtins, 'wmi', create=True, wmi_patcher = mock.patch.object(builtins, 'wmi', create=True,
new=self._mock_wmi) new=self._mock_wmi)
platform_patcher = mock.patch('sys.platform', 'win32') platform_patcher = mock.patch('sys.platform', 'win32')
hostutils_patcher = mock.patch.object(utilsfactory, 'utils') utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class')
old_utilsfactory_patcher = mock.patch.object(old_utilsfactory,
hostutils_patcher = mock.patch.multiple(utilsfactory, '_get_class')
_get_class=mock.DEFAULT)
platform_patcher.start() platform_patcher.start()
wmi_patcher.start() wmi_patcher.start()
hostutils_patcher.start() utilsfactory_patcher.start()
old_utilsfactory_patcher.start()
self.addCleanup(wmi_patcher.stop) self.addCleanup(wmi_patcher.stop)
self.addCleanup(platform_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 import mock
from nova import exception from nova import exception
from nova import safe_utils
from nova.virt import driver as base_driver from nova.virt import driver as base_driver
from os_win import exceptions as os_win_exc
from hyperv.nova import driver from hyperv.nova import driver
from hyperv.tests.unit import test_base from hyperv.tests.unit import test_base
@ -47,16 +49,45 @@ class HyperVDriverTestCase(test_base.HyperVBaseTestCase):
self.driver._serialconsoleops = mock.MagicMock() self.driver._serialconsoleops = mock.MagicMock()
self.driver._imagecache = mock.MagicMock() self.driver._imagecache = mock.MagicMock()
@mock.patch.object(driver.hostutils.HostUtils, 'check_min_windows_version') @mock.patch.object(driver.utilsfactory, 'get_hostutils')
def test_check_minimum_windows_version(self, mock_check_min_win_version): def test_check_minimum_windows_version(self, mock_get_hostutils):
mock_check_min_win_version.return_value = False mock_hostutils = mock_get_hostutils.return_value
mock_hostutils.check_min_windows_version.return_value = False
self.assertRaises(exception.HypervisorTooOld, self.assertRaises(exception.HypervisorTooOld,
self.driver._check_minimum_windows_version) self.driver._check_minimum_windows_version)
def test_public_api_signatures(self): def test_public_api_signatures(self):
self.assertPublicAPISignatures(base_driver.ComputeDriver(None), # NOTE(claudiub): wrapped functions do not keep the same signature in
self.driver) # 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): def test_need_legacy_block_device_info(self):
self.assertFalse(self.driver.need_legacy_block_device_info) self.assertFalse(self.driver.need_legacy_block_device_info)

View File

@ -16,11 +16,11 @@
import eventlet import eventlet
import mock 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 constants
from hyperv.nova import eventhandler from hyperv.nova import eventhandler
from hyperv.nova import utilsfactory
from hyperv.tests.unit import test_base from hyperv.tests.unit import test_base
@ -139,7 +139,8 @@ class EventHandlerTestCase(test_base.HyperVBaseTestCase):
side_effect = (mock.sentinel.instance_uuid side_effect = (mock.sentinel.instance_uuid
if not missing_uuid else None, ) if not missing_uuid else None, )
else: 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 = self._event_handler._vmutils.get_instance_uuid
mock_get_uuid.side_effect = side_effect mock_get_uuid.side_effect = side_effect

View File

@ -47,6 +47,7 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
self._hostops = hostops.HostOps() self._hostops = hostops.HostOps()
self._hostops._api = mock.MagicMock() self._hostops._api = mock.MagicMock()
self._hostops._vmops = mock.MagicMock() self._hostops._vmops = mock.MagicMock()
self._hostops._pathutils = mock.MagicMock()
def test_get_cpu_info(self): def test_get_cpu_info(self):
mock_processors = mock.MagicMock() 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') @mock.patch.object(imagecache.ImageCache, '_get_root_vhd_size_gb')
def test_resize_and_cache_vhd_smaller(self, mock_get_vhd_size_gb): def test_resize_and_cache_vhd_smaller(self, mock_get_vhd_size_gb):
self.imagecache._vhdutils.get_vhd_info.return_value = { self.imagecache._vhdutils.get_vhd_size.return_value = {
'MaxInternalSize': (self.FAKE_VHD_SIZE_GB + 1) * units.Gi 'VirtualSize': (self.FAKE_VHD_SIZE_GB + 1) * units.Gi
} }
mock_get_vhd_size_gb.return_value = self.FAKE_VHD_SIZE_GB mock_get_vhd_size_gb.return_value = self.FAKE_VHD_SIZE_GB
mock_internal_vhd_size = ( mock_internal_vhd_size = (
@ -63,7 +63,7 @@ class ImageCacheTestCase(test_base.HyperVBaseTestCase):
mock.sentinel.instance, mock.sentinel.instance,
mock.sentinel.vhd_path) 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.sentinel.vhd_path)
mock_get_vhd_size_gb.assert_called_once_with(mock.sentinel.instance) mock_get_vhd_size_gb.assert_called_once_with(mock.sentinel.instance)
mock_internal_vhd_size.assert_called_once_with( 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' fake_rescue_image_id = 'fake_rescue_image_id'
self.imagecache._vhdutils.get_vhd_info.return_value = { self.imagecache._vhdutils.get_vhd_info.return_value = {
'MaxInternalSize': self.instance.root_gb + 1} 'VirtualSize': self.instance.root_gb + 1}
(expected_path, (expected_path,
expected_vhd_path) = self._prepare_get_cached_image( expected_vhd_path) = self._prepare_get_cached_image(
rescue_image_id=fake_rescue_image_id, rescue_image_id=fake_rescue_image_id,

View File

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

View File

@ -16,11 +16,11 @@ import os
import mock import mock
from nova import exception from nova import exception
from os_win import exceptions as os_win_exc
from oslo_utils import units from oslo_utils import units
from hyperv.nova import constants from hyperv.nova import constants
from hyperv.nova import migrationops from hyperv.nova import migrationops
from hyperv.nova import vmutils
from hyperv.tests import fake_instance from hyperv.tests import fake_instance
from hyperv.tests.unit import test_base 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, recon_parent_vhd.assert_called_once_with(fake_diff_vhd_path,
base_vhd_copy_path) base_vhd_copy_path)
self._migrationops._vhdutils.merge_vhd.assert_called_once_with( 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( self._migrationops._pathutils.rename.assert_called_once_with(
base_vhd_copy_path, fake_diff_vhd_path) base_vhd_copy_path, fake_diff_vhd_path)
@ -327,10 +327,10 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
os.path.basename(fake_base_vhd_path)) os.path.basename(fake_base_vhd_path))
self._migrationops._vhdutils.reconnect_parent_vhd.side_effect = ( self._migrationops._vhdutils.reconnect_parent_vhd.side_effect = (
vmutils.HyperVException) os_win_exc.HyperVException)
self._migrationops._pathutils.exists.return_value = True self._migrationops._pathutils.exists.return_value = True
self.assertRaises(vmutils.HyperVException, self.assertRaises(os_win_exc.HyperVException,
self._migrationops._merge_base_vhd, self._migrationops._merge_base_vhd,
fake_diff_vhd_path, fake_base_vhd_path) fake_diff_vhd_path, fake_base_vhd_path)
self._migrationops._pathutils.exists.assert_called_once_with( self._migrationops._pathutils.exists.assert_called_once_with(
@ -341,7 +341,7 @@ class MigrationOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(migrationops.MigrationOps, '_resize_vhd') @mock.patch.object(migrationops.MigrationOps, '_resize_vhd')
def test_check_resize_vhd(self, mock_resize_vhd): def test_check_resize_vhd(self, mock_resize_vhd):
self._migrationops._check_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) new_size=2)
mock_resize_vhd.assert_called_once_with(mock.sentinel.vhd_path, 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.assertRaises(exception.CannotResizeDisk,
self._migrationops._check_resize_vhd, self._migrationops._check_resize_vhd,
mock.sentinel.vhd_path, mock.sentinel.vhd_path,
{'MaxInternalSize': 1}, 0) {'VirtualSize': 1}, 0)
@mock.patch.object(migrationops.MigrationOps, '_merge_base_vhd') @mock.patch.object(migrationops.MigrationOps, '_merge_base_vhd')
def test_resize_vhd(self, mock_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 constants
from hyperv.nova import pathutils from hyperv.nova import pathutils
from hyperv.nova import vmutils
from hyperv.tests.unit import test_base from hyperv.tests.unit import test_base
@ -36,45 +35,6 @@ class PathUtilsTestCase(test_base.HyperVBaseTestCase):
self._pathutils = pathutils.PathUtils() self._pathutils = pathutils.PathUtils()
self._pathutils._smb_conn_attr = mock.MagicMock() 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): def _mock_lookup_configdrive_path(self, ext, rescue=False):
self._pathutils.get_instance_dir = mock.MagicMock( self._pathutils.get_instance_dir = mock.MagicMock(
return_value=self.fake_instance_dir) return_value=self.fake_instance_dir)
@ -113,81 +73,6 @@ class PathUtilsTestCase(test_base.HyperVBaseTestCase):
self.fake_instance_name) self.fake_instance_name)
self.assertIsNone(configdrive_path) 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') @mock.patch('os.path.join')
def test_get_instances_sub_dir(self, fake_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_dir_name = "fake_dir_name"
fake_windows_error = WindowsError 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)) side_effect=WindowsError(pathutils.ERROR_INVALID_NAME))
with mock.patch.object(builtins, 'WindowsError', with mock.patch.object(builtins, 'WindowsError',
fake_windows_error, create=True): fake_windows_error, create=True):

View File

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

View File

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

View File

@ -17,6 +17,8 @@ import os
import mock import mock
from nova.compute import task_states 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.nova import snapshotops
from hyperv.tests import fake_instance from hyperv.tests import fake_instance
@ -96,7 +98,7 @@ class SnapshotOpsTestCase(test_base.HyperVBaseTestCase):
mock_reconnect.assert_called_once_with(dest_vhd_path, mock_reconnect.assert_called_once_with(dest_vhd_path,
base_dest_disk_path) base_dest_disk_path)
self._snapshotops._vhdutils.merge_vhd.assert_called_once_with( 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( mock_save_glance_image.assert_called_once_with(
self.context, mock.sentinel.IMAGE_ID, base_dest_disk_path) self.context, mock.sentinel.IMAGE_ID, base_dest_disk_path)
else: else:
@ -119,3 +121,18 @@ class SnapshotOpsTestCase(test_base.HyperVBaseTestCase):
def test_snapshot_no_base_disk(self): def test_snapshot_no_base_disk(self):
self._test_snapshot(base_disk_path=None) 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 utilsfactory
from hyperv.nova import vmutils from hyperv.nova import vmutils
from hyperv.nova import volumeutilsv2 from hyperv.nova import vmutilsv2
from hyperv.tests import test from hyperv.tests import test
class TestHyperVUtilsFactory(test.NoDBTestCase): class TestHyperVUtilsFactory(test.NoDBTestCase):
def test_get_class(self): @mock.patch.object(utilsfactory.utils, 'get_windows_version')
expected_instance = volumeutilsv2.VolumeUtilsV2() def test_get_class(self, mock_get_windows_version):
utilsfactory.utils = mock.MagicMock() expected_instance = vmutilsv2.VMUtilsV2()
utilsfactory.utils.get_windows_version.return_value = '6.2' mock_get_windows_version.return_value = '6.2'
instance = utilsfactory._get_class('volumeutils') instance = utilsfactory._get_class('vmutils')
self.assertEqual(type(expected_instance), type(instance)) self.assertEqual(type(expected_instance), type(instance))
def test_get_class_not_found(self): @mock.patch.object(utilsfactory.utils, 'get_windows_version')
utilsfactory.utils = mock.MagicMock() def test_get_class_not_found(self, mock_get_windows_version):
utilsfactory.utils.get_windows_version.return_value = '5.2' mock_get_windows_version.return_value = '5.2'
self.assertRaises(vmutils.HyperVException, utilsfactory._get_class, 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. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # License for the specific language governing permissions and limitations
# under the License. # under the License.
"""
Unit tests for the Hyper-V vif module.
"""
import mock 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.nova import vif
from hyperv.tests import fake_instance
from hyperv.tests.unit import test_base 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): def setUp(self):
super(HyperVOVSVIFDriverTestCase, self).setUp() super(HyperVNovaNetworkVIFDriverTestCase, self).setUp()
self.vif_driver = vif.HyperVNovaNetworkVIFDriver()
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'})
def test_plug(self): def test_plug(self):
mock_get_external_vswitch = self._vif._netutils.get_external_vswitch self.flags(vswitch_name=mock.sentinel.vswitch_name, group='hyperv')
mock_set_nic_connection = self._vif._vmutils.set_nic_connection fake_vif = {'id': mock.sentinel.fake_id}
self._vif._netutils.vswitch_port_needed.return_value = False
self._vif.plug(self.instance, self._fake_vif) self.vif_driver.plug(mock.sentinel.instance, fake_vif)
netutils = self.vif_driver._netutils
mock_set_nic_connection.assert_called_once_with( netutils.connect_vnic_to_vswitch.assert_called_once_with(
self.instance.name, mock.sentinel.vswitch_name, mock.sentinel.fake_id)
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)

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

View File

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

View File

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