Add cascade service REST api framework

Fisrt patch to implement the REST api framework based on pecan, with keystone authentication integrated.
No resources implemented in the first patch

Change-Id: I8bff9f934fbba183f0b0e7b540fa755965c229a0
implements: blueprint: https://blueprints.launchpad.net/tricircle/+spec/implement-rest-api-framework
This commit is contained in:
joehuang 2015-08-21 09:29:34 +08:00 committed by Chaoyi Huang
parent be30bb2fd8
commit e94e98ad4a
14 changed files with 997 additions and 12 deletions

63
cmd/api.py Executable file
View File

@ -0,0 +1,63 @@
# Copyright 2015 Huawei Technologies Co., Ltd.
# 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.
# Much of this module is based on the work of the Ironic team
# see http://git.openstack.org/cgit/openstack/ironic/tree/ironic/cmd/api.py
import logging as std_logging
import sys
from oslo_config import cfg
from oslo_log import log as logging
from werkzeug import serving
from tricircle.api import app
from tricircle.common import config
from tricircle.i18n import _LI
from tricircle.i18n import _LW
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
def main():
config.init(sys.argv[1:])
config.setup_logging()
application = app.setup_app()
host = CONF.bind_host
port = CONF.bind_port
workers = CONF.api_workers
if workers < 1:
LOG.warning(_LW("Wrong worker number, worker = %(workers)s"), workers)
workers = 1
LOG.info(_LI("Server on http://%(host)s:%(port)s with %(workers)s"),
{'host': host, 'port': port, 'workers': workers})
serving.run_simple(host, port,
application,
processes=workers)
LOG.info(_LI("Configuration:"))
CONF.log_opt_values(LOG, std_logging.INFO)
if __name__ == '__main__':
main()

View File

@ -29,10 +29,11 @@ PUBLIC_NETWORK_GATEWAY=10.100.100.3
Q_ENABLE_TRICIRCLE=True
enable_plugin tricircle https://git.openstack.org/cgit/stackforge/tricircle/ experiment
enable_plugin tricircle https://git.openstack.org/stackforge/tricircle master
# Tricircle Services
enable_service t-svc
enable_service t-svc-api
# Use Neutron instead of nova-network
disable_service n-net
@ -44,4 +45,4 @@ disable_service c-api
disable_service c-vol
disable_service c-bak
disable_service c-sch
disable_service cinder
disable_service cinder

View File

@ -1,4 +1,45 @@
# Devstack extras script to install Tricircle
# Test if any tricircle services are enabled
# is_tricircle_enabled
function is_tricircle_enabled {
[[ ,${ENABLED_SERVICES} =~ ,"t-svc-" ]] && return 0
return 1
}
# create_tricircle_accounts() - Set up common required tricircle
# service accounts in keystone
# Project User Roles
# -------------------------------------------------------------------------
# $SERVICE_TENANT_NAME tricircle service
function create_tricircle_accounts {
if [[ "$ENABLED_SERVICES" =~ "t-svc-api" ]]; then
create_service_user "tricircle"
if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then
local tricircle_cascade_service=$(get_or_create_service "tricircle" \
"Cascading" "OpenStack Cascading Service")
get_or_create_endpoint $tricircle_cascade_service \
"$REGION_NAME" \
"$SERVICE_PROTOCOL://$TRICIRCLE_CASCADE_API_HOST:$TRICIRCLE_CASCADE_API_PORT/v1.0" \
"$SERVICE_PROTOCOL://$TRICIRCLE_CASCADE_API_HOST:$TRICIRCLE_CASCADE_API_PORT/v1.0" \
"$SERVICE_PROTOCOL://$TRICIRCLE_CASCADE_API_HOST:$TRICIRCLE_CASCADE_API_PORT/v1.0"
fi
fi
}
# create_tricircle_cache_dir() - Set up cache dir for tricircle
function create_tricircle_cache_dir {
# Delete existing dir
sudo rm -rf $TRICIRCLE_AUTH_CACHE_DIR
sudo mkdir -p $TRICIRCLE_AUTH_CACHE_DIR
sudo chown `whoami` $TRICIRCLE_AUTH_CACHE_DIR
}
function configure_tricircle_plugin {
echo "Configuring Neutron for Tricircle"
@ -11,17 +52,43 @@ function configure_tricircle_plugin {
fi
if is_service_enabled t-svc ; then
echo "Configuring Neutron for Tricircle Cascade Service"
sudo install -d -o $STACK_USER -m 755 $TRICIRCLE_CONF_DIR
cp -p $TRICIRCLE_DIR/etc/cascade_service.conf $TRICIRCLE_CASCADE_CONF
echo "Configuring Neutron for Tricircle Cascade Service"
sudo install -d -o $STACK_USER -m 755 $TRICIRCLE_CONF_DIR
cp -p $TRICIRCLE_DIR/etc/cascade_service.conf $TRICIRCLE_CASCADE_CONF
iniset $TRICIRCLE_CASCADE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
iniset $TRICIRCLE_CASCADE_CONF DEFAULT verbose True
setup_colorized_logging $TRICIRCLE_CASCADE_CONF DEFAULT
iniset $TRICIRCLE_CASCADE_CONF DEFAULT bind_host $TRICIRCLE_CASCADE_LISTEN_ADDRESS
iniset $TRICIRCLE_CASCADE_CONF DEFAULT use_syslog $SYSLOG
iniset_rpc_backend tricircle $TRICIRCLE_CASCADE_CONF
iniset $TRICIRCLE_CASCADE_CONF database connection `database_connection_url tricircle`
fi
}
function configure_tricircle_cascade_api {
echo "Configuring tricircle cascade api service"
if is_service_enabled t-svc-api ; then
cp -p $TRICIRCLE_DIR/etc/api.conf $TRICIRCLE_CASCADE_API_CONF
iniset $TRICIRCLE_CASCADE_API_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
iniset $TRICIRCLE_CASCADE_API_CONF DEFAULT verbose True
iniset $TRICIRCLE_CASCADE_API_CONF DEFAULT use_syslog $SYSLOG
setup_colorized_logging $TRICIRCLE_CASCADE_API_CONF DEFAULT
if is_service_enabled keystone; then
create_tricircle_cache_dir
# Configure auth token middleware
configure_auth_token_middleware $TRICIRCLE_CASCADE_API_CONF tricircle \
$TRICIRCLE_AUTH_CACHE_DIR
else
iniset $TRICIRCLE_CASCADE_API_CONF DEFAULT auth_strategy noauth
fi
iniset $TRICIRCLE_CASCADE_CONF DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL
iniset $TRICIRCLE_CASCADE_CONF DEFAULT verbose true
setup_colorized_logging $TRICIRCLE_CASCADE_CONF DEFAULT
iniset $TRICIRCLE_CASCADE_CONF DEFAULT bind_host $TRICIRCLE_CASCADE_LISTEN_ADDRESS
iniset $TRICIRCLE_CASCADE_CONF DEFAULT use_syslog $SYSLOG
iniset_rpc_backend tricircle $TRICIRCLE_CASCADE_CONF
iniset $TRICIRCLE_CASCADE_CONF database connection `database_connection_url tricircle`
fi
}
@ -37,7 +104,10 @@ if [[ "$Q_ENABLE_TRICIRCLE" == "True" ]]; then
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
echo_summary "Configure Tricircle"
configure_tricircle_plugin
configure_tricircle_cascade_api
echo export PYTHONPATH=\$PYTHONPATH:$TRICIRCLE_DIR >> $RC_DIR/.localrc.auto
recreate_database tricircle
@ -49,6 +119,13 @@ if [[ "$Q_ENABLE_TRICIRCLE" == "True" ]]; then
if is_service_enabled t-svc; then
run_process t-svc "python $TRICIRCLE_CASCADE_SERVICE --config-file $TRICIRCLE_CASCADE_CONF"
fi
if is_service_enabled t-svc-api; then
create_tricircle_accounts
run_process t-svc-api "python $TRICIRCLE_CASCADE_API --config-file $TRICIRCLE_CASCADE_API_CONF"
fi
fi
if [[ "$1" == "unstack" ]]; then
@ -56,5 +133,9 @@ if [[ "$Q_ENABLE_TRICIRCLE" == "True" ]]; then
if is_service_enabled t-svc; then
stop_process t-svc
fi
if is_service_enabled t-svc-api; then
stop_process t-svc-api
fi
fi
fi

View File

@ -11,4 +11,15 @@ TRICIRCLE_CASCADE_SERVICE=$TRICIRCLE_DIR/cmd/cascade_service.py
TRICIRCLE_CASCADE_CONF=$TRICIRCLE_CONF_DIR/cascade_service.conf
TRICIRCLE_CASCADE_LISTEN_ADDRESS=${TRICIRCLE_CASCADE_LISTEN_ADDRESS:-0.0.0.0}
# cascade rest api
TRICIRCLE_CASCADE_API=$TRICIRCLE_DIR/cmd/api.py
TRICIRCLE_CASCADE_API_CONF=$TRICIRCLE_CONF_DIR/api.conf
TRICIRCLE_CASCADE_API_LISTEN_ADDRESS=${TRICIRCLE_CASCADE_API_LISTEN_ADDRESS:-0.0.0.0}
TRICIRCLE_CASCADE_API_HOST=${TRICIRCLE_CASCADE_API_HOST:-$SERVICE_HOST}
TRICIRCLE_CASCADE_API_PORT=${TRICIRCLE_CASCADE_API_PORT:-19999}
TRICIRCLE_CASCADE_API_PROTOCOL=${TRICIRCLE_CASCADE_API_PROTOCOL:-$SERVICE_PROTOCOL}
TRICIRCLE_AUTH_CACHE_DIR=${TRICIRCLE_AUTH_CACHE_DIR:-/var/cache/tricircle}
export PYTHONPATH=$PYTHONPATH:$TRICIRCLE_DIR

373
etc/api.conf Executable file
View File

@ -0,0 +1,373 @@
[DEFAULT]
# Print more verbose output (set logging level to INFO instead of default WARNING level).
# verbose = True
# Print debugging output (set logging level to DEBUG instead of default WARNING level).
# debug = False
# Where to store Tricircle state files. This directory must be writable by the
# user executing the agent.
# state_path = /var/lib/tricircle
# log_format = %(asctime)s %(levelname)8s [%(name)s] %(message)s
# log_date_format = %Y-%m-%d %H:%M:%S
# use_syslog -> syslog
# log_file and log_dir -> log_dir/log_file
# (not log_file) and log_dir -> log_dir/{binary_name}.log
# use_stderr -> stderr
# (not user_stderr) and (not log_file) -> stdout
# publish_errors -> notification system
# use_syslog = False
# syslog_log_facility = LOG_USER
# use_stderr = True
# log_file =
# log_dir =
# publish_errors = False
# Address to bind the API server to
# bind_host = 127.0.0.1
# Port the bind the API server to
# bind_port = 19999
# Paste configuration file
# api_paste_config = api-paste.ini
# (StrOpt) Hostname to be used by the tricircle server, agents and services
# running on this machine. All the agents and services running on this machine
# must use the same host value.
# The default value is hostname of the machine.
#
# host =
# admin_tenant_name = %SERVICE_TENANT_NAME%
# admin_user = %SERVICE_USER%
# admin_password = %SERVICE_PASSWORD%
# Enable or disable bulk create/update/delete operations
# allow_bulk = True
# Enable or disable pagination
# allow_pagination = False
# Enable or disable sorting
# allow_sorting = False
# Default maximum number of items returned in a single response,
# value == infinite and value < 0 means no max limit, and value must
# be greater than 0. If the number of items requested is greater than
# pagination_max_limit, server will just return pagination_max_limit
# of number of items.
# pagination_max_limit = -1
# =========== WSGI parameters related to the API server ==============
# Number of separate worker processes to spawn. The default, 0, runs the
# worker thread in the current process. Greater than 0 launches that number of
# child processes as workers. The parent process manages them.
# api_workers = 3
# Number of separate RPC worker processes to spawn. The default, 0, runs the
# worker thread in the current process. Greater than 0 launches that number of
# child processes as RPC workers. The parent process manages them.
# This feature is experimental until issues are addressed and testing has been
# enabled for various plugins for compatibility.
# rpc_workers = 0
# Timeout for client connections socket operations. If an
# incoming connection is idle for this number of seconds it
# will be closed. A value of '0' means wait forever. (integer
# value)
# client_socket_timeout = 900
# wsgi keepalive option. Determines if connections are allowed to be held open
# by clients after a request is fulfilled. A value of False will ensure that
# the socket connection will be explicitly closed once a response has been
# sent to the client.
# wsgi_keep_alive = True
# Sets the value of TCP_KEEPIDLE in seconds to use for each server socket when
# starting API server. Not supported on OS X.
# tcp_keepidle = 600
# Number of seconds to keep retrying to listen
# retry_until_window = 30
# Number of backlog requests to configure the socket with.
# backlog = 4096
# Max header line to accommodate large tokens
# max_header_line = 16384
# Enable SSL on the API server
# use_ssl = False
# Certificate file to use when starting API server securely
# ssl_cert_file = /path/to/certfile
# Private key file to use when starting API server securely
# ssl_key_file = /path/to/keyfile
# CA certificate file to use when starting API server securely to
# verify connecting clients. This is an optional parameter only required if
# API clients need to authenticate to the API server using SSL certificates
# signed by a trusted CA
# ssl_ca_file = /path/to/cafile
# ======== end of WSGI parameters related to the API server ==========
# The strategy to be used for auth.
# Supported values are 'keystone'(default), 'noauth'.
# auth_strategy = keystone
[filter:authtoken]
# paste.filter_factory = keystonemiddleware.auth_token:filter_factory
[keystone_authtoken]
# auth_uri = http://162.3.111.227:35357/v3
# identity_uri = http://162.3.111.227:35357
# admin_tenant_name = service
# admin_user = tricircle
# admin_password = 1234
# auth_version = 3
[database]
# This line MUST be changed to actually run the plugin.
# Example:
# connection = mysql://root:pass@127.0.0.1:3306/neutron
# Replace 127.0.0.1 above with the IP address of the database used by the
# main neutron server. (Leave it as is if the database runs on this host.)
# connection = sqlite://
# NOTE: In deployment the [database] section and its connection attribute may
# be set in the corresponding core plugin '.ini' file. However, it is suggested
# to put the [database] section and its connection attribute in this
# configuration file.
# Database engine for which script will be generated when using offline
# migration
# engine =
# The SQLAlchemy connection string used to connect to the slave database
# slave_connection =
# Database reconnection retry times - in event connectivity is lost
# set to -1 implies an infinite retry count
# max_retries = 10
# Database reconnection interval in seconds - if the initial connection to the
# database fails
# retry_interval = 10
# Minimum number of SQL connections to keep open in a pool
# min_pool_size = 1
# Maximum number of SQL connections to keep open in a pool
# max_pool_size = 10
# Timeout in seconds before idle sql connections are reaped
# idle_timeout = 3600
# If set, use this value for max_overflow with sqlalchemy
# max_overflow = 20
# Verbosity of SQL debugging information. 0=None, 100=Everything
# connection_debug = 0
# Add python stack traces to SQL as comment strings
# connection_trace = False
# If set, use this value for pool_timeout with sqlalchemy
# pool_timeout = 10
[oslo_concurrency]
# Directory to use for lock files. For security, the specified directory should
# only be writable by the user running the processes that need locking.
# Defaults to environment variable OSLO_LOCK_PATH. If external locks are used,
# a lock path must be set.
lock_path = $state_path/lock
# Enables or disables inter-process locks.
# disable_process_locking = False
[oslo_policy]
# The JSON file that defines policies.
# policy_file = policy.json
# Default rule. Enforced when a requested rule is not found.
# policy_default_rule = default
# Directories where policy configuration files are stored.
# They can be relative to any directory in the search path defined by the
# config_dir option, or absolute paths. The file defined by policy_file
# must exist for these directories to be searched. Missing or empty
# directories are ignored.
# policy_dirs = policy.d
[oslo_messaging_amqp]
#
# From oslo.messaging
#
# Address prefix used when sending to a specific server (string value)
# server_request_prefix = exclusive
# Address prefix used when broadcasting to all servers (string value)
# broadcast_prefix = broadcast
# Address prefix when sending to any server in group (string value)
# group_request_prefix = unicast
# Name for the AMQP container (string value)
# container_name =
# Timeout for inactive connections (in seconds) (integer value)
# idle_timeout = 0
# Debug: dump AMQP frames to stdout (boolean value)
# trace = false
# CA certificate PEM file for verifing server certificate (string value)
# ssl_ca_file =
# Identifying certificate PEM file to present to clients (string value)
# ssl_cert_file =
# Private key PEM file used to sign cert_file certificate (string value)
# ssl_key_file =
# Password for decrypting ssl_key_file (if encrypted) (string value)
# ssl_key_password =
# Accept clients using either SSL or plain TCP (boolean value)
# allow_insecure_clients = false
[oslo_messaging_qpid]
#
# From oslo.messaging
#
# Use durable queues in AMQP. (boolean value)
# amqp_durable_queues = false
# Auto-delete queues in AMQP. (boolean value)
# amqp_auto_delete = false
# Size of RPC connection pool. (integer value)
# rpc_conn_pool_size = 30
# Qpid broker hostname. (string value)
# qpid_hostname = localhost
# Qpid broker port. (integer value)
# qpid_port = 5672
# Qpid HA cluster host:port pairs. (list value)
# qpid_hosts = $qpid_hostname:$qpid_port
# Username for Qpid connection. (string value)
# qpid_username =
# Password for Qpid connection. (string value)
# qpid_password =
# Space separated list of SASL mechanisms to use for auth. (string value)
# qpid_sasl_mechanisms =
# Seconds between connection keepalive heartbeats. (integer value)
# qpid_heartbeat = 60
# Transport to use, either 'tcp' or 'ssl'. (string value)
# qpid_protocol = tcp
# Whether to disable the Nagle algorithm. (boolean value)
# qpid_tcp_nodelay = true
# The number of prefetched messages held by receiver. (integer value)
# qpid_receiver_capacity = 1
# The qpid topology version to use. Version 1 is what was originally used by
# impl_qpid. Version 2 includes some backwards-incompatible changes that allow
# broker federation to work. Users should update to version 2 when they are
# able to take everything down, as it requires a clean break. (integer value)
# qpid_topology_version = 1
[oslo_messaging_rabbit]
#
# From oslo.messaging
#
# Use durable queues in AMQP. (boolean value)
# amqp_durable_queues = false
# Auto-delete queues in AMQP. (boolean value)
# amqp_auto_delete = false
# Size of RPC connection pool. (integer value)
# rpc_conn_pool_size = 30
# SSL version to use (valid only if SSL enabled). Valid values are TLSv1 and
# SSLv23. SSLv2, SSLv3, TLSv1_1, and TLSv1_2 may be available on some
# distributions. (string value)
# kombu_ssl_version =
# SSL key file (valid only if SSL enabled). (string value)
# kombu_ssl_keyfile =
# SSL cert file (valid only if SSL enabled). (string value)
# kombu_ssl_certfile =
# SSL certification authority file (valid only if SSL enabled). (string value)
# kombu_ssl_ca_certs =
# How long to wait before reconnecting in response to an AMQP consumer cancel
# notification. (floating point value)
# kombu_reconnect_delay = 1.0
# The RabbitMQ broker address where a single node is used. (string value)
# rabbit_host = localhost
# The RabbitMQ broker port where a single node is used. (integer value)
# rabbit_port = 5672
# RabbitMQ HA cluster host:port pairs. (list value)
# rabbit_hosts = $rabbit_host:$rabbit_port
# Connect over SSL for RabbitMQ. (boolean value)
# rabbit_use_ssl = false
# The RabbitMQ userid. (string value)
# rabbit_userid = guest
# The RabbitMQ password. (string value)
# rabbit_password = guest
# The RabbitMQ login method. (string value)
# rabbit_login_method = AMQPLAIN
# The RabbitMQ virtual host. (string value)
# rabbit_virtual_host = /
# How frequently to retry connecting with RabbitMQ. (integer value)
# rabbit_retry_interval = 1
# How long to backoff for between retries when connecting to RabbitMQ. (integer
# value)
# rabbit_retry_backoff = 2
# Maximum number of RabbitMQ connection retries. Default is 0 (infinite retry
# count). (integer value)
# rabbit_max_retries = 0
# Use HA queues in RabbitMQ (x-ha-policy: all). If you change this option, you
# must wipe the RabbitMQ database. (boolean value)
# rabbit_ha_queues = false
# Deprecated, use rpc_backend=kombu+memory or rpc_backend=fake (boolean value)
# fake_rabbit = false

38
requirements.txt Executable file
View File

@ -0,0 +1,38 @@
# The order of packages is significant, because pip processes them in the order
# of appearance. Changing the order has an impact on the overall integration
# process, which may cause wedges in the gate later.
pbr<2.0,>=1.3
Paste
PasteDeploy>=1.5.0
Routes!=2.0,!=2.1,>=1.12.3;python_version=='2.7'
Routes!=2.0,>=1.12.3;python_version!='2.7'
debtcollector>=0.3.0 # Apache-2.0
eventlet>=0.17.4
pecan>=0.9.0
greenlet>=0.3.2
httplib2>=0.7.5
requests>=2.5.2
Jinja2>=2.6 # BSD License (3 clause)
keystonemiddleware>=2.0.0
netaddr>=0.7.12
retrying!=1.3.0,>=1.2.3 # Apache-2.0
SQLAlchemy<1.1.0,>=0.9.7
WebOb>=1.2.3
python-keystoneclient>=1.6.0
alembic>=0.7.2
six>=1.9.0
stevedore>=1.5.0 # Apache-2.0
oslo.concurrency>=2.1.0 # Apache-2.0
oslo.config>=1.11.0 # Apache-2.0
oslo.context>=0.2.0 # Apache-2.0
oslo.db>=1.12.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0
oslo.log>=1.6.0 # Apache-2.0
oslo.messaging>=1.16.0 # Apache-2.0
oslo.middleware>=2.4.0 # Apache-2.0
oslo.policy>=0.5.0 # Apache-2.0
oslo.rootwrap>=2.0.0 # Apache-2.0
oslo.serialization>=1.4.0 # Apache-2.0
oslo.service>=0.1.0 # Apache-2.0
oslo.utils>=1.9.0 # Apache-2.0

0
tricircle/api/__init__.py Executable file
View File

66
tricircle/api/app.py Executable file
View File

@ -0,0 +1,66 @@
# Copyright (c) 2015 Huawei, Tech. Co,. Ltd.
# 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 keystonemiddleware import auth_token
from oslo_config import cfg
from oslo_middleware import request_id
import pecan
from tricircle.common import exceptions as t_exc
def setup_app(*args, **kwargs):
config = {
'server': {
'port': cfg.CONF.bind_port,
'host': cfg.CONF.bind_host
},
'app': {
'root': 'tricircle.api.controllers.root.RootController',
'modules': ['tricircle.api'],
'errors': {
400: '/error',
'__force_dict__': True
}
}
}
pecan_config = pecan.configuration.conf_from_dict(config)
# app_hooks = [], hook collection will be put here later
app = pecan.make_app(
pecan_config.app.root,
debug=False,
wrap_app=_wrap_app,
force_canonical=False,
hooks=[],
guess_content_type_from_ext=True
)
return app
def _wrap_app(app):
app = request_id.RequestId(app)
if cfg.CONF.auth_strategy == 'noauth':
pass
elif cfg.CONF.auth_strategy == 'keystone':
app = auth_token.AuthProtocol(app, {})
else:
raise t_exc.InvalidConfigurationOption(
opt_name='auth_strategy', opt_value=cfg.CONF.auth_strategy)
return app

View File

102
tricircle/api/controllers/root.py Executable file
View File

@ -0,0 +1,102 @@
# Copyright (c) 2015 Huawei Tech. Co., Ltd.
# 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.
import pecan
from pecan import rest
def expose(*args, **kwargs):
kwargs.setdefault('content_type', 'application/json')
kwargs.setdefault('template', 'json')
return pecan.expose(*args, **kwargs)
def when(index, *args, **kwargs):
kwargs.setdefault('content_type', 'application/json')
kwargs.setdefault('template', 'json')
return index.when(*args, **kwargs)
class RootController(object):
@expose()
def _lookup(self, version, *remainder):
if version == 'v1.0':
return V1Controller(), remainder
@pecan.expose('json')
def index(self):
return {
"versions": [
{
"status": "CURRENT",
"links": [
{
"rel": "self",
"href": pecan.request.application_url + "/v1.0/"
}
],
"id": "v1.0",
"updated": "2015-09-09"
}
]
}
class V1Controller(object):
def __init__(self):
self.sub_controllers = {
"sites": SitesController()
}
for name, ctrl in self.sub_controllers.items():
setattr(self, name, ctrl)
@pecan.expose('json')
def index(self):
return {
"version": "1.0",
"links": [
{"rel": "self",
"href": pecan.request.application_url + "/v1.0"}
] + [
{"rel": name,
"href": pecan.request.application_url + "/v1.0/" + name}
for name in sorted(self.sub_controllers)
]
}
class SitesController(rest.RestController):
@expose(generic=True)
def index(self):
if pecan.request.method != 'GET':
pecan.abort(405)
return {'message': 'GET'}
@when(index, method='PUT')
def put(self, **kw):
return {'message': 'PUT'}
@when(index, method='POST')
def post(self, **kw):
return {'message': 'POST'}
@when(index, method='DELETE')
def delete(self):
return {'message': 'DELETE'}

120
tricircle/common/config.py Executable file
View File

@ -0,0 +1,120 @@
# Copyright 2015 Huawei Technologies Co., Ltd.
# 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.
"""
Routines for configuring tricircle, largely copy from Neutron
"""
import os
import sys
from oslo_config import cfg
from oslo_log import log as logging
from paste import deploy
from tricircle.i18n import _
from tricircle.i18n import _LI
# from tricircle import policy
from tricircle import version
LOG = logging.getLogger(__name__)
common_opts = [
cfg.StrOpt('bind_host', default='0.0.0.0',
help=_("The host IP to bind to")),
cfg.IntOpt('bind_port', default=19999,
help=_("The port to bind to")),
cfg.IntOpt('api_workers', default=1,
help=_("number of api workers")),
cfg.StrOpt('api_paste_config', default="api-paste.ini",
help=_("The API paste config file to use")),
cfg.StrOpt('api_extensions_path', default="",
help=_("The path for API extensions")),
cfg.StrOpt('auth_strategy', default='keystone',
help=_("The type of authentication to use")),
cfg.BoolOpt('allow_bulk', default=True,
help=_("Allow the usage of the bulk API")),
cfg.BoolOpt('allow_pagination', default=False,
help=_("Allow the usage of the pagination")),
cfg.BoolOpt('allow_sorting', default=False,
help=_("Allow the usage of the sorting")),
cfg.StrOpt('pagination_max_limit', default="-1",
help=_("The maximum number of items returned in a single "
"response, value was 'infinite' or negative integer "
"means no limit")),
]
def init(args, **kwargs):
# Register the configuration options
cfg.CONF.register_opts(common_opts)
# ks_session.Session.register_conf_options(cfg.CONF)
# auth.register_conf_options(cfg.CONF)
logging.register_options(cfg.CONF)
cfg.CONF(args=args, project='tricircle',
version='%%(prog)s %s' % version.version_info.release_string(),
**kwargs)
def setup_logging():
"""Sets up the logging options for a log with supplied name."""
product_name = "tricircle"
logging.setup(cfg.CONF, product_name)
LOG.info(_LI("Logging enabled!"))
LOG.info(_LI("%(prog)s version %(version)s"),
{'prog': sys.argv[0],
'version': version.version_info.release_string()})
LOG.debug("command line: %s", " ".join(sys.argv))
def reset_service():
# Reset worker in case SIGHUP is called.
# Note that this is called only in case a service is running in
# daemon mode.
setup_logging()
# (TODO) enforce policy later
# policy.refresh()
def load_paste_app(app_name):
"""Builds and returns a WSGI app from a paste config file.
:param app_name: Name of the application to load
:raises ConfigFilesNotFoundError when config file cannot be located
:raises RuntimeError when application cannot be loaded from config file
"""
config_path = cfg.CONF.find_file(cfg.CONF.api_paste_config)
if not config_path:
raise cfg.ConfigFilesNotFoundError(
config_files=[cfg.CONF.api_paste_config])
config_path = os.path.abspath(config_path)
LOG.info(_LI("Config paste file: %s"), config_path)
try:
app = deploy.loadapp("config:%s" % config_path, name=app_name)
except (LookupError, ImportError):
msg = (_("Unable to load %(app_name)s from "
"configuration file %(config_path)s.") %
{'app_name': app_name,
'config_path': config_path})
LOG.exception(msg)
raise RuntimeError(msg)
return app

83
tricircle/common/exceptions.py Executable file
View File

@ -0,0 +1,83 @@
# Copyright 2015 Huawei Technologies Co., Ltd.
# 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.
"""
Tricircle base exception handling.
"""
from oslo_utils import excutils
import six
from tricircle.i18n import _
class TricircleException(Exception):
"""Base Tricircle Exception.
To correctly use this class, inherit from it and define
a 'message' property. That message will get printf'd
with the keyword arguments provided to the constructor.
"""
message = _("An unknown exception occurred.")
def __init__(self, **kwargs):
try:
super(TricircleException, self).__init__(self.message % kwargs)
self.msg = self.message % kwargs
except Exception:
with excutils.save_and_reraise_exception() as ctxt:
if not self.use_fatal_exceptions():
ctxt.reraise = False
# at least get the core message out if something happened
super(TricircleException, self).__init__(self.message)
if six.PY2:
def __unicode__(self):
return unicode(self.msg)
def use_fatal_exceptions(self):
return False
class BadRequest(TricircleException):
message = _('Bad %(resource)s request: %(msg)s')
class NotFound(TricircleException):
pass
class Conflict(TricircleException):
pass
class NotAuthorized(TricircleException):
message = _("Not authorized.")
class ServiceUnavailable(TricircleException):
message = _("The service is unavailable")
class AdminRequired(NotAuthorized):
message = _("User does not have admin privileges: %(reason)s")
class InUse(TricircleException):
message = _("The resource is inuse")
class InvalidConfigurationOption(TricircleException):
message = _("An invalid value was provided for %(opt_name)s: "
"%(opt_value)s")

30
tricircle/i18n.py Executable file
View File

@ -0,0 +1,30 @@
# 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.
import oslo_i18n
_translators = oslo_i18n.TranslatorFactory(domain='tricircle')
# 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

17
tricircle/version.py Executable file
View File

@ -0,0 +1,17 @@
# Copyright 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 pbr.version
version_info = pbr.version.VersionInfo('tricircle')