From 2b6c26158c50bfbe4317a18815c52e4eec0b525f Mon Sep 17 00:00:00 2001 From: Jordan Pittier Date: Fri, 7 Aug 2015 16:14:20 +0200 Subject: [PATCH] ScalityVolume:fix how remote FS mount is detected A new version of Scality Ring doesn't have a 'sys' directory at the root level of the Scality (distributed) filesystem. But the LibvirtScalityVolumeDriver in Nova relies on the presence of this 'sys' directory to detect whether the Scality Filesystem has been properly mounted. This means that LibvirtScalityVolumeDriver doesn't detect that the filesystem was already mounted. Fix this be introducing a more robust way to detect that the remote FS is already mounted, based on what's in /proc/mounts While we are at it, also fix the unit tests because they had bad side effects (they would create a TempDir with a file in it). Change-Id: I0997f123aaed2f2c002c8084d6aab40e2f0dd5b8 Closes-Bug: #1482287 --- .../unit/virt/libvirt/volume/test_scality.py | 84 +++++++++++++++---- nova/virt/libvirt/volume/scality.py | 23 +++-- 2 files changed, 86 insertions(+), 21 deletions(-) diff --git a/nova/tests/unit/virt/libvirt/volume/test_scality.py b/nova/tests/unit/virt/libvirt/volume/test_scality.py index 329f5b01f630..8b490943f805 100644 --- a/nova/tests/unit/virt/libvirt/volume/test_scality.py +++ b/nova/tests/unit/virt/libvirt/volume/test_scality.py @@ -12,8 +12,9 @@ import os -import fixtures +import mock +import nova.exception from nova.tests.unit.virt.libvirt.volume import test_volume from nova.virt.libvirt.volume import scality @@ -21,10 +22,19 @@ from nova.virt.libvirt.volume import scality class LibvirtScalityVolumeDriverTestCase( test_volume.LibvirtVolumeBaseTestCase): - def test_libvirt_scality_driver(self): - tempdir = self.useFixture(fixtures.TempDir()).path - TEST_MOUNT = os.path.join(tempdir, 'fake_mount') - TEST_CONFIG = os.path.join(tempdir, 'fake_config') + def setUp(self): + super(LibvirtScalityVolumeDriverTestCase, self).setUp() + + self.scality_sofs_config = 'fake.conf' + self.scality_sofs_mount_point = '/fake' + self.flags(scality_sofs_config=self.scality_sofs_config, + scality_sofs_mount_point=self.scality_sofs_mount_point, + group='libvirt') + + self.drv = scality.LibvirtScalityVolumeDriver(self.fake_conn) + + @mock.patch('six.moves.urllib.request.urlopen') + def test_connect_volume(self, mock_urlopen): TEST_VOLDIR = 'volumes' TEST_VOLNAME = 'volume_name' TEST_CONN_INFO = { @@ -32,11 +42,9 @@ class LibvirtScalityVolumeDriverTestCase( 'sofs_path': os.path.join(TEST_VOLDIR, TEST_VOLNAME) } } - TEST_VOLPATH = os.path.join(TEST_MOUNT, + TEST_VOLPATH = os.path.join(self.scality_sofs_mount_point, TEST_VOLDIR, TEST_VOLNAME) - open(TEST_CONFIG, "w+").close() - os.makedirs(os.path.join(TEST_MOUNT, 'sys')) def _access_wrapper(path, flags): if path == '/sbin/mount.sofs': @@ -45,16 +53,62 @@ class LibvirtScalityVolumeDriverTestCase( return os.access(path, flags) self.stubs.Set(os, 'access', _access_wrapper) - self.flags(scality_sofs_config=TEST_CONFIG, - scality_sofs_mount_point=TEST_MOUNT, - group='libvirt') - driver = scality.LibvirtScalityVolumeDriver(self.fake_conn) - driver.connect_volume(TEST_CONN_INFO, self.disk_info) - device_path = os.path.join(TEST_MOUNT, + with mock.patch.object(self.drv, '_mount_sofs'): + self.drv.connect_volume(TEST_CONN_INFO, self.disk_info) + + device_path = os.path.join(self.scality_sofs_mount_point, TEST_CONN_INFO['data']['sofs_path']) self.assertEqual(TEST_CONN_INFO['data']['device_path'], device_path) - conf = driver.get_config(TEST_CONN_INFO, self.disk_info) + conf = self.drv.get_config(TEST_CONN_INFO, self.disk_info) tree = conf.format_dom() self._assertFileTypeEquals(tree, TEST_VOLPATH) + + @mock.patch('nova.utils.execute') + def test_mount_sofs_when_sofs_already_mounted(self, mock_execute): + with mock.patch.object(self.drv, '_sofs_is_mounted') as m_is_mounted: + m_is_mounted.return_value = True + + self.drv._mount_sofs() + + mock_execute.assert_called_once_with('mkdir', '-p', + self.scality_sofs_mount_point) + self.assertEqual(1, m_is_mounted.call_count) + + @mock.patch('nova.utils.execute', mock.Mock()) + def test_mount_sofs_when_mount_fails(self): + with mock.patch.object(self.drv, '_sofs_is_mounted') as m_is_mounted: + m_is_mounted.side_effect = [False, False] + + self.assertRaises(nova.exception.NovaException, + self.drv._mount_sofs) + + self.assertEqual(2, m_is_mounted.call_count) + + @mock.patch('nova.utils.execute') + def test_mount_sofs_when_sofs_is_not_mounted(self, mock_execute): + with mock.patch.object(self.drv, '_sofs_is_mounted') as m_is_mounted: + m_is_mounted.side_effect = [False, True] + + self.drv._mount_sofs() + + self.assertEqual(2, m_is_mounted.call_count) + self.assertEqual(2, mock_execute.call_count) + expected_calls = [ + mock.call('mkdir', '-p', self.scality_sofs_mount_point), + mock.call('mount', '-t', 'sofs', self.scality_sofs_config, + self.scality_sofs_mount_point, run_as_root=True) + ] + mock_execute.assert_has_calls(expected_calls) + + def test_sofs_is_mounted_when_sofs_is_not_mounted(self): + mock_open = mock.mock_open(read_data='tmpfs /dev/shm\n') + with mock.patch('io.open', mock_open) as mock_open: + self.assertFalse(self.drv._sofs_is_mounted()) + + def test_sofs_is_mounted_when_sofs_is_mounted(self): + proc_mount = '/dev/fuse ' + self.scality_sofs_mount_point + '\n' + mock_open = mock.mock_open(read_data=proc_mount) + with mock.patch('io.open', mock_open) as mock_open: + self.assertTrue(self.drv._sofs_is_mounted()) diff --git a/nova/virt/libvirt/volume/scality.py b/nova/virt/libvirt/volume/scality.py index 10561e122d1d..2f042a1b180e 100644 --- a/nova/virt/libvirt/volume/scality.py +++ b/nova/virt/libvirt/volume/scality.py @@ -10,6 +10,7 @@ # License for the specific language governing permissions and limitations # under the License. +import io import os from oslo_config import cfg @@ -118,17 +119,27 @@ class LibvirtScalityVolumeDriver(fs.LibvirtBaseFileSystemVolumeDriver): LOG.warn(msg) raise exception.NovaException(msg) + def _sofs_is_mounted(self): + """Detects whether Scality SOFS is already mounted.""" + mount_path = CONF.libvirt.scality_sofs_mount_point.rstrip('/') + with io.open('/proc/mounts') as mounts: + for mount in mounts.readlines(): + parts = mount.split() + if (parts[0].endswith('fuse') and + parts[1].rstrip('/') == mount_path): + return True + return False + def _mount_sofs(self): config = CONF.libvirt.scality_sofs_config mount_path = CONF.libvirt.scality_sofs_mount_point - sysdir = os.path.join(mount_path, 'sys') if not os.path.isdir(mount_path): utils.execute('mkdir', '-p', mount_path) - if not os.path.isdir(sysdir): + if not self._sofs_is_mounted(): utils.execute('mount', '-t', 'sofs', config, mount_path, run_as_root=True) - if not os.path.isdir(sysdir): - msg = _("Cannot mount Scality SOFS, check syslog for errors") - LOG.warn(msg) - raise exception.NovaException(msg) + if not self._sofs_is_mounted(): + msg = _("Cannot mount Scality SOFS, check syslog for errors") + LOG.warn(msg) + raise exception.NovaException(msg)