add initial files for topology api

Implements: blueprint get-topology-api
Change-Id: I3fa8a811edb04decf52dba875f5d8073b69b91a6
This commit is contained in:
Eyal 2015-11-29 15:00:22 +02:00
parent 87ce06e8ea
commit 7ba498cf58
14 changed files with 379 additions and 3 deletions

View File

@ -6,3 +6,8 @@ pbr>=1.6
Babel>=1.3
oslo.log>=1.12.0 # Apache-2.0
pecan>=0.8.0
PasteDeploy>=1.5.0
Werkzeug>=0.7
oslo.policy>=0.3.0
keystonemiddleware>=2.3.0

View File

@ -17,6 +17,16 @@ classifier =
Programming Language :: Python :: 2.7
Topic :: System :: Monitoring
[global]
setup-hooks =
pbr.hooks.setup_hook
console_scripts =
vitrage-api = vitrage.cmd.api:main
oslo.config.opts =
vitrage = vitrage.opts:list_opts
[files]
packages =
vitrage

101
vitrage/api/app.py Normal file
View File

@ -0,0 +1,101 @@
# 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 logging
import os
import pecan
from oslo_config import cfg
from oslo_log import log
from paste import deploy
from vitrage.api import hooks
from vitrage.i18n import _
from vitrage.i18n import _LW
from vitrage import service
from werkzeug import serving
LOG = log.getLogger(__name__)
PECAN_CONFIG = {
'app': {
'root': 'vitrage.api.controllers.root.RootController',
'modules': ['vitrage.api'],
},
}
def setup_app(pecan_config=PECAN_CONFIG, conf=None):
if conf is None:
raise RuntimeError("Config is actually mandatory")
app_hooks = [hooks.ConfigHook(conf),
hooks.TranslationHook()]
pecan.configuration.set_config(dict(pecan_config), overwrite=True)
pecan_debug = conf.api.pecan_debug
if conf.api.workers != 1 and pecan_debug:
pecan_debug = False
LOG.warning(_LW('pecan_debug cannot be enabled, if workers is > 1, '
'the value is overridden with False'))
app = pecan.make_app(
pecan_config['app']['root'],
debug=pecan_debug,
hooks=app_hooks,
guess_content_type_from_ext=False
)
return app
def load_app(conf):
# Build the WSGI app
cfg_file = None
cfg_path = conf.api.paste_config
if not os.path.isabs(cfg_path):
cfg_file = conf.find_file(cfg_path)
elif os.path.exists(cfg_path):
cfg_file = cfg_path
if not cfg_file:
raise cfg.ConfigFilesNotFoundError([conf.api.paste_config])
LOG.info("Full WSGI config used: %s" % cfg_file)
return deploy.loadapp("config:" + cfg_file)
def build_server(conf):
app = load_app(conf)
# Create the WSGI server and start it
host, port = conf.api.host, conf.api.port
LOG.info(_('Starting server in PID %s') % os.getpid())
LOG.info(_("Configuration:"))
conf.log_opt_values(LOG, logging.INFO)
if host == '0.0.0.0':
LOG.info(_(
'serving on 0.0.0.0:%(sport)s, view at http://127.0.0.1:%(vport)s')
% ({'sport': port, 'vport': port}))
else:
LOG.info(_("serving on http://%(host)s:%(port)s") % (
{'host': host, 'port': port}))
serving.run_simple(host, port,
app, processes=conf.api.workers)
def _app():
conf = service.prepare_service()
return setup_app(conf=conf)
def app_factory(global_config, **local_conf):
return _app()

23
vitrage/api/app.wsgi Normal file
View File

@ -0,0 +1,23 @@
# 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.
"""Use this file for deploying the API under mod_wsgi.
See http://pecan.readthedocs.org/en/latest/deployment.html for details.
"""
from oslo_config import cfg
from vitrage.api import app
from vitrage import service
# Initialize the oslo configuration library and logging
conf = service.prepare_service()
application = app.load_app(conf)

View File

View File

@ -0,0 +1,37 @@
# 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 pecan as pecan
from vitrage.api.controllers.v1 import root as v1
class RootController(object):
v1 = v1.V1Controller()
@staticmethod
@pecan.expose('json')
def index():
return {
"versions": [
{
"status": "CURRENT",
"links": [
{
"rel": "self",
"href": pecan.request.application_url + "/v1/"
}
],
"id": "v1.0",
"updated": "2015-11-29"
}
]
}

View File

View File

@ -1,5 +1,3 @@
# Copyright 2015 - Alcatel-Lucent
#
# 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
@ -12,4 +10,8 @@
# License for the specific language governing permissions and limitations
# under the License.
__author__ = 'stack'
from vitrage.api.controllers.v1 import topology
class V1Controller(object):
topology = topology.TopologyController()

View File

@ -0,0 +1,19 @@
# 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 pecan import rest
class TopologyController(rest.RestController):
"""Manages operations on the topology."""
pass

38
vitrage/api/hooks.py Normal file
View File

@ -0,0 +1,38 @@
# 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 oslo_policy import policy
from pecan import hooks
class ConfigHook(hooks.PecanHook):
"""Attach the configuration and policy enforcer object to the request. """
def __init__(self, conf):
self.conf = conf
self.enforcer = policy.Enforcer(conf)
def before(self, state):
state.request.cfg = self.conf
state.request.enforcer = self.enforcer
class TranslationHook(hooks.PecanHook):
def after(self, state):
# After a request has been done, we need to see if
# ClientSideError has added an error onto the response.
# If it has we need to get it info the thread-safe WSGI
# environ to be used by the ParsableErrorMiddleware.
if hasattr(state.response, 'translatable_error'):
state.request.environ['translatable_error'] = (
state.response.translatable_error)

19
vitrage/cmd/api.py Normal file
View File

@ -0,0 +1,19 @@
# 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 vitrage.api import app
from vitrage import service
def main():
conf = service.prepare_service()
app.build_server(conf)

42
vitrage/i18n.py Normal file
View File

@ -0,0 +1,42 @@
# 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.
"""oslo.i18n integration module.
See http://docs.openstack.org/developer/oslo.i18n/usage.html
"""
import oslo_i18n
DOMAIN = 'vitrage'
_translators = oslo_i18n.TranslatorFactory(domain=DOMAIN)
# The primary translation function using the well-known name "_"
_ = _translators.primary
# Translators for log levels.
#
# The abbreviated names are meant to reflect the usual use of a short
# name like '_'. The "L" is for "log" and the other letter comes from
# the level.
_LI = _translators.log_info
_LW = _translators.log_warning
_LE = _translators.log_error
_LC = _translators.log_critical
def translate(value, user_locale):
return oslo_i18n.translate(value, user_locale)
def get_available_languages():
return oslo_i18n.get_available_languages(DOMAIN)

24
vitrage/opts.py Normal file
View File

@ -0,0 +1,24 @@
# 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 oslo_config import cfg
def list_opts():
return [("api", (
cfg.IntOpt('workers', default=1,
min=1,
help='Number of workers for vitrage API server.'),
cfg.BoolOpt('pecan_debug', default=False,
help='Toggle Pecan Debug Middleware.')
))]

56
vitrage/service.py Normal file
View File

@ -0,0 +1,56 @@
# 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 keystoneclient.auth
import logging
from keystonemiddleware import opts as ks_opts
from oslo_config import cfg
from oslo_log import log
from oslo_policy import opts as policy_opts
from vitrage import opts
LOG = log.getLogger(__name__)
def prepare_service(args=None, default_opts=None, conf=None):
if conf is None:
conf = cfg.ConfigOpts()
log.register_options(conf)
for group, options in ks_opts.list_auth_token_opts():
conf.register_opts(list(options), group=group)
policy_opts.set_defaults(conf)
for group, options in opts.list_opts():
conf.register_opts(list(options),
group=None if group == "DEFAULT" else group)
for opt, value, group in default_opts or []:
conf.set_default(opt, value, group)
conf(args, project='vitrage', validate_default_values=True)
log.setup(conf, 'vitrage')
conf.log_opt_values(LOG, logging.DEBUG)
# NOTE(sileht): keystonemiddleware assume we use the global CONF object
# (LP#1428317). In gnocchi, this is not the case, so we have to register
# some keystoneclient options ourself. Missing options are hidden into
# private area of keystonemiddleware and keystoneclient, so we
# create a keystoneclient AuthPlugin object, that will register the options
# into our configuration object. This have to be done after the
# configuration files have been loaded because the authplugin options
# depends of the authplugin present in the configuration file.
keystoneclient.auth.register_conf_options(conf, 'keystone_authtoken')
keystoneclient.auth.load_from_conf_options(conf, 'keystone_authtoken')
return conf