Map volume/snapshot manage extensions to v3

Create routes for the resources in the volume_manage and snapshot_manage
extensions in v3, and bump the microversion.

APIImpact
DocImpact
Implements: blueprint list-manage-existing
Change-Id: Ia0df241a43f41cac4248efffc0b9828032daf63a
This commit is contained in:
Avishay Traeger 2016-04-18 18:19:40 +03:00
parent aaf820f429
commit 0b0000f8fc
8 changed files with 385 additions and 1 deletions

View File

@ -55,6 +55,7 @@ REST_API_VERSION_HISTORY = """
* 3.6 - Allows to set empty description and empty name for consistency
group in consisgroup-update operation.
* 3.7 - Add cluster API and cluster_name field to service list API
* 3.8 - Adds resources from volume_manage and snapshot_manage extensions.
"""
@ -63,7 +64,7 @@ REST_API_VERSION_HISTORY = """
# minimum version of the API supported.
# Explicitly using /v1 or /v2 enpoints will still work
_MIN_API_VERSION = "3.0"
_MAX_API_VERSION = "3.7"
_MAX_API_VERSION = "3.8"
_LEGACY_API_VERSION1 = "1.0"
_LEGACY_API_VERSION2 = "2.0"

View File

@ -146,3 +146,9 @@ user documentation.
...
}
]
3.8
---
Adds the following resources that were previously in extensions:
- os-volume-manage => /v3/<project_id>/manageable_volumes
- os-snapshot-manage => /v3/<project_id>/manageable_snapshots

View File

@ -29,6 +29,8 @@ from cinder.api.v2 import volume_metadata
from cinder.api.v3 import clusters
from cinder.api.v3 import consistencygroups
from cinder.api.v3 import messages
from cinder.api.v3 import snapshot_manage
from cinder.api.v3 import volume_manage
from cinder.api.v3 import volumes
from cinder.api import versions
@ -112,3 +114,14 @@ class APIRouter(cinder.api.openstack.APIRouter):
controller=self.resources['consistencygroups'],
collection={'detail': 'GET'},
member={'action': 'POST'})
self.resources['manageable_volumes'] = volume_manage.create_resource()
mapper.resource("manageable_volume", "manageable_volumes",
controller=self.resources['manageable_volumes'],
collection={'detail': 'GET'})
self.resources['manageable_snapshots'] = \
snapshot_manage.create_resource()
mapper.resource("manageable_snapshot", "manageable_snapshots",
controller=self.resources['manageable_snapshots'],
collection={'detail': 'GET'})

View File

@ -0,0 +1,45 @@
# Copyright (c) 2016 Stratoscale, Ltd.
#
# 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 cinder.api.contrib import snapshot_manage as snapshot_manage_v2
from cinder.api.openstack import wsgi
from cinder import exception
class SnapshotManageController(snapshot_manage_v2.SnapshotManageController):
def _ensure_min_version(self, req, allowed_version):
version = req.api_version_request
if not version.matches(allowed_version, None):
raise exception.VersionNotFoundForAPIMethod(version=version)
@wsgi.response(202)
def create(self, req, body):
self._ensure_min_version(req, "3.8")
return super(SnapshotManageController, self).create(req, body)
@wsgi.extends
def index(self, req):
"""Returns a summary list of snapshots available to manage."""
self._ensure_min_version(req, "3.8")
return super(SnapshotManageController, self).index(req)
@wsgi.extends
def detail(self, req):
"""Returns a detailed list of snapshots available to manage."""
self._ensure_min_version(req, "3.8")
return super(SnapshotManageController, self).detail(req)
def create_resource():
return wsgi.Resource(SnapshotManageController())

View File

@ -0,0 +1,45 @@
# Copyright (c) 2016 Stratoscale, Ltd.
#
# 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 cinder.api.contrib import volume_manage as volume_manage_v2
from cinder.api.openstack import wsgi
from cinder import exception
class VolumeManageController(volume_manage_v2.VolumeManageController):
def _ensure_min_version(self, req, allowed_version):
version = req.api_version_request
if not version.matches(allowed_version, None):
raise exception.VersionNotFoundForAPIMethod(version=version)
@wsgi.response(202)
def create(self, req, body):
self._ensure_min_version(req, "3.8")
return super(VolumeManageController, self).create(req, body)
@wsgi.extends
def index(self, req):
"""Returns a summary list of volumes available to manage."""
self._ensure_min_version(req, "3.8")
return super(VolumeManageController, self).index(req)
@wsgi.extends
def detail(self, req):
"""Returns a detailed list of volumes available to manage."""
self._ensure_min_version(req, "3.8")
return super(VolumeManageController, self).detail(req)
def create_resource():
return wsgi.Resource(VolumeManageController())

View File

@ -0,0 +1,134 @@
# Copyright (c) 2016 Stratoscale, Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_serialization import jsonutils
try:
from urllib import urlencode
except ImportError:
from urllib.parse import urlencode
import webob
from cinder.api.v3 import router as router_v3
from cinder import context
from cinder import test
from cinder.tests.unit.api.contrib import test_snapshot_manage as test_contrib
from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_constants as fake
from cinder.tests.unit import fake_service
def app():
# no auth, just let environ['cinder.context'] pass through
api = router_v3.APIRouter()
mapper = fakes.urlmap.URLMap()
mapper['/v3'] = api
return mapper
@mock.patch('cinder.volume.api.API.get', test_contrib.volume_get)
class SnapshotManageTest(test.TestCase):
"""Test cases for cinder/api/v3/snapshot_manage.py"""
def setUp(self):
super(SnapshotManageTest, self).setUp()
self._admin_ctxt = context.RequestContext(fake.USER_ID,
fake.PROJECT_ID,
True)
def _get_resp_post(self, body, version="3.8"):
"""Helper to execute a POST manageable_snapshots API call."""
req = webob.Request.blank('/v3/%s/manageable_snapshots' %
fake.PROJECT_ID)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt
req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(app())
return res
@mock.patch('cinder.volume.rpcapi.VolumeAPI.manage_existing_snapshot')
@mock.patch('cinder.volume.api.API.create_snapshot_in_db')
@mock.patch('cinder.objects.service.Service.get_by_args')
def test_manage_snapshot_route(self, mock_service_get,
mock_create_snapshot, mock_rpcapi):
"""Test call to manage snapshot.
There is currently no change between the API in contrib and the API in
v3, so here we simply check that the call is routed properly, rather
than copying all the tests.
"""
mock_service_get.return_value = fake_service.fake_service_obj(
self._admin_ctxt,
binary='cinder-volume')
body = {'snapshot': {'volume_id': fake.VOLUME_ID, 'ref': 'fake_ref'}}
res = self._get_resp_post(body)
self.assertEqual(202, res.status_int, res)
def test_manage_snapshot_previous_version(self):
body = {'snapshot': {'volume_id': fake.VOLUME_ID, 'ref': 'fake_ref'}}
res = self._get_resp_post(body, version="3.7")
self.assertEqual(404, res.status_int, res)
def _get_resp_get(self, host, detailed, paging, version="3.8"):
"""Helper to execute a GET os-snapshot-manage API call."""
params = {'host': host}
if paging:
params.update({'marker': '1234', 'limit': 10,
'offset': 4, 'sort': 'reference:asc'})
query_string = "?%s" % urlencode(params)
detail = ""
if detailed:
detail = "/detail"
req = webob.Request.blank('/v3/%s/manageable_snapshots%s%s' %
(fake.PROJECT_ID, detail, query_string))
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt
res = req.get_response(app())
return res
@mock.patch('cinder.volume.api.API.get_manageable_snapshots',
wraps=test_contrib.api_get_manageable_snapshots)
def test_get_manageable_snapshots_route(self, mock_api_manageable):
"""Test call to get manageable volumes.
There is currently no change between the API in contrib and the API in
v3, so here we simply check that the call is routed properly, rather
than copying all the tests.
"""
res = self._get_resp_get('fakehost', False, False)
self.assertEqual(200, res.status_int)
def test_get_manageable_snapshots_previous_version(self):
res = self._get_resp_get('fakehost', False, False, version="3.7")
self.assertEqual(404, res.status_int)
@mock.patch('cinder.volume.api.API.get_manageable_snapshots',
wraps=test_contrib.api_get_manageable_snapshots)
def test_get_manageable_snapshots_detail_route(self, mock_api_manageable):
"""Test call to get manageable volumes (detailed).
There is currently no change between the API in contrib and the API in
v3, so here we simply check that the call is routed properly, rather
than copying all the tests.
"""
res = self._get_resp_get('fakehost', True, True)
self.assertEqual(200, res.status_int)
def test_get_manageable_snapshots_detail_previous_version(self):
res = self._get_resp_get('fakehost', True, True, version="3.7")
self.assertEqual(404, res.status_int)

View File

@ -0,0 +1,136 @@
# Copyright (c) 2016 Stratoscale, Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_serialization import jsonutils
try:
from urllib import urlencode
except ImportError:
from urllib.parse import urlencode
import webob
from cinder.api.v3 import router as router_v3
from cinder import context
from cinder import test
from cinder.tests.unit.api.contrib import test_volume_manage as test_contrib
from cinder.tests.unit.api import fakes
from cinder.tests.unit import fake_constants as fake
def app():
# no auth, just let environ['cinder.context'] pass through
api = router_v3.APIRouter()
mapper = fakes.urlmap.URLMap()
mapper['/v3'] = api
return mapper
@mock.patch('cinder.objects.service.Service.get_by_host_and_topic',
test_contrib.service_get_by_host_and_topic)
@mock.patch('cinder.volume.volume_types.get_volume_type_by_name',
test_contrib.vt_get_volume_type_by_name)
@mock.patch('cinder.volume.volume_types.get_volume_type',
test_contrib.vt_get_volume_type)
class VolumeManageTest(test.TestCase):
"""Test cases for cinder/api/v3/volume_manage.py"""
def setUp(self):
super(VolumeManageTest, self).setUp()
self._admin_ctxt = context.RequestContext(fake.USER_ID,
fake.PROJECT_ID,
True)
def _get_resp_post(self, body, version="3.8"):
"""Helper to execute a POST manageable_volumes API call."""
req = webob.Request.blank('/v3/%s/manageable_volumes' %
fake.PROJECT_ID)
req.method = 'POST'
req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt
req.body = jsonutils.dump_as_bytes(body)
res = req.get_response(app())
return res
@mock.patch('cinder.volume.api.API.manage_existing',
wraps=test_contrib.api_manage)
@mock.patch(
'cinder.api.openstack.wsgi.Controller.validate_name_and_description')
def test_manage_volume_route(self, mock_validate, mock_api_manage):
"""Test call to manage volume.
There is currently no change between the API in contrib and the API in
v3, so here we simply check that the call is routed properly, rather
than copying all the tests.
"""
body = {'volume': {'host': 'host_ok', 'ref': 'fake_ref'}}
res = self._get_resp_post(body)
self.assertEqual(202, res.status_int, res)
def test_manage_volume_previous_version(self):
body = {'volume': {'host': 'host_ok', 'ref': 'fake_ref'}}
res = self._get_resp_post(body)
self.assertEqual(404, res.status_int, res)
def _get_resp_get(self, host, detailed, paging, version="3.8"):
"""Helper to execute a GET os-volume-manage API call."""
params = {'host': host}
if paging:
params.update({'marker': '1234', 'limit': 10,
'offset': 4, 'sort': 'reference:asc'})
query_string = "?%s" % urlencode(params)
detail = ""
if detailed:
detail = "/detail"
req = webob.Request.blank('/v3/%s/manageable_volumes%s%s' %
(fake.PROJECT_ID, detail, query_string))
req.method = 'GET'
req.headers['Content-Type'] = 'application/json'
req.headers['OpenStack-API-Version'] = 'volume ' + version
req.environ['cinder.context'] = self._admin_ctxt
res = req.get_response(app())
return res
@mock.patch('cinder.volume.api.API.get_manageable_volumes',
wraps=test_contrib.api_get_manageable_volumes)
def test_get_manageable_volumes_route(self, mock_api_manageable):
"""Test call to get manageable volumes.
There is currently no change between the API in contrib and the API in
v3, so here we simply check that the call is routed properly, rather
than copying all the tests.
"""
res = self._get_resp_get('fakehost', False, True)
self.assertEqual(200, res.status_int)
def test_get_manageable_volumes_previous_version(self):
res = self._get_resp_get('fakehost', False, True, version="3.7")
self.assertEqual(404, res.status_int)
@mock.patch('cinder.volume.api.API.get_manageable_volumes',
wraps=test_contrib.api_get_manageable_volumes)
def test_get_manageable_volumes_detail_route(self, mock_api_manageable):
"""Test call to get manageable volumes (detailed).
There is currently no change between the API in contrib and the API in
v3, so here we simply check that the call is routed properly, rather
than copying all the tests.
"""
res = self._get_resp_get('fakehost', True, False)
self.assertEqual(200, res.status_int)
def test_get_manageable_volumes_detail_previous_version(self):
res = self._get_resp_get('fakehost', True, False, version="3.7")
self.assertEqual(404, res.status_int)

View File

@ -0,0 +1,4 @@
---
features:
- The v2 API extensions os-volume-manage and os-snapshot-manage have been
mapped to the v3 resources manageable_volumes and manageable_snapshots