+++ /dev/null
-# Copyright 2014 OpenStack Foundation
-# 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 collections
-
-import mock
-from oslo_log import log as logging
-from oslo_utils import importutils
-from oslo_utils import timeutils
-
-from cinder import context
-from cinder import test
-from cinder.volume import configuration as conf
-
-
-class mock_dbus():
- def __init__(self):
- pass
-
- @staticmethod
- def Array(defaults, signature=None):
- return defaults
-
-
-class mock_dm_utils():
-
- @staticmethod
- def dict_to_aux_props(x):
- return x
-
-
-class mock_dm_const():
-
- TQ_GET_PATH = "get_path"
-
-
-class mock_dm_exc():
-
- DM_SUCCESS = 0
- DM_EEXIST = 1
- DM_ENOENT = 2
- DM_ERROR = 1000
-
- pass
-
-
-import sys
-sys.modules['dbus'] = mock_dbus
-sys.modules['drbdmanage'] = collections.namedtuple(
- 'module', ['consts', 'exceptions', 'utils'])
-sys.modules['drbdmanage.utils'] = collections.namedtuple(
- 'module', ['dict_to_aux_props'])
-sys.modules['drbdmanage.consts'] = collections.namedtuple(
- 'module', [])
-sys.modules['drbdmanage.exceptions'] = collections.namedtuple(
- 'module', ['DM_EEXIST'])
-
-
-from cinder.volume.drivers import drbdmanagedrv
-
-
-LOG = logging.getLogger(__name__)
-
-
-def create_configuration():
- configuration = mock.MockObject(conf.Configuration)
- configuration.san_is_local = False
- configuration.append_config_values(mock.IgnoreArg())
- return configuration
-
-
-class DrbdManageFakeDriver():
-
- resources = {}
-
- def __init__(self):
- self.calls = []
-
- def list_resources(self, res, serial, prop, req):
- self.calls.append(["list_resources", res, prop, req])
- if 'cinder-id' in prop and prop['cinder-id'].startswith("deadbeef"):
- return ([mock_dm_exc.DM_ENOENT, "none", []],
- [])
- else:
- return ([[mock_dm_exc.DM_SUCCESS, "ACK", []]],
- [("res", dict(prop))])
-
- def create_resource(self, res, props):
- self.calls.append(["create_resource", res, props])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []]]
-
- def create_volume(self, res, size, props):
- self.calls.append(["create_volume", res, size, props])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []]]
-
- def auto_deploy(self, res, red, delta, site_clients):
- self.calls.append(["auto_deploy", res, red, delta, site_clients])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []] * red]
-
- def list_volumes(self, res, ser, prop, req):
- self.calls.append(["list_volumes", res, ser, prop, req])
- if 'cinder-id' in prop and prop['cinder-id'].startswith("deadbeef"):
- return ([mock_dm_exc.DM_ENOENT, "none", []],
- [])
- else:
- return ([[mock_dm_exc.DM_SUCCESS, "ACK", []]],
- [("res", dict(), [(2, dict(prop))])
- ])
-
- def remove_volume(self, res, nr, force):
- self.calls.append(["remove_volume", res, nr, force])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []]]
-
- def text_query(self, cmd):
- self.calls.append(["text_query", cmd])
- if cmd[0] == mock_dm_const.TQ_GET_PATH:
- return ([(mock_dm_exc.DM_SUCCESS, "ack", [])], ['/dev/drbd0'])
- return ([(mock_dm_exc.DM_ERROR, 'unknown command', [])], [])
-
- def list_assignments(self, nodes, res, ser, prop, req):
- self.calls.append(["list_assignments", nodes, res, ser, prop, req])
- if 'cinder-id' in prop and prop['cinder-id'].startswith("deadbeef"):
- return ([mock_dm_exc.DM_ENOENT, "none", []],
- [])
- else:
- return ([[mock_dm_exc.DM_SUCCESS, "ACK", []]],
- [("node", "res", dict(), [(2, dict(prop))])
- ])
-
- def create_snapshot(self, res, snap, nodes, props):
- self.calls.append(["create_snapshot", res, snap, nodes, props])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []]]
-
- def list_snapshots(self, res, sn, prop, req):
- self.calls.append(["list_snapshots", res, sn, prop, req])
- if 'cinder-id' in prop and prop['cinder-id'].startswith("deadbeef"):
- return ([mock_dm_exc.DM_ENOENT, "none", []],
- [])
- else:
- return ([[mock_dm_exc.DM_SUCCESS, "ACK", []]],
- [("res", [("snap", dict(prop))])
- ])
-
- def remove_snapshot(self, res, snap, force):
- self.calls.append(["remove_snapshot", res, snap, force])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []]]
-
- def resize_volume(self, res, vol, ser, size, delta):
- self.calls.append(["resize_volume", res, vol, ser, size, delta])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []]]
-
- def restore_snapshot(self, res, snap, new, rprop, vprops):
- self.calls.append(["restore_snapshot", res, snap, new, rprop, vprops])
- return [[mock_dm_exc.DM_SUCCESS, "ack", []]]
-
-
-class DrbdManageTestCase(test.TestCase):
-
- def setUp(self):
- self.ctxt = context.get_admin_context()
- self._mock = mock.Mock()
- self.configuration = mock.Mock(conf.Configuration)
- self.configuration.san_is_local = True
- self.configuration.reserved_percentage = 1
-
- super(DrbdManageTestCase, self).setUp()
-
- self.stubs.Set(importutils, 'import_object',
- self.fake_import_object)
- self.stubs.Set(drbdmanagedrv.DrbdManageDriver,
- 'call_or_reconnect',
- self.fake_issue_dbus_call)
- self.stubs.Set(drbdmanagedrv.DrbdManageDriver,
- 'dbus_connect',
- self.fake_issue_dbus_connect)
-
- sys.modules['cinder.volume.drivers.drbdmanagedrv'].dm_const \
- = mock_dm_const
- sys.modules['cinder.volume.drivers.drbdmanagedrv'].dm_utils \
- = mock_dm_utils
- sys.modules['cinder.volume.drivers.drbdmanagedrv'].dm_exc \
- = mock_dm_exc
-
- self.configuration.safe_get = lambda x: 'fake'
-
- # Infrastructure
- def fake_import_object(self, what, configuration, db, executor):
- return None
-
- def fake_issue_dbus_call(self, fn, *args):
- return apply(fn, args)
-
- def fake_issue_dbus_connect(self):
- self.odm = DrbdManageFakeDriver()
-
- def call_or_reconnect(self, method, *params):
- return apply(method, params)
-
- # Tests per se
-
- def test_create_volume(self):
- testvol = {'project_id': 'testprjid',
- 'name': 'testvol',
- 'size': 1,
- 'id': 'ba253fd0-8068-11e4-98c0-5254008ea111',
- 'volume_type_id': 'drbdmanage',
- 'created_at': timeutils.utcnow()}
-
- dmd = drbdmanagedrv.DrbdManageDriver(configuration=self.configuration)
- dmd.odm = DrbdManageFakeDriver()
- dmd.create_volume(testvol)
- self.assertEqual(dmd.odm.calls[0][0], "create_resource")
- self.assertEqual(dmd.odm.calls[1][0], "create_volume")
- self.assertEqual(dmd.odm.calls[1][2], 1048576)
- self.assertEqual(dmd.odm.calls[2][0], "auto_deploy")
-
- def test_delete_volume(self):
- testvol = {'project_id': 'testprjid',
- 'name': 'testvol',
- 'size': 1,
- 'id': 'ba253fd0-8068-11e4-98c0-5254008ea111',
- 'volume_type_id': 'drbdmanage',
- 'created_at': timeutils.utcnow()}
-
- dmd = drbdmanagedrv.DrbdManageDriver(configuration=self.configuration)
- dmd.odm = DrbdManageFakeDriver()
- dmd.delete_volume(testvol)
- self.assertEqual(dmd.odm.calls[0][0], "list_volumes")
- self.assertEqual(dmd.odm.calls[0][3]["cinder-id"], testvol['id'])
- self.assertEqual(dmd.odm.calls[1][0], "remove_volume")
-
- def test_local_path(self):
- testvol = {'project_id': 'testprjid',
- 'name': 'testvol',
- 'size': 1,
- 'id': 'ba253fd0-8068-11e4-98c0-5254008ea111',
- 'volume_type_id': 'drbdmanage',
- 'created_at': timeutils.utcnow()}
-
- dmd = drbdmanagedrv.DrbdManageDriver(configuration=self.configuration)
- dmd.odm = DrbdManageFakeDriver()
- data = dmd.local_path(testvol)
- self.assertTrue(data.startswith("/dev/drbd"))
-
- def test_create_snapshot(self):
- testsnap = {'id': 'ca253fd0-8068-11e4-98c0-5254008ea111',
- 'volume_id': 'ba253fd0-8068-11e4-98c0-5254008ea111'}
-
- dmd = drbdmanagedrv.DrbdManageDriver(configuration=self.configuration)
- dmd.odm = DrbdManageFakeDriver()
- dmd.create_snapshot(testsnap)
- self.assertEqual(dmd.odm.calls[0][0], "list_volumes")
- self.assertEqual(dmd.odm.calls[1][0], "list_assignments")
- self.assertEqual(dmd.odm.calls[2][0], "create_snapshot")
- self.assertTrue('node' in dmd.odm.calls[2][3])
-
- def test_delete_snapshot(self):
- testsnap = {'id': 'ca253fd0-8068-11e4-98c0-5254008ea111'}
-
- dmd = drbdmanagedrv.DrbdManageDriver(configuration=self.configuration)
- dmd.odm = DrbdManageFakeDriver()
- dmd.delete_snapshot(testsnap)
- self.assertEqual(dmd.odm.calls[0][0], "list_snapshots")
- self.assertEqual(dmd.odm.calls[1][0], "remove_snapshot")
-
- def test_extend_volume(self):
- testvol = {'project_id': 'testprjid',
- 'name': 'testvol',
- 'size': 1,
- 'id': 'ba253fd0-8068-11e4-98c0-5254008ea111',
- 'volume_type_id': 'drbdmanage',
- 'created_at': timeutils.utcnow()}
-
- dmd = drbdmanagedrv.DrbdManageDriver(configuration=self.configuration)
- dmd.odm = DrbdManageFakeDriver()
- dmd.extend_volume(testvol, 5)
- self.assertEqual(dmd.odm.calls[0][0], "list_volumes")
- self.assertEqual(dmd.odm.calls[0][3]["cinder-id"], testvol['id'])
- self.assertEqual(dmd.odm.calls[1][0], "resize_volume")
- self.assertEqual(dmd.odm.calls[1][1], "res")
- self.assertEqual(dmd.odm.calls[1][2], 2)
- self.assertEqual(dmd.odm.calls[1][3], -1)
- self.assertEqual(dmd.odm.calls[1][4]['size'], 5242880)
-
- def test_create_cloned_volume(self):
- srcvol = {'project_id': 'testprjid',
- 'name': 'testvol',
- 'size': 1,
- 'id': 'ba253fd0-8068-11e4-98c0-5254008ea111',
- 'volume_type_id': 'drbdmanage',
- 'created_at': timeutils.utcnow()}
-
- newvol = {'id': 'ca253fd0-8068-11e4-98c0-5254008ea111'}
-
- dmd = drbdmanagedrv.DrbdManageDriver(configuration=self.configuration)
- dmd.odm = DrbdManageFakeDriver()
- dmd.create_cloned_volume(newvol, srcvol)
- self.assertEqual(dmd.odm.calls[0][0], "list_volumes")
- self.assertEqual(dmd.odm.calls[1][0], "list_assignments")
- self.assertEqual(dmd.odm.calls[2][0], "create_snapshot")
- self.assertEqual(dmd.odm.calls[3][0], "list_snapshots")
- self.assertEqual(dmd.odm.calls[4][0], "restore_snapshot")
- self.assertEqual(dmd.odm.calls[5][0], "list_snapshots")
- self.assertEqual(dmd.odm.calls[6][0], "remove_snapshot")
- self.assertEqual(dmd.odm.calls[6][0], "remove_snapshot")
+++ /dev/null
-# Copyright (c) 2014 LINBIT HA Solutions GmbH
-# 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.
-
-
-"""
-
-This driver connects Cinder to an installed DRBDmanage instance, see
- http://oss.linbit.com/drbdmanage/
- http://git.linbit.com/drbdmanage.git/
-for more details.
-
-"""
-
-import uuid
-
-from oslo_config import cfg
-from oslo_log import log as logging
-from oslo_utils import importutils
-from oslo_utils import units
-import six
-
-
-from cinder import exception
-from cinder.i18n import _, _LW
-from cinder.volume import driver
-
-try:
- import dbus
- import drbdmanage.consts as dm_const
- import drbdmanage.exceptions as dm_exc
- import drbdmanage.utils as dm_utils
-except ImportError:
- dbus = None
- dm_const = None
- dm_exc = None
- dm_utils = None
-
-
-LOG = logging.getLogger(__name__)
-
-drbd_opts = [
- cfg.StrOpt('drbdmanage_redundancy',
- default='1',
- help='Number of nodes that should replicate the data.'),
- # TODO(PM): offsite_redundancy?
- # TODO(PM): choose DRBDmanage storage pool?
-]
-
-
-CONF = cfg.CONF
-CONF.register_opts(drbd_opts)
-
-
-CINDER_AUX_PROP_id = "cinder-id"
-DM_VN_PREFIX = 'CV_' # sadly 2CV isn't allowed by DRBDmanage
-
-
-class DrbdManageDriver(driver.VolumeDriver):
- """Cinder driver that uses DRBDmanage as data store.
- """
-
- VERSION = '1.0.0'
- drbdmanage_dbus_name = 'org.drbd.drbdmanaged'
- drbdmanage_dbus_interface = '/interface'
-
- def __init__(self, execute=None, *args, **kwargs):
- self.empty_list = dbus.Array([], signature="a(ss)")
- super(DrbdManageDriver, self).__init__(*args, **kwargs)
- if self.configuration:
- self.configuration.append_config_values(drbd_opts)
- if not self.drbdmanage_dbus_name:
- self.drbdmanage_dbus_name = 'org.drbd.drbdmanaged'
- if not self.drbdmanage_dbus_interface:
- self.drbdmanage_dbus_interface = '/interface'
- self.drbdmanage_redundancy = int(getattr(self.configuration,
- 'drbdmanage_redundancy', 1))
- self.dm_control_vol = ".drbdctrl"
-
- # Copied from the LVM driver, see
- # I43190d1dac33748fe55fa00f260f32ab209be656
- target_driver = \
- self.target_mapping[self.configuration.safe_get('iscsi_helper')]
-
- LOG.debug('Attempting to initialize DRBD driver with the '
- 'following target_driver: %s',
- target_driver)
-
- self.target_driver = importutils.import_object(
- target_driver,
- configuration=self.configuration,
- db=self.db,
- executor=self._execute)
-
- def dbus_connect(self):
- self.odm = dbus.SystemBus().get_object(self.drbdmanage_dbus_name,
- self.drbdmanage_dbus_interface)
- self.odm.ping()
-
- def call_or_reconnect(self, fn, *args):
- """Call DBUS function; if it got disconnected,
- try once to reconnect.
- """
- try:
- return fn(*args)
- except dbus.DBusException as e:
- LOG.warn(_LW("got disconnected; trying to reconnect. (%s)") %
- six.text_type(e))
- self.dbus_connect()
- # Old function object is invalid, get new one.
- return getattr(self.odm, fn._method_name)(*args)
-
- def do_setup(self, context):
- """Any initialization the volume driver does while starting."""
- super(DrbdManageDriver, self).do_setup(context)
- self.dbus_connect()
-
- def check_for_setup_error(self):
- """Verify that requirements are in place to use DRBDmanage driver."""
- if not all((dbus, dm_exc, dm_const, dm_utils)):
- msg = _('DRBDmanage driver setup error: some required '
- 'libraries (dbus, drbdmanage.*) not found.')
- LOG.error(msg)
- raise exception.VolumeDriverException(message=msg)
- if self.odm.ping() != 0:
- message = _('Cannot ping DRBDmanage backend')
- raise exception.VolumeBackendAPIException(data=message)
-
- def _clean_uuid(self):
- """Returns a UUID string, WITHOUT braces."""
- # Some uuid library versions put braces around the result!?
- # We don't want them, just a plain [0-9a-f-]+ string.
- id = str(uuid.uuid4())
- return id.translate(None, "{}")
-
- def _check_result(self, res, ignore=None, ret=0):
- seen_success = False
- seen_error = False
- result = ret
- for (code, fmt, arg_l) in res:
- # convert from DBUS to Python
- arg = dict(arg_l)
- if ignore and code in ignore:
- if not result:
- result = code
- continue
- if code == dm_exc.DM_SUCCESS:
- seen_success = True
- continue
- seen_error = _("Received error string: %s") % (fmt % arg)
-
- if seen_error:
- raise exception.VolumeBackendAPIException(data=seen_error)
- if seen_success:
- return ret
- # by default okay - or the ignored error code.
- return ret
-
- # DRBDmanage works in kiB units; Cinder uses float GiB.
- def _vol_size_to_dm(self, size):
- return int(size * units.Gi / units.Ki)
-
- def _vol_size_to_cinder(self, size):
- return int(size * units.Ki / units.Gi)
-
- def is_clean_volume_name(self, name):
- try:
- if name.startswith(CONF.volume_name_template % "") and \
- uuid.UUID(name[7:]) is not None:
- return DM_VN_PREFIX + name[7:]
- except ValueError:
- return None
-
- try:
- if uuid.UUID(name) is not None:
- return DM_VN_PREFIX + name
- except ValueError:
- return None
-
- def _priv_hash_from_volume(self, volume):
- return dm_utils.dict_to_aux_props({
- CINDER_AUX_PROP_id: volume['id'],
- })
-
- def snapshot_name_from_cinder_snapshot(self, snapshot):
- sn_name = self.is_clean_volume_name(snapshot['id'])
- return sn_name
-
- def _res_and_vl_data_for_volume(self, volume, empty_ok=False):
- """A DRBD resource might consist of several "volumes"
- (think consistency groups).
- So we have to find the number of the volume within one resource.
- Returns resource name, volume number, and resource
- and volume properties.
- """
-
- # If we get a string, use it as-is.
- # Else it's a dictionary; then get the ID.
- if type(volume) is str or type(volume) is unicode:
- v_uuid = volume
- else:
- v_uuid = volume['id']
-
- res, rl = self.call_or_reconnect(self.odm.list_volumes,
- self.empty_list,
- 0,
- dm_utils.dict_to_aux_props(
- {CINDER_AUX_PROP_id: v_uuid}),
- self.empty_list)
- self._check_result(res)
-
- if (not rl) or (len(rl) == 0):
- if empty_ok:
- LOG.debug("No volume %s found." % v_uuid)
- return None, None, None, None
- raise exception.VolumeBackendAPIException(
- data=_("volume %s not found in drbdmanage") % v_uuid)
- if len(rl) > 1:
- raise exception.VolumeBackendAPIException(
- data=_("multiple resources with name %s found by drbdmanage") %
- v_uuid)
-
- (r_name, r_props, vols) = rl[0]
- if len(vols) != 1:
- raise exception.VolumeBackendAPIException(
- data=_("not exactly one volume with id %s") %
- v_uuid)
-
- (v_nr, v_props) = vols[0]
-
- LOG.debug("volume %s is %s/%d; %s, %s" %
- (v_uuid, r_name, v_nr, r_props, v_props))
-
- return r_name, v_nr, r_props, v_props
-
- def _resource_and_snap_data_from_snapshot(self, snapshot, empty_ok=False):
- """Find the DRBD Resource and the snapshot name
- from the snapshot ID.
- """
- s_uuid = snapshot['id']
- res, rs = self.call_or_reconnect(self.odm.list_snapshots,
- self.empty_list,
- self.empty_list,
- dm_utils.dict_to_aux_props(
- {CINDER_AUX_PROP_id: s_uuid}),
- self.empty_list)
- self._check_result(res)
-
- if (not rs) or (len(rs) == 0):
- if empty_ok:
- return None
- else:
- raise exception.VolumeBackendAPIException(
- data=_("no snapshot with id %s found in drbdmanage") %
- s_uuid)
- if len(rs) > 1:
- raise exception.VolumeBackendAPIException(
- data=_("multiple resources with snapshot ID %s found") %
- s_uuid)
-
- (r_name, snaps) = rs[0]
- if len(snaps) != 1:
- raise exception.VolumeBackendAPIException(
- data=_("not exactly one snapshot with id %s") % s_uuid)
-
- (s_name, s_props) = snaps[0]
-
- LOG.debug("snapshot %s is %s/%s" % (s_uuid, r_name, s_name))
-
- return r_name, s_name, s_props
-
- def _resource_name_volnr_for_volume(self, volume, empty_ok=False):
- res, vol, _, _ = self._res_and_vl_data_for_volume(volume, empty_ok)
- return res, vol
-
- def local_path(self, volume):
- dres, dvol = self._resource_name_volnr_for_volume(volume)
-
- res, data = self.call_or_reconnect(self.odm.text_query,
- [dm_const.TQ_GET_PATH,
- dres,
- str(dvol)])
- self._check_result(res)
- if len(data) == 1:
- return data[0]
- message = _('Got bad path information from DRBDmanage! (%s)') % data
- raise exception.VolumeBackendAPIException(data=message)
-
- def create_volume(self, volume):
- """Creates a DRBD resource.
- We address it later on via the ID that gets stored
- as a private property.
- """
-
- # TODO(PM): consistency groups
- dres = self.is_clean_volume_name(volume['id'])
-
- LOG.debug("create vol: make %s" % dres)
- res = self.call_or_reconnect(self.odm.create_resource,
- dres,
- self.empty_list)
- exist = self._check_result(res, ignore=[dm_exc.DM_EEXIST], ret=None)
- if exist == dm_exc.DM_EEXIST:
- # Volume already exists, eg. because deploy gave an error
- # on a previous try (like ENOSPC)
- pass
- else:
-
- props = self._priv_hash_from_volume(volume)
- # TODO(PM): properties - redundancy, etc
- res = self.call_or_reconnect(self.odm.create_volume,
- dres,
- self._vol_size_to_dm(volume['size']),
- props)
- self._check_result(res)
-
- res = self.call_or_reconnect(self.odm.auto_deploy,
- dres, self.drbdmanage_redundancy,
- 0, True)
- self._check_result(res)
-
- return 0
-
- def delete_volume(self, volume):
- """Deletes a resource."""
- dres, dvol = self._resource_name_volnr_for_volume(
- volume,
- empty_ok=True)
-
- if not dres:
- # OK, already gone.
- return 0
-
- # TODO(PM): check if in use? Ask whether Primary, or just check result?
- res = self.call_or_reconnect(self.odm.remove_volume, dres, dvol, False)
- return self._check_result(res, ignore=[dm_exc.DM_ENOENT])
- # TODO(PM): delete resource if empty?
-
- def create_volume_from_snapshot(self, volume, snapshot):
- """Creates a volume from a snapshot."""
-
- LOG.debug("create vol from snap: from %s make %s" %
- (snapshot['id'], volume['id']))
- # TODO(PM): Consistency groups.
- dres, sname, sprop = self._resource_and_snap_data_from_snapshot(
- snapshot)
-
- new_res = self.is_clean_volume_name(volume['id'])
-
- r_props = self.empty_list
- v_props = self._priv_hash_from_volume(volume)
-
- res = self.call_or_reconnect(self.odm.restore_snapshot,
- new_res,
- dres,
- sname,
- r_props,
- v_props)
- return self._check_result(res, ignore=[dm_exc.DM_ENOENT])
-
- def create_cloned_volume(self, volume, src_vref):
- temp_id = self._clean_uuid()
- snapshot = {'id': temp_id}
-
- self.create_snapshot(dict(snapshot.items() +
- [('volume_id', src_vref['id'])]))
-
- self.create_volume_from_snapshot(volume, snapshot)
-
- self.delete_snapshot(snapshot)
-
- def _update_volume_stats(self):
- LOG.debug("Updating volume stats")
-
- data = {}
-
- data["vendor_name"] = 'LINBIT'
- data["vendor_name"] = 'Open Source'
- data["driver_version"] = self.VERSION
- data["storage_protocol"] = "iSCSI"
- # This has to match the name set in the cinder volume driver spec,
- # so keep it lowercase
- data["volume_backend_name"] = "drbdmanage"
- data["pools"] = []
-
- res, free, total = self.call_or_reconnect(self.odm.cluster_free_query,
- self.drbdmanage_redundancy)
- self._check_result(res)
-
- location_info =\
- ('DrbdManageDriver:%(cvol)s:%(dbus)s' %
- {'cvol': self.dm_control_vol,
- 'dbus': self.drbdmanage_dbus_name})
-
- # TODO(PM): multiple DRBDmanage instances and/or multiple pools
- single_pool = {}
- single_pool.update(dict(
- pool_name=data["volume_backend_name"],
- free_capacity_gb=self._vol_size_to_cinder(free),
- total_capacity_gb=self._vol_size_to_cinder(total),
- reserved_percentage=self.configuration.reserved_percentage,
- location_info=location_info,
- QoS_support=False))
-
- data["pools"].append(single_pool)
-
- self._stats = data
-
- def get_volume_stats(self, refresh=False):
- """Get volume status.
-
- If 'refresh' is True, run update the stats first.
- """
-
- if refresh:
- self._update_volume_stats()
- return self._stats
-
- def extend_volume(self, volume, new_size):
- dres, dvol = self._resource_name_volnr_for_volume(volume)
-
- res = self.call_or_reconnect(self.odm.resize_volume,
- dres, dvol, -1,
- {"size": self._vol_size_to_dm(new_size)},
- 0)
- self._check_result(res)
- return 0
-
- def create_snapshot(self, snapshot):
- """Creates a snapshot."""
- sn_name = self.snapshot_name_from_cinder_snapshot(snapshot)
-
- LOG.debug("create snapshot: from %s make %s" %
- (snapshot['volume_id'], snapshot['id']))
- dres, dvol = self._resource_name_volnr_for_volume(
- snapshot["volume_id"])
-
- res, data = self.call_or_reconnect(self.odm.list_assignments,
- self.empty_list,
- [dres],
- 0,
- self.empty_list,
- self.empty_list)
- self._check_result(res)
-
- nodes = map(lambda d: d[0], data)
- if len(nodes) < 1:
- raise exception.VolumeBackendAPIException(
- _('Snapshot res "%s" that is not deployed anywhere?') %
- (dres))
-
- props = self._priv_hash_from_volume(snapshot)
- res = self.call_or_reconnect(self.odm.create_snapshot,
- dres, sn_name, nodes, props)
- return self._check_result(res)
-
- def delete_snapshot(self, snapshot):
- """Deletes a snapshot."""
-
- force = False # during testing
- dres, sname, _ = self._resource_and_snap_data_from_snapshot(
- snapshot, empty_ok=not force)
-
- if not dres:
- # resource already gone?
- if force:
- return 0
- raise exception.VolumeBackendAPIException(
- _('Resource "%(res)s" for snapshot "%(sn)s" not found') %
- {"res": dres, "sn": sname})
-
- res = self.call_or_reconnect(self.odm.remove_snapshot,
- dres, sname, force)
- return self._check_result(res, ignore=[dm_exc.DM_ENOENT])
-
- # ####### Interface methods for DataPath (Target Driver) ########
-
- def ensure_export(self, context, volume):
- volume_path = self.local_path(volume)
- return self.target_driver.ensure_export(
- context,
- volume,
- volume_path)
-
- def create_export(self, context, volume):
- volume_path = self.local_path(volume)
- export_info = self.target_driver.create_export(
- context,
- volume,
- volume_path)
-
- return {'provider_location': export_info['location'],
- 'provider_auth': export_info['auth'], }
-
- def remove_export(self, context, volume):
- return self.target_driver.remove_export(context, volume)
-
- def initialize_connection(self, volume, connector):
- return self.target_driver.initialize_connection(volume, connector)
-
- def validate_connector(self, connector):
- return self.target_driver.validate_connector(connector)
-
- def terminate_connection(self, volume, connector, **kwargs):
- return None