Merge "add access_key to share_access_map"
This commit is contained in:
commit
addb65225d
@ -373,6 +373,13 @@ access_id:
|
|||||||
in: body
|
in: body
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
access_key:
|
||||||
|
description: |
|
||||||
|
The access credential of the entity granted share access.
|
||||||
|
in: body
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
min_version: 2.21
|
||||||
access_level:
|
access_level:
|
||||||
description: |
|
description: |
|
||||||
The access level to the share. To grant or deny
|
The access level to the share. To grant or deny
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"access_type": "ip",
|
"access_type": "ip",
|
||||||
"access_to": "0.0.0.0/0",
|
"access_to": "0.0.0.0/0",
|
||||||
"access_level": "rw",
|
"access_level": "rw",
|
||||||
|
"access_key": null,
|
||||||
"id": "a25b2df3-90bd-4add-afa6-5f0dbbd50452"
|
"id": "a25b2df3-90bd-4add-afa6-5f0dbbd50452"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,16 @@
|
|||||||
"state": "error",
|
"state": "error",
|
||||||
"id": "507bf114-36f2-4f56-8cf4-857985ca87c1",
|
"id": "507bf114-36f2-4f56-8cf4-857985ca87c1",
|
||||||
"access_type": "cert",
|
"access_type": "cert",
|
||||||
"access_to": "example.com"
|
"access_to": "example.com",
|
||||||
|
"access_key": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"access_level": "rw",
|
"access_level": "rw",
|
||||||
"state": "active",
|
"state": "active",
|
||||||
"id": "a25b2df3-90bd-4add-afa6-5f0dbbd50452",
|
"id": "a25b2df3-90bd-4add-afa6-5f0dbbd50452",
|
||||||
"access_type": "ip",
|
"access_type": "ip",
|
||||||
"access_to": "0.0.0.0/0"
|
"access_to": "0.0.0.0/0",
|
||||||
|
"access_key": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,7 @@ Response parameters
|
|||||||
- updated_at: updated_at_3
|
- updated_at: updated_at_3
|
||||||
- access_type: access_type_2
|
- access_type: access_type_2
|
||||||
- access_to: access_to_1
|
- access_to: access_to_1
|
||||||
|
- access_key: access_key
|
||||||
- access: access
|
- access: access
|
||||||
- access_level: access_level_2
|
- access_level: access_level_2
|
||||||
- id: id_7
|
- id: id_7
|
||||||
@ -169,6 +170,7 @@ Response parameters
|
|||||||
.. rest_parameters:: parameters.yaml
|
.. rest_parameters:: parameters.yaml
|
||||||
|
|
||||||
- access_type: access_type_1
|
- access_type: access_type_1
|
||||||
|
- access_key: access_key
|
||||||
- access_to: access_to_1
|
- access_to: access_to_1
|
||||||
- access_level: access_level
|
- access_level: access_level
|
||||||
- state: state
|
- state: state
|
||||||
|
@ -72,14 +72,14 @@ REST_API_VERSION_HISTORY = """
|
|||||||
* 2.19 - Share snapshot instances admin APIs
|
* 2.19 - Share snapshot instances admin APIs
|
||||||
(list/show/detail/reset-status).
|
(list/show/detail/reset-status).
|
||||||
* 2.20 - Add MTU to the JSON response of share network show API.
|
* 2.20 - Add MTU to the JSON response of share network show API.
|
||||||
|
* 2.21 - Add access_key to the response of access_list API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# The minimum and maximum versions of the API supported
|
# The minimum and maximum versions of the API supported
|
||||||
# The default api version request is defined to be the
|
# The default api version request is defined to be the
|
||||||
# the minimum version of the API supported.
|
# the minimum version of the API supported.
|
||||||
_MIN_API_VERSION = "2.0"
|
_MIN_API_VERSION = "2.0"
|
||||||
_MAX_API_VERSION = "2.20"
|
_MAX_API_VERSION = "2.21"
|
||||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
@ -126,3 +126,7 @@ user documentation.
|
|||||||
2.20
|
2.20
|
||||||
----
|
----
|
||||||
Add MTU in share network show API.
|
Add MTU in share network show API.
|
||||||
|
|
||||||
|
2.21
|
||||||
|
----
|
||||||
|
Add access_key in access_list API.
|
||||||
|
@ -28,6 +28,7 @@ from webob import exc
|
|||||||
|
|
||||||
from manila.api import common
|
from manila.api import common
|
||||||
from manila.api.openstack import wsgi
|
from manila.api.openstack import wsgi
|
||||||
|
from manila.api.views import share_accesses as share_access_views
|
||||||
from manila.api.views import shares as share_views
|
from manila.api.views import shares as share_views
|
||||||
from manila import db
|
from manila import db
|
||||||
from manila import exception
|
from manila import exception
|
||||||
@ -497,7 +498,8 @@ class ShareMixin(object):
|
|||||||
access_data.get('access_level'))
|
access_data.get('access_level'))
|
||||||
except exception.ShareAccessExists as e:
|
except exception.ShareAccessExists as e:
|
||||||
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
raise webob.exc.HTTPBadRequest(explanation=e.msg)
|
||||||
return {'access': access}
|
|
||||||
|
return self._access_view_builder.view(req, access)
|
||||||
|
|
||||||
def _deny_access(self, req, id, body):
|
def _deny_access(self, req, id, body):
|
||||||
"""Remove share access rule."""
|
"""Remove share access rule."""
|
||||||
@ -521,8 +523,9 @@ class ShareMixin(object):
|
|||||||
context = req.environ['manila.context']
|
context = req.environ['manila.context']
|
||||||
|
|
||||||
share = self.share_api.get(context, id)
|
share = self.share_api.get(context, id)
|
||||||
access_list = self.share_api.access_get_all(context, share)
|
access_rules = self.share_api.access_get_all(context, share)
|
||||||
return {'access_list': access_list}
|
|
||||||
|
return self._access_view_builder.list_view(req, access_rules)
|
||||||
|
|
||||||
def _extend(self, req, id, body):
|
def _extend(self, req, id, body):
|
||||||
"""Extend size of a share."""
|
"""Extend size of a share."""
|
||||||
@ -576,6 +579,7 @@ class ShareController(wsgi.Controller, ShareMixin, wsgi.AdminActionsMixin):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(self.__class__, self).__init__()
|
super(self.__class__, self).__init__()
|
||||||
self.share_api = share.API()
|
self.share_api = share.API()
|
||||||
|
self._access_view_builder = share_access_views.ViewBuilder()
|
||||||
|
|
||||||
@wsgi.action('os-reset_status')
|
@wsgi.action('os-reset_status')
|
||||||
def share_reset_status(self, req, id, body):
|
def share_reset_status(self, req, id, body):
|
||||||
|
@ -18,6 +18,7 @@ from manila.api.openstack import wsgi
|
|||||||
from manila.api.v1 import share_manage
|
from manila.api.v1 import share_manage
|
||||||
from manila.api.v1 import share_unmanage
|
from manila.api.v1 import share_unmanage
|
||||||
from manila.api.v1 import shares
|
from manila.api.v1 import shares
|
||||||
|
from manila.api.views import share_accesses as share_access_views
|
||||||
from manila.api.views import shares as share_views
|
from manila.api.views import shares as share_views
|
||||||
from manila import share
|
from manila import share
|
||||||
|
|
||||||
@ -34,6 +35,7 @@ class ShareController(shares.ShareMixin,
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(self.__class__, self).__init__()
|
super(self.__class__, self).__init__()
|
||||||
self.share_api = share.API()
|
self.share_api = share.API()
|
||||||
|
self._access_view_builder = share_access_views.ViewBuilder()
|
||||||
|
|
||||||
@wsgi.Controller.api_version("2.4")
|
@wsgi.Controller.api_version("2.4")
|
||||||
def create(self, req, body):
|
def create(self, req, body):
|
||||||
|
61
manila/api/views/share_accesses.py
Normal file
61
manila/api/views/share_accesses.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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.
|
||||||
|
|
||||||
|
from manila.api import common
|
||||||
|
|
||||||
|
|
||||||
|
class ViewBuilder(common.ViewBuilder):
|
||||||
|
"""Model a share access API response as a python dictionary."""
|
||||||
|
|
||||||
|
_collection_name = 'share_accesses'
|
||||||
|
_detail_version_modifiers = [
|
||||||
|
"add_access_key",
|
||||||
|
]
|
||||||
|
|
||||||
|
def list_view(self, request, accesses):
|
||||||
|
"""View of a list of share accesses."""
|
||||||
|
return {'access_list': [self.summary_view(request, access)['access']
|
||||||
|
for access in accesses]}
|
||||||
|
|
||||||
|
def summary_view(self, request, access):
|
||||||
|
"""Summarized view of a single share access."""
|
||||||
|
access_dict = {
|
||||||
|
'id': access.get('id'),
|
||||||
|
'access_level': access.get('access_level'),
|
||||||
|
'access_to': access.get('access_to'),
|
||||||
|
'access_type': access.get('access_type'),
|
||||||
|
'state': access.get('state'),
|
||||||
|
}
|
||||||
|
self.update_versioned_resource_dict(
|
||||||
|
request, access_dict, access)
|
||||||
|
return {'access': access_dict}
|
||||||
|
|
||||||
|
def view(self, request, access):
|
||||||
|
"""Generic view of a single share access."""
|
||||||
|
access_dict = {
|
||||||
|
'id': access.get('id'),
|
||||||
|
'share_id': access.get('share_id'),
|
||||||
|
'access_level': access.get('access_level'),
|
||||||
|
'access_to': access.get('access_to'),
|
||||||
|
'access_type': access.get('access_type'),
|
||||||
|
'state': access.get('state'),
|
||||||
|
}
|
||||||
|
self.update_versioned_resource_dict(
|
||||||
|
request, access_dict, access)
|
||||||
|
return {'access': access_dict}
|
||||||
|
|
||||||
|
@common.ViewBuilder.versioned_method("2.21")
|
||||||
|
def add_access_key(self, context, access_dict, access):
|
||||||
|
access_dict['access_key'] = access.get('access_key')
|
@ -415,6 +415,11 @@ def share_access_get_all_for_share(context, share_id):
|
|||||||
return IMPL.share_access_get_all_for_share(context, share_id)
|
return IMPL.share_access_get_all_for_share(context, share_id)
|
||||||
|
|
||||||
|
|
||||||
|
def share_access_update_access_key(context, access_id, access_key):
|
||||||
|
"""Update the access_key field of a share access mapping."""
|
||||||
|
return IMPL.share_access_update_access_key(context, access_id, access_key)
|
||||||
|
|
||||||
|
|
||||||
def share_access_get_all_for_instance(context, instance_id, session=None):
|
def share_access_get_all_for_instance(context, instance_id, session=None):
|
||||||
"""Get all access rules related to a certain share instance."""
|
"""Get all access rules related to a certain share instance."""
|
||||||
return IMPL.share_access_get_all_for_instance(
|
return IMPL.share_access_get_all_for_instance(
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""add_access_key
|
||||||
|
|
||||||
|
Revision ID: 63809d875e32
|
||||||
|
Revises: 493eaffd79e1
|
||||||
|
Create Date: 2016-07-16 20:53:05.958896
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '63809d875e32'
|
||||||
|
down_revision = '493eaffd79e1'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column(
|
||||||
|
'share_access_map',
|
||||||
|
sa.Column('access_key', sa.String(255), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('share_access_map', 'access_key')
|
@ -1813,6 +1813,17 @@ def share_access_delete_all_by_share(context, share_id):
|
|||||||
filter_by(share_id=share_id).soft_delete()
|
filter_by(share_id=share_id).soft_delete()
|
||||||
|
|
||||||
|
|
||||||
|
@require_context
|
||||||
|
def share_access_update_access_key(context, access_id, access_key):
|
||||||
|
session = get_session()
|
||||||
|
with session.begin():
|
||||||
|
mapping = (session.query(models.ShareAccessMapping).
|
||||||
|
filter_by(id=access_id).first())
|
||||||
|
mapping.update({'access_key': access_key})
|
||||||
|
mapping.save(session=session)
|
||||||
|
return mapping
|
||||||
|
|
||||||
|
|
||||||
@require_context
|
@require_context
|
||||||
def share_instance_access_delete(context, mapping_id):
|
def share_instance_access_delete(context, mapping_id):
|
||||||
session = get_session()
|
session = get_session()
|
||||||
|
@ -538,6 +538,7 @@ class ShareAccessMapping(BASE, ManilaBase):
|
|||||||
share_id = Column(String(36), ForeignKey('shares.id'))
|
share_id = Column(String(36), ForeignKey('shares.id'))
|
||||||
access_type = Column(String(255))
|
access_type = Column(String(255))
|
||||||
access_to = Column(String(255))
|
access_to = Column(String(255))
|
||||||
|
access_key = Column(String(255), nullable=True)
|
||||||
|
|
||||||
access_level = Column(Enum(*constants.ACCESS_LEVELS),
|
access_level = Column(Enum(*constants.ACCESS_LEVELS),
|
||||||
default=constants.ACCESS_LEVEL_RW)
|
default=constants.ACCESS_LEVEL_RW)
|
||||||
|
@ -17,6 +17,8 @@ from oslo_log import log
|
|||||||
import six
|
import six
|
||||||
|
|
||||||
from manila.common import constants
|
from manila.common import constants
|
||||||
|
from manila import exception
|
||||||
|
from manila.i18n import _
|
||||||
from manila.i18n import _LI
|
from manila.i18n import _LI
|
||||||
from manila import utils
|
from manila import utils
|
||||||
|
|
||||||
@ -100,8 +102,9 @@ class ShareInstanceAccess(object):
|
|||||||
delete_rules = []
|
delete_rules = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
access_keys = None
|
||||||
try:
|
try:
|
||||||
self.driver.update_access(
|
access_keys = self.driver.update_access(
|
||||||
context,
|
context,
|
||||||
share_instance,
|
share_instance,
|
||||||
rules,
|
rules,
|
||||||
@ -116,6 +119,15 @@ class ShareInstanceAccess(object):
|
|||||||
self._update_access_fallback(add_rules, context, delete_rules,
|
self._update_access_fallback(add_rules, context, delete_rules,
|
||||||
remove_rules, share_instance,
|
remove_rules, share_instance,
|
||||||
share_server)
|
share_server)
|
||||||
|
|
||||||
|
if access_keys:
|
||||||
|
self._validate_access_keys(rules, add_rules, delete_rules,
|
||||||
|
access_keys)
|
||||||
|
|
||||||
|
for access_id, access_key in access_keys.items():
|
||||||
|
self.db.share_access_update_access_key(
|
||||||
|
context, access_id, access_key)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.db.share_instance_update_access_status(
|
self.db.share_instance_update_access_status(
|
||||||
context,
|
context,
|
||||||
@ -146,6 +158,32 @@ class ShareInstanceAccess(object):
|
|||||||
"share instance: %s"),
|
"share instance: %s"),
|
||||||
share_instance['id'])
|
share_instance['id'])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _validate_access_keys(access_rules, add_rules, delete_rules,
|
||||||
|
access_keys):
|
||||||
|
if not isinstance(access_keys, dict):
|
||||||
|
msg = _("The access keys must be supplied as a dictionary that "
|
||||||
|
"maps rule IDs to access keys.")
|
||||||
|
raise exception.Invalid(message=msg)
|
||||||
|
|
||||||
|
actual_rule_ids = sorted(access_keys)
|
||||||
|
expected_rule_ids = []
|
||||||
|
if not (add_rules or delete_rules):
|
||||||
|
expected_rule_ids = [rule['id'] for rule in access_rules]
|
||||||
|
else:
|
||||||
|
expected_rule_ids = [rule['id'] for rule in add_rules]
|
||||||
|
if actual_rule_ids != sorted(expected_rule_ids):
|
||||||
|
msg = (_("The rule IDs supplied: %(actual)s do not match the "
|
||||||
|
"rule IDs that are expected: %(expected)s.")
|
||||||
|
% {'actual': actual_rule_ids,
|
||||||
|
'expected': expected_rule_ids})
|
||||||
|
raise exception.Invalid(message=msg)
|
||||||
|
|
||||||
|
for access_key in access_keys.values():
|
||||||
|
if not isinstance(access_key, six.string_types):
|
||||||
|
msg = (_("Access key %s is not string type.") % access_key)
|
||||||
|
raise exception.Invalid(message=msg)
|
||||||
|
|
||||||
def _check_needs_refresh(self, context, rules, share_instance):
|
def _check_needs_refresh(self, context, rules, share_instance):
|
||||||
rule_ids = set([rule['id'] for rule in rules])
|
rule_ids = set([rule['id'] for rule in rules])
|
||||||
queried_rules = self.db.share_access_get_all_for_instance(
|
queried_rules = self.db.share_access_get_all_for_instance(
|
||||||
|
@ -1213,14 +1213,7 @@ class API(base.Base):
|
|||||||
# NOTE(tpsilva): refreshing share_access model
|
# NOTE(tpsilva): refreshing share_access model
|
||||||
access = self.db.share_access_get(ctx, access['id'])
|
access = self.db.share_access_get(ctx, access['id'])
|
||||||
|
|
||||||
return {
|
return access
|
||||||
'id': access['id'],
|
|
||||||
'share_id': access['share_id'],
|
|
||||||
'access_type': access['access_type'],
|
|
||||||
'access_to': access['access_to'],
|
|
||||||
'access_level': access['access_level'],
|
|
||||||
'state': access['state'],
|
|
||||||
}
|
|
||||||
|
|
||||||
def allow_access_to_instance(self, context, share_instance, access):
|
def allow_access_to_instance(self, context, share_instance, access):
|
||||||
policy.check_policy(context, 'share', 'allow_access')
|
policy.check_policy(context, 'share', 'allow_access')
|
||||||
@ -1302,12 +1295,7 @@ class API(base.Base):
|
|||||||
"""Returns all access rules for share."""
|
"""Returns all access rules for share."""
|
||||||
policy.check_policy(context, 'share', 'access_get_all')
|
policy.check_policy(context, 'share', 'access_get_all')
|
||||||
rules = self.db.share_access_get_all_for_share(context, share['id'])
|
rules = self.db.share_access_get_all_for_share(context, share['id'])
|
||||||
return [{'id': rule.id,
|
return rules
|
||||||
'access_type': rule.access_type,
|
|
||||||
'access_to': rule.access_to,
|
|
||||||
'access_level': rule.access_level,
|
|
||||||
'state': rule.state,
|
|
||||||
} for rule in rules]
|
|
||||||
|
|
||||||
def access_get(self, context, access_id):
|
def access_get(self, context, access_id):
|
||||||
"""Returns access rule with the id."""
|
"""Returns access rule with the id."""
|
||||||
|
@ -532,6 +532,14 @@ class ShareDriver(object):
|
|||||||
:param delete_rules: Empty List or List of access rules which should be
|
:param delete_rules: Empty List or List of access rules which should be
|
||||||
removed. access_rules doesn't contain these rules.
|
removed. access_rules doesn't contain these rules.
|
||||||
:param share_server: None or Share server model
|
:param share_server: None or Share server model
|
||||||
|
:returns: None, or a dictionary of ``access_id``, ``access_key`` as
|
||||||
|
key: value pairs for the rules added, where, ``access_id``
|
||||||
|
is the UUID (string) of the access rule, and ``access_key``
|
||||||
|
is the credential (string) of the entity granted access.
|
||||||
|
During recovery after error, the returned dictionary must
|
||||||
|
contain ``access_id``, ``access_key`` for all the rules that
|
||||||
|
the driver is ordered to resync, i.e. rules in the
|
||||||
|
``access_rules`` parameter.
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -827,6 +827,9 @@ class ShareActionsTest(test.TestCase):
|
|||||||
self.mock_object(share_api.API,
|
self.mock_object(share_api.API,
|
||||||
'allow_access',
|
'allow_access',
|
||||||
mock.Mock(return_value={'fake': 'fake'}))
|
mock.Mock(return_value={'fake': 'fake'}))
|
||||||
|
self.mock_object(self.controller._access_view_builder, 'view',
|
||||||
|
mock.Mock(return_value={'access':
|
||||||
|
{'fake': 'fake'}}))
|
||||||
|
|
||||||
id = 'fake_share_id'
|
id = 'fake_share_id'
|
||||||
body = {'os-allow_access': access}
|
body = {'os-allow_access': access}
|
||||||
@ -887,20 +890,23 @@ class ShareActionsTest(test.TestCase):
|
|||||||
body)
|
body)
|
||||||
|
|
||||||
def test_access_list(self):
|
def test_access_list(self):
|
||||||
def _fake_access_get_all(*args, **kwargs):
|
fake_access_list = [
|
||||||
return [{"state": "fakestatus",
|
{
|
||||||
"id": "fake_share_id",
|
"state": "fakestatus",
|
||||||
"access_type": "fakeip",
|
"id": "fake_access_id",
|
||||||
"access_to": "127.0.0.1"}]
|
"access_type": "fakeip",
|
||||||
|
"access_to": "127.0.0.1",
|
||||||
self.mock_object(share_api.API, "access_get_all",
|
}
|
||||||
_fake_access_get_all)
|
]
|
||||||
|
self.mock_object(self.controller._access_view_builder, 'list_view',
|
||||||
|
mock.Mock(return_value={'access_list':
|
||||||
|
fake_access_list}))
|
||||||
id = 'fake_share_id'
|
id = 'fake_share_id'
|
||||||
body = {"os-access_list": None}
|
body = {"os-access_list": None}
|
||||||
req = fakes.HTTPRequest.blank('/v1/tenant1/shares/%s/action' % id)
|
req = fakes.HTTPRequest.blank('/v1/tenant1/shares/%s/action' % id)
|
||||||
|
|
||||||
res_dict = self.controller._access_list(req, id, body)
|
res_dict = self.controller._access_list(req, id, body)
|
||||||
expected = _fake_access_get_all()
|
self.assertEqual({'access_list': fake_access_list}, res_dict)
|
||||||
self.assertEqual(expected, res_dict['access_list'])
|
|
||||||
|
|
||||||
def test_extend(self):
|
def test_extend(self):
|
||||||
id = 'fake_share_id'
|
id = 'fake_share_id'
|
||||||
|
@ -1328,6 +1328,9 @@ class ShareActionsTest(test.TestCase):
|
|||||||
self.mock_object(share_api.API,
|
self.mock_object(share_api.API,
|
||||||
'allow_access',
|
'allow_access',
|
||||||
mock.Mock(return_value={'fake': 'fake'}))
|
mock.Mock(return_value={'fake': 'fake'}))
|
||||||
|
self.mock_object(self.controller._access_view_builder, 'view',
|
||||||
|
mock.Mock(return_value={'access':
|
||||||
|
{'fake': 'fake'}}))
|
||||||
|
|
||||||
id = 'fake_share_id'
|
id = 'fake_share_id'
|
||||||
body = {'allow_access': access}
|
body = {'allow_access': access}
|
||||||
@ -1370,6 +1373,9 @@ class ShareActionsTest(test.TestCase):
|
|||||||
self.mock_object(share_api.API,
|
self.mock_object(share_api.API,
|
||||||
'allow_access',
|
'allow_access',
|
||||||
mock.Mock(return_value={'fake': 'fake'}))
|
mock.Mock(return_value={'fake': 'fake'}))
|
||||||
|
self.mock_object(self.controller._access_view_builder, 'view',
|
||||||
|
mock.Mock(return_value={'access':
|
||||||
|
{'fake': 'fake'}}))
|
||||||
|
|
||||||
req = fakes.HTTPRequest.blank(
|
req = fakes.HTTPRequest.blank(
|
||||||
'/v2/shares/%s/action' % share_id, version=version)
|
'/v2/shares/%s/action' % share_id, version=version)
|
||||||
@ -1419,20 +1425,23 @@ class ShareActionsTest(test.TestCase):
|
|||||||
body)
|
body)
|
||||||
|
|
||||||
def test_access_list(self):
|
def test_access_list(self):
|
||||||
def _fake_access_get_all(*args, **kwargs):
|
fake_access_list = [
|
||||||
return [{"state": "fakestatus",
|
{
|
||||||
"id": "fake_share_id",
|
"state": "fakestatus",
|
||||||
"access_type": "fakeip",
|
"id": "fake_access_id",
|
||||||
"access_to": "127.0.0.1"}]
|
"access_type": "fakeip",
|
||||||
|
"access_to": "127.0.0.1",
|
||||||
self.mock_object(share_api.API, "access_get_all",
|
}
|
||||||
_fake_access_get_all)
|
]
|
||||||
|
self.mock_object(self.controller._access_view_builder, 'list_view',
|
||||||
|
mock.Mock(return_value={'access_list':
|
||||||
|
fake_access_list}))
|
||||||
id = 'fake_share_id'
|
id = 'fake_share_id'
|
||||||
body = {"os-access_list": None}
|
body = {"os-access_list": None}
|
||||||
req = fakes.HTTPRequest.blank('/v1/tenant1/shares/%s/action' % id)
|
req = fakes.HTTPRequest.blank('/v2/tenant1/shares/%s/action' % id)
|
||||||
|
|
||||||
res_dict = self.controller._access_list(req, id, body)
|
res_dict = self.controller._access_list(req, id, body)
|
||||||
expected = _fake_access_get_all()
|
self.assertEqual({'access_list': fake_access_list}, res_dict)
|
||||||
self.assertEqual(expected, res_dict['access_list'])
|
|
||||||
|
|
||||||
@ddt.unpack
|
@ddt.unpack
|
||||||
@ddt.data(
|
@ddt.data(
|
||||||
|
80
manila/tests/api/views/test_share_accesses.py
Normal file
80
manila/tests/api/views/test_share_accesses.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# Copyright (c) 2016 Red Hat, 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 ddt
|
||||||
|
|
||||||
|
from manila.api.openstack import api_version_request as api_version
|
||||||
|
from manila.api.views import share_accesses
|
||||||
|
from manila import test
|
||||||
|
from manila.tests.api import fakes
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
|
class ViewBuilderTestCase(test.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ViewBuilderTestCase, self).setUp()
|
||||||
|
self.builder = share_accesses.ViewBuilder()
|
||||||
|
self.fake_access = {
|
||||||
|
'id': 'fakeaccessid',
|
||||||
|
'share_id': 'fakeshareid',
|
||||||
|
'access_level': 'fakeaccesslevel',
|
||||||
|
'access_to': 'fakeacccessto',
|
||||||
|
'access_type': 'fakeaccesstype',
|
||||||
|
'state': 'fakeaccessstate',
|
||||||
|
'access_key': 'fakeaccesskey',
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_collection_name(self):
|
||||||
|
self.assertEqual('share_accesses', self.builder._collection_name)
|
||||||
|
|
||||||
|
@ddt.data("2.20", "2.21")
|
||||||
|
def test_view(self, version):
|
||||||
|
req = fakes.HTTPRequest.blank('/shares', version=version)
|
||||||
|
|
||||||
|
result = self.builder.view(req, self.fake_access)
|
||||||
|
|
||||||
|
if (api_version.APIVersionRequest(version) <
|
||||||
|
api_version.APIVersionRequest("2.21")):
|
||||||
|
del self.fake_access['access_key']
|
||||||
|
|
||||||
|
self.assertEqual({'access': self.fake_access}, result)
|
||||||
|
|
||||||
|
@ddt.data("2.20", "2.21")
|
||||||
|
def test_summary_view(self, version):
|
||||||
|
req = fakes.HTTPRequest.blank('/shares', version=version)
|
||||||
|
|
||||||
|
result = self.builder.summary_view(req, self.fake_access)
|
||||||
|
|
||||||
|
if (api_version.APIVersionRequest(version) <
|
||||||
|
api_version.APIVersionRequest("2.21")):
|
||||||
|
del self.fake_access['access_key']
|
||||||
|
del self.fake_access['share_id']
|
||||||
|
|
||||||
|
self.assertEqual({'access': self.fake_access}, result)
|
||||||
|
|
||||||
|
@ddt.data("2.20", "2.21")
|
||||||
|
def test_list_view(self, version):
|
||||||
|
req = fakes.HTTPRequest.blank('/shares', version=version)
|
||||||
|
accesses = [self.fake_access, ]
|
||||||
|
|
||||||
|
result = self.builder.list_view(req, accesses)
|
||||||
|
|
||||||
|
if (api_version.APIVersionRequest(version) <
|
||||||
|
api_version.APIVersionRequest("2.21")):
|
||||||
|
del self.fake_access['access_key']
|
||||||
|
del self.fake_access['share_id']
|
||||||
|
|
||||||
|
self.assertEqual({'access_list': accesses}, result)
|
@ -920,3 +920,67 @@ class NewMTUColumnChecks(BaseMigrationChecks):
|
|||||||
self.test_case.assertTrue(db_result.rowcount >= len(ids))
|
self.test_case.assertTrue(db_result.rowcount >= len(ids))
|
||||||
for record in db_result:
|
for record in db_result:
|
||||||
self.test_case.assertFalse(hasattr(record, 'mtu'))
|
self.test_case.assertFalse(hasattr(record, 'mtu'))
|
||||||
|
|
||||||
|
|
||||||
|
@map_to_migration('63809d875e32')
|
||||||
|
class AddAccessKeyToShareAccessMapping(BaseMigrationChecks):
|
||||||
|
table_name = 'share_access_map'
|
||||||
|
access_key_column_name = 'access_key'
|
||||||
|
|
||||||
|
def setup_upgrade_data(self, engine):
|
||||||
|
share_data = {
|
||||||
|
'id': uuidutils.generate_uuid(),
|
||||||
|
'share_proto': "CEPHFS",
|
||||||
|
'size': 1,
|
||||||
|
'snapshot_id': None,
|
||||||
|
'user_id': 'fake',
|
||||||
|
'project_id': 'fake'
|
||||||
|
}
|
||||||
|
share_table = utils.load_table('shares', engine)
|
||||||
|
engine.execute(share_table.insert(share_data))
|
||||||
|
|
||||||
|
share_instance_data = {
|
||||||
|
'id': uuidutils.generate_uuid(),
|
||||||
|
'deleted': 'False',
|
||||||
|
'host': 'fake',
|
||||||
|
'share_id': share_data['id'],
|
||||||
|
'status': 'available',
|
||||||
|
'access_rules_status': 'active'
|
||||||
|
}
|
||||||
|
share_instance_table = utils.load_table('share_instances', engine)
|
||||||
|
engine.execute(share_instance_table.insert(share_instance_data))
|
||||||
|
|
||||||
|
share_access_data = {
|
||||||
|
'id': uuidutils.generate_uuid(),
|
||||||
|
'share_id': share_data['id'],
|
||||||
|
'access_type': 'cephx',
|
||||||
|
'access_to': 'alice',
|
||||||
|
'deleted': 'False'
|
||||||
|
}
|
||||||
|
share_access_table = utils.load_table(self.table_name, engine)
|
||||||
|
engine.execute(share_access_table.insert(share_access_data))
|
||||||
|
|
||||||
|
share_instance_access_data = {
|
||||||
|
'id': uuidutils.generate_uuid(),
|
||||||
|
'share_instance_id': share_instance_data['id'],
|
||||||
|
'access_id': share_access_data['id'],
|
||||||
|
'deleted': 'False'
|
||||||
|
}
|
||||||
|
share_instance_access_table = utils.load_table(
|
||||||
|
'share_instance_access_map', engine)
|
||||||
|
engine.execute(share_instance_access_table.insert(
|
||||||
|
share_instance_access_data))
|
||||||
|
|
||||||
|
def check_upgrade(self, engine, data):
|
||||||
|
share_access_table = utils.load_table(self.table_name, engine)
|
||||||
|
rows = engine.execute(share_access_table.select())
|
||||||
|
for row in rows:
|
||||||
|
self.test_case.assertTrue(hasattr(row,
|
||||||
|
self.access_key_column_name))
|
||||||
|
|
||||||
|
def check_downgrade(self, engine):
|
||||||
|
share_access_table = utils.load_table(self.table_name, engine)
|
||||||
|
rows = engine.execute(share_access_table.select())
|
||||||
|
for row in rows:
|
||||||
|
self.test_case.assertFalse(hasattr(row,
|
||||||
|
self.access_key_column_name))
|
||||||
|
@ -120,6 +120,17 @@ class ShareAccessDatabaseAPITestCase(test.TestCase):
|
|||||||
"fake_status"
|
"fake_status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ddt.data(None, 'rhubarb')
|
||||||
|
def test_share_access_update_access_key(self, key_value):
|
||||||
|
share = db_utils.create_share()
|
||||||
|
access = db_utils.create_access(share_id=share['id'])
|
||||||
|
|
||||||
|
db_api.share_access_update_access_key(self.ctxt, access['id'],
|
||||||
|
key_value)
|
||||||
|
|
||||||
|
access = db_api.share_access_get(self.ctxt, access['id'])
|
||||||
|
self.assertEqual(key_value, access['access_key'])
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
@ddt.ddt
|
||||||
class ShareDatabaseAPITestCase(test.TestCase):
|
class ShareDatabaseAPITestCase(test.TestCase):
|
||||||
|
@ -37,6 +37,10 @@ class ShareInstanceAccessTestCase(test.TestCase):
|
|||||||
self.share_instance = db_utils.create_share_instance(
|
self.share_instance = db_utils.create_share_instance(
|
||||||
share_id=self.share['id'],
|
share_id=self.share['id'],
|
||||||
access_rules_status=constants.STATUS_ERROR)
|
access_rules_status=constants.STATUS_ERROR)
|
||||||
|
self.rule = db_utils.create_access(
|
||||||
|
id='fakeaccessid',
|
||||||
|
share_id=self.share['id'],
|
||||||
|
access_to='fakeaccessto')
|
||||||
|
|
||||||
@ddt.data(True, False)
|
@ddt.data(True, False)
|
||||||
def test_update_access_rules_maintenance_mode(self, maintenance_mode):
|
def test_update_access_rules_maintenance_mode(self, maintenance_mode):
|
||||||
@ -64,7 +68,8 @@ class ShareInstanceAccessTestCase(test.TestCase):
|
|||||||
mock.Mock(return_value=existing_rules))
|
mock.Mock(return_value=existing_rules))
|
||||||
self.mock_object(db, "share_instance_update_access_status",
|
self.mock_object(db, "share_instance_update_access_status",
|
||||||
mock.Mock())
|
mock.Mock())
|
||||||
self.mock_object(self.driver, "update_access", mock.Mock())
|
self.mock_object(self.driver, "update_access",
|
||||||
|
mock.Mock(return_value=None))
|
||||||
self.mock_object(self.share_access_helper,
|
self.mock_object(self.share_access_helper,
|
||||||
"_remove_access_rules", mock.Mock())
|
"_remove_access_rules", mock.Mock())
|
||||||
self.mock_object(self.share_access_helper, "_check_needs_refresh",
|
self.mock_object(self.share_access_helper, "_check_needs_refresh",
|
||||||
@ -85,6 +90,87 @@ class ShareInstanceAccessTestCase(test.TestCase):
|
|||||||
db.share_instance_update_access_status.assert_called_with(
|
db.share_instance_update_access_status.assert_called_with(
|
||||||
self.context, share_instance['id'], constants.STATUS_ACTIVE)
|
self.context, share_instance['id'], constants.STATUS_ACTIVE)
|
||||||
|
|
||||||
|
@ddt.data(None, {'fakeaccessid': 'fakeaccesskey'})
|
||||||
|
def test_update_access_rules_returns_access_keys(self, access_keys):
|
||||||
|
share_instance = db_utils.create_share_instance(
|
||||||
|
id='fakeshareinstanceid',
|
||||||
|
share_id=self.share['id'],
|
||||||
|
access_rules_status=constants.STATUS_ACTIVE)
|
||||||
|
rules = [self.rule]
|
||||||
|
|
||||||
|
self.mock_object(db, "share_instance_get", mock.Mock(
|
||||||
|
return_value=share_instance))
|
||||||
|
self.mock_object(db, "share_access_get_all_for_instance",
|
||||||
|
mock.Mock(return_value=rules))
|
||||||
|
self.mock_object(db, "share_instance_update_access_status",
|
||||||
|
mock.Mock())
|
||||||
|
self.mock_object(db, "share_access_update_access_key",
|
||||||
|
mock.Mock())
|
||||||
|
self.mock_object(self.driver, "update_access",
|
||||||
|
mock.Mock(return_value=access_keys))
|
||||||
|
self.mock_object(self.share_access_helper,
|
||||||
|
"_remove_access_rules", mock.Mock())
|
||||||
|
self.mock_object(self.share_access_helper, "_check_needs_refresh",
|
||||||
|
mock.Mock(return_value=False))
|
||||||
|
|
||||||
|
self.share_access_helper.update_access_rules(
|
||||||
|
self.context, share_instance['id'], add_rules=rules)
|
||||||
|
|
||||||
|
self.driver.update_access.assert_called_once_with(
|
||||||
|
self.context, share_instance, rules, add_rules=rules,
|
||||||
|
delete_rules=[], share_server=None)
|
||||||
|
self.share_access_helper._remove_access_rules.assert_called_once_with(
|
||||||
|
self.context, [], share_instance['id'])
|
||||||
|
self.share_access_helper._check_needs_refresh.assert_called_once_with(
|
||||||
|
self.context, rules, share_instance)
|
||||||
|
if access_keys:
|
||||||
|
db.share_access_update_access_key.assert_called_with(
|
||||||
|
self.context, 'fakeaccessid', 'fakeaccesskey')
|
||||||
|
else:
|
||||||
|
self.assertFalse(db.share_access_update_access_key.called)
|
||||||
|
db.share_instance_update_access_status.assert_called_with(
|
||||||
|
self.context, share_instance['id'], constants.STATUS_ACTIVE)
|
||||||
|
|
||||||
|
@ddt.data({'maintenance_mode': True,
|
||||||
|
'access_keys': ['invalidaccesskey']},
|
||||||
|
{'maintenance_mode': True,
|
||||||
|
'access_keys': {'invalidaccessid': 'accesskey'}},
|
||||||
|
{'maintenance_mode': True,
|
||||||
|
'access_keys': {'fakeaccessid': 9}},
|
||||||
|
{'maintenance_mode': False,
|
||||||
|
'access_keys': {'fakeaccessid': 9}})
|
||||||
|
@ddt.unpack
|
||||||
|
def test_update_access_rules_invalid_access_keys(self, maintenance_mode,
|
||||||
|
access_keys):
|
||||||
|
access_rules_status = (
|
||||||
|
constants.STATUS_ERROR if maintenance_mode
|
||||||
|
else constants.STATUS_ACTIVE)
|
||||||
|
share_instance = db_utils.create_share_instance(
|
||||||
|
id='fakeid',
|
||||||
|
share_id=self.share['id'],
|
||||||
|
access_rules_status=access_rules_status)
|
||||||
|
|
||||||
|
rules = [self.rule]
|
||||||
|
add_rules = [] if maintenance_mode else rules
|
||||||
|
|
||||||
|
self.mock_object(db, "share_instance_get", mock.Mock(
|
||||||
|
return_value=share_instance))
|
||||||
|
self.mock_object(db, "share_access_get_all_for_instance",
|
||||||
|
mock.Mock(return_value=rules))
|
||||||
|
self.mock_object(db, "share_instance_update_access_status",
|
||||||
|
mock.Mock())
|
||||||
|
self.mock_object(self.driver, "update_access",
|
||||||
|
mock.Mock(return_value=access_keys))
|
||||||
|
|
||||||
|
self.assertRaises(exception.Invalid,
|
||||||
|
self.share_access_helper.update_access_rules,
|
||||||
|
self.context, share_instance['id'],
|
||||||
|
add_rules=add_rules)
|
||||||
|
|
||||||
|
self.driver.update_access.assert_called_once_with(
|
||||||
|
self.context, share_instance, rules, add_rules=add_rules,
|
||||||
|
delete_rules=[], share_server=None)
|
||||||
|
|
||||||
def test_update_access_rules_fallback(self):
|
def test_update_access_rules_fallback(self):
|
||||||
add_rules = [db_utils.create_access(share_id=self.share['id'])]
|
add_rules = [db_utils.create_access(share_id=self.share['id'])]
|
||||||
delete_rules = [db_utils.create_access(share_id=self.share['id'])]
|
delete_rules = [db_utils.create_access(share_id=self.share['id'])]
|
||||||
@ -158,7 +244,8 @@ class ShareInstanceAccessTestCase(test.TestCase):
|
|||||||
return_value=share_instance))
|
return_value=share_instance))
|
||||||
self.mock_object(db, "share_access_get_all_for_instance",
|
self.mock_object(db, "share_access_get_all_for_instance",
|
||||||
mock.Mock(return_value=original_rules))
|
mock.Mock(return_value=original_rules))
|
||||||
mock_update_access = self.mock_object(self.driver, "update_access")
|
mock_update_access = self.mock_object(self.driver, "update_access",
|
||||||
|
mock.Mock(return_value=None))
|
||||||
self.mock_object(self.share_access_helper, '_check_needs_refresh',
|
self.mock_object(self.share_access_helper, '_check_needs_refresh',
|
||||||
mock.Mock(side_effect=[True, False]))
|
mock.Mock(side_effect=[True, False]))
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ def fake_access(id, **kwargs):
|
|||||||
'access_type': 'fakeacctype',
|
'access_type': 'fakeacctype',
|
||||||
'access_to': 'fakeaccto',
|
'access_to': 'fakeaccto',
|
||||||
'access_level': 'rw',
|
'access_level': 'rw',
|
||||||
|
'access_key': None,
|
||||||
'state': 'fakeactive',
|
'state': 'fakeactive',
|
||||||
'STATE_NEW': 'fakenew',
|
'STATE_NEW': 'fakenew',
|
||||||
'STATE_ACTIVE': 'fakeactive',
|
'STATE_ACTIVE': 'fakeactive',
|
||||||
@ -1630,13 +1631,10 @@ class ShareAPITestCase(test.TestCase):
|
|||||||
'access_to': 'fake_access_to',
|
'access_to': 'fake_access_to',
|
||||||
'access_level': level,
|
'access_level': level,
|
||||||
}
|
}
|
||||||
fake_access_expected = copy.deepcopy(values)
|
fake_access = copy.deepcopy(values)
|
||||||
fake_access_expected.update({
|
fake_access.update({
|
||||||
'id': 'fake_access_id',
|
'id': 'fake_access_id',
|
||||||
'state': constants.STATUS_ACTIVE,
|
'state': constants.STATUS_ACTIVE,
|
||||||
})
|
|
||||||
fake_access = copy.deepcopy(fake_access_expected)
|
|
||||||
fake_access.update({
|
|
||||||
'deleted': 'fake_deleted',
|
'deleted': 'fake_deleted',
|
||||||
'deleted_at': 'fake_deleted_at',
|
'deleted_at': 'fake_deleted_at',
|
||||||
'instance_mappings': ['foo', 'bar'],
|
'instance_mappings': ['foo', 'bar'],
|
||||||
@ -1650,7 +1648,7 @@ class ShareAPITestCase(test.TestCase):
|
|||||||
self.context, share, fake_access['access_type'],
|
self.context, share, fake_access['access_type'],
|
||||||
fake_access['access_to'], level)
|
fake_access['access_to'], level)
|
||||||
|
|
||||||
self.assertEqual(fake_access_expected, access)
|
self.assertEqual(fake_access, access)
|
||||||
self.share_rpcapi.allow_access.assert_called_once_with(
|
self.share_rpcapi.allow_access.assert_called_once_with(
|
||||||
self.context, utils.IsAMatcher(models.ShareInstance),
|
self.context, utils.IsAMatcher(models.ShareInstance),
|
||||||
fake_access)
|
fake_access)
|
||||||
@ -1865,11 +1863,8 @@ class ShareAPITestCase(test.TestCase):
|
|||||||
self.mock_object(db_api, 'share_access_get_all_for_share',
|
self.mock_object(db_api, 'share_access_get_all_for_share',
|
||||||
mock.Mock(return_value=rules))
|
mock.Mock(return_value=rules))
|
||||||
actual = self.api.access_get_all(self.context, share)
|
actual = self.api.access_get_all(self.context, share)
|
||||||
for access in actual:
|
|
||||||
expected_access = values[access['id']]
|
|
||||||
expected_access.pop('share_id')
|
|
||||||
self.assertEqual(expected_access, access)
|
|
||||||
|
|
||||||
|
self.assertEqual(rules, actual)
|
||||||
share_api.policy.check_policy.assert_called_once_with(
|
share_api.policy.check_policy.assert_called_once_with(
|
||||||
self.context, 'share', 'access_get_all')
|
self.context, 'share', 'access_get_all')
|
||||||
db_api.share_access_get_all_for_share.assert_called_once_with(
|
db_api.share_access_get_all_for_share.assert_called_once_with(
|
||||||
|
@ -2419,6 +2419,10 @@ class ShareManagerTestCase(test.TestCase):
|
|||||||
mock.Mock(side_effect=exception.ShareResourceNotFound(
|
mock.Mock(side_effect=exception.ShareResourceNotFound(
|
||||||
share_id=share['id'])))
|
share_id=share['id'])))
|
||||||
if side_effect == 'delete_share':
|
if side_effect == 'delete_share':
|
||||||
|
self.mock_object(
|
||||||
|
self.share_manager.access_helper.driver, 'update_access',
|
||||||
|
mock.Mock(return_value=None)
|
||||||
|
)
|
||||||
self.mock_object(
|
self.mock_object(
|
||||||
self.share_manager.driver, 'delete_share',
|
self.share_manager.driver, 'delete_share',
|
||||||
mock.Mock(side_effect=exception.ShareResourceNotFound(
|
mock.Mock(side_effect=exception.ShareResourceNotFound(
|
||||||
|
@ -34,7 +34,7 @@ ShareGroup = [
|
|||||||
help="The minimum api microversion is configured to be the "
|
help="The minimum api microversion is configured to be the "
|
||||||
"value of the minimum microversion supported by Manila."),
|
"value of the minimum microversion supported by Manila."),
|
||||||
cfg.StrOpt("max_api_microversion",
|
cfg.StrOpt("max_api_microversion",
|
||||||
default="2.20",
|
default="2.21",
|
||||||
help="The maximum api microversion is configured to be the "
|
help="The maximum api microversion is configured to be the "
|
||||||
"value of the latest microversion supported by Manila."),
|
"value of the latest microversion supported by Manila."),
|
||||||
cfg.StrOpt("region",
|
cfg.StrOpt("region",
|
||||||
|
@ -424,7 +424,7 @@ class ShareRulesTest(base.BaseSharesTest):
|
|||||||
elif CONF.share.enable_cephx_rules_for_protocols:
|
elif CONF.share.enable_cephx_rules_for_protocols:
|
||||||
cls.protocol = CONF.share.enable_cephx_rules_for_protocols[0]
|
cls.protocol = CONF.share.enable_cephx_rules_for_protocols[0]
|
||||||
cls.access_type = "cephx"
|
cls.access_type = "cephx"
|
||||||
cls.access_to = "alice"
|
cls.access_to = "eve"
|
||||||
cls.shares_v2_client.share_protocol = cls.protocol
|
cls.shares_v2_client.share_protocol = cls.protocol
|
||||||
cls.share = cls.create_share()
|
cls.share = cls.create_share()
|
||||||
|
|
||||||
@ -465,7 +465,10 @@ class ShareRulesTest(base.BaseSharesTest):
|
|||||||
version=version)
|
version=version)
|
||||||
|
|
||||||
# verify keys
|
# verify keys
|
||||||
for key in ("id", "access_type", "access_to", "access_level"):
|
keys = ("id", "access_type", "access_to", "access_level")
|
||||||
|
if utils.is_microversion_ge(version, '2.21'):
|
||||||
|
keys += ("access_key", )
|
||||||
|
for key in keys:
|
||||||
[self.assertIn(key, r.keys()) for r in rules]
|
[self.assertIn(key, r.keys()) for r in rules]
|
||||||
for key in ('deleted', 'deleted_at', 'instance_mappings'):
|
for key in ('deleted', 'deleted_at', 'instance_mappings'):
|
||||||
[self.assertNotIn(key, r.keys()) for r in rules]
|
[self.assertNotIn(key, r.keys()) for r in rules]
|
||||||
@ -474,6 +477,11 @@ class ShareRulesTest(base.BaseSharesTest):
|
|||||||
self.assertEqual(self.access_type, rules[0]["access_type"])
|
self.assertEqual(self.access_type, rules[0]["access_type"])
|
||||||
self.assertEqual(self.access_to, rules[0]["access_to"])
|
self.assertEqual(self.access_to, rules[0]["access_to"])
|
||||||
self.assertEqual('rw', rules[0]["access_level"])
|
self.assertEqual('rw', rules[0]["access_level"])
|
||||||
|
if utils.is_microversion_ge(version, '2.21'):
|
||||||
|
if self.access_type == 'cephx':
|
||||||
|
self.assertIsNotNone(rules[0]['access_key'])
|
||||||
|
else:
|
||||||
|
self.assertIsNone(rules[0]['access_key'])
|
||||||
|
|
||||||
# our share id in list and have no duplicates
|
# our share id in list and have no duplicates
|
||||||
gen = [r["id"] for r in rules if r["id"] in rule["id"]]
|
gen = [r["id"] for r in rules if r["id"] in rule["id"]]
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Driver may return ``access_key``, an access credential, for client
|
||||||
|
identities granted share access.
|
||||||
|
- Added ``access_key`` to the JSON response of ``access_list`` API.
|
Loading…
Reference in New Issue
Block a user