Merge "Windows SMBFS: allow mounting vhd/x images"

This commit is contained in:
Zuul 2018-03-12 21:02:53 +00:00 committed by Gerrit Code Review
commit 682e36d664
2 changed files with 76 additions and 5 deletions
os_brick
initiator/windows
tests/windows

@ -22,6 +22,8 @@ from os_brick.remotefs import windows_remotefs as remotefs
from os_brick import utils
# The Windows SMBFS connector expects to receive VHD/x images stored on SMB
# shares, exposed by the Cinder SMBFS driver.
class WindowsSMBFSConnector(win_conn_base.BaseWindowsConnector):
def __init__(self, *args, **kwargs):
super(WindowsSMBFSConnector, self).__init__(*args, **kwargs)
@ -30,10 +32,14 @@ class WindowsSMBFSConnector(win_conn_base.BaseWindowsConnector):
# for the Hyper-C scenario.
self._local_path_for_loopback = kwargs.get('local_path_for_loopback',
False)
self._expect_raw_disk = kwargs.get('expect_raw_disk', False)
self._remotefsclient = remotefs.WindowsRemoteFsClient(
mount_type='smbfs',
*args, **kwargs)
self._smbutils = utilsfactory.get_smbutils()
self._vhdutils = utilsfactory.get_vhdutils()
self._diskutils = utilsfactory.get_diskutils()
@staticmethod
def get_connector_properties(*args, **kwargs):
@ -43,15 +49,39 @@ class WindowsSMBFSConnector(win_conn_base.BaseWindowsConnector):
@utils.trace
def connect_volume(self, connection_properties):
self.ensure_share_mounted(connection_properties)
# This will be a virtual disk image path.
disk_path = self._get_disk_path(connection_properties)
if self._expect_raw_disk:
# The caller expects a direct accessible raw disk. We'll
# mount the image and bring the new disk offline, which will
# allow direct IO, while ensuring that any partiton residing
# on it will be unmounted.
read_only = connection_properties.get('access_mode') == 'ro'
self._vhdutils.attach_virtual_disk(disk_path, read_only=read_only)
raw_disk_path = self._vhdutils.get_virtual_disk_physical_path(
disk_path)
dev_num = self._diskutils.get_device_number_from_device_name(
raw_disk_path)
self._diskutils.set_disk_offline(dev_num)
else:
raw_disk_path = None
device_info = {'type': 'file',
'path': disk_path}
'path': raw_disk_path if self._expect_raw_disk
else disk_path}
return device_info
@utils.trace
def disconnect_volume(self, connection_properties, device_info=None,
force=False, ignore_errors=False):
export_path = self._get_export_path(connection_properties)
disk_path = self._get_disk_path(connection_properties)
# The detach method will silently continue if the disk is
# not attached.
self._vhdutils.detach_virtual_disk(disk_path)
self._remotefsclient.unmount(export_path)
def _get_export_path(self, connection_properties):

@ -25,13 +25,19 @@ from os_brick.tests.windows import test_base
@ddt.ddt
class WindowsSMBFSConnectorTestCase(test_base.WindowsConnectorTestBase):
@mock.patch.object(windows_remotefs, 'WindowsRemoteFsClient')
def setUp(self, mock_remotefs_cls):
def setUp(self):
super(WindowsSMBFSConnectorTestCase, self).setUp()
self._connector = smbfs.WindowsSMBFSConnector()
self._load_connector()
@mock.patch.object(windows_remotefs, 'WindowsRemoteFsClient')
def _load_connector(self, mock_remotefs_cls, *args, **kwargs):
self._connector = smbfs.WindowsSMBFSConnector(*args, **kwargs)
self._remotefs = mock_remotefs_cls.return_value
self._vhdutils = self._connector._vhdutils
self._diskutils = self._connector._diskutils
@mock.patch.object(smbfs.WindowsSMBFSConnector, '_get_disk_path')
@mock.patch.object(smbfs.WindowsSMBFSConnector, 'ensure_share_mounted')
def test_connect_volume(self, mock_ensure_mounted,
@ -44,11 +50,46 @@ class WindowsSMBFSConnectorTestCase(test_base.WindowsConnectorTestBase):
mock_ensure_mounted.assert_called_once_with(mock.sentinel.conn_props)
mock_get_disk_path.assert_called_once_with(mock.sentinel.conn_props)
@ddt.data(True, False)
@mock.patch.object(smbfs.WindowsSMBFSConnector, '_get_disk_path')
@mock.patch.object(smbfs.WindowsSMBFSConnector, 'ensure_share_mounted')
def test_connect_and_mount_volume(self, read_only,
mock_ensure_mounted,
mock_get_disk_path):
self._load_connector(expect_raw_disk=True)
fake_conn_props = dict(access_mode='ro' if read_only else 'rw')
self._vhdutils.get_virtual_disk_physical_path.return_value = (
mock.sentinel.raw_disk_path)
mock_get_disk_path.return_value = mock.sentinel.image_path
device_info = self._connector.connect_volume(fake_conn_props)
expected_info = dict(type='file',
path=mock.sentinel.raw_disk_path)
self.assertEqual(expected_info, device_info)
self._vhdutils.attach_virtual_disk.assert_called_once_with(
mock.sentinel.image_path,
read_only=read_only)
self._vhdutils.get_virtual_disk_physical_path.assert_called_once_with(
mock.sentinel.image_path)
get_dev_num = self._diskutils.get_device_number_from_device_name
get_dev_num.assert_called_once_with(mock.sentinel.raw_disk_path)
self._diskutils.set_disk_offline.assert_called_once_with(
get_dev_num.return_value)
@mock.patch.object(smbfs.WindowsSMBFSConnector, '_get_disk_path')
@mock.patch.object(smbfs.WindowsSMBFSConnector, '_get_export_path')
def test_disconnect_volume(self, mock_get_export_path):
def test_disconnect_volume(self, mock_get_export_path,
mock_get_disk_path):
self._connector.disconnect_volume(mock.sentinel.conn_props,
mock.sentinel.dev_info)
mock_get_disk_path.assert_called_once_with(
mock.sentinel.conn_props)
self._vhdutils.detach_virtual_disk.assert_called_once_with(
mock_get_disk_path.return_value)
self._remotefs.unmount.assert_called_once_with(
mock_get_export_path.return_value)
mock_get_export_path.assert_called_once_with(mock.sentinel.conn_props)