# -*- encoding: utf-8 -*- # Copyright © 2012 New Dream Network, LLC (DreamHost) # 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 iotronic.api import config from iotronic.api.controllers import base from iotronic.api import hooks from iotronic.api import middleware from iotronic.api.middleware import auth_token from oslo_config import cfg import oslo_middleware.cors as cors_middleware import pecan from pecan import make_app opts = [ cfg.StrOpt( 'auth_strategy', default='keystone', help=('Authentication strategy used by iotronic-api: "keystone" ' 'or "noauth". "noauth" should not be used in a production ' 'environment because all authentication will be disabled.')), cfg.BoolOpt('debug_tracebacks_in_api', default=False, help=('Return server tracebacks in the API response for any ' 'error responses. WARNING: this is insecure ' 'and should not be used in a production environment.')), cfg.BoolOpt( 'pecan_debug', default=False, help=( 'Enable pecan debug mode. WARNING: this is insecure ' 'and should not be used in a production environment.')), ] api_opts = [ cfg.StrOpt('host_ip', default='0.0.0.0', help=('The IP address on which iotronic-api listens.')), cfg.PortOpt('port', default=1288, help=('The TCP port on which iotronic-api listens.')), cfg.IntOpt('max_limit', default=1000, help=('The maximum number of items returned in a single ' 'response from a collection resource.')), cfg.StrOpt('public_endpoint', help=("Public URL to use when building the links to the API " "resources." " If None the links will be built using the request's " "host URL. If the API is operating behind a proxy, you " "will want to change this to represent the proxy's URL. " "Defaults to None.")), cfg.IntOpt('api_workers', help=('Number of workers for OpenStack Iotronic API service. ' 'The default is equal to the number of CPUs available ' 'if that can be determined, else a default worker ' 'count of 1 is returned.')), cfg.BoolOpt('enable_ssl_api', default=False, help=("Enable the integrated stand-alone API to service " "requests via HTTPS instead of HTTP. If there is a " "front-end service performing HTTPS offloading from " "the service, this option should be False; note, you " "will want to change public API endpoint to represent " "SSL termination URL with 'public_endpoint' option.")), ] opt_group = cfg.OptGroup(name='api', title='Options for the iotronic-api service') CONF = cfg.CONF CONF.register_opts(opts,) CONF.register_opts(api_opts, 'api') def get_pecan_config(): # Set up the pecan configuration filename = config.__file__.replace('.pyc', '.py') return pecan.configuration.conf_from_file(filename) class IotronicCORS(cors_middleware.CORS): """Iotronic-specific CORS class We're adding the Iotronic-specific version headers to the list of simple headers in order that a request bearing those headers might be accepted by the Iotronic REST API. """ simple_headers = cors_middleware.CORS.simple_headers + [ 'X-Auth-Token', base.Version.max_string, base.Version.min_string, base.Version.string ] def setup_app(config=None): app_hooks = [hooks.ConfigHook(), hooks.DBHook(), hooks.ContextHook(config.app.acl_public_routes), hooks.RPCHook(), hooks.NoExceptionTracebackHook(), hooks.PublicUrlHook()] app_conf = dict(config.app) app = make_app( app_conf.pop('root'), hooks=app_hooks, force_canonical=getattr(config.app, 'force_canonical', True), wrap_app=middleware.ParsableErrorMiddleware, **app_conf ) if CONF.auth_strategy == "keystone": app = auth_token.AuthTokenMiddleware( app, dict(cfg.CONF), public_api_routes=config.app.acl_public_routes) # Create a CORS wrapper, and attach iotronic-specific defaults that must be # included in all CORS responses. app = IotronicCORS(app, CONF) cors_middleware.set_defaults( allow_methods=['GET', 'PUT', 'POST', 'DELETE', 'PATCH'], expose_headers=[base.Version.max_string, base.Version.min_string, base.Version.string] ) return app class VersionSelectorApplication(object): def __init__(self): pc = get_pecan_config() pc.app.enable_acl = (CONF.auth_strategy == 'keystone') self.v1 = setup_app(config=pc) def __call__(self, environ, start_response): return self.v1(environ, start_response)