Promote v1.0 to current

* Mark v0.1 as deprecated
* Add v1.0 to version list
* Use subclasses for wsgi Routers
* Add melange-manage route command to print routes
* Fixes certain routes not being exposed in the v1.0 api (LP947327)

Change-Id: Iac8294f2534decbbddf53230386fdbe8c622ff84
This commit is contained in:
Jason Kölker 2012-02-29 10:14:15 -06:00
parent b69e9db4c1
commit 01cb899ef7
4 changed files with 125 additions and 103 deletions

View File

@ -39,6 +39,7 @@ from melange import version
from melange.common import config from melange.common import config
from melange.common import utils from melange.common import utils
from melange.db import db_api from melange.db import db_api
from melange.ipam import service
def create_options(parser): def create_options(parser):
@ -69,11 +70,23 @@ class Commands(object):
def db_downgrade(self, version, repo_path=None): def db_downgrade(self, version, repo_path=None):
db_api.db_downgrade(self.conf, version, repo_path=repo_path) db_api.db_downgrade(self.conf, version, repo_path=repo_path)
def routes(self, version):
version = version.split('=')[-1].upper().replace('.', '')
if not version.startswith('V'):
version = 'V' + version
api = getattr(service, 'API' + version, None)
if not api:
print _('Could not import Router %s') % api
for route in api().map.matchlist:
print route.routepath, route.conditions['method']
def execute(self, command_name, *args): def execute(self, command_name, *args):
if self.has(command_name): if self.has(command_name):
return getattr(self, command_name)(*args) return getattr(self, command_name)(*args)
_commands = ['db_sync', 'db_upgrade', 'db_downgrade'] _commands = ['db_sync', 'db_upgrade', 'db_downgrade', 'routes']
@classmethod @classmethod
def has(cls, command_name): def has(cls, command_name):

View File

@ -568,106 +568,11 @@ class InterfaceAllowedIpsController(BaseController):
interface.disallow_ip(ip) interface.disallow_ip(ip)
class APIV01(wsgi.Router): class APICommon(wsgi.Router):
def __init__(self): def __init__(self):
mapper = routes.Mapper() mapper = routes.Mapper()
super(APIV01, self).__init__(mapper) super(APICommon, self).__init__(mapper)
self._networks_maper(mapper)
self._interface_ip_allocations_mapper(mapper)
self._interface_mapper(mapper)
self._allowed_ips_mapper(mapper)
APICommon(mapper)
def _networks_maper(self, mapper):
resource = NetworksController().create_resource()
path = "/ipam/tenants/{tenant_id}/networks/{network_id}"
mapper.resource("networks", path, controller=resource)
def _interface_ip_allocations_mapper(self, mapper):
path = ("/ipam/tenants/{tenant_id}/networks"
"/{network_id}/interfaces/{interface_id}")
resource = InterfaceIpAllocationsController().create_resource()
with mapper.submapper(controller=resource, path_prefix=path) as submap:
_connect(submap, "/ip_allocations", action='create',
conditions=dict(method=['POST']))
_connect(submap,
"/ip_allocations",
action='index',
conditions=dict(method=['GET']))
_connect(submap, "/ip_allocations", action='bulk_delete',
conditions=dict(method=['DELETE']))
def _interface_mapper(self, mapper):
interface_res = InterfacesController().create_resource()
path = "/ipam/interfaces"
_connect(mapper,
"/ipam/tenants/{tenant_id}/"
"interfaces/{virtual_interface_id}",
controller=interface_res,
action="show",
conditions=dict(method=['GET']))
_connect(mapper,
"/ipam/interfaces/{virtual_interface_id}",
controller=interface_res,
action="delete",
conditions=dict(method=['DELETE']))
mapper.resource("interfaces", path, controller=interface_res)
def _allowed_ips_mapper(self, mapper):
interface_allowed_ips = InterfaceAllowedIpsController()
mapper.connect("/ipam/tenants/{tenant_id}/"
"interfaces/{interface_id}/allowed_ips/{address:.+?}",
action="delete",
controller=interface_allowed_ips.create_resource(),
conditions=dict(method=["DELETE"]))
mapper.connect("/ipam/tenants/{tenant_id}/"
"interfaces/{interface_id}/allowed_ips/{address:.+?}",
action="show",
controller=interface_allowed_ips.create_resource(),
conditions=dict(method=["GET"]))
mapper.resource("allowed_ips",
"/allowed_ips",
controller=interface_allowed_ips.create_resource(),
path_prefix=("/ipam/tenants/{tenant_id}/"
"interfaces/{interface_id}"))
@classmethod
def app_factory(cls, global_conf, **local_conf):
return APIV01()
class APIV10(wsgi.Router):
def __init__(self):
mapper = routes.Mapper()
super(APIV10, self).__init__(mapper)
APICommon(mapper)
self._instance_interface_ips_mapper(mapper)
def _instance_interface_ips_mapper(self, mapper):
res = InstanceInterfaceIpsController().create_resource()
path_prefix = ("/ipam/instances/{device_id}/"
"interfaces/{interface_id}")
with mapper.submapper(controller=res,
path_prefix=path_prefix) as submap:
_connect(submap,
"/ip_addresses/{address:.+?}",
action="delete",
conditions=dict(method=["DELETE"]))
mapper.resource("ip_addresses",
"/ip_addresses",
controller=res,
path_prefix=path_prefix)
@classmethod
def app_factory(cls, global_conf, **local_conf):
return APIV10()
class APICommon():
def __init__(self, mapper):
self._natting_mapper(mapper, self._natting_mapper(mapper,
"inside_globals", "inside_globals",
InsideGlobalsController().create_resource()) InsideGlobalsController().create_resource())
@ -816,5 +721,97 @@ class APICommon():
conditions=dict(method=["DELETE"])) conditions=dict(method=["DELETE"]))
class APIV01(APICommon):
def __init__(self):
super(APIV01, self).__init__()
self._networks_maper(self.map)
self._interface_ip_allocations_mapper(self.map)
self._interface_mapper(self.map)
self._allowed_ips_mapper(self.map)
def _networks_maper(self, mapper):
resource = NetworksController().create_resource()
path = "/ipam/tenants/{tenant_id}/networks/{network_id}"
mapper.resource("networks", path, controller=resource)
def _interface_ip_allocations_mapper(self, mapper):
path = ("/ipam/tenants/{tenant_id}/networks"
"/{network_id}/interfaces/{interface_id}")
resource = InterfaceIpAllocationsController().create_resource()
with mapper.submapper(controller=resource, path_prefix=path) as submap:
_connect(submap, "/ip_allocations", action='create',
conditions=dict(method=['POST']))
_connect(submap,
"/ip_allocations",
action='index',
conditions=dict(method=['GET']))
_connect(submap, "/ip_allocations", action='bulk_delete',
conditions=dict(method=['DELETE']))
def _interface_mapper(self, mapper):
interface_res = InterfacesController().create_resource()
path = "/ipam/interfaces"
_connect(mapper,
"/ipam/tenants/{tenant_id}/"
"interfaces/{virtual_interface_id}",
controller=interface_res,
action="show",
conditions=dict(method=['GET']))
_connect(mapper,
"/ipam/interfaces/{virtual_interface_id}",
controller=interface_res,
action="delete",
conditions=dict(method=['DELETE']))
mapper.resource("interfaces", path, controller=interface_res)
def _allowed_ips_mapper(self, mapper):
interface_allowed_ips = InterfaceAllowedIpsController()
mapper.connect("/ipam/tenants/{tenant_id}/"
"interfaces/{interface_id}/allowed_ips/{address:.+?}",
action="delete",
controller=interface_allowed_ips.create_resource(),
conditions=dict(method=["DELETE"]))
mapper.connect("/ipam/tenants/{tenant_id}/"
"interfaces/{interface_id}/allowed_ips/{address:.+?}",
action="show",
controller=interface_allowed_ips.create_resource(),
conditions=dict(method=["GET"]))
mapper.resource("allowed_ips",
"/allowed_ips",
controller=interface_allowed_ips.create_resource(),
path_prefix=("/ipam/tenants/{tenant_id}/"
"interfaces/{interface_id}"))
@classmethod
def app_factory(cls, global_conf, **local_conf):
return APIV01()
class APIV10(APICommon):
def __init__(self):
super(APIV10, self).__init__()
self._instance_interface_ips_mapper(self.map)
def _instance_interface_ips_mapper(self, mapper):
res = InstanceInterfaceIpsController().create_resource()
path_prefix = ("/ipam/instances/{device_id}/"
"interfaces/{interface_id}")
with mapper.submapper(controller=res,
path_prefix=path_prefix) as submap:
_connect(submap,
"/ip_addresses/{address:.+?}",
action="delete",
conditions=dict(method=["DELETE"]))
mapper.resource("ip_addresses",
"/ip_addresses",
controller=res,
path_prefix=path_prefix)
@classmethod
def app_factory(cls, global_conf, **local_conf):
return APIV10()
def _connect(mapper, path, *args, **kwargs): def _connect(mapper, path, *args, **kwargs):
return mapper.connect(path + "{.format:(json|xml)?}", *args, **kwargs) return mapper.connect(path + "{.format:(json|xml)?}", *args, **kwargs)

View File

@ -31,12 +31,18 @@ class TestVersionsController(tests.BaseTest):
def test_versions_index(self): def test_versions_index(self):
response = self.test_app.get("/") response = self.test_app.get("/")
link = [{'href': "http://localhost/v0.1", 'rel': 'self'}] v01link = [{'href': "http://localhost/v0.1", 'rel': 'self'}]
v10link = [{'href': "http://localhost/v1.0", 'rel': 'self'}]
self.assertEqual(response.json, {'versions': self.assertEqual(response.json, {'versions':
[{ [{
'status':'CURRENT', 'status':'DEPRECATED',
'name': 'v0.1', 'name': 'v0.1',
'links': link, 'links': v01link,
},
{
'status':'CURRENT',
'name': 'v1.0',
'links': v10link,
}] }]
}) })
@ -48,10 +54,15 @@ class TestVersionsController(tests.BaseTest):
self.assertEqual(response.xml.tag, 'versions') self.assertEqual(response.xml.tag, 'versions')
self.assertEqual(response.body, self.assertEqual(response.body,
"""<versions> """<versions>
<version name="v0.1" status="CURRENT"> <version name="v0.1" status="DEPRECATED">
<links> <links>
<link href="http://localhost/v0.1" rel="self"/> <link href="http://localhost/v0.1" rel="self"/>
</links> </links>
</version> </version>
<version name="v1.0" status="CURRENT">
<links>
<link href="http://localhost/v1.0" rel="self"/>
</links>
</version>
</versions> </versions>
""") """)

View File

@ -26,7 +26,8 @@ class VersionsController(wsgi.Controller):
def index(self, request): def index(self, request):
"""Respond to a request for all OpenStack API versions.""" """Respond to a request for all OpenStack API versions."""
versions = [Version("v0.1", "CURRENT", request.application_url)] versions = [Version("v0.1", "DEPRECATED", request.application_url),
Version("v1.0", "CURRENT", request.application_url)]
return wsgi.Result(VersionsDataView(versions)) return wsgi.Result(VersionsDataView(versions))