Merge "Add Share instances Admin API"
This commit is contained in:
commit
18fb649e4c
@ -30,6 +30,9 @@ class AdminActionsTest(base.BaseSharesAdminTest):
|
||||
cls.states = ["error", "available"]
|
||||
cls.bad_status = "error_deleting"
|
||||
cls.sh = cls.create_share()
|
||||
cls.sh_instance = (
|
||||
cls.shares_client.get_instances_of_share(cls.sh["id"])[0]
|
||||
)
|
||||
if CONF.share.run_snapshot_tests:
|
||||
cls.sn = cls.create_snapshot_wait_for_active(cls.sh["id"])
|
||||
|
||||
@ -39,6 +42,14 @@ class AdminActionsTest(base.BaseSharesAdminTest):
|
||||
self.shares_client.reset_state(self.sh["id"], status=status)
|
||||
self.shares_client.wait_for_share_status(self.sh["id"], status)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_reset_share_instance_state(self):
|
||||
id = self.sh_instance["id"]
|
||||
for status in self.states:
|
||||
self.shares_client.reset_state(
|
||||
id, s_type="share_instances", status=status)
|
||||
self.shares_client.wait_for_share_instance_status(id, status)
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
"Snapshot tests are disabled.")
|
||||
@ -63,6 +74,29 @@ class AdminActionsTest(base.BaseSharesAdminTest):
|
||||
self.shares_client.force_delete(share["id"])
|
||||
self.shares_client.wait_for_resource_deletion(share_id=share["id"])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
def test_force_delete_share_instance(self):
|
||||
share = self.create_share(cleanup_in_class=False)
|
||||
instances = self.shares_client.get_instances_of_share(share["id"])
|
||||
# Check that instance was created
|
||||
self.assertEqual(1, len(instances))
|
||||
|
||||
instance = instances[0]
|
||||
|
||||
# Change status from 'available' to 'error_deleting'
|
||||
self.shares_client.reset_state(
|
||||
instance["id"], s_type="share_instances", status=self.bad_status)
|
||||
|
||||
# Check that status was changed
|
||||
check_status = self.shares_client.get_share_instance(instance["id"])
|
||||
self.assertEqual(self.bad_status, check_status["status"])
|
||||
|
||||
# Share with status 'error_deleting' should be deleted
|
||||
self.shares_client.force_delete(
|
||||
instance["id"], s_type="share_instances")
|
||||
self.shares_client.wait_for_resource_deletion(
|
||||
share_instance_id=instance["id"])
|
||||
|
||||
@test.attr(type=["gate", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
"Snapshot tests are disabled.")
|
||||
|
@ -30,6 +30,9 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
def resource_setup(cls):
|
||||
super(AdminActionsNegativeTest, cls).resource_setup()
|
||||
cls.sh = cls.create_share()
|
||||
cls.sh_instance = (
|
||||
cls.shares_client.get_instances_of_share(cls.sh["id"])[0]
|
||||
)
|
||||
if CONF.share.run_snapshot_tests:
|
||||
cls.sn = cls.create_snapshot_wait_for_active(cls.sh["id"])
|
||||
cls.member_shares_client = clients.Manager().shares_client
|
||||
@ -39,6 +42,11 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.reset_state, "fake")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_reset_nonexistent_share_instance_state(self):
|
||||
self.assertRaises(lib_exc.NotFound, self.shares_client.reset_state,
|
||||
"fake", s_type="share_instances")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
"Snapshot tests are disabled.")
|
||||
@ -52,6 +60,16 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.shares_client.reset_state,
|
||||
self.sh["id"], status="fake")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_reset_share_instance_state_to_unacceptable_state(self):
|
||||
self.assertRaises(
|
||||
lib_exc.BadRequest,
|
||||
self.shares_client.reset_state,
|
||||
self.sh_instance["id"],
|
||||
s_type="share_instances",
|
||||
status="fake"
|
||||
)
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
"Snapshot tests are disabled.")
|
||||
@ -67,6 +85,13 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.member_shares_client.reset_state,
|
||||
self.sh["id"])
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_try_reset_share_instance_state_with_member(self):
|
||||
# Even if member from another tenant, it should be unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.reset_state,
|
||||
self.sh_instance["id"], s_type="share_instances")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
"Snapshot tests are disabled.")
|
||||
@ -81,6 +106,13 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.force_delete, "fake")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_force_delete_nonexistent_share_instance(self):
|
||||
self.assertRaises(lib_exc.NotFound,
|
||||
self.shares_client.force_delete,
|
||||
"fake",
|
||||
s_type="share_instances")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
"Snapshot tests are disabled.")
|
||||
@ -97,6 +129,13 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.member_shares_client.force_delete,
|
||||
self.sh["id"])
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_try_force_delete_share_instance_with_member(self):
|
||||
# If a non-admin tries to do force_delete, it should be unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.force_delete,
|
||||
self.sh_instance["id"], s_type="share_instances")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
@testtools.skipUnless(CONF.share.run_snapshot_tests,
|
||||
"Snapshot tests are disabled.")
|
||||
@ -105,3 +144,24 @@ class AdminActionsNegativeTest(base.BaseSharesAdminTest):
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.force_delete,
|
||||
self.sn["id"], s_type="snapshots")
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_try_get_share_instance_with_member(self):
|
||||
# If a non-admin tries to get instance, it should be unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.get_share_instance,
|
||||
self.sh_instance["id"])
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_try_list_share_instance_with_member(self):
|
||||
# If a non-admin tries to list instances, it should be unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.list_share_instances)
|
||||
|
||||
@test.attr(type=["gate", "negative", ])
|
||||
def test_try_get_instances_of_share_with_member(self):
|
||||
# If a non-admin tries to list instances of given share, it should be
|
||||
# unauthorized
|
||||
self.assertRaises(lib_exc.Forbidden,
|
||||
self.member_shares_client.get_instances_of_share,
|
||||
self.sh['id'])
|
||||
|
@ -36,7 +36,7 @@ ShareGroup = [
|
||||
help="The minimum api microversion is configured to be the "
|
||||
"value of the minimum microversion supported by Manila."),
|
||||
cfg.StrOpt("max_api_microversion",
|
||||
default="1.3",
|
||||
default="1.4",
|
||||
help="The maximum api microversion is configured to be the "
|
||||
"value of the latest microversion supported by Manila."),
|
||||
cfg.StrOpt("region",
|
||||
|
@ -48,6 +48,9 @@ class SharesClient(rest_client.RestClient):
|
||||
self.build_timeout = CONF.share.build_timeout
|
||||
self.API_MICROVERSIONS_HEADER = 'x-openstack-manila-api-version'
|
||||
|
||||
def _get_version_dict(self, version):
|
||||
return {self.API_MICROVERSIONS_HEADER: version}
|
||||
|
||||
def send_microversion_request(self, version=None):
|
||||
"""Prepare and send the HTTP GET Request to the base URL.
|
||||
|
||||
@ -144,6 +147,27 @@ class SharesClient(rest_client.RestClient):
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_instances_of_share(self, share_id):
|
||||
resp, body = self.get("shares/%s/instances" % share_id,
|
||||
headers=self._get_version_dict('1.4'),
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def list_share_instances(self):
|
||||
resp, body = self.get("share_instances",
|
||||
headers=self._get_version_dict('1.4'),
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def get_share_instance(self, instance_id):
|
||||
resp, body = self.get("share_instances/%s" % instance_id,
|
||||
headers=self._get_version_dict('1.4'),
|
||||
extra_headers=True)
|
||||
self.expected_success(200, resp.status)
|
||||
return self._parse_resp(body)
|
||||
|
||||
def create_access_rule(self, share_id, access_type="ip",
|
||||
access_to="0.0.0.0", access_level=None):
|
||||
post_body = {
|
||||
@ -262,6 +286,28 @@ class SharesClient(rest_client.RestClient):
|
||||
(share_name, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_share_instance_status(self, instance_id, status):
|
||||
"""Waits for a share to reach a given status."""
|
||||
body = self.get_share_instance(instance_id)
|
||||
instance_status = body['status']
|
||||
start = int(time.time())
|
||||
|
||||
while instance_status != status:
|
||||
time.sleep(self.build_interval)
|
||||
body = self.get_share(instance_id)
|
||||
instance_status = body['status']
|
||||
if instance_status == status:
|
||||
return
|
||||
elif 'error' in instance_status.lower():
|
||||
raise share_exceptions.\
|
||||
ShareInstanceBuildErrorException(id=instance_id)
|
||||
|
||||
if int(time.time()) - start >= self.build_timeout:
|
||||
message = ('Share instance %s failed to reach %s status within'
|
||||
' the required time (%s s).' %
|
||||
(instance_id, status, self.build_timeout))
|
||||
raise exceptions.TimeoutException(message)
|
||||
|
||||
def wait_for_snapshot_status(self, snapshot_id, status):
|
||||
"""Waits for a snapshot to reach a given status."""
|
||||
body = self.get_snapshot(snapshot_id)
|
||||
@ -375,6 +421,9 @@ class SharesClient(rest_client.RestClient):
|
||||
else:
|
||||
return self._is_resource_deleted(
|
||||
self.get_share, kwargs.get("share_id"))
|
||||
elif "share_instance_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_share_instance, kwargs.get("share_instance_id"))
|
||||
elif "snapshot_id" in kwargs:
|
||||
return self._is_resource_deleted(
|
||||
self.get_snapshot, kwargs.get("snapshot_id"))
|
||||
|
@ -20,6 +20,10 @@ class ShareBuildErrorException(exceptions.TempestException):
|
||||
message = "Share %(share_id)s failed to build and is in ERROR status"
|
||||
|
||||
|
||||
class ShareInstanceBuildErrorException(exceptions.TempestException):
|
||||
message = "Share instance %(id)s failed to build and is in ERROR status"
|
||||
|
||||
|
||||
class AccessRuleBuildErrorException(exceptions.TempestException):
|
||||
message = "Share's rule with id %(rule_id)s is in ERROR status"
|
||||
|
||||
|
@ -26,6 +26,9 @@
|
||||
"share:delete_share_metadata": "rule:default",
|
||||
"share:update_share_metadata": "rule:default",
|
||||
|
||||
"share_instance:index": "rule:admin_api",
|
||||
"share_instance:show": "rule:admin_api",
|
||||
|
||||
"share_extension:quotas:show": "",
|
||||
"share_extension:quotas:update": "rule:admin_api",
|
||||
"share_extension:quotas:delete": "rule:admin_api",
|
||||
@ -35,6 +38,8 @@
|
||||
"share_extension:share_admin_actions:reset_status": "rule:admin_api",
|
||||
"share_extension:snapshot_admin_actions:force_delete": "rule:admin_api",
|
||||
"share_extension:snapshot_admin_actions:reset_status": "rule:admin_api",
|
||||
"share_extension:share_instance_admin_actions:force_delete": "rule:admin_api",
|
||||
"share_extension:share_instance_admin_actions:reset_status": "rule:admin_api",
|
||||
|
||||
"share_extension:services": "rule:admin_api",
|
||||
"share_extension:availability_zones": "",
|
||||
|
@ -113,6 +113,21 @@ class ShareAdminController(AdminController):
|
||||
return self.share_api.delete(*args, **kwargs)
|
||||
|
||||
|
||||
class ShareInstancesAdminController(AdminController):
|
||||
"""AdminController for Share instances."""
|
||||
|
||||
collection = 'share_instances'
|
||||
|
||||
def _get(self, *args, **kwargs):
|
||||
return db.share_instance_get(*args, **kwargs)
|
||||
|
||||
def _update(self, *args, **kwargs):
|
||||
db.share_instance_update(*args, **kwargs)
|
||||
|
||||
def _delete(self, *args, **kwargs):
|
||||
return self.share_api.delete_instance(*args, **kwargs)
|
||||
|
||||
|
||||
class SnapshotAdminController(AdminController):
|
||||
"""AdminController for Snapshots."""
|
||||
|
||||
@ -133,11 +148,13 @@ class Admin_actions(extensions.ExtensionDescriptor):
|
||||
|
||||
name = "AdminActions"
|
||||
alias = "os-admin-actions"
|
||||
updated = "2012-08-25T00:00:00+00:00"
|
||||
updated = "2015-08-03T00:00:00+00:00"
|
||||
|
||||
def get_controller_extensions(self):
|
||||
exts = []
|
||||
for class_ in (ShareAdminController, SnapshotAdminController):
|
||||
controllers = (ShareAdminController, SnapshotAdminController,
|
||||
ShareInstancesAdminController)
|
||||
for class_ in controllers:
|
||||
controller = class_()
|
||||
extension = extensions.ControllerExtension(
|
||||
self, class_.collection, controller)
|
||||
|
@ -48,6 +48,7 @@ REST_API_VERSION_HISTORY = """
|
||||
* 1.1 - Versions API updated to reflect beginning of microversions epoch.
|
||||
* 1.2 - Share create() doesn't ignore availability_zone field of share.
|
||||
* 1.3 - Snapshots become optional feature.
|
||||
* 1.4 - Share instances admin API
|
||||
|
||||
"""
|
||||
|
||||
@ -55,7 +56,7 @@ REST_API_VERSION_HISTORY = """
|
||||
# The default api version request is defined to be the
|
||||
# the minimum version of the API supported.
|
||||
_MIN_API_VERSION = "1.0"
|
||||
_MAX_API_VERSION = "1.3"
|
||||
_MAX_API_VERSION = "1.4"
|
||||
DEFAULT_API_VERSION = _MIN_API_VERSION
|
||||
|
||||
|
||||
|
@ -40,3 +40,7 @@ user documentation.
|
||||
---
|
||||
Snapshots become optional and share payload now has
|
||||
boolean attr 'snapshot_support'.
|
||||
|
||||
1.4
|
||||
---
|
||||
Share instances admin API and update of Admin Actions extension.
|
||||
|
@ -26,6 +26,7 @@ import manila.api.openstack
|
||||
from manila.api.v1 import limits
|
||||
from manila.api.v1 import scheduler_stats
|
||||
from manila.api.v1 import security_service
|
||||
from manila.api.v1 import share_instances
|
||||
from manila.api.v1 import share_metadata
|
||||
from manila.api.v1 import share_networks
|
||||
from manila.api.v1 import share_servers
|
||||
@ -59,6 +60,18 @@ class APIRouter(manila.api.openstack.APIRouter):
|
||||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
|
||||
self.resources['share_instances'] = share_instances.create_resource()
|
||||
mapper.resource("share_instance", "share_instances",
|
||||
controller=self.resources['share_instances'],
|
||||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
|
||||
mapper.connect("share_instance",
|
||||
"/{project_id}/shares/{share_id}/instances",
|
||||
controller=self.resources['share_instances'],
|
||||
action='get_share_instances',
|
||||
conditions={"method": ['GET']})
|
||||
|
||||
self.resources['snapshots'] = share_snapshots.create_resource()
|
||||
mapper.resource("snapshot", "snapshots",
|
||||
controller=self.resources['snapshots'],
|
||||
|
77
manila/api/v1/share_instances.py
Normal file
77
manila/api/v1/share_instances.py
Normal file
@ -0,0 +1,77 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from webob import exc
|
||||
|
||||
from manila.api.openstack import wsgi
|
||||
from manila.api.views import share_instance as instance_view
|
||||
from manila import db
|
||||
from manila import exception
|
||||
from manila import policy
|
||||
from manila import share
|
||||
|
||||
|
||||
class ShareInstancesController(wsgi.Controller):
|
||||
"""The share instances API controller for the OpenStack API."""
|
||||
|
||||
resource_name = 'share_instance'
|
||||
_view_builder_class = instance_view.ViewBuilder
|
||||
|
||||
def __init__(self):
|
||||
self.share_api = share.API()
|
||||
super(ShareInstancesController, self).__init__()
|
||||
|
||||
def _authorize(self, context, action):
|
||||
try:
|
||||
policy.check_policy(context, self.resource_name, action)
|
||||
except exception.PolicyNotAuthorized:
|
||||
raise exc.HTTPForbidden()
|
||||
|
||||
@wsgi.Controller.api_version("1.4")
|
||||
def index(self, req):
|
||||
context = req.environ['manila.context']
|
||||
self._authorize(context, 'index')
|
||||
|
||||
instances = db.share_instances_get_all(context)
|
||||
return self._view_builder.detail_list(req, instances)
|
||||
|
||||
@wsgi.Controller.api_version("1.4")
|
||||
def show(self, req, id):
|
||||
context = req.environ['manila.context']
|
||||
self._authorize(context, 'show')
|
||||
|
||||
try:
|
||||
instance = db.share_instance_get(context, id)
|
||||
except exception.NotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
|
||||
return self._view_builder.detail(req, instance)
|
||||
|
||||
@wsgi.Controller.api_version("1.4")
|
||||
def get_share_instances(self, req, share_id):
|
||||
context = req.environ['manila.context']
|
||||
self._authorize(context, 'index')
|
||||
|
||||
try:
|
||||
share = self.share_api.get(context, share_id)
|
||||
except exception.NotFound:
|
||||
raise exc.HTTPNotFound()
|
||||
|
||||
view = instance_view.ViewBuilder()
|
||||
return view.detail_list(req, share.instances)
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(ShareInstancesController())
|
56
manila/api/views/share_instance.py
Normal file
56
manila/api/views/share_instance.py
Normal file
@ -0,0 +1,56 @@
|
||||
# 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 server API response as a python dictionary."""
|
||||
|
||||
_collection_name = 'share_instances'
|
||||
|
||||
def detail_list(self, request, instances):
|
||||
"""Detailed view of a list of share instances."""
|
||||
return self._list_view(self.detail, request, instances)
|
||||
|
||||
def detail(self, request, share_instance):
|
||||
"""Detailed view of a single share instance."""
|
||||
export_locations = [e['path'] for e in share_instance.export_locations]
|
||||
|
||||
instance_dict = {
|
||||
'id': share_instance.get('id'),
|
||||
'share_id': share_instance.get('share_id'),
|
||||
'availability_zone': share_instance.get('availability_zone'),
|
||||
'created_at': share_instance.get('created_at'),
|
||||
'host': share_instance.get('host'),
|
||||
'status': share_instance.get('status'),
|
||||
'share_network_id': share_instance.get('share_network_id'),
|
||||
'share_server_id': share_instance.get('share_server_id'),
|
||||
'export_location': share_instance.get('export_location'),
|
||||
'export_locations': export_locations,
|
||||
}
|
||||
|
||||
return {'share_instance': instance_dict}
|
||||
|
||||
def _list_view(self, func, request, instances):
|
||||
"""Provide a view for a list of share instances."""
|
||||
instances_list = [func(request, instance)['share_instance']
|
||||
for instance in instances]
|
||||
instances_links = self._get_collection_links(request,
|
||||
instances,
|
||||
self._collection_name)
|
||||
instances_dict = {self._collection_name: instances_list}
|
||||
|
||||
if instances_links:
|
||||
instances_dict[self._collection_name] = instances_links
|
||||
|
||||
return instances_dict
|
@ -303,6 +303,11 @@ def share_instance_update(context, instance_id, values, with_share_data=False):
|
||||
with_share_data=with_share_data)
|
||||
|
||||
|
||||
def share_instances_get_all(context):
|
||||
"""Returns all share instances."""
|
||||
return IMPL.share_instances_get_all(context)
|
||||
|
||||
|
||||
def share_instances_get_all_by_share_server(context, share_server_id):
|
||||
"""Returns all share instances with given share_server_id."""
|
||||
return IMPL.share_instances_get_all_by_share_server(context,
|
||||
|
@ -1184,6 +1184,15 @@ def share_instance_get(context, share_instance_id, session=None,
|
||||
return result
|
||||
|
||||
|
||||
@require_admin_context
|
||||
def share_instances_get_all(context):
|
||||
session = get_session()
|
||||
return (
|
||||
model_query(context, models.ShareInstance, session=session,
|
||||
read_deleted="no").all()
|
||||
)
|
||||
|
||||
|
||||
@require_context
|
||||
def share_instance_delete(context, instance_id, session=None):
|
||||
if session is None:
|
||||
|
@ -87,6 +87,14 @@ class AdminActionsTest(test.TestCase):
|
||||
snapshot['id'])
|
||||
return snapshot, req
|
||||
|
||||
def _setup_share_instance_data(self, instance=None):
|
||||
if instance is None:
|
||||
instance = db_utils.create_share(status=constants.STATUS_AVAILABLE,
|
||||
size='1').instance
|
||||
req = webob.Request.blank(
|
||||
'/v1/fake/share_instances/%s/action' % instance['id'])
|
||||
return instance, req
|
||||
|
||||
def _reset_status(self, ctxt, model, req, db_access_method,
|
||||
valid_code, valid_status=None, body=None):
|
||||
if body is None:
|
||||
@ -130,6 +138,17 @@ class AdminActionsTest(test.TestCase):
|
||||
self._reset_status(ctxt, snapshot, req, db.share_snapshot_get,
|
||||
valid_code, valid_status)
|
||||
|
||||
@ddt.data(*fixture_reset_status_with_different_roles)
|
||||
@ddt.unpack
|
||||
def test_share_instances_reset_status_with_different_roles(self, role,
|
||||
valid_code,
|
||||
valid_status):
|
||||
ctxt = self._get_context(role)
|
||||
instance, req = self._setup_share_instance_data()
|
||||
|
||||
self._reset_status(ctxt, instance, req, db.share_instance_get,
|
||||
valid_code, valid_status)
|
||||
|
||||
@ddt.data(*fixture_invalid_reset_status_body)
|
||||
def test_share_invalid_reset_status_body(self, body):
|
||||
share, req = self._setup_share_data()
|
||||
@ -146,6 +165,14 @@ class AdminActionsTest(test.TestCase):
|
||||
db.share_snapshot_get, 400,
|
||||
constants.STATUS_AVAILABLE, body)
|
||||
|
||||
@ddt.data(*fixture_invalid_reset_status_body)
|
||||
def test_share_instance_invalid_reset_status_body(self, body):
|
||||
instance, req = self._setup_share_instance_data()
|
||||
|
||||
self._reset_status(self.admin_context, instance, req,
|
||||
db.share_instance_get, 400,
|
||||
constants.STATUS_AVAILABLE, body)
|
||||
|
||||
def test_share_reset_status_for_missing(self):
|
||||
fake_share = {'id': 'missing-share-id'}
|
||||
req = webob.Request.blank('/v1/fake/shares/%s/action' %
|
||||
@ -201,3 +228,19 @@ class AdminActionsTest(test.TestCase):
|
||||
ctxt = self._get_context('admin')
|
||||
|
||||
self._force_delete(ctxt, snapshot, req, db.share_snapshot_get, 404)
|
||||
|
||||
@ddt.data(*fixture_force_delete_with_different_roles)
|
||||
@ddt.unpack
|
||||
def test_instance_force_delete_with_different_roles(self, role, resp_code):
|
||||
instance, req = self._setup_share_instance_data()
|
||||
ctxt = self._get_context(role)
|
||||
|
||||
self._force_delete(ctxt, instance, req, db.share_instance_get,
|
||||
resp_code)
|
||||
|
||||
def test_instance_force_delete_missing(self):
|
||||
instance, req = self._setup_share_instance_data(
|
||||
instance={'id': 'fake'})
|
||||
ctxt = self._get_context('admin')
|
||||
|
||||
self._force_delete(ctxt, instance, req, db.share_instance_get, 404)
|
||||
|
93
manila/tests/api/v1/test_share_instances.py
Normal file
93
manila/tests/api/v1/test_share_instances.py
Normal file
@ -0,0 +1,93 @@
|
||||
# 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 oslo_config import cfg
|
||||
from webob import exc as webob_exc
|
||||
|
||||
from manila.api.v1 import share_instances
|
||||
from manila import context
|
||||
from manila import test
|
||||
from manila.tests.api import fakes
|
||||
from manila.tests import db_utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class ShareInstancesApiTest(test.TestCase):
|
||||
"""Share Api Test."""
|
||||
def setUp(self):
|
||||
super(ShareInstancesApiTest, self).setUp()
|
||||
self.controller = share_instances.ShareInstancesController()
|
||||
self.context = context.RequestContext('admin', 'fake', True)
|
||||
|
||||
def _get_request(self, uri, context=None):
|
||||
if context is None:
|
||||
context = self.context
|
||||
req = fakes.HTTPRequest.blank('/shares', version="1.4")
|
||||
req.environ['manila.context'] = context
|
||||
return req
|
||||
|
||||
def _validate_ids_in_share_instances_list(self, expected, actual):
|
||||
self.assertEqual(len(expected), len(actual))
|
||||
self.assertEqual([i['id'] for i in expected],
|
||||
[i['id'] for i in actual])
|
||||
|
||||
def test_index(self):
|
||||
req = self._get_request('/share_instances')
|
||||
share_instances_count = 3
|
||||
test_instances = [
|
||||
db_utils.create_share(size=s + 1).instance
|
||||
for s in range(0, share_instances_count)
|
||||
]
|
||||
|
||||
actual_result = self.controller.index(req)
|
||||
|
||||
self._validate_ids_in_share_instances_list(
|
||||
test_instances, actual_result['share_instances'])
|
||||
|
||||
def test_show(self):
|
||||
test_instance = db_utils.create_share(size=1).instance
|
||||
id = test_instance['id']
|
||||
|
||||
actual_result = self.controller.show(self._get_request('fake'), id)
|
||||
|
||||
self.assertEqual(id, actual_result['share_instance']['id'])
|
||||
|
||||
def test_get_share_instances(self):
|
||||
test_share = db_utils.create_share(size=1)
|
||||
id = test_share['id']
|
||||
req = self._get_request('fake')
|
||||
|
||||
actual_result = self.controller.get_share_instances(req, id)
|
||||
|
||||
self._validate_ids_in_share_instances_list(
|
||||
[test_share.instance],
|
||||
actual_result['share_instances']
|
||||
)
|
||||
|
||||
@ddt.data('show', 'get_share_instances')
|
||||
def test_not_found(self, target_method_name):
|
||||
method = getattr(self.controller, target_method_name)
|
||||
self.assertRaises(webob_exc.HTTPNotFound, method,
|
||||
self._get_request('fake'), 'fake')
|
||||
|
||||
@ddt.data(('show', 2), ('get_share_instances', 2), ('index', 1))
|
||||
@ddt.unpack
|
||||
def test_access(self, target_method_name, args_count):
|
||||
user_context = context.RequestContext('fake', 'fake')
|
||||
req = self._get_request('fake', user_context)
|
||||
target_method = getattr(self.controller, target_method_name)
|
||||
args = [i for i in range(1, args_count)]
|
||||
|
||||
self.assertRaises(webob_exc.HTTPForbidden, target_method, req, *args)
|
@ -18,6 +18,9 @@
|
||||
"share:extend": "",
|
||||
"share:shrink": "",
|
||||
|
||||
"share_instance:index": "rule:admin_api",
|
||||
"share_instance:show": "rule:admin_api",
|
||||
|
||||
"share_network:create": "",
|
||||
"share_network:index": "",
|
||||
"share_network:detail": "",
|
||||
@ -38,6 +41,8 @@
|
||||
"share_extension:share_admin_actions:reset_status": "rule:admin_api",
|
||||
"share_extension:snapshot_admin_actions:force_delete": "rule:admin_api",
|
||||
"share_extension:snapshot_admin_actions:reset_status": "rule:admin_api",
|
||||
"share_extension:share_instance_admin_actions:force_delete": "rule:admin_api",
|
||||
"share_extension:share_instance_admin_actions:reset_status": "rule:admin_api",
|
||||
"share_extension:types_manage": "",
|
||||
"share_extension:types_extra_specs": "",
|
||||
"share_extension:share_type_access": "",
|
||||
|
Loading…
Reference in New Issue
Block a user