Merge "Adds API version discovery support for V3"
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