Merge "Add multiattach policy"

This commit is contained in:
Zuul 2018-01-18 06:46:00 +00:00 committed by Gerrit Code Review
commit 71173c3056
6 changed files with 137 additions and 0 deletions

View File

@ -21,6 +21,7 @@ from cinder.policies import base
CREATE_POLICY = 'volume:attachment_create'
UPDATE_POLICY = 'volume:attachment_update'
DELETE_POLICY = 'volume:attachment_delete'
MULTIATTACH_BOOTABLE_VOLUME_POLICY = 'volume:multiattach_bootable_volume'
attachments_policies = [
policy.DocumentedRuleDefault(
@ -53,6 +54,16 @@ attachments_policies = [
'path': '/attachments/{attachment_id}'
}
]),
policy.DocumentedRuleDefault(
name=MULTIATTACH_BOOTABLE_VOLUME_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Allow multiattach of bootable volumes.",
operations=[
{
'method': 'POST',
'path': '/attachments'
}
]),
]

View File

@ -29,6 +29,7 @@ HOST_ATTRIBUTE_POLICY = "volume_extension:volume_host_attribute"
TENANT_ATTRIBUTE_POLICY = "volume_extension:volume_tenant_attribute"
MIG_ATTRIBUTE_POLICY = "volume_extension:volume_mig_status_attribute"
ENCRYPTION_METADATA_POLICY = "volume_extension:volume_encryption_metadata"
MULTIATTACH_POLICY = "volume:multiattach"
volumes_policies = [
policy.DocumentedRuleDefault(
@ -161,6 +162,16 @@ volumes_policies = [
'path': '/volumes/{volume_id}/encryption/{encryption_key}'
}
]),
policy.DocumentedRuleDefault(
name=MULTIATTACH_POLICY,
check_str=base.RULE_ADMIN_OR_OWNER,
description="Create multiattach capable volume.",
operations=[
{
'method': 'POST',
'path': '/volumes'
}
]),
]

View File

@ -12,11 +12,15 @@
import mock
from oslo_config import cfg
from oslo_policy import policy as oslo_policy
from cinder import context
from cinder import db
from cinder import exception
from cinder import objects
from cinder.policies import attachments as attachment_policy
from cinder.policies import base as base_policy
from cinder import policy
from cinder import test
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import utils as tests_utils
@ -38,6 +42,9 @@ class AttachmentManagerTestCase(test.TestCase):
self.project_id = fake.PROJECT3_ID
self.context.project_id = self.project_id
self.volume_api = volume_api.API()
self.user_context = context.RequestContext(
user_id=fake.USER_ID,
project_id=fake.PROJECT3_ID)
def test_attachment_create_no_connector(self):
"""Test attachment_create no connector."""
@ -237,3 +244,23 @@ class AttachmentManagerTestCase(test.TestCase):
vref.id)
self.assertEqual('reserved', vref.status)
self.assertEqual(1, len(vref.volume_attachment))
def test_attachment_create_bootable_multiattach_policy(self):
"""Test attachment_create no connector."""
volume_params = {'status': 'available'}
vref = tests_utils.create_volume(self.context, **volume_params)
vref.multiattach = True
vref.bootable = True
vref.status = 'in-use'
rules = {
attachment_policy.MULTIATTACH_BOOTABLE_VOLUME_POLICY: base_policy.RULE_ADMIN_API # noqa
}
policy.set_rules(oslo_policy.Rules.from_dict(rules))
self.addCleanup(policy.reset)
self.assertRaises(exception.PolicyNotAuthorized,
self.volume_api.attachment_create,
self.user_context,
vref,
fake.UUID2)

View File

@ -54,6 +54,7 @@ from cinder.volume import driver
from cinder.volume import manager as vol_manager
from cinder.volume import rpcapi as volume_rpcapi
import cinder.volume.targets.tgt
from cinder.volume import volume_types
QUOTAS = quota.QUOTAS
@ -606,6 +607,26 @@ class VolumeTestCase(base.BaseVolumeTestCase):
volume_type=db_vol_type)
self.assertEqual(db_vol_type.get('id'), volume['volume_type_id'])
def test_create_volume_with_multiattach_volume_type(self):
"""Test volume creation with multiattach volume type."""
elevated = context.get_admin_context()
volume_api = cinder.volume.api.API()
especs = dict(multiattach="<is> True")
volume_types.create(elevated,
"multiattach-type",
especs,
description="test-multiattach")
foo = objects.VolumeType.get_by_name_or_id(elevated,
"multiattach-type")
vol = volume_api.create(self.context,
1,
'admin-vol',
'description',
volume_type=foo)
self.assertEqual(foo['id'], vol['volume_type_id'])
@mock.patch.object(key_manager, 'API', fake_keymgr.fake_api)
def test_create_volume_with_encrypted_volume_type_aes(self):
ctxt = context.get_admin_context()

View File

@ -193,6 +193,10 @@ class API(base.Base):
return (volume['migration_status'] not in
self.AVAILABLE_MIGRATION_STATUS)
def _is_multiattach(self, volume_type):
specs = getattr(volume_type, 'extra_specs', {})
return specs.get('multiattach', 'False') == '<is> True'
def create(self, context, size, name, description, snapshot=None,
image_id=None, volume_type=None, metadata=None,
availability_zone=None, source_volume=None,
@ -285,6 +289,9 @@ class API(base.Base):
utils.check_metadata_properties(metadata)
if (volume_type and self._is_multiattach(volume_type)):
context.authorize(vol_policy.MULTIATTACH_POLICY)
create_what = {
'context': context,
'raw_size': size,
@ -1622,6 +1629,30 @@ class API(base.Base):
vol_type_id = vol_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
# because there's things the hypervisor needs when attaching, so
# we just disallow retype of in-use volumes in this case. You still
# 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 src_is_multiattach != tgt_is_multiattach:
if volume.status != "available":
msg = _('Invalid volume_type passed, retypes affecting '
'multiattach are only allowed on available volumes, '
'the specified volume however currently has a status '
'of: %s.') % volume.status
LOG.info(msg)
raise exception.InvalidInput(reason=msg)
context.authorize(vol_policy.MULTIATTACH_POLICY)
# We're checking here in so that we can report any quota issues as
# early as possible, but won't commit until we change the type. We
# pass the reservations onward in case we need to roll back.
@ -2021,6 +2052,14 @@ class API(base.Base):
# creation of other new reserves/attachments while in this state
# so we avoid contention issues with shared connections
# Multiattach of bootable volumes is a special case with it's own
# policy, check that here right off the bat
if (vref.get('multiattach', False) and
vref.status == 'in-use' and
vref.bootable):
context.authorize(
attachment_policy.MULTIATTACH_BOOTABLE_VOLUME_POLICY)
# FIXME(JDG): We want to be able to do things here like reserve a
# volume for Nova to do BFV WHILE the volume may be in the process of
# downloading image, we add downloading here; that's easy enough but
@ -2032,6 +2071,7 @@ class API(base.Base):
'status': (('available', 'in-use', 'downloading')
if vref.multiattach
else ('available', 'downloading'))}
result = vref.conditional_update({'status': 'reserved'}, expected)
if not result:

View File

@ -0,0 +1,27 @@
---
features:
- Added policies to disallow multiattach operations. This includes two
policies, the first being a general policy to allow the creation or
retyping of multiattach volumes is a volume create policy with the name
``MULTIATTACH_POLICY``.
The second policy is specifically for disallowing the ability to create
multiple attachments on a volume that is marked as bootable, and is an
attachment policy with the name ``MULTIATTACH_BOOTABLE_VOLUME_POLICY``.
The default for these new policies is ``ADMIN_OR_OWNER``; be aware that
if you wish to disable either of these policies for your users you will
need to modify the default policy settings.
upgrade:
- Added policies to disallow multiattach operations. This includes two
policies, the first being a general policy to allow the creation or
retyping of multiattach volumes is a volume create policy with the name
``MULTIATTACH_POLICY``.
The second policy is specifically for disallowing the ability to create
multiple attachments on a volume that is marked as bootable, and is an
attachment policy with the name ``MULTIATTACH_BOOTABLE_VOLUME_POLICY``.
The default for these new policies is ``ADMIN_OR_OWNER``; be aware that
if you wish to disable either of these policies for your users you will
need to modify the default policy settings.