Add extensions for API v2 controllers

Controllers (and API endpoints) should be able to be selected using
configuration flags, as we do for plugins

TODO:
 - Extra Hooks to be configured using stevedore

Implements blueprint: pecan-wsme
Implements blueprint: api-extensions

Change-Id: I12f1bc1accbc870aacb0d1e08c9e0d08223c4b18
This commit is contained in:
Sylvain Bauza 2014-03-05 18:03:27 +01:00 committed by Sylvain Bauza
parent 954f5034ce
commit 23e327f79c
8 changed files with 141 additions and 17 deletions

View File

@ -16,24 +16,58 @@
"""Version 2 of the API.
"""
from oslo.config import cfg
import pecan
from pecan import rest
from stevedore import enabled
from climate.api.v2.controllers import host
from climate.api.v2.controllers import lease
from climate.openstack.common.gettextutils import _ # noqa
from climate import exceptions
from climate.openstack.common.gettextutils import _
from climate.openstack.common import log as logging
LOG = logging.getLogger(__name__)
api_opts = [
cfg.ListOpt('api_v2_controllers',
default=['oshosts', 'leases'],
help='API extensions to use'),
]
class V2Controller(pecan.rest.RestController):
CONF = cfg.CONF
CONF.register_opts(api_opts, 'api')
class V2Controller(rest.RestController):
"""Version 2 API controller root."""
_routes = {'os-hosts': 'oshosts',
'oshosts': 'None'}
_routes = {}
leases = lease.LeasesController()
oshosts = host.HostsController()
def _log_missing_plugins(self, names):
for name in names:
if name not in self.extension_manager.names():
LOG.error(_("API Plugin %s was not loaded") % name)
def __init__(self):
extensions = []
self.extension_manager = enabled.EnabledExtensionManager(
check_func=lambda ext: ext.name in CONF.api.api_v2_controllers,
namespace='climate.api.v2.controllers.extensions',
invoke_on_load=True
)
self._log_missing_plugins(CONF.api.api_v2_controllers)
for ext in self.extension_manager.extensions:
try:
setattr(self, ext.obj.name, ext.obj)
except TypeError:
raise exceptions.ClimateException(
_("API name must be specified for "
"extension {0}").format(ext.name))
self._routes.update(ext.obj.extra_routes)
extensions.append(ext.obj.name)
LOG.debug(_("Loaded extensions: {0}").format(extensions))
@pecan.expose()
def _route(self, args):
@ -44,7 +78,12 @@ class V2Controller(pecan.rest.RestController):
"""
try:
args[0] = self._routes.get(args[0], args[0])
route = self._routes.get(args[0], args[0])
if route is None:
# NOTE(sbauza): Route must map to a non-existing controller
args[0] = 'http404-nonexistingcontroller'
else:
args[0] = route
except IndexError:
LOG.error(_("No args found on V2 controller"))
return super(V2Controller, self)._route(args)

View File

@ -0,0 +1,63 @@
# Copyright (c) 2014 Bull.
#
# 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 abc
from oslo.config import cfg
from pecan import rest
from climate.openstack.common import log as logging
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
class BaseController(rest.RestController):
__metaclass__ = abc.ABCMeta
"""Mandatory API method name."""
name = None
"""Optional extra routes to add.
Dict of key/value pairs, where :
key : API method name redirect (public URL)
value : redirect target (can be None for routing to HTTP 404)
"""
extra_routes = {}
@abc.abstractmethod
def get_one(self, resource_id):
"""Get a single resource."""
pass
@abc.abstractmethod
def get_all(self):
"""Get all resources."""
pass
@abc.abstractmethod
def post(self, resource):
"""Create a resource."""
pass
@abc.abstractmethod
def put(self, resource):
"""Update a resource."""
pass
@abc.abstractmethod
def delete(self, resource_id):
"""Delete a single resource."""
pass

View File

@ -14,11 +14,11 @@
# limitations under the License.
import pecan
from pecan import rest
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from climate.api.v2.controllers import base
from climate.api.v2.controllers import extensions
from climate.api.v2.controllers import types
from climate import exceptions
from climate.openstack.common.gettextutils import _ # noqa
@ -95,10 +95,14 @@ class Host(base._Base):
)
class HostsController(rest.RestController):
class HostsController(extensions.BaseController):
"""Manages operations on hosts.
"""
name = 'oshosts'
extra_routes = {'os-hosts': 'oshosts',
'oshosts': None}
@policy.authorize('oshosts', 'get')
@wsme_pecan.wsexpose(Host, types.IntegerType())
def get_one(self, id):

View File

@ -14,11 +14,11 @@
# limitations under the License.
import pecan
from pecan import rest
from wsme import types as wtypes
import wsmeext.pecan as wsme_pecan
from climate.api.v2.controllers import base
from climate.api.v2.controllers import extensions
from climate.api.v2.controllers import types
from climate import exceptions
from climate.manager import service
@ -87,10 +87,12 @@ class Lease(base._Base):
)
class LeasesController(rest.RestController):
class LeasesController(extensions.BaseController):
"""Manages operations on leases.
"""
name = 'leases'
@policy.authorize('leases', 'get')
@wsme_pecan.wsexpose(Lease, types.UuidType())
def get_one(self, id):

View File

@ -104,6 +104,8 @@ function configure_climate() {
iniset $CLIMATE_CONF_FILE manager plugins basic.vm.plugin,physical.host.plugin
iniset $CLIMATE_CONF_FILE api api_v2_controllers oshosts,leases
iniset $CLIMATE_CONF_FILE database connection `database_connection_url climate`
iniset $CLIMATE_CONF_FILE DEFAULT use_syslog $SYSLOG

View File

@ -35,10 +35,10 @@ Lease is the main abstraction for the user in the Climate case. Lease means
some kind of contract where start time, end time and resources to be reserved
are mentioned.
.. rest-controller:: climate.api.v2.controllers.lease:LeasesController
.. rest-controller:: climate.api.v2.controllers.extensions.lease:LeasesController
:webprefix: /v2/leases
.. autotype:: climate.api.v2.controllers.lease.Lease
.. autotype:: climate.api.v2.controllers.extensions.lease.Lease
:members:
@ -50,8 +50,8 @@ are mentioned.
Host is the abstraction for a computehost in the Climate case. Host means
a specific type of resource to be allocated.
.. rest-controller:: climate.api.v2.controllers.host:HostsController
.. rest-controller:: climate.api.v2.controllers.extensions.host:HostsController
:webprefix: /v2/os-hosts
.. autotype:: climate.api.v2.controllers.host.Host
.. autotype:: climate.api.v2.controllers.extensions.host.Host
:members:

View File

@ -448,6 +448,16 @@
#image_prefix=reserved_
[api]
#
# Options defined in climate.api.v2.controllers
#
# API extensions to use (list value)
#api_v2_controllers=oshosts,leases
[database]
#

View File

@ -37,6 +37,10 @@ climate.resource.plugins =
physical.host.plugin=climate.plugins.oshosts.host_plugin:PhysicalHostPlugin
basic.vm.plugin=climate.plugins.instances.vm_plugin:VMPlugin
climate.api.v2.controllers.extensions =
oshosts=climate.api.v2.controllers.extensions.host:HostsController
leases=climate.api.v2.controllers.extensions.lease:LeasesController
[build_sphinx]
all_files = 1
build-dir = doc/build