Skip to content

Commit

Permalink
Merge branch 'xattr-removal'
Browse files Browse the repository at this point in the history
  • Loading branch information
casusbelli committed Mar 23, 2017
2 parents 11d758a + 9117a12 commit bd0242f
Show file tree
Hide file tree
Showing 4 changed files with 957 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ __Important__: Installations that applied the previous [Nova Mitaka external mou
Removal can easily be done via:

patch -p1 -R < /path/to/patchfile

## xattr-removal_patch

Backports of performance optimizations that remove the usage of xattr from the Nova and Cinder drivers and mount Quobyte volumes without xattr support to improve iops.
320 changes: 320 additions & 0 deletions xattr-removal_patch/Mitaka/xattr-removal_mitaka-nova_plain-diff.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
diff --git a/etc/nova/rootwrap.d/compute.filters b/etc/nova/rootwrap.d/compute.filters
index e471848..aeb3170 100644
--- a/etc/nova/rootwrap.d/compute.filters
+++ b/etc/nova/rootwrap.d/compute.filters
@@ -261,5 +261,9 @@
# nova/virt/libvirt/utils.py:
touch: CommandFilter, touch, root

+# nova/virt/libvirt/volume/quobyte.py
+mount.quobyte: CommandFilter, mount.quobyte, root
+umount.quobyte: CommandFilter, umount.quobyte, root
+
# nova/virt/libvirt/volume/vzstorage.py
pstorage-mount: CommandFilter, pstorage-mount, root
diff --git a/nova/tests/unit/virt/libvirt/volume/test_quobyte.py b/nova/tests/unit/virt/libvirt/volume/test_quobyte.py
index a43a79a..2197c78 100644
--- a/nova/tests/unit/virt/libvirt/volume/test_quobyte.py
+++ b/nova/tests/unit/virt/libvirt/volume/test_quobyte.py
@@ -15,12 +15,15 @@
"""Unit tests for the Quobyte volume driver module."""

import os
+import traceback

import mock
from oslo_concurrency import processutils
from oslo_utils import fileutils
+import psutil
+import six

-from nova import exception
+from nova import exception as nova_exception
from nova import test
from nova.tests.unit.virt.libvirt.volume import test_volume
from nova import utils
@@ -30,6 +33,31 @@

class QuobyteTestCase(test.NoDBTestCase):
"""Tests the nova.virt.libvirt.volume.quobyte module utilities."""
+
+ TEST_MNT_POINT = "/tmp/test_mpt"
+
+ def assertRaisesAndMessageMatches(
+ self, excClass, msg, callableObj, *args, **kwargs):
+ """Ensure that the specified exception was raised. """
+
+ caught = False
+ try:
+ callableObj(*args, **kwargs)
+ except Exception as exc:
+ caught = True
+ self.assertIsInstance(exc, excClass,
+ 'Wrong exception caught: %s Stacktrace: %s' %
+ (exc, traceback.format_exc()))
+ self.assertIn(msg, six.text_type(exc))
+
+ if not caught:
+ self.fail('Expected raised exception but nothing caught.')
+
+ def get_mock_partitions(self):
+ mypart = mock.Mock()
+ mypart.device = "quobyte@"
+ mypart.mountpoint = self.TEST_MNT_POINT
+ return [mypart]

@mock.patch.object(fileutils, "ensure_tree")
@mock.patch.object(utils, "execute")
@@ -43,6 +71,7 @@

mock_ensure_tree.assert_called_once_with(export_mnt_base)
expected_commands = [mock.call('mount.quobyte',
+ '--disable-xattrs',
quobyte_volume,
export_mnt_base,
check_exit_code=[0, 4])
@@ -66,6 +95,7 @@

mock_ensure_tree.assert_called_once_with(export_mnt_base)
expected_commands = [mock.call('mount.quobyte',
+ '--disable-xattrs',
quobyte_volume,
export_mnt_base,
'-c',
@@ -141,49 +171,82 @@
"the Quobyte Volume at %s",
export_mnt_base))

- @mock.patch.object(os, "access", return_value=True)
- @mock.patch.object(utils, "execute")
- def test_quobyte_is_valid_volume(self, mock_execute, mock_access):
- mnt_base = '/mnt'
- quobyte_volume = '192.168.1.1/volume-00001'
- export_mnt_base = os.path.join(mnt_base,
- utils.get_hash_str(quobyte_volume))
+ @mock.patch.object(psutil, "disk_partitions")
+ @mock.patch.object(os, "stat")
+ def test_validate_volume_all_good(self, stat_mock, part_mock):
+ part_mock.return_value = self.get_mock_partitions()
+ drv = quobyte

- quobyte.validate_volume(export_mnt_base)
+ def statMockCall(*args):
+ if args[0] == self.TEST_MNT_POINT:
+ stat_result = mock.Mock()
+ stat_result.st_size = 0
+ return stat_result
+ return os.stat(args)
+ stat_mock.side_effect = statMockCall

- mock_execute.assert_called_once_with('getfattr',
- '-n',
- 'quobyte.info',
- export_mnt_base)
+ drv.validate_volume(self.TEST_MNT_POINT)

- @mock.patch.object(utils, "execute",
- side_effect=(processutils.
- ProcessExecutionError))
- def test_quobyte_is_valid_volume_vol_not_valid_volume(self, mock_execute):
- mnt_base = '/mnt'
- quobyte_volume = '192.168.1.1/volume-00001'
- export_mnt_base = os.path.join(mnt_base,
- utils.get_hash_str(quobyte_volume))
+ stat_mock.assert_called_once_with(self.TEST_MNT_POINT)
+ part_mock.assert_called_once_with(all=True)

- self.assertRaises(exception.NovaException,
- quobyte.validate_volume,
- export_mnt_base)
+ @mock.patch.object(psutil, "disk_partitions")
+ @mock.patch.object(os, "stat")
+ def test_validate_volume_mount_not_working(self, stat_mock, part_mock):
+ part_mock.return_value = self.get_mock_partitions()
+ drv = quobyte

- @mock.patch.object(os, "access", return_value=False)
- @mock.patch.object(utils, "execute",
- side_effect=(processutils.
- ProcessExecutionError))
- def test_quobyte_is_valid_volume_vol_no_valid_access(self,
- mock_execute,
- mock_access):
- mnt_base = '/mnt'
- quobyte_volume = '192.168.1.1/volume-00001'
- export_mnt_base = os.path.join(mnt_base,
- utils.get_hash_str(quobyte_volume))
+ def statMockCall(*args):
+ print (args)
+ if args[0] == self.TEST_MNT_POINT:
+ raise nova_exception.InvalidVolume()
+ stat_mock.side_effect = [statMockCall, os.stat]

- self.assertRaises(exception.NovaException,
- quobyte.validate_volume,
- export_mnt_base)
+ self.assertRaises(
+ excClass=nova_exception.InvalidVolume,
+ callableObj=drv.validate_volume,
+ mount_path=self.TEST_MNT_POINT)
+ stat_mock.assert_called_with(self.TEST_MNT_POINT)
+ part_mock.assert_called_once_with(all=True)
+
+ def test_validate_volume_no_mtab_entry(self):
+ msg = ("No matching Quobyte mount entry for %(mpt)s"
+ " could be found for validation in partition list."
+ % {'mpt': self.TEST_MNT_POINT})
+
+ self.assertRaisesAndMessageMatches(
+ nova_exception.InvalidVolume,
+ msg,
+ quobyte.validate_volume,
+ self.TEST_MNT_POINT)
+
+ @mock.patch.object(psutil, "disk_partitions")
+ def test_validate_volume_wrong_mount_type(self, part_mock):
+ mypart = mock.Mock()
+ mypart.device = "not-quobyte"
+ mypart.mountpoint = self.TEST_MNT_POINT
+ part_mock.return_value = [mypart]
+ msg = ("The mount %(mpt)s is not a valid"
+ " Quobyte volume according to partition list."
+ % {'mpt': self.TEST_MNT_POINT})
+
+ self.assertRaisesAndMessageMatches(
+ nova_exception.InvalidVolume,
+ msg,
+ quobyte.validate_volume,
+ self.TEST_MNT_POINT)
+ part_mock.assert_called_once_with(all=True)
+
+ @mock.patch.object(psutil, "disk_partitions")
+ def test_validate_volume_stale_mount(self, part_mock):
+ part_mock.return_value = self.get_mock_partitions()
+
+ # As this uses a local fs the dir size is >0, raising an exception
+ self.assertRaises(
+ nova_exception.InvalidVolume,
+ quobyte.validate_volume,
+ self.TEST_MNT_POINT)
+ part_mock.assert_called_once_with(all=True)


class LibvirtQuobyteVolumeDriverTestCase(
@@ -332,12 +395,12 @@

def exe_side_effect(*cmd, **kwargs):
if cmd == mock.ANY:
- raise exception.NovaException()
+ raise nova_exception.NovaException()

with mock.patch.object(quobyte,
'validate_volume') as mock_execute:
mock_execute.side_effect = exe_side_effect
- self.assertRaises(exception.NovaException,
+ self.assertRaises(nova_exception.NovaException,
libvirt_driver.connect_volume,
connection_info,
self.disk_info)
diff --git a/nova/virt/libvirt/volume/quobyte.py b/nova/virt/libvirt/volume/quobyte.py
index 284a084..97a36ea 100644
--- a/nova/virt/libvirt/volume/quobyte.py
+++ b/nova/virt/libvirt/volume/quobyte.py
@@ -19,6 +19,7 @@
from oslo_concurrency import processutils
from oslo_log import log as logging
from oslo_utils import fileutils
+import psutil
import six

import nova.conf
@@ -26,6 +27,7 @@
from nova.i18n import _
from nova.i18n import _LE
from nova.i18n import _LI
+from nova.i18n import _LW
from nova import utils
from nova.virt.libvirt import utils as libvirt_utils
from nova.virt.libvirt.volume import fs
@@ -44,7 +46,7 @@
"""Wraps execute calls for mounting a Quobyte volume"""
fileutils.ensure_tree(mnt_base)

- command = ['mount.quobyte', volume, mnt_base]
+ command = ['mount.quobyte', '--disable-xattrs', volume, mnt_base]
if configfile:
command.extend(['-c', configfile])

@@ -69,21 +71,46 @@
mnt_base)


-def validate_volume(mnt_base):
- """Wraps execute calls for checking validity of a Quobyte volume"""
- command = ['getfattr', "-n", "quobyte.info", mnt_base]
- try:
- utils.execute(*command)
- except processutils.ProcessExecutionError as exc:
- msg = (_("The mount %(mount_path)s is not a valid"
- " Quobyte volume. Error: %(exc)s")
- % {'mount_path': mnt_base, 'exc': exc})
- raise nova_exception.InternalError(msg)
-
- if not os.access(mnt_base, os.W_OK | os.X_OK):
- msg = (_LE("Volume is not writable. Please broaden the file"
- " permissions. Mount: %s") % mnt_base)
- raise nova_exception.InternalError(msg)
+def validate_volume(mount_path):
+ """Runs a number of tests on the expected Quobyte mount"""
+ partitions = psutil.disk_partitions(all=True)
+ for p in partitions:
+ if mount_path != p.mountpoint:
+ continue
+ if p.device.startswith("quobyte@"):
+ try:
+ statresult = os.stat(mount_path)
+ # Note(kaisers): Quobyte always shows mount points with size 0
+ if statresult.st_size == 0:
+ # client looks healthy
+ if not os.access(mount_path,
+ os.W_OK | os.X_OK):
+ LOG.warning(_LW("Volume is not writable. "
+ "Please broaden the file"
+ " permissions."
+ " Mount: %s"),
+ mount_path)
+ return # we're happy here
+ else:
+ msg = (_("The mount %(mount_path)s is not a "
+ "valid Quobyte volume. Stale mount?")
+ % {'mount_path': mount_path})
+ raise nova_exception.InvalidVolume(msg)
+ except Exception as exc:
+ msg = (_("The mount %(mount_path)s is not a valid"
+ " Quobyte volume. Error: %(exc)s . "
+ " Possibly a Quobyte client crash?")
+ % {'mount_path': mount_path, 'exc': exc})
+ raise nova_exception.InvalidVolume(msg)
+ else:
+ msg = (_("The mount %(mount_path)s is not a valid"
+ " Quobyte volume according to partition list.")
+ % {'mount_path': mount_path})
+ raise nova_exception.InvalidVolume(msg)
+ msg = (_("No matching Quobyte mount entry for %(mount_path)s"
+ " could be found for validation in partition list.")
+ % {'mount_path': mount_path})
+ raise nova_exception.InvalidVolume(msg)


class LibvirtQuobyteVolumeDriver(fs.LibvirtBaseFileSystemVolumeDriver):
@@ -127,8 +154,8 @@

if not mounted:
mount_volume(quobyte_volume,
- mount_path,
- CONF.libvirt.quobyte_client_cfg)
+ mount_path,
+ CONF.libvirt.quobyte_client_cfg)

validate_volume(mount_path)

Loading

0 comments on commit bd0242f

Please sign in to comment.