Libvirt: Created Nova driver for Quobyte
Driver for Quobyte storage product. Mounts respective Quobyte volume of VM images. This is the Nova part of integrating Quobyte. Corresponding Cinder Driver Blueprint: https://blueprints.launchpad.net/cinder/+spec/quobyte-usp-driver The Cinder driver has been merged into Cinder, it's review can be found at: https://review.openstack.org/#/c/94186/ blueprint quobyte-nova-driver DocImpact: Adding option 'quobyte_mount_point_base' and 'quobyte_client_cfg' Change-Id: Ica1820031f1fc8b66d7ed7fe76ffeb985cf0ef35
This commit is contained in:
parent
bb1a6b6dda
commit
9f7b1b0d99
|
@ -0,0 +1,183 @@
|
|||
# Copyright (c) 2015 Quobyte Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
"""Unit tests for the Quobyte volume driver module."""
|
||||
|
||||
import mock
|
||||
import os
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import fileutils
|
||||
from nova import test
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import quobyte
|
||||
|
||||
|
||||
class QuobyteTestCase(test.NoDBTestCase):
|
||||
|
||||
@mock.patch.object(fileutils, "ensure_tree")
|
||||
@mock.patch.object(utils, "execute")
|
||||
def test_quobyte_mount_volume(self, mock_execute, mock_ensure_tree):
|
||||
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))
|
||||
|
||||
quobyte.mount_volume(quobyte_volume, export_mnt_base)
|
||||
|
||||
mock_ensure_tree.assert_called_once_with(export_mnt_base)
|
||||
expected_commands = [mock.call('mount.quobyte',
|
||||
quobyte_volume,
|
||||
export_mnt_base,
|
||||
check_exit_code=[0, 4])
|
||||
]
|
||||
mock_execute.assert_has_calls(expected_commands)
|
||||
|
||||
@mock.patch.object(fileutils, "ensure_tree")
|
||||
@mock.patch.object(utils, "execute")
|
||||
def test_quobyte_mount_volume_with_config(self,
|
||||
mock_execute,
|
||||
mock_ensure_tree):
|
||||
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))
|
||||
config_file_dummy = "/etc/quobyte/dummy.conf"
|
||||
|
||||
quobyte.mount_volume(quobyte_volume,
|
||||
export_mnt_base,
|
||||
config_file_dummy)
|
||||
|
||||
mock_ensure_tree.assert_called_once_with(export_mnt_base)
|
||||
expected_commands = [mock.call('mount.quobyte',
|
||||
quobyte_volume,
|
||||
export_mnt_base,
|
||||
'-c',
|
||||
config_file_dummy,
|
||||
check_exit_code=[0, 4])
|
||||
]
|
||||
mock_execute.assert_has_calls(expected_commands)
|
||||
|
||||
@mock.patch.object(fileutils, "ensure_tree")
|
||||
@mock.patch.object(utils, "execute",
|
||||
side_effect=(processutils.
|
||||
ProcessExecutionError))
|
||||
def test_quobyte_mount_volume_fails(self, mock_execute, mock_ensure_tree):
|
||||
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))
|
||||
|
||||
self.assertRaises(processutils.ProcessExecutionError,
|
||||
quobyte.mount_volume,
|
||||
quobyte_volume,
|
||||
export_mnt_base)
|
||||
|
||||
@mock.patch.object(utils, "execute")
|
||||
def test_quobyte_umount_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))
|
||||
|
||||
quobyte.umount_volume(export_mnt_base)
|
||||
|
||||
mock_execute.assert_called_once_with('umount.quobyte',
|
||||
export_mnt_base)
|
||||
|
||||
@mock.patch.object(quobyte.LOG, "error")
|
||||
@mock.patch.object(utils, "execute")
|
||||
def test_quobyte_umount_volume_warns(self,
|
||||
mock_execute,
|
||||
mock_debug):
|
||||
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 exec_side_effect(*cmd, **kwargs):
|
||||
exerror = processutils.ProcessExecutionError()
|
||||
exerror.message = "Device or resource busy"
|
||||
raise exerror
|
||||
mock_execute.side_effect = exec_side_effect
|
||||
|
||||
quobyte.umount_volume(export_mnt_base)
|
||||
|
||||
(mock_debug.
|
||||
assert_called_once_with("The Quobyte volume at %s is still in use.",
|
||||
export_mnt_base))
|
||||
|
||||
@mock.patch.object(quobyte.LOG, "exception")
|
||||
@mock.patch.object(utils, "execute",
|
||||
side_effect=(processutils.ProcessExecutionError))
|
||||
def test_quobyte_umount_volume_fails(self,
|
||||
mock_execute,
|
||||
mock_exception):
|
||||
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))
|
||||
|
||||
quobyte.umount_volume(export_mnt_base)
|
||||
|
||||
(mock_exception.
|
||||
assert_called_once_with("Couldn't unmount "
|
||||
"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))
|
||||
|
||||
quobyte.validate_volume(export_mnt_base)
|
||||
|
||||
mock_execute.assert_called_once_with('getfattr',
|
||||
'-n',
|
||||
'quobyte.info',
|
||||
export_mnt_base)
|
||||
|
||||
@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))
|
||||
|
||||
self.assertRaises(exception.NovaException,
|
||||
quobyte.validate_volume,
|
||||
export_mnt_base)
|
||||
|
||||
@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))
|
||||
|
||||
self.assertRaises(exception.NovaException,
|
||||
quobyte.validate_volume,
|
||||
export_mnt_base)
|
|
@ -31,6 +31,7 @@ from nova.tests.unit.virt.libvirt import fake_libvirt_utils
|
|||
from nova.tests.unit.virt.libvirt import fakelibvirt
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import host
|
||||
from nova.virt.libvirt import quobyte
|
||||
from nova.virt.libvirt import utils as libvirt_utils
|
||||
from nova.virt.libvirt import volume
|
||||
|
||||
|
@ -1472,3 +1473,177 @@ Setting up iSCSI targets: unused
|
|||
tree = conf.format_dom()
|
||||
self.assertEqual('file', tree.get('type'))
|
||||
self.assertEqual('fake_serial', tree.find('./serial').text)
|
||||
|
||||
@mock.patch.object(quobyte, 'validate_volume')
|
||||
@mock.patch.object(quobyte, 'mount_volume')
|
||||
@mock.patch.object(libvirt_utils, 'is_mounted', return_value=False)
|
||||
def test_libvirt_quobyte_driver_mount(self,
|
||||
mock_is_mounted,
|
||||
mock_mount_volume,
|
||||
mock_validate_volume
|
||||
):
|
||||
mnt_base = '/mnt'
|
||||
self.flags(quobyte_mount_point_base=mnt_base, group='libvirt')
|
||||
|
||||
libvirt_driver = volume.LibvirtQuobyteVolumeDriver(self.fake_conn)
|
||||
export_string = 'quobyte://192.168.1.1/volume-00001'
|
||||
quobyte_volume = '192.168.1.1/volume-00001'
|
||||
export_mnt_base = os.path.join(mnt_base,
|
||||
utils.get_hash_str(quobyte_volume))
|
||||
file_path = os.path.join(export_mnt_base, self.name)
|
||||
|
||||
connection_info = {'data': {'export': export_string,
|
||||
'name': self.name}}
|
||||
libvirt_driver.connect_volume(connection_info, self.disk_info)
|
||||
|
||||
conf = libvirt_driver.get_config(connection_info, self.disk_info)
|
||||
tree = conf.format_dom()
|
||||
|
||||
self._assertFileTypeEquals(tree, file_path)
|
||||
|
||||
mock_mount_volume.assert_called_once_with(quobyte_volume,
|
||||
export_mnt_base,
|
||||
mock.ANY)
|
||||
mock_validate_volume.assert_called_with(export_mnt_base)
|
||||
|
||||
@mock.patch.object(quobyte, 'validate_volume')
|
||||
@mock.patch.object(quobyte, 'umount_volume')
|
||||
@mock.patch.object(libvirt_utils, 'is_mounted', return_value=True)
|
||||
def test_libvirt_quobyte_driver_umount(self, mock_is_mounted,
|
||||
mock_umount_volume,
|
||||
mock_validate_volume):
|
||||
mnt_base = '/mnt'
|
||||
self.flags(quobyte_mount_point_base=mnt_base, group='libvirt')
|
||||
|
||||
libvirt_driver = volume.LibvirtQuobyteVolumeDriver(self.fake_conn)
|
||||
export_string = 'quobyte://192.168.1.1/volume-00001'
|
||||
quobyte_volume = '192.168.1.1/volume-00001'
|
||||
export_mnt_base = os.path.join(mnt_base,
|
||||
utils.get_hash_str(quobyte_volume))
|
||||
file_path = os.path.join(export_mnt_base, self.name)
|
||||
|
||||
connection_info = {'data': {'export': export_string,
|
||||
'name': self.name}}
|
||||
libvirt_driver.connect_volume(connection_info, self.disk_info)
|
||||
|
||||
conf = libvirt_driver.get_config(connection_info, self.disk_info)
|
||||
tree = conf.format_dom()
|
||||
self._assertFileTypeEquals(tree, file_path)
|
||||
|
||||
libvirt_driver.disconnect_volume(connection_info, "vde")
|
||||
|
||||
mock_validate_volume.assert_called_once_with(export_mnt_base)
|
||||
mock_umount_volume.assert_called_once_with(export_mnt_base)
|
||||
|
||||
@mock.patch.object(quobyte, 'validate_volume')
|
||||
@mock.patch.object(quobyte, 'umount_volume')
|
||||
def test_libvirt_quobyte_driver_already_mounted(self,
|
||||
mock_umount_volume,
|
||||
mock_validate_volume
|
||||
):
|
||||
mnt_base = '/mnt'
|
||||
self.flags(quobyte_mount_point_base=mnt_base, group='libvirt')
|
||||
|
||||
libvirt_driver = volume.LibvirtQuobyteVolumeDriver(self.fake_conn)
|
||||
export_string = 'quobyte://192.168.1.1/volume-00001'
|
||||
quobyte_volume = '192.168.1.1/volume-00001'
|
||||
export_mnt_base = os.path.join(mnt_base,
|
||||
utils.get_hash_str(quobyte_volume))
|
||||
file_path = os.path.join(export_mnt_base, self.name)
|
||||
|
||||
connection_info = {'data': {'export': export_string,
|
||||
'name': self.name}}
|
||||
|
||||
libvirt_driver.connect_volume(connection_info, self.disk_info)
|
||||
|
||||
conf = libvirt_driver.get_config(connection_info, self.disk_info)
|
||||
tree = conf.format_dom()
|
||||
self._assertFileTypeEquals(tree, file_path)
|
||||
libvirt_driver.disconnect_volume(connection_info, "vde")
|
||||
|
||||
expected_commands = [
|
||||
('findmnt', '--target', export_mnt_base,
|
||||
'--source', "quobyte@" + quobyte_volume),
|
||||
('findmnt', '--target', export_mnt_base,
|
||||
'--source', "quobyte@" + quobyte_volume),
|
||||
]
|
||||
self.assertEqual(expected_commands, self.executes)
|
||||
|
||||
mock_umount_volume.assert_called_once_with(export_mnt_base)
|
||||
mock_validate_volume.assert_called_once_with(export_mnt_base)
|
||||
|
||||
@mock.patch.object(quobyte, 'validate_volume')
|
||||
@mock.patch.object(quobyte, 'mount_volume')
|
||||
@mock.patch.object(libvirt_utils, 'is_mounted', return_value=False)
|
||||
def test_libvirt_quobyte_driver_qcow2(self, mock_is_mounted,
|
||||
mock_mount_volume,
|
||||
mock_validate_volume
|
||||
):
|
||||
mnt_base = '/mnt'
|
||||
self.flags(quobyte_mount_point_base=mnt_base, group='libvirt')
|
||||
libvirt_driver = volume.LibvirtQuobyteVolumeDriver(self.fake_conn)
|
||||
export_string = 'quobyte://192.168.1.1/volume-00001'
|
||||
name = 'volume-00001'
|
||||
image_format = 'qcow2'
|
||||
quobyte_volume = '192.168.1.1/volume-00001'
|
||||
|
||||
connection_info = {'data': {'export': export_string,
|
||||
'name': name,
|
||||
'format': image_format}}
|
||||
|
||||
export_mnt_base = os.path.join(mnt_base,
|
||||
utils.get_hash_str(quobyte_volume))
|
||||
|
||||
libvirt_driver.connect_volume(connection_info, self.disk_info)
|
||||
conf = libvirt_driver.get_config(connection_info, self.disk_info)
|
||||
tree = conf.format_dom()
|
||||
self.assertEqual(tree.get('type'), 'file')
|
||||
self.assertEqual(tree.find('./driver').get('type'), 'qcow2')
|
||||
|
||||
(mock_mount_volume.
|
||||
assert_called_once_with('192.168.1.1/volume-00001',
|
||||
export_mnt_base,
|
||||
mock.ANY))
|
||||
mock_validate_volume.assert_called_with(export_mnt_base)
|
||||
|
||||
libvirt_driver.disconnect_volume(connection_info, "vde")
|
||||
|
||||
def test_libvirt_quobyte_driver_mount_non_quobyte_volume(self):
|
||||
mnt_base = '/mnt'
|
||||
self.flags(quobyte_mount_point_base=mnt_base, group='libvirt')
|
||||
|
||||
libvirt_driver = volume.LibvirtQuobyteVolumeDriver(self.fake_conn)
|
||||
export_string = 'quobyte://192.168.1.1/volume-00001'
|
||||
|
||||
connection_info = {'data': {'export': export_string,
|
||||
'name': self.name}}
|
||||
|
||||
def exe_side_effect(*cmd, **kwargs):
|
||||
if cmd == mock.ANY:
|
||||
raise exception.NovaException()
|
||||
|
||||
with mock.patch.object(quobyte,
|
||||
'validate_volume') as mock_execute:
|
||||
mock_execute.side_effect = exe_side_effect
|
||||
self.assertRaises(exception.NovaException,
|
||||
libvirt_driver.connect_volume,
|
||||
connection_info,
|
||||
self.disk_info)
|
||||
|
||||
def test_libvirt_quobyte_driver_normalize_url_with_protocol(self):
|
||||
mnt_base = '/mnt'
|
||||
self.flags(quobyte_mount_point_base=mnt_base, group='libvirt')
|
||||
|
||||
libvirt_driver = volume.LibvirtQuobyteVolumeDriver(self.fake_conn)
|
||||
export_string = 'quobyte://192.168.1.1/volume-00001'
|
||||
self.assertEqual(libvirt_driver._normalize_url(export_string),
|
||||
"192.168.1.1/volume-00001")
|
||||
|
||||
def test_libvirt_quobyte_driver_normalize_url_without_protocol(self):
|
||||
mnt_base = '/mnt'
|
||||
self.flags(quobyte_mount_point_base=mnt_base, group='libvirt')
|
||||
|
||||
libvirt_driver = volume.LibvirtQuobyteVolumeDriver(self.fake_conn)
|
||||
export_string = '192.168.1.1/volume-00001'
|
||||
self.assertEqual(libvirt_driver._normalize_url(export_string),
|
||||
"192.168.1.1/volume-00001")
|
||||
|
|
|
@ -181,6 +181,8 @@ libvirt_opts = [
|
|||
'nova.virt.libvirt.volume.LibvirtScalityVolumeDriver',
|
||||
'gpfs='
|
||||
'nova.virt.libvirt.volume.LibvirtGPFSVolumeDriver',
|
||||
'quobyte='
|
||||
'nova.virt.libvirt.volume.LibvirtQuobyteVolumeDriver',
|
||||
],
|
||||
help='DEPRECATED. Libvirt handlers for remote volumes. '
|
||||
'This option is deprecated and will be removed in the '
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
# Copyright (c) 2015 Quobyte Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
|
||||
from nova import exception as nova_exception
|
||||
from nova.i18n import _
|
||||
from nova.i18n import _LE
|
||||
from nova.i18n import _LI
|
||||
from nova.openstack.common import fileutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
SOURCE_PROTOCOL = 'quobyte'
|
||||
SOURCE_TYPE = 'file'
|
||||
DRIVER_CACHE = 'none'
|
||||
DRIVER_IO = 'native'
|
||||
|
||||
|
||||
def mount_volume(volume, mnt_base, configfile=None):
|
||||
"""Wraps execute calls for mounting a Quobyte volume"""
|
||||
fileutils.ensure_tree(mnt_base)
|
||||
|
||||
command = ['mount.quobyte', volume, mnt_base]
|
||||
if configfile:
|
||||
command.extend(['-c', configfile])
|
||||
|
||||
LOG.debug('Mounting volume %s at mount point %s ...',
|
||||
volume,
|
||||
mnt_base)
|
||||
# Run mount command but do not fail on already mounted exit code
|
||||
utils.execute(*command, check_exit_code=[0, 4])
|
||||
LOG.info(_LI('Mounted volume: %s'), volume)
|
||||
|
||||
|
||||
def umount_volume(mnt_base):
|
||||
"""Wraps execute calls for unmouting a Quobyte volume"""
|
||||
try:
|
||||
utils.execute('umount.quobyte', mnt_base)
|
||||
except processutils.ProcessExecutionError as exc:
|
||||
if 'Device or resource busy' in exc.message:
|
||||
LOG.error(_LE("The Quobyte volume at %s is still in use."),
|
||||
mnt_base)
|
||||
else:
|
||||
LOG.exception(_LE("Couldn't unmount the Quobyte Volume at %s"),
|
||||
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.NovaException(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.NovaException(msg)
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
"""Volume drivers for libvirt."""
|
||||
|
||||
import errno
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
|
@ -31,6 +32,7 @@ import six.moves.urllib.parse as urlparse
|
|||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.i18n import _LE
|
||||
from nova.i18n import _LI
|
||||
from nova.i18n import _LW
|
||||
from nova.openstack.common import log as logging
|
||||
from nova.openstack.common import loopingcall
|
||||
|
@ -38,6 +40,7 @@ from nova import paths
|
|||
from nova.storage import linuxscsi
|
||||
from nova import utils
|
||||
from nova.virt.libvirt import config as vconfig
|
||||
from nova.virt.libvirt import quobyte
|
||||
from nova.virt.libvirt import remotefs
|
||||
from nova.virt.libvirt import utils as libvirt_utils
|
||||
|
||||
|
@ -93,6 +96,12 @@ volume_opts = [
|
|||
default=[],
|
||||
help='Protocols listed here will be accessed directly '
|
||||
'from QEMU. Currently supported protocols: [gluster]'),
|
||||
cfg.StrOpt('quobyte_mount_point_base',
|
||||
default=paths.state_path_def('mnt'),
|
||||
help='Directory where the Quobyte volume is mounted on the '
|
||||
'compute node'),
|
||||
cfg.StrOpt('quobyte_client_cfg',
|
||||
help='Path to a Quobyte Client configuration file.'),
|
||||
cfg.StrOpt('iscsi_transport',
|
||||
default=None,
|
||||
help='The iSCSI transport to use to connect to target in case '
|
||||
|
@ -1351,3 +1360,82 @@ class LibvirtGPFSVolumeDriver(LibvirtBaseVolumeDriver):
|
|||
conf.source_type = "file"
|
||||
conf.source_path = connection_info['data']['device_path']
|
||||
return conf
|
||||
|
||||
|
||||
class LibvirtQuobyteVolumeDriver(LibvirtBaseVolumeDriver):
|
||||
"""Class implements libvirt part of volume driver for Quobyte."""
|
||||
|
||||
def __init__(self, connection):
|
||||
"""Create back-end to Quobyte."""
|
||||
super(LibvirtQuobyteVolumeDriver,
|
||||
self).__init__(connection, is_block_dev=False)
|
||||
|
||||
def get_config(self, connection_info, disk_info):
|
||||
conf = super(LibvirtQuobyteVolumeDriver,
|
||||
self).get_config(connection_info, disk_info)
|
||||
data = connection_info['data']
|
||||
conf.source_protocol = quobyte.SOURCE_PROTOCOL
|
||||
conf.source_type = quobyte.SOURCE_TYPE
|
||||
conf.driver_cache = quobyte.DRIVER_CACHE
|
||||
conf.driver_io = quobyte.DRIVER_IO
|
||||
conf.driver_format = data.get('format', 'raw')
|
||||
|
||||
quobyte_volume = self._normalize_url(data['export'])
|
||||
path = os.path.join(self._get_mount_point_for_share(quobyte_volume),
|
||||
data['name'])
|
||||
conf.source_path = path
|
||||
|
||||
return conf
|
||||
|
||||
@utils.synchronized('connect_volume')
|
||||
def connect_volume(self, connection_info, disk_info):
|
||||
"""Connect the volume."""
|
||||
data = connection_info['data']
|
||||
quobyte_volume = self._normalize_url(data['export'])
|
||||
mount_path = self._get_mount_point_for_share(quobyte_volume)
|
||||
mounted = libvirt_utils.is_mounted(mount_path,
|
||||
quobyte.SOURCE_PROTOCOL
|
||||
+ '@' + quobyte_volume)
|
||||
if mounted:
|
||||
try:
|
||||
os.stat(mount_path)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOTCONN:
|
||||
mounted = False
|
||||
LOG.info(_LI('Fixing previous mount %s which was not'
|
||||
' unmounted correctly.'), mount_path)
|
||||
quobyte.umount_volume(mount_path)
|
||||
|
||||
if not mounted:
|
||||
quobyte.mount_volume(quobyte_volume,
|
||||
mount_path,
|
||||
CONF.libvirt.quobyte_client_cfg)
|
||||
|
||||
quobyte.validate_volume(mount_path)
|
||||
|
||||
@utils.synchronized('connect_volume')
|
||||
def disconnect_volume(self, connection_info, disk_dev):
|
||||
"""Disconnect the volume."""
|
||||
|
||||
quobyte_volume = self._normalize_url(connection_info['data']['export'])
|
||||
mount_path = self._get_mount_point_for_share(quobyte_volume)
|
||||
|
||||
if libvirt_utils.is_mounted(mount_path, 'quobyte@' + quobyte_volume):
|
||||
quobyte.umount_volume(mount_path)
|
||||
else:
|
||||
LOG.info(_LI("Trying to disconnected unmounted volume at %s"),
|
||||
mount_path)
|
||||
|
||||
def _normalize_url(self, export):
|
||||
protocol = quobyte.SOURCE_PROTOCOL + "://"
|
||||
if export.startswith(protocol):
|
||||
export = export[len(protocol):]
|
||||
return export
|
||||
|
||||
def _get_mount_point_for_share(self, quobyte_volume):
|
||||
"""Return mount point for Quobyte volume.
|
||||
|
||||
:param quobyte_volume: Example: storage-host/openstack-volumes
|
||||
"""
|
||||
return os.path.join(CONF.libvirt.quobyte_mount_point_base,
|
||||
utils.get_hash_str(quobyte_volume))
|
||||
|
|
Loading…
Reference in New Issue