Merge "Windows SMBFS: allow mounting vhd/x images"
This commit is contained in:
commit
682e36d664
os_brick
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user