From 52be490ef98ffddc59cade7f482afe03f877d87a Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Tue, 28 Jul 2015 14:18:26 -0700 Subject: [PATCH] libvirt: move LibvirtSMBFSVolumeDriver into it's own module Part of blueprint consolidate-libvirt-fs-volume-drivers Change-Id: I4c7f31e49ec6fb202e24eabe6bcd96d92a748e79 --- .../unit/virt/libvirt/volume/test_smbfs.py | 104 ++++++++++++++++++ .../unit/virt/libvirt/volume/test_volume.py | 84 -------------- nova/virt/libvirt/driver.py | 2 +- nova/virt/libvirt/volume/smbfs.py | 101 +++++++++++++++++ nova/virt/libvirt/volume/volume.py | 75 ------------- 5 files changed, 206 insertions(+), 160 deletions(-) create mode 100644 nova/tests/unit/virt/libvirt/volume/test_smbfs.py create mode 100644 nova/virt/libvirt/volume/smbfs.py diff --git a/nova/tests/unit/virt/libvirt/volume/test_smbfs.py b/nova/tests/unit/virt/libvirt/volume/test_smbfs.py new file mode 100644 index 000000000000..d66be692dfa6 --- /dev/null +++ b/nova/tests/unit/virt/libvirt/volume/test_smbfs.py @@ -0,0 +1,104 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os + +import mock + +from nova.tests.unit.virt.libvirt.volume import test_volume +from nova import utils +from nova.virt.libvirt import utils as libvirt_utils +from nova.virt.libvirt.volume import smbfs + + +class LibvirtSMBFSVolumeDriverTestCase(test_volume.LibvirtVolumeBaseTestCase): + """Tests the libvirt SMBFS volume driver.""" + + def setUp(self): + super(LibvirtSMBFSVolumeDriverTestCase, self).setUp() + self.mnt_base = '/mnt' + self.flags(smbfs_mount_point_base=self.mnt_base, group='libvirt') + + @mock.patch.object(libvirt_utils, 'is_mounted') + def test_libvirt_smbfs_driver(self, mock_is_mounted): + mock_is_mounted.return_value = False + + libvirt_driver = smbfs.LibvirtSMBFSVolumeDriver(self.fake_conn) + export_string = '//192.168.1.1/volumes' + export_mnt_base = os.path.join(self.mnt_base, + utils.get_hash_str(export_string)) + connection_info = {'data': {'export': export_string, + 'name': self.name, + 'options': None}} + libvirt_driver.connect_volume(connection_info, self.disk_info) + libvirt_driver.disconnect_volume(connection_info, "vde") + + expected_commands = [ + ('mkdir', '-p', export_mnt_base), + ('mount', '-t', 'cifs', '-o', 'username=guest', + export_string, export_mnt_base), + ('umount', export_mnt_base)] + self.assertEqual(expected_commands, self.executes) + + def test_libvirt_smbfs_driver_already_mounted(self): + libvirt_driver = smbfs.LibvirtSMBFSVolumeDriver(self.fake_conn) + export_string = '//192.168.1.1/volumes' + export_mnt_base = os.path.join(self.mnt_base, + utils.get_hash_str(export_string)) + connection_info = {'data': {'export': export_string, + 'name': self.name}} + + libvirt_driver.connect_volume(connection_info, self.disk_info) + libvirt_driver.disconnect_volume(connection_info, "vde") + + expected_commands = [ + ('findmnt', '--target', export_mnt_base, + '--source', export_string), + ('umount', export_mnt_base)] + self.assertEqual(expected_commands, self.executes) + + def test_libvirt_smbfs_driver_get_config(self): + libvirt_driver = smbfs.LibvirtSMBFSVolumeDriver(self.fake_conn) + export_string = '//192.168.1.1/volumes' + export_mnt_base = os.path.join(self.mnt_base, + utils.get_hash_str(export_string)) + file_path = os.path.join(export_mnt_base, self.name) + + connection_info = {'data': {'export': export_string, + 'name': self.name, + 'device_path': file_path}} + conf = libvirt_driver.get_config(connection_info, self.disk_info) + tree = conf.format_dom() + self._assertFileTypeEquals(tree, file_path) + + @mock.patch.object(libvirt_utils, 'is_mounted') + def test_libvirt_smbfs_driver_with_opts(self, mock_is_mounted): + mock_is_mounted.return_value = False + + libvirt_driver = smbfs.LibvirtSMBFSVolumeDriver(self.fake_conn) + export_string = '//192.168.1.1/volumes' + options = '-o user=guest,uid=107,gid=105' + export_mnt_base = os.path.join(self.mnt_base, + utils.get_hash_str(export_string)) + connection_info = {'data': {'export': export_string, + 'name': self.name, + 'options': options}} + + libvirt_driver.connect_volume(connection_info, self.disk_info) + libvirt_driver.disconnect_volume(connection_info, "vde") + + expected_commands = [ + ('mkdir', '-p', export_mnt_base), + ('mount', '-t', 'cifs', '-o', 'user=guest,uid=107,gid=105', + export_string, export_mnt_base), + ('umount', export_mnt_base)] + self.assertEqual(expected_commands, self.executes) diff --git a/nova/tests/unit/virt/libvirt/volume/test_volume.py b/nova/tests/unit/virt/libvirt/volume/test_volume.py index 207d62d5bb8d..5f882d163992 100644 --- a/nova/tests/unit/virt/libvirt/volume/test_volume.py +++ b/nova/tests/unit/virt/libvirt/volume/test_volume.py @@ -644,87 +644,3 @@ Setting up iSCSI targets: unused ('umount', export_mnt_base), ] self.assertEqual(expected_commands, self.executes) - - @mock.patch.object(libvirt_utils, 'is_mounted') - def test_libvirt_smbfs_driver(self, mock_is_mounted): - mnt_base = '/mnt' - self.flags(smbfs_mount_point_base=mnt_base, group='libvirt') - mock_is_mounted.return_value = False - - libvirt_driver = volume.LibvirtSMBFSVolumeDriver(self.fake_conn) - export_string = '//192.168.1.1/volumes' - export_mnt_base = os.path.join(mnt_base, - utils.get_hash_str(export_string)) - connection_info = {'data': {'export': export_string, - 'name': self.name, - 'options': None}} - libvirt_driver.connect_volume(connection_info, self.disk_info) - libvirt_driver.disconnect_volume(connection_info, "vde") - - expected_commands = [ - ('mkdir', '-p', export_mnt_base), - ('mount', '-t', 'cifs', '-o', 'username=guest', - export_string, export_mnt_base), - ('umount', export_mnt_base)] - self.assertEqual(expected_commands, self.executes) - - def test_libvirt_smbfs_driver_already_mounted(self): - mnt_base = '/mnt' - self.flags(smbfs_mount_point_base=mnt_base, group='libvirt') - - libvirt_driver = volume.LibvirtSMBFSVolumeDriver(self.fake_conn) - export_string = '//192.168.1.1/volumes' - export_mnt_base = os.path.join(mnt_base, - utils.get_hash_str(export_string)) - connection_info = {'data': {'export': export_string, - 'name': self.name}} - - libvirt_driver.connect_volume(connection_info, self.disk_info) - libvirt_driver.disconnect_volume(connection_info, "vde") - - expected_commands = [ - ('findmnt', '--target', export_mnt_base, - '--source', export_string), - ('umount', export_mnt_base)] - self.assertEqual(expected_commands, self.executes) - - def test_libvirt_smbfs_driver_get_config(self): - mnt_base = '/mnt' - self.flags(smbfs_mount_point_base=mnt_base, group='libvirt') - libvirt_driver = volume.LibvirtSMBFSVolumeDriver(self.fake_conn) - export_string = '//192.168.1.1/volumes' - export_mnt_base = os.path.join(mnt_base, - utils.get_hash_str(export_string)) - file_path = os.path.join(export_mnt_base, self.name) - - connection_info = {'data': {'export': export_string, - 'name': self.name, - 'device_path': file_path}} - conf = libvirt_driver.get_config(connection_info, self.disk_info) - tree = conf.format_dom() - self._assertFileTypeEquals(tree, file_path) - - @mock.patch.object(libvirt_utils, 'is_mounted') - def test_libvirt_smbfs_driver_with_opts(self, mock_is_mounted): - mnt_base = '/mnt' - self.flags(smbfs_mount_point_base=mnt_base, group='libvirt') - mock_is_mounted.return_value = False - - libvirt_driver = volume.LibvirtSMBFSVolumeDriver(self.fake_conn) - export_string = '//192.168.1.1/volumes' - options = '-o user=guest,uid=107,gid=105' - export_mnt_base = os.path.join(mnt_base, - utils.get_hash_str(export_string)) - connection_info = {'data': {'export': export_string, - 'name': self.name, - 'options': options}} - - libvirt_driver.connect_volume(connection_info, self.disk_info) - libvirt_driver.disconnect_volume(connection_info, "vde") - - expected_commands = [ - ('mkdir', '-p', export_mnt_base), - ('mount', '-t', 'cifs', '-o', 'user=guest,uid=107,gid=105', - export_string, export_mnt_base), - ('umount', export_mnt_base)] - self.assertEqual(expected_commands, self.executes) diff --git a/nova/virt/libvirt/driver.py b/nova/virt/libvirt/driver.py index 80a431f7b104..2530e6e3f209 100644 --- a/nova/virt/libvirt/driver.py +++ b/nova/virt/libvirt/driver.py @@ -280,7 +280,7 @@ libvirt_volume_drivers = [ 'rbd=nova.virt.libvirt.volume.volume.LibvirtNetVolumeDriver', 'sheepdog=nova.virt.libvirt.volume.volume.LibvirtNetVolumeDriver', 'nfs=nova.virt.libvirt.volume.volume.LibvirtNFSVolumeDriver', - 'smbfs=nova.virt.libvirt.volume.volume.LibvirtSMBFSVolumeDriver', + 'smbfs=nova.virt.libvirt.volume.smbfs.LibvirtSMBFSVolumeDriver', 'aoe=nova.virt.libvirt.volume.aoe.LibvirtAOEVolumeDriver', 'glusterfs=' 'nova.virt.libvirt.volume.glusterfs.LibvirtGlusterfsVolumeDriver', diff --git a/nova/virt/libvirt/volume/smbfs.py b/nova/virt/libvirt/volume/smbfs.py new file mode 100644 index 000000000000..3df46602b441 --- /dev/null +++ b/nova/virt/libvirt/volume/smbfs.py @@ -0,0 +1,101 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +import re + +from oslo_config import cfg + +from nova import paths +from nova import utils +from nova.virt.libvirt import utils as libvirt_utils +from nova.virt.libvirt.volume import remotefs +from nova.virt.libvirt.volume import volume as libvirt_volume + +volume_opts = [ + cfg.StrOpt('smbfs_mount_point_base', + default=paths.state_path_def('mnt'), + help='Directory where the SMBFS shares are mounted on the ' + 'compute node'), + cfg.StrOpt('smbfs_mount_options', + default='', + help='Mount options passed to the SMBFS client. See ' + 'mount.cifs man page for details. Note that the ' + 'libvirt-qemu uid and gid must be specified.'), + ] + +CONF = cfg.CONF +CONF.register_opts(volume_opts, 'libvirt') + + +class LibvirtSMBFSVolumeDriver(libvirt_volume.LibvirtBaseVolumeDriver): + """Class implements libvirt part of volume driver for SMBFS.""" + + def __init__(self, connection): + super(LibvirtSMBFSVolumeDriver, + self).__init__(connection, is_block_dev=False) + self.username_regex = re.compile( + r"(user(?:name)?)=(?:[^ ,]+\\)?([^ ,]+)") + + def _get_device_path(self, connection_info): + smbfs_share = connection_info['data']['export'] + mount_path = self._get_mount_path(smbfs_share) + volume_path = os.path.join(mount_path, + connection_info['data']['name']) + return volume_path + + def _get_mount_path(self, smbfs_share): + mount_path = os.path.join(CONF.libvirt.smbfs_mount_point_base, + utils.get_hash_str(smbfs_share)) + return mount_path + + def get_config(self, connection_info, disk_info): + """Returns xml for libvirt.""" + conf = super(LibvirtSMBFSVolumeDriver, + self).get_config(connection_info, disk_info) + + conf.source_type = 'file' + conf.driver_cache = 'writethrough' + conf.source_path = connection_info['data']['device_path'] + conf.driver_format = connection_info['data'].get('format', 'raw') + return conf + + def connect_volume(self, connection_info, disk_info): + """Connect the volume.""" + smbfs_share = connection_info['data']['export'] + mount_path = self._get_mount_path(smbfs_share) + + if not libvirt_utils.is_mounted(mount_path, smbfs_share): + mount_options = self._parse_mount_options(connection_info) + remotefs.mount_share(mount_path, smbfs_share, + export_type='cifs', options=mount_options) + + device_path = self._get_device_path(connection_info) + connection_info['data']['device_path'] = device_path + + def disconnect_volume(self, connection_info, disk_dev): + """Disconnect the volume.""" + smbfs_share = connection_info['data']['export'] + mount_path = self._get_mount_path(smbfs_share) + remotefs.unmount_share(mount_path, smbfs_share) + + def _parse_mount_options(self, connection_info): + mount_options = " ".join( + [connection_info['data'].get('options') or '', + CONF.libvirt.smbfs_mount_options]) + + if not self.username_regex.findall(mount_options): + mount_options = mount_options + ' -o username=guest' + else: + # Remove the Domain Name from user name + mount_options = self.username_regex.sub(r'\1=\2', mount_options) + return mount_options.strip(", ").split(' ') diff --git a/nova/virt/libvirt/volume/volume.py b/nova/virt/libvirt/volume/volume.py index 53457846f23e..192f4b3be9ac 100644 --- a/nova/virt/libvirt/volume/volume.py +++ b/nova/virt/libvirt/volume/volume.py @@ -17,7 +17,6 @@ """Volume drivers for libvirt.""" import os -import re from os_brick.initiator import connector from oslo_concurrency import processutils @@ -33,7 +32,6 @@ from nova import paths from nova import utils from nova.virt.libvirt import config as vconfig from nova.virt.libvirt import utils as libvirt_utils -from nova.virt.libvirt.volume import remotefs LOG = logging.getLogger(__name__) @@ -56,15 +54,6 @@ volume_opts = [ cfg.StrOpt('nfs_mount_options', help='Mount options passed to the NFS client. See section ' 'of the nfs man page for details'), - cfg.StrOpt('smbfs_mount_point_base', - default=paths.state_path_def('mnt'), - help='Directory where the SMBFS shares are mounted on the ' - 'compute node'), - cfg.StrOpt('smbfs_mount_options', - default='', - help='Mount options passed to the SMBFS client. See ' - 'mount.cifs man page for details. Note that the ' - 'libvirt-qemu uid and gid must be specified.'), cfg.BoolOpt('iscsi_use_multipath', default=False, help='Use multipath connection of the iSCSI volume'), @@ -417,67 +406,3 @@ class LibvirtNFSVolumeDriver(LibvirtBaseVolumeDriver): LOG.warn(_LW("%s is already mounted"), nfs_share) else: raise - - -class LibvirtSMBFSVolumeDriver(LibvirtBaseVolumeDriver): - """Class implements libvirt part of volume driver for SMBFS.""" - - def __init__(self, connection): - super(LibvirtSMBFSVolumeDriver, - self).__init__(connection, is_block_dev=False) - self.username_regex = re.compile( - r"(user(?:name)?)=(?:[^ ,]+\\)?([^ ,]+)") - - def _get_device_path(self, connection_info): - smbfs_share = connection_info['data']['export'] - mount_path = self._get_mount_path(smbfs_share) - volume_path = os.path.join(mount_path, - connection_info['data']['name']) - return volume_path - - def _get_mount_path(self, smbfs_share): - mount_path = os.path.join(CONF.libvirt.smbfs_mount_point_base, - utils.get_hash_str(smbfs_share)) - return mount_path - - def get_config(self, connection_info, disk_info): - """Returns xml for libvirt.""" - conf = super(LibvirtSMBFSVolumeDriver, - self).get_config(connection_info, disk_info) - - conf.source_type = 'file' - conf.driver_cache = 'writethrough' - conf.source_path = connection_info['data']['device_path'] - conf.driver_format = connection_info['data'].get('format', 'raw') - return conf - - def connect_volume(self, connection_info, disk_info): - """Connect the volume.""" - smbfs_share = connection_info['data']['export'] - mount_path = self._get_mount_path(smbfs_share) - - if not libvirt_utils.is_mounted(mount_path, smbfs_share): - mount_options = self._parse_mount_options(connection_info) - remotefs.mount_share(mount_path, smbfs_share, - export_type='cifs', options=mount_options) - - device_path = self._get_device_path(connection_info) - connection_info['data']['device_path'] = device_path - - def disconnect_volume(self, connection_info, disk_dev): - """Disconnect the volume.""" - smbfs_share = connection_info['data']['export'] - mount_path = self._get_mount_path(smbfs_share) - remotefs.unmount_share(mount_path, smbfs_share) - - def _parse_mount_options(self, connection_info): - mount_options = " ".join( - [connection_info['data'].get('options') or '', - CONF.libvirt.smbfs_mount_options]) - - if not self.username_regex.findall(mount_options): - mount_options = mount_options + ' -o username=guest' - else: - # Remove the Domain Name from user name - mount_options = self.username_regex.sub(r'\1=\2', mount_options) - return mount_options.strip(", ").split(' ')