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:
parent
e155510be7
commit
2b0832ee2b
@ -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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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:
|
||||||
|
@ -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()
|
||||||
|
@ -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. "
|
||||||
|
@ -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()
|
||||||
|
@ -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__)
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
try:
|
||||||
instance_synchronized_snapshot()
|
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 "
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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,7 +122,7 @@ 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):
|
||||||
@ -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,10 +486,10 @@ 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)
|
||||||
|
|
||||||
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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):
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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)
|
||||||
|
@ -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')
|
||||||
|
@ -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)
|
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user