diff --git a/README.rst b/README.rst index 047e441..1ff1b57 100644 --- a/README.rst +++ b/README.rst @@ -1,19 +1,24 @@ =============================== -kingbird +Kingbird =============================== -Kingbird is a centralised synchronization service for multi-region OpenStack deployments. +Centralized service for multi-region OpenStack deployments. + +Kingbird is an centralized OpenStack service that provides resource operation +and management across multiple OpenStack instances in a multi-region OpenStack +deployments. This service is part of the OPNFV Multisite project that intends +to address the use cases related to distributed cloud environments. + +Kingbird provides features like centralized quota management, centralized view +for distributed virtual resources, global view for tenant level IP/MAC address +space management, synchronisation of ssh keys, images, flavors, security +groups, etc. across regions. -Please feel here a long description which must be at least 3 lines wrapped on -80 cols, so that distribution package maintainers can use it in their packages. -Note that this is a hard requirement. * Free software: Apache license +* Wiki:https://wiki.openstack.org/wiki/kingbird * Documentation: http://docs.openstack.org/developer/kingbird -* Source: http://git.openstack.org/cgit/openstack/kingbird +* Source: https://github.com/openstack/kingbird * Bugs: http://bugs.launchpad.net/kingbird +* Blueprints: https://launchpad.net/kingbird -Features --------- - -* TODO diff --git a/cmd/api.py b/cmd/api.py new file mode 100755 index 0000000..3d21ed2 --- /dev/null +++ b/cmd/api.py @@ -0,0 +1,64 @@ +# 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 sys + +from oslo_config import cfg +from oslo_log import log as logging + +import logging as std_logging + +from werkzeug import serving + +from kingbird.api import apicfg +from kingbird.api import app + +from kingbird.common.i18n import _LI +from kingbird.common.i18n import _LW + +CONF = cfg.CONF +LOG = logging.getLogger(__name__) + + +def main(): + apicfg.init(sys.argv[1:]) + apicfg.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() diff --git a/etc/api.conf b/etc/api.conf new file mode 100755 index 0000000..d0aeaf8 --- /dev/null +++ b/etc/api.conf @@ -0,0 +1,370 @@ +[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 Kingbird state files. This directory must be writable by the +# user executing the agent. +# state_path = /var/lib/kingbird + +# 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 = 8118 + +# Paste configuration file +# api_paste_config = api-paste.ini + +# (StrOpt) Hostname to be used by the kingbird 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 = noauth + +[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 = kingbird +# 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 diff --git a/etc/policy.json b/etc/policy.json new file mode 100755 index 0000000..9507586 --- /dev/null +++ b/etc/policy.json @@ -0,0 +1,8 @@ +{ + "context_is_admin": "role:admin", + "admin_or_owner": "is_admin:True or project_id:%(project_id)s", + "default": "rule:admin_or_owner", + + "kingbird:create_quota": "rule:admin_or_owner", + "kingbird:update_quota": "rule:admin_or_owner" +} diff --git a/kingbird/api/__init__.py b/kingbird/api/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/kingbird/api/apicfg.py b/kingbird/api/apicfg.py new file mode 100755 index 0000000..9877294 --- /dev/null +++ b/kingbird/api/apicfg.py @@ -0,0 +1,95 @@ +# 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 kingbird, largely copy from Neutron +""" + +import sys + + +from oslo_config import cfg +from oslo_log import log as logging + +from kingbird.common.i18n import _ +from kingbird.common.i18n import _LI + + +# from kingbird import policy +# from kingbird.common import rpc +from kingbird.common 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=8118, + help=_("The port to bind to")), + cfg.IntOpt('api_workers', default=2, + 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='kingbird', + version='%%(prog)s %s' % version.version_info.release_string(), + **kwargs) + + # rpc.init(cfg.CONF) + + +def setup_logging(): + """Sets up the logging options for a log with supplied name.""" + product_name = "kingbird" + 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(joehuang) enforce policy later + # policy.refresh() diff --git a/kingbird/api/app.py b/kingbird/api/app.py new file mode 100755 index 0000000..95f4ab5 --- /dev/null +++ b/kingbird/api/app.py @@ -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 kingbird.common import exceptions as k_exc + + +def setup_app(*args, **kwargs): + config = { + 'server': { + 'port': cfg.CONF.bind_port, + 'host': cfg.CONF.bind_host + }, + 'app': { + 'root': 'kingbird.api.controllers.root.RootController', + 'modules': ['kingbird.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 k_exc.InvalidConfigurationOption( + opt_name='auth_strategy', opt_value=cfg.CONF.auth_strategy) + + return app diff --git a/kingbird/api/controllers/__init__.py b/kingbird/api/controllers/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/kingbird/api/controllers/helloworld.py b/kingbird/api/controllers/helloworld.py new file mode 100755 index 0000000..796b1ee --- /dev/null +++ b/kingbird/api/controllers/helloworld.py @@ -0,0 +1,63 @@ +# 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 kingbird.jobdaemon import jdrpcapi + +import pecan +from pecan import expose +from pecan import rest +import restcomm + + +class HelloWorldController(rest.RestController): + + def __init__(self, *args, **kwargs): + super(HelloWorldController, self).__init__(*args, **kwargs) + # self.jd_api = jdrpcapi.JobDaemonAPI() + + @expose(generic=True, template='json') + def index(self): + if pecan.request.method != 'GET': + pecan.abort(405) + + context = restcomm.extract_context_from_environ() + if context.is_admin: + return {'hello world message for admin': 'GET'} + else: + return {'hello world message for non-admin': 'GET'} + + @index.when(method='PUT', template='json') + def put(self, **kw): + context = restcomm.extract_context_from_environ() + if context.is_admin: + return {'hello world message for admin': 'PUT'} + else: + return {'hello world message for non-admin': 'PUT'} + + @index.when(method='POST', template='json') + def post(self, **kw): + context = restcomm.extract_context_from_environ() + if context.is_admin: + return {'hello world message for admin': 'POST'} + else: + return {'hello world message for non-admin': 'POST'} + + @index.when(method='delete', template='json') + def delete(self): + context = restcomm.extract_context_from_environ() + if context.is_admin: + return {'hello world message for admin': 'delete'} + else: + return {'hello world message for non-admin': 'delete'} diff --git a/kingbird/api/controllers/restcomm.py b/kingbird/api/controllers/restcomm.py new file mode 100755 index 0000000..d0ea651 --- /dev/null +++ b/kingbird/api/controllers/restcomm.py @@ -0,0 +1,40 @@ +# 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 pecan import request + +import kingbird.common.context as k_context + + +def extract_context_from_environ(): + context_paras = {'auth_token': 'HTTP_X_AUTH_TOKEN', + 'user': 'HTTP_X_USER_ID', + 'tenant': 'HTTP_X_TENANT_ID', + 'user_name': 'HTTP_X_USER_NAME', + 'tenant_name': 'HTTP_X_PROJECT_NAME', + 'domain': 'HTTP_X_DOMAIN_ID', + 'user_domain': 'HTTP_X_USER_DOMAIN_ID', + 'project_domain': 'HTTP_X_PROJECT_DOMAIN_ID', + 'request_id': 'openstack.request_id'} + + environ = request.environ + + for key in context_paras: + context_paras[key] = environ.get(context_paras[key]) + role = environ.get('HTTP_X_ROLE') + + context_paras['is_admin'] = role == 'admin' + return k_context.Context(**context_paras) diff --git a/kingbird/api/controllers/root.py b/kingbird/api/controllers/root.py new file mode 100755 index 0000000..80c0840 --- /dev/null +++ b/kingbird/api/controllers/root.py @@ -0,0 +1,69 @@ +# 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 helloworld +import pecan + + +class RootController(object): + + @pecan.expose('json') + 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 = { + "helloworld": helloworld.HelloWorldController() + } + + 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) + ] + } diff --git a/kingbird/common/__init__.py b/kingbird/common/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/kingbird/common/context.py b/kingbird/common/context.py new file mode 100755 index 0000000..c067157 --- /dev/null +++ b/kingbird/common/context.py @@ -0,0 +1,70 @@ +# 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. + +from oslo_context import context as oslo_ctx + + +class ContextBase(oslo_ctx.RequestContext): + def __init__(self, auth_token=None, user_id=None, tenant_id=None, + is_admin=False, request_id=None, overwrite=True, + user_name=None, tenant_name=None, **kwargs): + super(ContextBase, self).__init__( + auth_token=auth_token, + user=user_id or kwargs.get('user', None), + tenant=tenant_id or kwargs.get('tenant', None), + domain=kwargs.get('domain', None), + user_domain=kwargs.get('user_domain', None), + project_domain=kwargs.get('project_domain', None), + is_admin=is_admin, + read_only=kwargs.get('read_only', False), + show_deleted=kwargs.get('show_deleted', False), + request_id=request_id, + resource_uuid=kwargs.get('resource_uuid', None), + overwrite=overwrite) + self.user_name = user_name + self.tenant_name = tenant_name + + def to_dict(self): + ctx_dict = super(ContextBase, self).to_dict() + ctx_dict.update({ + 'user_name': self.user_name, + 'tenant_name': self.tenant_name + }) + return ctx_dict + + @classmethod + def from_dict(cls, ctx): + return cls(**ctx) + + +class Context(ContextBase): + def __init__(self, **kwargs): + super(Context, self).__init__(**kwargs) + self._session = None + + @property + def session(self): + # todo get db session in the context + # if not self._session: + # self._session = dal.get_session() + return self._session + + +def get_admin_context(read_only=True): + return ContextBase(user_id=None, + project_id=None, + is_admin=True, + overwrite=False, + read_only=read_only) diff --git a/kingbird/common/exceptions.py b/kingbird/common/exceptions.py new file mode 100755 index 0000000..a19df2f --- /dev/null +++ b/kingbird/common/exceptions.py @@ -0,0 +1,84 @@ +# 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. + +""" +Kingbird base exception handling. +""" + +from oslo_utils import excutils +import six + +from kingbird.common.i18n import _ + + +class KingbirdException(Exception): + """Base Kingbird 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(KingbirdException, 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(KingbirdException, self).__init__(self.message) + + if six.PY2: + def __unicode__(self): + return unicode(self.msg) + + def use_fatal_exceptions(self): + return False + + +class BadRequest(KingbirdException): + message = _('Bad %(resource)s request: %(msg)s') + + +class NotFound(KingbirdException): + pass + + +class Conflict(KingbirdException): + pass + + +class NotAuthorized(KingbirdException): + message = _("Not authorized.") + + +class ServiceUnavailable(KingbirdException): + message = _("The service is unavailable") + + +class AdminRequired(NotAuthorized): + message = _("User does not have admin privileges: %(reason)s") + + +class InUse(KingbirdException): + message = _("The resource is inuse") + + +class InvalidConfigurationOption(KingbirdException): + message = _("An invalid value was provided for %(opt_name)s: " + "%(opt_value)s") diff --git a/kingbird/common/i18n.py b/kingbird/common/i18n.py new file mode 100755 index 0000000..4f4932e --- /dev/null +++ b/kingbird/common/i18n.py @@ -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='kingbird') + +# 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 diff --git a/kingbird/common/utils.py b/kingbird/common/utils.py new file mode 100755 index 0000000..5201cee --- /dev/null +++ b/kingbird/common/utils.py @@ -0,0 +1,18 @@ +# Copyright 2015 Huawei Technologies Co., Ltd. +# +# 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. + + +def get_import_path(cls): + return cls.__module__ + "." + cls.__name__ diff --git a/kingbird/common/version.py b/kingbird/common/version.py new file mode 100755 index 0000000..47069bd --- /dev/null +++ b/kingbird/common/version.py @@ -0,0 +1,41 @@ +# 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 + +KINGBIRD_VENDOR = "OpenStack Foundation" +KINGBIRD_PRODUCT = "OpenStack Kingbird" +KINGBIRD_PACKAGE = None # OS distro package version suffix + +version_info = pbr.version.VersionInfo('kingbird') +version_string = version_info.version_string + + +def vendor_string(): + return KINGBIRD_VENDOR + + +def product_string(): + return KINGBIRD_PRODUCT + + +def package_string(): + return KINGBIRD_PACKAGE + + +def version_string_with_package(): + if package_string() is None: + return version_info.version_string() + else: + return "%s-%s" % (version_info.version_string(), package_string()) diff --git a/requirements.txt b/requirements.txt index 08b0f01..990aeb7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,41 @@ # 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>=1.6 Babel>=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>=1.0.0 +greenlet>=0.3.2 +httplib2>=0.7.5 +requests!=2.8.0,>=2.5.2 +Werkzeug>=0.7 # BSD License +Jinja2>=2.8 # BSD License (3 clause) +keystonemiddleware!=2.4.0,>=2.0.0 +netaddr!=0.7.16,>=0.7.12 +retrying!=1.3.0,>=1.2.3 # Apache-2.0 +SQLAlchemy<1.1.0,>=0.9.9 +WebOb>=1.2.3 +python-keystoneclient!=1.8.0,>=1.6.0 +alembic>=0.8.0 +six>=1.9.0 +stevedore>=1.5.0 # Apache-2.0 +oslo.concurrency>=2.3.0 # Apache-2.0 +oslo.config>=2.6.0 # Apache-2.0 +oslo.context>=0.2.0 # Apache-2.0 +oslo.db>=3.0.0 # Apache-2.0 +oslo.i18n>=1.5.0 # Apache-2.0 +oslo.log>=1.12.0 # Apache-2.0 +oslo.messaging!=2.8.0,>2.6.1 # Apache-2.0 +oslo.middleware>=2.9.0 # Apache-2.0 +oslo.policy>=0.5.0 # Apache-2.0 +oslo.rootwrap>=2.0.0 # Apache-2.0 +oslo.serialization>=1.10.0 # Apache-2.0 +oslo.service>=0.12.0 # Apache-2.0 +oslo.utils!=2.6.0,>=2.4.0 # Apache-2.0 +oslo.versionedobjects>=0.9.0 diff --git a/test-requirements.txt b/test-requirements.txt index 0ecbfef..38fd96a 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,15 +1,23 @@ # 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. - hacking<0.11,>=0.10.2 +cliff>=1.14.0 # Apache-2.0 coverage>=3.6 -discover +fixtures>=1.3.1 +mock>=1.2 python-subunit>=0.0.18 +requests-mock>=0.6.0 # Apache-2.0 sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 oslosphinx>=2.5.0 # Apache-2.0 -oslotest>=1.10.0 # Apache-2.0 testrepository>=0.0.18 -testscenarios>=0.4 testtools>=1.4.0 +testresources>=0.2.4 +testscenarios>=0.4 +WebTest>=2.0 +oslotest>=1.10.0 # Apache-2.0 +os-testr>=0.4.1 +tempest-lib>=0.10.0 +ddt>=0.7.0 +pylint==1.4.4 # GNU GPL v2