Hyper-V: properly handle UNC instance paths

If the configured instances dir is an UNC path such as
\\ip\share\instances_dir, the HyperV driver fails to query the available
disk size, as it expects a path containing a drive letter.

Also, when attempting to move instance files during live migrations, it
will incorrectly try to build the remote path.

This change addresses those issues and is part of a bigger series,
attempting to fix HyperV shared storage related issues.

Change-Id: Ibdb9f7038bf5078867d64aef7fc63974ed8482af
Partial-Bug: #1565895
This commit is contained in:
Lucian Petrut 2016-06-16 11:41:20 +03:00
parent fd1501bcb6
commit b48ed5217a
4 changed files with 57 additions and 16 deletions

View File

@ -44,6 +44,7 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
self._hostops = hostops.HostOps()
self._hostops._hostutils = mock.MagicMock()
self._hostops._pathutils = mock.MagicMock()
self._hostops._diskutils = mock.MagicMock()
def test_get_cpu_info(self):
mock_processors = mock.MagicMock()
@ -90,13 +91,14 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
self._hostops._hostutils.get_memory_info.assert_called_once_with()
self.assertEqual((2, 1, 1), response)
def test_get_local_hdd_info_gb(self):
def test_get_storage_info_gb(self):
self._hostops._pathutils.get_instances_dir.return_value = ''
self._hostops._hostutils.get_volume_info.return_value = (2 * units.Gi,
1 * units.Gi)
response = self._hostops._get_local_hdd_info_gb()
self._hostops._diskutils.get_disk_capacity.return_value = (
2 * units.Gi, 1 * units.Gi)
response = self._hostops._get_storage_info_gb()
self._hostops._pathutils.get_instances_dir.assert_called_once_with()
self._hostops._hostutils.get_volume_info.assert_called_once_with('')
self._hostops._diskutils.get_disk_capacity.assert_called_once_with('')
self.assertEqual((2, 1, 1), response)
def test_get_hypervisor_version(self):
@ -135,16 +137,16 @@ class HostOpsTestCase(test_base.HyperVBaseTestCase):
@mock.patch.object(hostops.HostOps, '_get_cpu_info')
@mock.patch.object(hostops.HostOps, '_get_memory_info')
@mock.patch.object(hostops.HostOps, '_get_hypervisor_version')
@mock.patch.object(hostops.HostOps, '_get_local_hdd_info_gb')
@mock.patch.object(hostops.HostOps, '_get_storage_info_gb')
@mock.patch('platform.node')
def test_get_available_resource(self, mock_node,
mock_get_local_hdd_info_gb,
mock_get_storage_info_gb,
mock_get_hypervisor_version,
mock_get_memory_info, mock_get_cpu_info,
mock_get_gpu_info):
mock_get_local_hdd_info_gb.return_value = (mock.sentinel.LOCAL_GB,
mock.sentinel.LOCAL_GB_FREE,
mock.sentinel.LOCAL_GB_USED)
mock_get_storage_info_gb.return_value = (mock.sentinel.LOCAL_GB,
mock.sentinel.LOCAL_GB_FREE,
mock.sentinel.LOCAL_GB_USED)
mock_get_memory_info.return_value = (mock.sentinel.MEMORY_MB,
mock.sentinel.MEMORY_MB_FREE,
mock.sentinel.MEMORY_MB_USED)

View File

@ -72,6 +72,44 @@ class PathUtilsTestCase(test_base.HyperVBaseTestCase):
self.fake_instance_name)
self.assertIsNone(configdrive_path)
def test_get_instances_dir_local(self):
self.flags(instances_path=self.fake_instance_dir)
instances_dir = self._pathutils.get_instances_dir()
self.assertEqual(self.fake_instance_dir, instances_dir)
def test_get_instances_dir_remote_instance_share(self):
# The Hyper-V driver allows using a pre-configured share exporting
# the instances dir. The same share name should be used across nodes.
fake_instances_dir_share = 'fake_instances_dir_share'
fake_remote = 'fake_remote'
expected_instance_dir = r'\\%s\%s' % (fake_remote,
fake_instances_dir_share)
self.flags(instances_path_share=fake_instances_dir_share,
group='hyperv')
instances_dir = self._pathutils.get_instances_dir(
remote_server=fake_remote)
self.assertEqual(expected_instance_dir, instances_dir)
def test_get_instances_dir_administrative_share(self):
self.flags(instances_path=r'C:\fake_instance_dir')
fake_remote = 'fake_remote'
expected_instance_dir = r'\\fake_remote\C$\fake_instance_dir'
instances_dir = self._pathutils.get_instances_dir(
remote_server=fake_remote)
self.assertEqual(expected_instance_dir, instances_dir)
def test_get_instances_dir_unc_path(self):
fake_instance_dir = r'\\fake_addr\fake_share\fake_instance_dir'
self.flags(instances_path=fake_instance_dir)
fake_remote = 'fake_remote'
instances_dir = self._pathutils.get_instances_dir(
remote_server=fake_remote)
self.assertEqual(fake_instance_dir, instances_dir)
@mock.patch('os.path.join')
def test_get_instances_sub_dir(self, fake_path_join):

View File

@ -17,7 +17,6 @@
Management class for host operations.
"""
import datetime
import os
import platform
import time
@ -41,6 +40,7 @@ LOG = logging.getLogger(__name__)
class HostOps(object):
def __init__(self):
self._diskutils = utilsfactory.get_diskutils()
self._hostutils = utilsfactory.get_hostutils()
self._pathutils = pathutils.PathUtils()
@ -80,9 +80,10 @@ class HostOps(object):
free_mem_mb = free_mem_kb // 1024
return (total_mem_mb, free_mem_mb, total_mem_mb - free_mem_mb)
def _get_local_hdd_info_gb(self):
drive = os.path.splitdrive(self._pathutils.get_instances_dir())[0]
(size, free_space) = self._hostutils.get_volume_info(drive)
def _get_storage_info_gb(self):
instances_dir = self._pathutils.get_instances_dir()
(size, free_space) = self._diskutils.get_disk_capacity(
instances_dir)
total_gb = size // units.Gi
free_gb = free_space // units.Gi
@ -138,7 +139,7 @@ class HostOps(object):
(total_hdd_gb,
free_hdd_gb,
used_hdd_gb) = self._get_local_hdd_info_gb()
used_hdd_gb) = self._get_storage_info_gb()
cpu_info = self._get_cpu_info()
cpu_topology = cpu_info['topology']

View File

@ -38,7 +38,7 @@ class PathUtils(pathutils.PathUtils):
def get_instances_dir(self, remote_server=None):
local_instance_path = os.path.normpath(CONF.instances_path)
if remote_server:
if remote_server and not local_instance_path.startswith(r'\\'):
if CONF.hyperv.instances_path_share:
path = CONF.hyperv.instances_path_share
else: