Merge "Enable multiattach capability"

This commit is contained in:
Zuul 2018-01-18 15:29:39 +00:00 committed by Gerrit Code Review
commit feec9e4dac
7 changed files with 176 additions and 11 deletions

@ -137,6 +137,8 @@ VOLUME_SHARED_TARGETS_AND_SERVICE_FIELDS = '3.48'
BACKEND_STATE_REPORT = '3.49'
MULTIATTACH_VOLUMES = '3.50'
def get_mv_header(version):
"""Gets a formatted HTTP microversion header.

@ -113,6 +113,7 @@ REST_API_VERSION_HISTORY = """
* 3.47 - Support create volume from backup.
* 3.48 - Add ``shared_targets`` and ``service_uuid`` fields to volume.
* 3.49 - Support report backend storage state in service list.
* 3.50 - Add multiattach capability
"""
# The minimum and maximum versions of the API supported
@ -120,7 +121,7 @@ REST_API_VERSION_HISTORY = """
# minimum version of the API supported.
# Explicitly using /v2 endpoints will still work
_MIN_API_VERSION = "3.0"
_MAX_API_VERSION = "3.49"
_MAX_API_VERSION = "3.50"
_LEGACY_API_VERSION2 = "2.0"
UPDATED = "2017-09-19T20:18:14Z"

@ -626,6 +626,7 @@ class VolumeTestCase(base.BaseVolumeTestCase):
'description',
volume_type=foo)
self.assertEqual(foo['id'], vol['volume_type_id'])
self.assertTrue(vol['multiattach'])
@mock.patch.object(key_manager, 'API', fake_keymgr.fake_api)
def test_create_volume_with_encrypted_volume_type_aes(self):

@ -0,0 +1,108 @@
# 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.
"""Tests for Volume retype Code."""
import mock
from oslo_config import cfg
from cinder import context
from cinder import exception
from cinder import objects
from cinder import quota
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import volume as base
from cinder.volume import volume_types
QUOTAS = quota.QUOTAS
CONF = cfg.CONF
class VolumeRetypeTestCase(base.BaseVolumeTestCase):
def setUp(self):
super(VolumeRetypeTestCase, self).setUp()
self.patch('cinder.volume.utils.clear_volume', autospec=True)
self.expected_status = 'available'
self.service_id = 1
self.user_context = context.RequestContext(user_id=fake.USER_ID,
project_id=fake.PROJECT_ID)
volume_types.create(self.context,
"old-type",
{},
description="test-multiattach")
volume_types.create(self.context,
"fake_vol_type",
{},
description="fake_type")
volume_types.create(self.context,
"multiattach-type",
{'multiattach': "<is> True"},
description="test-multiattach")
self.default_vol_type = objects.VolumeType.get_by_name_or_id(
self.context,
'fake_vol_type')
self.multiattach_type = objects.VolumeType.get_by_name_or_id(
self.context,
'multiattach-type')
def fake_get_vtype(self, context, identifier):
if identifier == "multiattach-type":
return self.multiattach_type
else:
return self.default_vol_type
@mock.patch.object(volume_types, 'get_by_name_or_id')
def test_retype_multiattach(self, _mock_get_types):
"""Verify multiattach retype restrictions."""
_mock_get_types.side_effect = self.fake_get_vtype
# Test going from default type to multiattach
vol = self.volume_api.create(self.context,
1,
'test-vol',
'')
vol.update({'status': 'available'})
vol.save()
self.volume_api.retype(self.user_context,
vol,
'multiattach-type')
vol = objects.Volume.get_by_id(self.context, vol.id)
self.assertTrue(vol.multiattach)
# Test going from multiattach to a non-multiattach type
vol = self.volume_api.create(
self.context,
1,
'test-multiattachvol',
'',
volume_type=self.multiattach_type)
vol.update({'status': 'available'})
vol.save()
self.volume_api.retype(self.user_context,
vol,
'fake_vol-type')
vol = objects.Volume.get_by_id(self.context, vol.id)
self.assertFalse(vol.multiattach)
# Test trying to retype an in-use volume
vol.update({'status': 'in-use'})
vol.save()
self.assertRaises(exception.InvalidInput,
self.volume_api.retype,
self.context,
vol,
'multiattach-type')

@ -349,6 +349,8 @@ class API(base.Base):
# Refresh the object here, otherwise things ain't right
vref = objects.Volume.get_by_id(
context, vref['id'])
vref.multiattach = self._is_multiattach(volume_type)
vref.save()
LOG.info("Create volume request issued successfully.",
resource=vref)
return vref
@ -1620,14 +1622,14 @@ class API(base.Base):
# Support specifying volume type by ID or name
try:
vol_type = (
new_type = (
volume_types.get_by_name_or_id(context.elevated(), new_type))
except exception.InvalidVolumeType:
msg = _('Invalid volume_type passed: %s.') % new_type
LOG.error(msg)
raise exception.InvalidInput(reason=msg)
vol_type_id = vol_type['id']
new_type_id = new_type['id']
# NOTE(jdg): We check here if multiattach is involved in either side
# of the retype, we can't change multiattach on an in-use volume
@ -1636,12 +1638,11 @@ class API(base.Base):
# have to get through scheduling if all the conditions are met, we
# should consider an up front capabilities check to give fast feedback
# rather than "No hosts found" and error status
src_is_multiattach = volume.multiattach
tgt_is_multiattach = False
if (vol_type and
self._is_multiattach(vol_type)):
tgt_is_multiattach = True
if new_type:
tgt_is_multiattach = self._is_multiattach(new_type)
if src_is_multiattach != tgt_is_multiattach:
if volume.status != "available":
@ -1657,7 +1658,7 @@ class API(base.Base):
# early as possible, but won't commit until we change the type. We
# pass the reservations onward in case we need to roll back.
reservations = quota_utils.get_volume_type_reservation(
context, volume, vol_type_id, reserve_vol_type_only=True)
context, volume, new_type_id, reserve_vol_type_only=True)
# Get old reservations
try:
@ -1685,12 +1686,12 @@ class API(base.Base):
'migration_status': self.AVAILABLE_MIGRATION_STATUS,
'consistencygroup_id': (None, ''),
'group_id': (None, ''),
'volume_type_id': db.Not(vol_type_id)}
'volume_type_id': db.Not(new_type_id)}
# We don't support changing QoS at the front-end yet for in-use volumes
# TODO(avishay): Call Nova to change QoS setting (libvirt has support
# - virDomainSetBlockIoTune() - Nova does not have support yet).
filters = [db.volume_qos_allows_retype(vol_type_id)]
filters = [db.volume_qos_allows_retype(new_type_id)]
updates = {'status': 'retyping',
'previous_status': objects.Volume.model.status}
@ -1708,7 +1709,7 @@ class API(base.Base):
request_spec = {'volume_properties': volume,
'volume_id': volume.id,
'volume_type': vol_type,
'volume_type': new_type,
'migration_policy': migration_policy,
'quota_reservations': reservations,
'old_reservations': old_reservations}
@ -1716,6 +1717,8 @@ class API(base.Base):
self.scheduler_rpcapi.retype(context, volume,
request_spec=request_spec,
filter_properties={})
volume.multiattach = tgt_is_multiattach
volume.save()
LOG.info("Retype volume request issued successfully.",
resource=volume)

@ -0,0 +1,49 @@
.. _volume_multiattach:
=============================================
Enable attaching a volume to multiple servers
=============================================
When configured to allow it and for backends that support it, Cinder
allows a volume to be attached to more than one host/server at a time.
By default this feature is only enabled for administrators, and is
controlled by policy. If the user is not an admin or the policy file
isn't modified only a single attachment per volume is allowed.
In addition, the ability to attach a volume to multiple hosts/servers
requires that the volume is of a special type that includes an extra-spec
capability setting of multiattach: True::
.. code-block:: console
$ cinder type-create multiattach
$ cinder type-key multiattach set multiattach="<is> True"
Now any volume of this type is capable of having multiple simultaneous
attachments. You'll need to ensure you have a backend device that reports
support of the multiattach capability, otherwise scheduling will fail on
create.
At this point Cinder will no longer check in-use status when creating/updating
attachments.
.. note::
This feature is only supported when using the new attachment API's,
attachment-create, attachment-update etc.
In addition, it's possible to retype a volume to be multiattach capable.
Currently however we do NOT allow retyping a volume to multiattach:True or
multiattach:False if it's status is not ``avaialable``. This is because some
consumers/hypervisors need to make special considerations at attach-time for
multiattach volumes (ie disable caching) and there's no mechanism currently to
go back to ``in-use`` volumes and update them. While going from
``multiattach:True`` --> ``multiattach:False`` isn't as problematic, it is
error prone when it comes to special cases like shelve, migrate etc. The bottom
line is it's *safer* to just avoid changing this setting on ``in-use`` volumes.
Finally, note that Cinder (nor its backends) does not do anything in terms of file
systems or control of the volumes. In other words, it's up to the user to
ensure that a multiattach or clustered file system is used on the volumes.
Otherwise there may be a high probability of data corruption.

@ -44,6 +44,7 @@ Amazon EC2 Elastic Block Storage (EBS) offering.
blockstorage-volume-backups-export-import.rst
blockstorage-volume-backups.rst
blockstorage-volume-migration.rst
blockstorage-volume-multiattach.rst
blockstorage-volume-number-weigher.rst
blockstorage-report-backend-state.rst