Remove unsupported BlockDeviceDriver

BlockDeviceDriver was deprecated in Ocata release and marked as
'unsupported'. There is no CI for it too.

Change-Id: I145e4aeb535073b018cb4fa6ca9c939b1403ddb5
This commit is contained in:
Ivan Kolodyazhny 2017-09-05 16:51:57 +03:00
parent 4ee1bc921f
commit 711e88a8f9
4 changed files with 7 additions and 750 deletions

View File

@ -70,8 +70,6 @@ from cinder import ssh_utils as cinder_sshutils
from cinder.transfer import api as cinder_transfer_api
from cinder.volume import api as cinder_volume_api
from cinder.volume import driver as cinder_volume_driver
from cinder.volume.drivers import block_device as \
cinder_volume_drivers_blockdevice
from cinder.volume.drivers import blockbridge as \
cinder_volume_drivers_blockbridge
from cinder.volume.drivers import coho as cinder_volume_drivers_coho
@ -300,7 +298,6 @@ def list_opts():
itertools.chain(
cinder_volume_driver.volume_opts,
cinder_volume_driver.iser_opts,
cinder_volume_drivers_blockdevice.volume_opts,
cinder_volume_drivers_blockbridge.blockbridge_opts,
cinder_volume_drivers_coho.coho_opts,
cinder_volume_drivers_coprhd_common.volume_opts,

View File

@ -1,421 +0,0 @@
# Copyright (c) 2013 Mirantis, 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 mock
from oslo_config import cfg
from cinder import context
from cinder import db
import cinder.exception
from cinder.objects import fields
from cinder.objects import snapshot as obj_snap
from cinder.objects import volume as obj_volume
import cinder.test
from cinder.tests.unit import fake_constants as fake
from cinder.volume import configuration as conf
from cinder.volume.drivers import block_device
from cinder.volume import utils as volutils
class TestBlockDeviceDriver(cinder.test.TestCase):
def setUp(self):
fake_opt = [cfg.StrOpt('fake_opt', default='fake', help='fake option')]
super(TestBlockDeviceDriver, self).setUp()
self.configuration = conf.Configuration(fake_opt, 'fake_group')
self.configuration.available_devices = ['/dev/loop1', '/dev/loop2']
self.configuration.iscsi_helper = 'tgtadm'
self.host = 'localhost'
self.configuration.iscsi_port = 3260
self.configuration.volume_dd_blocksize = 1234
self.drv = block_device.BlockDeviceDriver(
configuration=self.configuration,
host='localhost', db=db)
def test_initialize_connection(self):
TEST_VOLUME1 = obj_volume.Volume(host='localhost1',
provider_location='1 2 3 /dev/loop1',
provider_auth='a b c',
attached_mode='rw',
id=fake.VOLUME_ID)
TEST_CONNECTOR = {'host': 'localhost1'}
data = self.drv.initialize_connection(TEST_VOLUME1, TEST_CONNECTOR)
expected_data = {'data': {'device_path': '/dev/loop1'},
'driver_volume_type': 'local'}
self.assertEqual(expected_data, data)
@mock.patch('cinder.volume.driver.ISCSIDriver.initialize_connection')
def test_initialize_connection_different_hosts(self, _init_conn):
TEST_CONNECTOR = {'host': 'localhost1'}
TEST_VOLUME2 = obj_volume.Volume(host='localhost2',
provider_location='1 2 3 /dev/loop2',
provider_auth='d e f',
attached_mode='rw',
id=fake.VOLUME2_ID)
_init_conn.return_value = 'data'
data = self.drv.initialize_connection(TEST_VOLUME2, TEST_CONNECTOR)
expected_data = {'data': {'auth_method': 'd',
'auth_password': 'f',
'auth_username': 'e',
'encrypted': False,
'target_discovered': False,
'target_iqn': '2',
'target_lun': 3,
'target_portal': '1',
'volume_id': fake.VOLUME2_ID}}
self.assertEqual(expected_data['data'], data['data'])
@mock.patch('cinder.volume.drivers.block_device.BlockDeviceDriver.'
'local_path', return_value=None)
@mock.patch('cinder.volume.utils.clear_volume')
def test_delete_not_volume_provider_location(self, _clear_volume,
_local_path):
TEST_VOLUME2 = obj_volume.Volume(provider_location=None)
self.drv.delete_volume(TEST_VOLUME2)
_local_path.assert_called_once_with(TEST_VOLUME2)
@mock.patch('os.path.exists', return_value=True)
@mock.patch('cinder.volume.utils.clear_volume')
def test_delete_volume_path_exist(self, _clear_volume, _exists):
TEST_VOLUME = obj_volume.Volume(name_id=fake.VOLUME_NAME_ID,
size=1,
provider_location='/dev/loop1',
display_name='vol1',
status='available')
with mock.patch.object(self.drv, 'local_path',
return_value='/dev/loop1') as lp_mocked:
with mock.patch.object(self.drv, '_get_devices_sizes',
return_value={'/dev/loop1': 1}) as \
gds_mocked:
volutils.clear_volume(gds_mocked, lp_mocked)
self.drv.delete_volume(TEST_VOLUME)
lp_mocked.assert_called_once_with(TEST_VOLUME)
gds_mocked.assert_called_once_with(['/dev/loop1'])
self.assertTrue(_exists.called)
self.assertTrue(_clear_volume.called)
def test_delete_path_is_not_in_list_of_available_devices(self):
TEST_VOLUME2 = obj_volume.Volume(provider_location='/dev/loop0')
with mock.patch.object(self.drv, 'local_path',
return_value='/dev/loop0') as lp_mocked:
self.drv.delete_volume(TEST_VOLUME2)
lp_mocked.assert_called_once_with(TEST_VOLUME2)
def test__update_provider_location(self):
TEST_VOLUME = obj_volume.Volume(name_id=fake.VOLUME_NAME_ID,
size=1,
display_name='vol1')
with mock.patch.object(obj_volume.Volume, 'update') as update_mocked, \
mock.patch.object(obj_volume.Volume, 'save') as save_mocked:
self.drv._update_provider_location(TEST_VOLUME, 'dev_path')
self.assertEqual(1, update_mocked.call_count)
save_mocked.assert_called_once_with()
def test_create_volume(self):
TEST_VOLUME = obj_volume.Volume(name_id=fake.VOLUME_NAME_ID,
size=1,
display_name='vol1')
with mock.patch.object(self.drv, 'find_appropriate_size_device',
return_value='dev_path') as fasd_mocked:
with mock.patch.object(self.drv, '_update_provider_location') as \
upl_mocked:
self.drv.create_volume(TEST_VOLUME)
fasd_mocked.assert_called_once_with(TEST_VOLUME.size)
upl_mocked.assert_called_once_with(TEST_VOLUME, 'dev_path')
def test_update_volume_stats(self):
with mock.patch.object(self.drv, '_devices_sizes',
return_value={'/dev/loop1': 1024,
'/dev/loop2': 1024}) as \
ds_mocked:
with mock.patch.object(self.drv, '_get_used_devices') as \
gud_mocked:
self.drv._update_volume_stats()
reserved_percentage = self.configuration.reserved_percentage
self.assertEqual({
'vendor_name': "Open Source",
'driver_version': self.drv.VERSION,
'volume_backend_name': 'BlockDev',
'storage_protocol': 'unknown',
'pools': [{
'QoS_support': False,
'total_capacity_gb': 2,
'free_capacity_gb': 2,
'reserved_percentage': reserved_percentage,
'pool_name': 'BlockDev'}]}, self.drv._stats)
gud_mocked.assert_called_once_with()
ds_mocked.assert_called_once_with()
@mock.patch('cinder.volume.utils.copy_volume')
def test_create_cloned_volume(self, _copy_volume):
TEST_SRC = obj_volume.Volume(id=fake.VOLUME_ID,
name_id=fake.VOLUME_NAME_ID,
size=1,
provider_location='/dev/loop1')
TEST_VOLUME = obj_volume.Volume(name_id=fake.VOLUME2_NAME_ID,
size=1,
display_name='vol1')
with mock.patch.object(self.drv, 'find_appropriate_size_device',
return_value='/dev/loop2') as fasd_mocked:
with mock.patch.object(self.drv, '_get_devices_sizes',
return_value={'/dev/loop2': 2}) as \
gds_mocked:
with mock.patch.object(self.drv, 'local_path',
return_value='/dev/loop1') as \
lp_mocked:
with mock.patch.object(self.drv,
'_update_provider_location') as \
upl_mocked:
volutils.copy_volume('/dev/loop1', fasd_mocked, 2,
mock.sentinel,
execute=self.drv._execute)
self.drv.create_cloned_volume(TEST_VOLUME, TEST_SRC)
fasd_mocked.assert_called_once_with(TEST_SRC.size)
lp_mocked.assert_called_once_with(TEST_SRC)
gds_mocked.assert_called_once_with(['/dev/loop2'])
upl_mocked.assert_called_once_with(
TEST_VOLUME, '/dev/loop2')
@mock.patch.object(cinder.image.image_utils, 'fetch_to_raw')
def test_copy_image_to_volume(self, _fetch_to_raw):
TEST_VOLUME = obj_volume.Volume(name_id=fake.VOLUME_NAME_ID,
size=1,
provider_location='/dev/loop1')
TEST_IMAGE_SERVICE = "image_service"
TEST_IMAGE_ID = "image_id"
with mock.patch.object(self.drv, 'local_path',
return_value='/dev/loop1') as lp_mocked:
self.drv.copy_image_to_volume(context, TEST_VOLUME,
TEST_IMAGE_SERVICE, TEST_IMAGE_ID)
lp_mocked.assert_called_once_with(TEST_VOLUME)
_fetch_to_raw.assert_called_once_with(context, TEST_IMAGE_SERVICE,
TEST_IMAGE_ID, '/dev/loop1',
1234, size=1)
def test_copy_volume_to_image(self):
TEST_VOLUME = {'provider_location': '/dev/loop1'}
TEST_IMAGE_SERVICE = "image_service"
TEST_IMAGE_META = "image_meta"
with mock.patch.object(cinder.image.image_utils, 'upload_volume') as \
_upload_volume:
with mock.patch.object(self.drv, 'local_path') as _local_path:
_local_path.return_value = '/dev/loop1'
self.drv.copy_volume_to_image(context, TEST_VOLUME,
TEST_IMAGE_SERVICE,
TEST_IMAGE_META)
self.assertTrue(_local_path.called)
_upload_volume.assert_called_once_with(context,
TEST_IMAGE_SERVICE,
TEST_IMAGE_META,
'/dev/loop1')
def test_get_used_devices(self):
TEST_VOLUME1 = {'host': 'localhost',
'provider_location': '/dev/loop1'}
TEST_VOLUME2 = {'host': 'localhost',
'provider_location': '/dev/loop2'}
def fake_local_path(vol):
return vol['provider_location'].split()[-1]
with mock.patch.object(obj_volume.VolumeList, 'get_all_by_host',
return_value=[TEST_VOLUME1, TEST_VOLUME2]),\
mock.patch.object(obj_snap.SnapshotList, 'get_by_host',
return_value=[]):
with mock.patch.object(context, 'get_admin_context'):
with mock.patch.object(self.drv, 'local_path',
return_value=fake_local_path):
path1 = self.drv.local_path(TEST_VOLUME1)
path2 = self.drv.local_path(TEST_VOLUME2)
self.assertEqual(set([path1, path2]),
self.drv._get_used_devices())
def test_get_devices_sizes(self):
dev_paths = ['/dev/loop1', '/dev/loop2', '/dev/loop3']
out = '4294967296\n2147483648\n3221225472\nn'
with mock.patch.object(self.drv,
'_execute',
return_value=(out, None)) as _execute:
actual = self.drv._get_devices_sizes(dev_paths)
self.assertEqual(3, len(actual))
self.assertEqual({'/dev/loop1': 4096, '/dev/loop2': 2048,
'/dev/loop3': 3072}, actual)
_execute.assert_called_once_with('blockdev', '--getsize64',
*dev_paths, run_as_root=True)
def test_devices_sizes(self):
with mock.patch.object(self.drv, '_get_devices_sizes') as \
_get_dvc_size:
_get_dvc_size.return_value = {'/dev/loop1': 1, '/dev/loop2': 1}
self.assertEqual(2, len(self.drv._devices_sizes()))
self.assertEqual({'/dev/loop1': 1, '/dev/loop2': 1},
self.drv._devices_sizes())
def test_find_appropriate_size_device_no_free_disks(self):
size = 1
with mock.patch.object(self.drv, '_devices_sizes') as _dvc_sizes:
with mock.patch.object(self.drv, '_get_used_devices') as \
_get_used_dvc:
_dvc_sizes.return_value = {'/dev/loop1': 1,
'/dev/loop2': 1}
_get_used_dvc.return_value = set(['/dev/loop1', '/dev/loop2'])
self.assertRaises(cinder.exception.CinderException,
self.drv.find_appropriate_size_device, size)
def test_find_appropriate_size_device_not_big_enough_disk(self):
size = 2948
with mock.patch.object(self.drv, '_devices_sizes') as _dvc_sizes:
with mock.patch.object(self.drv, '_get_used_devices') as \
_get_used_dvc:
_dvc_sizes.return_value = {'/dev/loop1': 1024,
'/dev/loop2': 1924}
_get_used_dvc.return_value = set(['/dev/loop1'])
self.assertRaises(cinder.exception.CinderException,
self.drv.find_appropriate_size_device, size)
def test_find_appropriate_size_device(self):
size = 1
with mock.patch.object(self.drv, '_devices_sizes') as _dvc_sizes:
with mock.patch.object(self.drv, '_get_used_devices') as \
_get_used_dvc:
_dvc_sizes.return_value = {'/dev/loop1': 2048,
'/dev/loop2': 1024}
_get_used_dvc.return_value = set()
self.assertEqual('/dev/loop2',
self.drv.find_appropriate_size_device(size))
def test_extend_volume_exists(self):
TEST_VOLUME = {'name': 'vol1', 'id': 123}
with mock.patch.object(self.drv, '_get_devices_sizes',
return_value={'/dev/loop1': 1024}) as \
mock_get_size:
with mock.patch.object(self.drv, 'local_path',
return_value='/dev/loop1') as lp_mocked:
self.assertRaises(cinder.exception.CinderException,
self.drv.extend_volume, TEST_VOLUME, 2)
lp_mocked.assert_called_once_with(TEST_VOLUME)
mock_get_size.assert_called_once_with(['/dev/loop1'])
@mock.patch('cinder.volume.utils.copy_volume')
def test_create_snapshot(self, _copy_volume):
TEST_VOLUME = obj_volume.Volume(id=fake.VOLUME_ID,
name_id=fake.VOLUME_NAME_ID,
size=1,
display_name='vol1',
status='available',
provider_location='/dev/loop1')
TEST_SNAP = obj_snap.Snapshot(id=fake.SNAPSHOT_ID,
volume_id=fake.VOLUME_ID,
volume_size=1024,
provider_location='/dev/loop2',
volume=TEST_VOLUME)
with mock.patch.object(self.drv, 'find_appropriate_size_device',
return_value='/dev/loop2') as fasd_mocked:
with mock.patch.object(self.drv, '_get_devices_sizes',
return_value={'/dev/loop2': 1024}) as \
gds_mocked:
with mock.patch.object(self.drv,
'_update_provider_location') as \
upl_mocked:
volutils.copy_volume('/dev/loop1', fasd_mocked, 1024,
mock.sentinel,
execute=self.drv._execute)
self.drv.create_snapshot(TEST_SNAP)
fasd_mocked.assert_called_once_with(TEST_SNAP.volume_size)
gds_mocked.assert_called_once_with(['/dev/loop2'])
upl_mocked.assert_called_once_with(
TEST_SNAP, '/dev/loop2')
def test_create_snapshot_with_not_available_volume(self):
TEST_VOLUME = obj_volume.Volume(id=fake.VOLUME_ID,
name_id=fake.VOLUME_NAME_ID,
size=1,
display_name='vol1',
status='in use',
provider_location='/dev/loop1')
TEST_SNAP = obj_snap.Snapshot(id=fake.SNAPSHOT_ID,
volume_id=fake.VOLUME_ID,
volume_size=1024,
provider_location='/dev/loop2',
volume=TEST_VOLUME)
self.assertRaises(cinder.exception.CinderException,
self.drv.create_snapshot, TEST_SNAP)
@mock.patch('cinder.volume.utils.copy_volume')
def test_create_volume_from_snapshot(self, _copy_volume):
TEST_SNAP = obj_snap.Snapshot(volume_id=fake.VOLUME_ID,
volume_size=1024,
provider_location='/dev/loop1')
TEST_VOLUME = obj_volume.Volume(id=fake.VOLUME_ID,
name_id=fake.VOLUME_NAME_ID,
size=1,
display_name='vol1',
provider_location='/dev/loop2')
with mock.patch.object(self.drv, 'find_appropriate_size_device',
return_value='/dev/loop2') as fasd_mocked:
with mock.patch.object(self.drv, '_get_devices_sizes',
return_value={'/dev/loop2': 1024}) as \
gds_mocked:
with mock.patch.object(self.drv,
'_update_provider_location') as \
upl_mocked:
volutils.copy_volume('/dev/loop1', fasd_mocked, 1024,
mock.sentinel,
execute=self.drv._execute)
self.drv.create_volume_from_snapshot(
TEST_VOLUME, TEST_SNAP)
fasd_mocked.assert_called_once_with(
TEST_SNAP.volume_size)
gds_mocked.assert_called_once_with(['/dev/loop2'])
upl_mocked.assert_called_once_with(
TEST_VOLUME, '/dev/loop2')
@mock.patch('os.path.exists', return_value=True)
@mock.patch('cinder.volume.utils.clear_volume')
def test_delete_snapshot(self, _clear_volume, _exists):
TEST_SNAP = obj_snap.Snapshot(volume_id=fake.VOLUME_ID,
provider_location='/dev/loop1',
status=fields.SnapshotStatus.AVAILABLE)
with mock.patch.object(self.drv, 'local_path',
return_value='/dev/loop1') as lp_mocked:
with mock.patch.object(self.drv, '_get_devices_sizes',
return_value={'/dev/loop1': 1}) as \
gds_mocked:
volutils.clear_volume(gds_mocked, lp_mocked)
self.drv.delete_snapshot(TEST_SNAP)
lp_mocked.assert_called_once_with(TEST_SNAP)
gds_mocked.assert_called_once_with(['/dev/loop1'])
self.assertTrue(_exists.called)
self.assertTrue(_clear_volume.called)

View File

@ -1,326 +0,0 @@
# Copyright (c) 2013 Mirantis, 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_config import cfg
from oslo_log import log as logging
from oslo_log import versionutils
from oslo_utils import importutils
from oslo_utils import units
from cinder import context
from cinder import exception
from cinder.i18n import _
from cinder.image import image_utils
from cinder import interface
from cinder import objects
from cinder import utils
from cinder.volume import configuration
from cinder.volume import driver
from cinder.volume import utils as volutils
LOG = logging.getLogger(__name__)
volume_opts = [
cfg.ListOpt('available_devices',
default=[],
help='List of all available devices'),
]
CONF = cfg.CONF
CONF.register_opts(volume_opts, group=configuration.SHARED_CONF_GROUP)
@interface.volumedriver
class BlockDeviceDriver(driver.BaseVD,
driver.CloneableImageVD):
VERSION = '2.3.0'
# ThirdPartySystems wiki page
CI_WIKI_NAME = "Cinder_Jenkins"
SUPPORTED = False
def __init__(self, *args, **kwargs):
super(BlockDeviceDriver, self).__init__(*args, **kwargs)
# This driver has been marked as deprecated in the Ocata release, as
# per the standard OpenStack deprecation policy it can be removed in
# the Queens release.
msg = _("The block_device driver is deprecated and will be "
"removed in a future release.")
versionutils.report_deprecated_feature(LOG, msg)
self.configuration.append_config_values(volume_opts)
self.backend_name = \
self.configuration.safe_get('volume_backend_name') or "BlockDev"
target_driver =\
self.target_mapping[self.configuration.safe_get('iscsi_helper')]
self.target_driver = importutils.import_object(
target_driver,
configuration=self.configuration,
db=self.db,
executor=self._execute)
def check_for_setup_error(self):
pass
def _update_provider_location(self, obj, device):
# We update provider_location and host to mark device as used to
# avoid race with other threads.
# TODO(ynesenenko): need to remove DB access from driver
host = '{host}#{pool}'.format(host=self.host, pool=self.get_pool(obj))
obj.update({'provider_location': device, 'host': host})
obj.save()
@utils.synchronized('block_device', external=True)
def create_volume(self, volume):
device = self.find_appropriate_size_device(volume.size)
LOG.info("Creating %(volume)s on %(device)s",
{"volume": volume.name, "device": device})
self._update_provider_location(volume, device)
def delete_volume(self, volume):
"""Deletes a logical volume."""
self._clear_block_device(volume)
def _clear_block_device(self, device):
"""Deletes a block device."""
dev_path = self.local_path(device)
if not dev_path or dev_path not in \
self.configuration.available_devices:
return
if os.path.exists(dev_path) and \
self.configuration.volume_clear != 'none':
dev_size = self._get_devices_sizes([dev_path])
volutils.clear_volume(
dev_size[dev_path], dev_path,
volume_clear=self.configuration.volume_clear,
volume_clear_size=self.configuration.volume_clear_size)
else:
LOG.warning("The device %s won't be cleared.", device)
if device.status == "error_deleting":
msg = _("Failed to delete device.")
LOG.error(msg, resource=device)
raise exception.VolumeDriverException(msg)
def local_path(self, device):
if device.provider_location:
path = device.provider_location.rsplit(" ", 1)
return path[-1]
else:
return None
def copy_image_to_volume(self, context, volume, image_service, image_id):
"""Fetch the image from image_service and write it to the volume."""
image_utils.fetch_to_raw(context,
image_service,
image_id,
self.local_path(volume),
self.configuration.volume_dd_blocksize,
size=volume.size)
def copy_volume_to_image(self, context, volume, image_service, image_meta):
"""Copy the volume to the specified image."""
image_utils.upload_volume(context,
image_service,
image_meta,
self.local_path(volume))
@utils.synchronized('block_device', external=True)
def create_cloned_volume(self, volume, src_vref):
LOG.info('Creating clone of volume: %s.', src_vref.id)
device = self.find_appropriate_size_device(src_vref.size)
dev_size = self._get_devices_sizes([device])
volutils.copy_volume(
self.local_path(src_vref), device,
dev_size[device],
self.configuration.volume_dd_blocksize,
execute=self._execute)
self._update_provider_location(volume, device)
def get_volume_stats(self, refresh=False):
if refresh:
self._update_volume_stats()
return self._stats
def _update_volume_stats(self):
"""Retrieve stats info from volume group."""
dict_of_devices_sizes = self._devices_sizes()
used_devices = self._get_used_devices()
total_size = 0
free_size = 0
for device, size in dict_of_devices_sizes.items():
if device not in used_devices:
free_size += size
total_size += size
LOG.debug("Updating volume stats.")
data = {
'volume_backend_name': self.backend_name,
'vendor_name': "Open Source",
'driver_version': self.VERSION,
'storage_protocol': 'unknown',
'pools': []}
single_pool = {
'pool_name': data['volume_backend_name'],
'total_capacity_gb': total_size / units.Ki,
'free_capacity_gb': free_size / units.Ki,
'reserved_percentage': self.configuration.reserved_percentage,
'QoS_support': False}
data['pools'].append(single_pool)
self._stats = data
def get_pool(self, volume):
return self.backend_name
def _get_used_paths(self, lst):
used_dev = set()
for item in lst:
local_path = self.local_path(item)
if local_path:
used_dev.add(local_path)
return used_dev
def _get_used_devices(self):
lst = objects.VolumeList.get_all_by_host(context.get_admin_context(),
self.host)
used_devices = self._get_used_paths(lst)
snp_lst = objects.SnapshotList.get_by_host(context.get_admin_context(),
self.host)
return used_devices.union(self._get_used_paths(snp_lst))
def _get_devices_sizes(self, dev_paths):
"""Return devices' sizes in Mb"""
out, _err = self._execute('blockdev', '--getsize64', *dev_paths,
run_as_root=True)
dev_sizes = {}
out = out.split('\n')
# blockdev returns devices' sizes in order that
# they have been passed to it.
for n, size in enumerate(out[:-1]):
dev_sizes[dev_paths[n]] = int(size) / units.Mi
return dev_sizes
def _devices_sizes(self):
available_devices = self.configuration.available_devices
return self._get_devices_sizes(available_devices)
def find_appropriate_size_device(self, size):
dict_of_devices_sizes = self._devices_sizes()
free_devices = (set(self.configuration.available_devices) -
self._get_used_devices())
if not free_devices:
raise exception.CinderException(_("No free disk"))
possible_device = None
possible_device_size = None
for device in free_devices:
dev_size = dict_of_devices_sizes[device]
if (size * units.Ki <= dev_size and
(possible_device is None or
dev_size < possible_device_size)):
possible_device = device
possible_device_size = dev_size
if possible_device:
return possible_device
else:
raise exception.CinderException(_("No big enough free disk"))
def extend_volume(self, volume, new_size):
dev_path = self.local_path(volume)
total_size = self._get_devices_sizes([dev_path])
# Convert from Megabytes to Gigabytes
size = total_size[dev_path] / units.Ki
if size < new_size:
msg = _("Insufficient free space available to extend volume.")
LOG.error(msg, resource=volume)
raise exception.CinderException(msg)
@utils.synchronized('block_device', external=True)
def create_snapshot(self, snapshot):
volume = snapshot.volume
if volume.status != 'available':
msg = _("Volume is not available.")
LOG.error(msg, resource=volume)
raise exception.CinderException(msg)
LOG.info('Creating volume snapshot: %s.', snapshot.id)
device = self.find_appropriate_size_device(snapshot.volume_size)
dev_size = self._get_devices_sizes([device])
volutils.copy_volume(
self.local_path(volume), device,
dev_size[device],
self.configuration.volume_dd_blocksize,
execute=self._execute)
self._update_provider_location(snapshot, device)
def delete_snapshot(self, snapshot):
self._clear_block_device(snapshot)
@utils.synchronized('block_device', external=True)
def create_volume_from_snapshot(self, volume, snapshot):
LOG.info('Creating volume %s from snapshot.', volume.id)
device = self.find_appropriate_size_device(snapshot.volume_size)
dev_size = self._get_devices_sizes([device])
volutils.copy_volume(
self.local_path(snapshot), device,
dev_size[device],
self.configuration.volume_dd_blocksize,
execute=self._execute)
self._update_provider_location(volume, device)
# ####### Interface methods for DataPath (Target Driver) ########
def ensure_export(self, context, volume):
volume_path = self.local_path(volume)
model_update = \
self.target_driver.ensure_export(
context,
volume,
volume_path)
return model_update
def create_export(self, context, volume, connector):
volume_path = self.local_path(volume)
export_info = self.target_driver.create_export(context,
volume,
volume_path)
return {
'provider_location': export_info['location'] + ' ' + volume_path,
'provider_auth': export_info['auth'],
}
def remove_export(self, context, volume):
self.target_driver.remove_export(context, volume)
def initialize_connection(self, volume, connector):
if connector['host'] != volutils.extract_host(volume.host, 'host'):
return self.target_driver.initialize_connection(volume, connector)
else:
return {
'driver_volume_type': 'local',
'data': {'device_path': self.local_path(volume)},
}
def validate_connector(self, connector):
return self.target_driver.validate_connector(connector)
def terminate_connection(self, volume, connector, **kwargs):
pass

View File

@ -0,0 +1,7 @@
---
upgrade:
- |
BlockDeviceDriver was deprecated in Ocata release and marked as
'unsupported'. There is no CI for it too. If you used this driver before
you have to migrate your volumes to LVM with LIO target yourself before
upgrading to Queens release to get your volumes working.