Adds API version discovery support for V3
Adds version information for the V3 API which is only displayed when the V3 API is enabled. Even if the the V3 API is enabled the V3 API status is "EXPERIMENTAL" and the V2 one "CURRENT". This was done so autodiscovery tools would not yet use the V3 version by default. Ports the relevant parts of the version extension and associated tests to the V3 API to display V3 version information for /v3 GET requests. DocImpact Partially implements blueprint nova-v3-api Change-Id: Idd335ce0df63d91e94a4a757f1fbae94b576c37e
This commit is contained in:
@@ -10,6 +10,17 @@
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-01-21T11:33:21Z"
|
||||
},
|
||||
{
|
||||
"id": "v3.0",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v3/",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@@ -3,4 +3,7 @@
|
||||
<version status="CURRENT" updated="2011-01-21T11:33:21Z" id="v2.0">
|
||||
<atom:link href="http://openstack.example.com/v2/" rel="self"/>
|
||||
</version>
|
||||
</versions>
|
||||
<version status="EXPERIMENTAL" updated="2013-07-23T11:33:21Z" id="v3.0">
|
||||
<atom:link href="http://openstack.example.com/v3/" rel="self"/>
|
||||
</version>
|
||||
</versions>
|
||||
|
57
nova/api/openstack/compute/plugins/v3/versions.py
Normal file
57
nova/api/openstack/compute/plugins/v3/versions.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 IBM Corp.
|
||||
# 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 nova.api.openstack.compute import versions
|
||||
from nova.api.openstack.compute.views import versions as views_versions
|
||||
from nova.api.openstack import extensions
|
||||
from nova.api.openstack import wsgi
|
||||
|
||||
|
||||
ALIAS = "versions"
|
||||
|
||||
|
||||
class VersionsController(object):
|
||||
@extensions.expected_errors(())
|
||||
@wsgi.serializers(xml=versions.VersionTemplate,
|
||||
atom=versions.VersionAtomSerializer)
|
||||
def show(self, req):
|
||||
builder = views_versions.get_view_builder(req)
|
||||
return builder.build_version(versions.VERSIONS['v3.0'])
|
||||
|
||||
|
||||
class Versions(extensions.V3APIExtensionBase):
|
||||
"""API Version information."""
|
||||
|
||||
name = "Versions"
|
||||
alias = ALIAS
|
||||
namespace = "http://docs.openstack.org/compute/core/versions/v3"
|
||||
version = 1
|
||||
|
||||
def get_resources(self):
|
||||
resources = [
|
||||
extensions.ResourceExtension(ALIAS, VersionsController(),
|
||||
custom_routes_fn=self.version_map)]
|
||||
return resources
|
||||
|
||||
def get_controller_extensions(self):
|
||||
return []
|
||||
|
||||
def version_map(self, mapper, wsgi_resource):
|
||||
mapper.connect("versions", "/",
|
||||
controller=wsgi_resource,
|
||||
action='show', conditions={"method": ['GET']})
|
||||
mapper.redirect("", "/")
|
@@ -16,6 +16,7 @@
|
||||
# under the License.
|
||||
|
||||
from lxml import etree
|
||||
from oslo.config import cfg
|
||||
|
||||
from nova.api.openstack.compute.views import versions as views_versions
|
||||
from nova.api.openstack import wsgi
|
||||
@@ -23,6 +24,9 @@ from nova.api.openstack import xmlutil
|
||||
from nova.openstack.common import timeutils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('enabled', 'nova.api.openstack', group='osapi_v3')
|
||||
|
||||
LINKS = {
|
||||
'v2.0': {
|
||||
'pdf': 'http://docs.openstack.org/'
|
||||
@@ -30,6 +34,12 @@ LINKS = {
|
||||
'wadl': 'http://docs.openstack.org/'
|
||||
'api/openstack-compute/2/wadl/os-compute-2.wadl'
|
||||
},
|
||||
'v3.0': {
|
||||
'pdf': 'http://docs.openstack.org/'
|
||||
'api/openstack-compute/3/os-compute-devguide-3.pdf',
|
||||
'wadl': 'http://docs.openstack.org/'
|
||||
'api/openstack-compute/3/wadl/os-compute-3.wadl'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +70,33 @@ VERSIONS = {
|
||||
"type": "application/vnd.openstack.compute+json;version=2",
|
||||
}
|
||||
],
|
||||
},
|
||||
"v3.0": {
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": LINKS['v3.0']['pdf'],
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": LINKS['v3.0']['wadl'],
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute+xml;version=3",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute+json;version=3",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,6 +242,8 @@ class VersionAtomSerializer(AtomSerializer):
|
||||
class Versions(wsgi.Resource):
|
||||
def __init__(self):
|
||||
super(Versions, self).__init__(None)
|
||||
if not CONF.osapi_v3.enabled:
|
||||
del VERSIONS["v3.0"]
|
||||
|
||||
@wsgi.serializers(xml=VersionsTemplate,
|
||||
atom=VersionsAtomSerializer)
|
||||
|
@@ -44,7 +44,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": self.generate_href(req.path),
|
||||
"href": self.generate_href(version['id'], req.path),
|
||||
},
|
||||
],
|
||||
"media-types": version['media-types'],
|
||||
@@ -75,7 +75,7 @@ class ViewBuilder(common.ViewBuilder):
|
||||
|
||||
def _build_links(self, version_data):
|
||||
"""Generate a container of links that refer to the provided version."""
|
||||
href = self.generate_href()
|
||||
href = self.generate_href(version_data['id'])
|
||||
|
||||
links = [
|
||||
{
|
||||
@@ -86,10 +86,14 @@ class ViewBuilder(common.ViewBuilder):
|
||||
|
||||
return links
|
||||
|
||||
def generate_href(self, path=None):
|
||||
def generate_href(self, version, path=None):
|
||||
"""Create an url that refers to a specific version_number."""
|
||||
prefix = self._update_compute_link_prefix(self.base_url)
|
||||
version_number = 'v2'
|
||||
if version.find('v3.') == 0:
|
||||
version_number = 'v3'
|
||||
else:
|
||||
version_number = 'v2'
|
||||
|
||||
if path:
|
||||
path = path.strip('/')
|
||||
return os.path.join(prefix, version_number, path)
|
||||
|
246
nova/tests/api/openstack/compute/plugins/v3/test_versions.py
Normal file
246
nova/tests/api/openstack/compute/plugins/v3/test_versions.py
Normal file
@@ -0,0 +1,246 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2013 IBM Corp.
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
#
|
||||
# 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 feedparser
|
||||
from lxml import etree
|
||||
import webob
|
||||
|
||||
from nova.api.openstack import xmlutil
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova import test
|
||||
from nova.tests.api.openstack import common
|
||||
from nova.tests.api.openstack import fakes
|
||||
|
||||
|
||||
NS = {
|
||||
'atom': 'http://www.w3.org/2005/Atom',
|
||||
'ns': 'http://docs.openstack.org/common/api/v1.0'
|
||||
}
|
||||
|
||||
|
||||
EXP_LINKS = {
|
||||
'v3.0': {
|
||||
'pdf': 'http://docs.openstack.org/'
|
||||
'api/openstack-compute/3/os-compute-devguide-3.pdf',
|
||||
'wadl': 'http://docs.openstack.org/'
|
||||
'api/openstack-compute/3/wadl/os-compute-3.wadl'
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
EXP_VERSIONS = {
|
||||
"v3.0": {
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": EXP_LINKS['v3.0']['pdf'],
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": EXP_LINKS['v3.0']['wadl'],
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute+xml;version=3",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute+json;version=3",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VersionsTest(test.TestCase):
|
||||
|
||||
def test_get_version_list_302(self):
|
||||
req = webob.Request.blank('/v3')
|
||||
req.accept = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app_v3())
|
||||
self.assertEqual(res.status_int, 302)
|
||||
redirect_req = webob.Request.blank('/v3/')
|
||||
self.assertEqual(res.location, redirect_req.url)
|
||||
|
||||
def test_get_version_3_detail(self):
|
||||
req = webob.Request.blank('/v3/')
|
||||
req.accept = "application/json"
|
||||
res = req.get_response(fakes.wsgi_app_v3())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/json")
|
||||
version = jsonutils.loads(res.body)
|
||||
expected = {
|
||||
"version": {
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v3/",
|
||||
},
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v3/",
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": EXP_LINKS['v3.0']['pdf'],
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": EXP_LINKS['v3.0']['wadl'],
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute+xml;version=3",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute+json;version=3",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, version)
|
||||
|
||||
def test_get_version_3_detail_content_type(self):
|
||||
req = webob.Request.blank('/')
|
||||
req.accept = "application/json;version=3"
|
||||
res = req.get_response(fakes.wsgi_app_v3())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/json")
|
||||
version = jsonutils.loads(res.body)
|
||||
expected = {
|
||||
"version": {
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v3/",
|
||||
},
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v3/",
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": EXP_LINKS['v3.0']['pdf'],
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": EXP_LINKS['v3.0']['wadl'],
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute+xml;version=3",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/"
|
||||
"vnd.openstack.compute+json;version=3",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, version)
|
||||
|
||||
def test_get_version_3_detail_xml(self):
|
||||
req = webob.Request.blank('/v3/')
|
||||
req.accept = "application/xml"
|
||||
res = req.get_response(fakes.wsgi_app_v3())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual(res.content_type, "application/xml")
|
||||
|
||||
version = etree.XML(res.body)
|
||||
xmlutil.validate_schema(version, 'version')
|
||||
|
||||
expected = EXP_VERSIONS['v3.0']
|
||||
self.assertTrue(version.xpath('/ns:version', namespaces=NS))
|
||||
media_types = version.xpath('ns:media-types/ns:media-type',
|
||||
namespaces=NS)
|
||||
self.assertTrue(common.compare_media_types(media_types,
|
||||
expected['media-types']))
|
||||
for key in ['id', 'status', 'updated']:
|
||||
self.assertEqual(version.get(key), expected[key])
|
||||
links = version.xpath('atom:link', namespaces=NS)
|
||||
self.assertTrue(common.compare_links(links,
|
||||
[{'rel': 'self', 'href': 'http://localhost/v3/'}]
|
||||
+ expected['links']))
|
||||
|
||||
def test_get_version_3_detail_atom(self):
|
||||
req = webob.Request.blank('/v3/')
|
||||
req.accept = "application/atom+xml"
|
||||
res = req.get_response(fakes.wsgi_app_v3())
|
||||
self.assertEqual(res.status_int, 200)
|
||||
self.assertEqual("application/atom+xml", res.content_type)
|
||||
|
||||
xmlutil.validate_schema(etree.XML(res.body), 'atom')
|
||||
|
||||
f = feedparser.parse(res.body)
|
||||
self.assertEqual(f.feed.title, 'About This Version')
|
||||
self.assertEqual(f.feed.updated, '2013-07-23T11:33:21Z')
|
||||
self.assertEqual(f.feed.id, 'http://localhost/v3/')
|
||||
self.assertEqual(f.feed.author, 'Rackspace')
|
||||
self.assertEqual(f.feed.author_detail.href,
|
||||
'http://www.rackspace.com/')
|
||||
self.assertEqual(f.feed.links[0]['href'], 'http://localhost/v3/')
|
||||
self.assertEqual(f.feed.links[0]['rel'], 'self')
|
||||
|
||||
self.assertEqual(len(f.entries), 1)
|
||||
entry = f.entries[0]
|
||||
self.assertEqual(entry.id, 'http://localhost/v3/')
|
||||
self.assertEqual(entry.title, 'Version v3.0')
|
||||
self.assertEqual(entry.updated, '2013-07-23T11:33:21Z')
|
||||
self.assertEqual(len(entry.content), 1)
|
||||
self.assertEqual(entry.content[0].value,
|
||||
'Version v3.0 EXPERIMENTAL (2013-07-23T11:33:21Z)')
|
||||
self.assertEqual(len(entry.links), 3)
|
||||
self.assertEqual(entry.links[0]['href'], 'http://localhost/v3/')
|
||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
||||
self.assertEqual(entry.links[1], {
|
||||
'href': EXP_LINKS['v3.0']['pdf'],
|
||||
'type': 'application/pdf',
|
||||
'rel': 'describedby'})
|
||||
self.assertEqual(entry.links[2], {
|
||||
'href': EXP_LINKS['v3.0']['wadl'],
|
||||
'type': 'application/vnd.sun.wadl+xml',
|
||||
'rel': 'describedby'})
|
@@ -75,6 +75,21 @@ EXP_VERSIONS = {
|
||||
},
|
||||
],
|
||||
},
|
||||
"v3.0": {
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.compute+xml;version=3",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.compute+json;version=3",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +113,16 @@ class VersionsTest(test.TestCase):
|
||||
"href": "http://localhost/v2/",
|
||||
}],
|
||||
},
|
||||
{
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "self",
|
||||
"href": "http://localhost/v3/",
|
||||
}],
|
||||
},
|
||||
]
|
||||
self.assertEqual(versions, expected)
|
||||
|
||||
@@ -232,9 +257,9 @@ class VersionsTest(test.TestCase):
|
||||
|
||||
self.assertTrue(root.xpath('/ns:versions', namespaces=NS))
|
||||
versions = root.xpath('ns:version', namespaces=NS)
|
||||
self.assertEqual(len(versions), 1)
|
||||
self.assertEqual(len(versions), 2)
|
||||
|
||||
for i, v in enumerate(['v2.0']):
|
||||
for i, v in enumerate(['v2.0', 'v3.0']):
|
||||
version = versions[i]
|
||||
expected = EXP_VERSIONS[v]
|
||||
for key in ['id', 'status', 'updated']:
|
||||
@@ -291,7 +316,7 @@ class VersionsTest(test.TestCase):
|
||||
|
||||
f = feedparser.parse(res.body)
|
||||
self.assertEqual(f.feed.title, 'Available API Versions')
|
||||
self.assertEqual(f.feed.updated, '2011-01-21T11:33:21Z')
|
||||
self.assertEqual(f.feed.updated, '2013-07-23T11:33:21Z')
|
||||
self.assertEqual(f.feed.id, 'http://localhost/')
|
||||
self.assertEqual(f.feed.author, 'Rackspace')
|
||||
self.assertEqual(f.feed.author_detail.href,
|
||||
@@ -299,7 +324,7 @@ class VersionsTest(test.TestCase):
|
||||
self.assertEqual(f.feed.links[0]['href'], 'http://localhost/')
|
||||
self.assertEqual(f.feed.links[0]['rel'], 'self')
|
||||
|
||||
self.assertEqual(len(f.entries), 1)
|
||||
self.assertEqual(len(f.entries), 2)
|
||||
entry = f.entries[0]
|
||||
self.assertEqual(entry.id, 'http://localhost/v2/')
|
||||
self.assertEqual(entry.title, 'Version v2.0')
|
||||
@@ -311,6 +336,17 @@ class VersionsTest(test.TestCase):
|
||||
self.assertEqual(entry.links[0]['href'], 'http://localhost/v2/')
|
||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
||||
|
||||
entry = f.entries[1]
|
||||
self.assertEqual(entry.id, 'http://localhost/v3/')
|
||||
self.assertEqual(entry.title, 'Version v3.0')
|
||||
self.assertEqual(entry.updated, '2013-07-23T11:33:21Z')
|
||||
self.assertEqual(len(entry.content), 1)
|
||||
self.assertEqual(entry.content[0].value,
|
||||
'Version v3.0 EXPERIMENTAL (2013-07-23T11:33:21Z)')
|
||||
self.assertEqual(len(entry.links), 1)
|
||||
self.assertEqual(entry.links[0]['href'], 'http://localhost/v3/')
|
||||
self.assertEqual(entry.links[0]['rel'], 'self')
|
||||
|
||||
def test_multi_choice_image(self):
|
||||
req = webob.Request.blank('/images/1')
|
||||
req.accept = "application/json"
|
||||
@@ -320,6 +356,28 @@ class VersionsTest(test.TestCase):
|
||||
|
||||
expected = {
|
||||
"choices": [
|
||||
{
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://localhost/v3/images/1",
|
||||
"rel": "self",
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type":
|
||||
"application/vnd.openstack.compute+xml;version=3",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type":
|
||||
"application/vnd.openstack.compute+json;version=3",
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "v2.0",
|
||||
"status": "CURRENT",
|
||||
@@ -357,9 +415,9 @@ class VersionsTest(test.TestCase):
|
||||
root = etree.XML(res.body)
|
||||
self.assertTrue(root.xpath('/ns:choices', namespaces=NS))
|
||||
versions = root.xpath('ns:version', namespaces=NS)
|
||||
self.assertEqual(len(versions), 1)
|
||||
self.assertEqual(len(versions), 2)
|
||||
|
||||
version = versions[0]
|
||||
version = versions[1]
|
||||
self.assertEqual(version.get('id'), 'v2.0')
|
||||
self.assertEqual(version.get('status'), 'CURRENT')
|
||||
media_types = version.xpath('ns:media-types/ns:media-type',
|
||||
@@ -373,6 +431,20 @@ class VersionsTest(test.TestCase):
|
||||
self.assertTrue(common.compare_links(links,
|
||||
[{'rel': 'self', 'href': 'http://localhost/v2/images/1'}]))
|
||||
|
||||
version = versions[0]
|
||||
self.assertEqual(version.get('id'), 'v3.0')
|
||||
self.assertEqual(version.get('status'), 'EXPERIMENTAL')
|
||||
media_types = version.xpath('ns:media-types/ns:media-type',
|
||||
namespaces=NS)
|
||||
self.assertTrue(common.
|
||||
compare_media_types(media_types,
|
||||
EXP_VERSIONS['v3.0']['media-types']
|
||||
))
|
||||
|
||||
links = version.xpath('atom:link', namespaces=NS)
|
||||
self.assertTrue(common.compare_links(links,
|
||||
[{'rel': 'self', 'href': 'http://localhost/v3/images/1'}]))
|
||||
|
||||
def test_multi_choice_server_atom(self):
|
||||
"""
|
||||
Make sure multi choice responses do not have content-type
|
||||
@@ -394,6 +466,28 @@ class VersionsTest(test.TestCase):
|
||||
|
||||
expected = {
|
||||
"choices": [
|
||||
{
|
||||
"id": "v3.0",
|
||||
"status": "EXPERIMENTAL",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://localhost/v3/servers/" + uuid,
|
||||
"rel": "self",
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type":
|
||||
"application/vnd.openstack.compute+xml;version=3",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type":
|
||||
"application/vnd.openstack.compute+json;version=3",
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": "v2.0",
|
||||
"status": "CURRENT",
|
||||
@@ -461,7 +555,27 @@ class VersionsViewBuilderTests(test.TestCase):
|
||||
expected = "http://example.org/app/v2/"
|
||||
|
||||
builder = views.versions.ViewBuilder(base_url)
|
||||
actual = builder.generate_href()
|
||||
actual = builder.generate_href('v2')
|
||||
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_generate_href_v3(self):
|
||||
base_url = "http://example.org/app/"
|
||||
|
||||
expected = "http://example.org/app/v3/"
|
||||
|
||||
builder = views.versions.ViewBuilder(base_url)
|
||||
actual = builder.generate_href('v3.0')
|
||||
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_generate_href_unknown(self):
|
||||
base_url = "http://example.org/app/"
|
||||
|
||||
expected = "http://example.org/app/v2/"
|
||||
|
||||
builder = views.versions.ViewBuilder(base_url)
|
||||
actual = builder.generate_href('foo')
|
||||
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
|
@@ -4,12 +4,23 @@
|
||||
"id": "v2.0",
|
||||
"links": [
|
||||
{
|
||||
"href": "%(host)s/v2/",
|
||||
"href": "http://openstack.example.com/v2/",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"status": "CURRENT",
|
||||
"updated": "2011-01-21T11:33:21Z"
|
||||
},
|
||||
{
|
||||
"id": "v3.0",
|
||||
"links": [
|
||||
{
|
||||
"href": "http://openstack.example.com/v3/",
|
||||
"rel": "self"
|
||||
}
|
||||
],
|
||||
"status": "EXPERIMENTAL",
|
||||
"updated": "2013-07-23T11:33:21Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -3,4 +3,7 @@
|
||||
<version status="CURRENT" updated="2011-01-21T11:33:21Z" id="v2.0">
|
||||
<atom:link href="http://openstack.example.com/v2/" rel="self"/>
|
||||
</version>
|
||||
<version status="EXPERIMENTAL" updated="2013-07-23T11:33:21Z" id="v3.0">
|
||||
<atom:link href="http://openstack.example.com/v3/" rel="self"/>
|
||||
</version>
|
||||
</versions>
|
||||
|
@@ -106,6 +106,7 @@ nova.api.v3.extensions =
|
||||
services = nova.api.openstack.compute.plugins.v3.services:Services
|
||||
simple_tenant_usage = nova.api.openstack.compute.plugins.v3.simple_tenant_usage:SimpleTenantUsage
|
||||
used_limits = nova.api.openstack.compute.plugins.v3.used_limits:UsedLimits
|
||||
versions = nova.api.openstack.compute.plugins.v3.versions:Versions
|
||||
|
||||
nova.api.v3.extensions.server.create =
|
||||
availability_zone = nova.api.openstack.compute.plugins.v3.availability_zone:AvailabilityZone
|
||||
|
Reference in New Issue
Block a user