summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rwxr-xr-xbin/moniker-api25
-rw-r--r--etc/moniker-api-paste.ini.sample29
-rw-r--r--etc/moniker-api.conf.sample9
-rw-r--r--moniker/api/__init__.py35
-rw-r--r--moniker/api/app.py52
-rw-r--r--moniker/api/auth.py53
-rw-r--r--moniker/api/service.py44
-rw-r--r--moniker/api/v1/__init__.py8
-rw-r--r--moniker/api/v1/domains.py10
-rw-r--r--moniker/api/v1/records.py10
-rw-r--r--moniker/api/v1/servers.py10
-rw-r--r--moniker/exceptions.py4
-rw-r--r--moniker/openstack/common/wsgi.py2
-rw-r--r--moniker/utils.py34
-rw-r--r--moniker/wsgi.py27
16 files changed, 256 insertions, 97 deletions
diff --git a/.gitignore b/.gitignore
index 92078f6f..cb65f92e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,5 +11,6 @@ venv
*.sqlite
var/*
etc/*.conf
+etc/*.ini
AUTHORS
ChangeLog
diff --git a/bin/moniker-api b/bin/moniker-api
index 412a1c53..0cfe6ca4 100755
--- a/bin/moniker-api
+++ b/bin/moniker-api
@@ -15,29 +15,24 @@
# License for the specific language governing permissions and limitations
# under the License.
import sys
+import eventlet
from moniker.openstack.common import cfg
from moniker.openstack.common import log as logging
-from moniker.api.app import app
+from moniker.openstack.common import service
+from moniker.api import service as api_service
-config_files = cfg.find_config_files(project='moniker', prog='moniker-api')
-config_files.append('./etc/moniker-api.conf')
+eventlet.monkey_patch()
-# Jerry Rig the keystone middleware.
-from keystone.middleware import auth_token
-auth_token.CONF = cfg.CONF
-cfg.CONF.register_opts(auth_token.opts, group='keystone_authtoken')
+config_files = cfg.find_config_files(project='moniker',
+ prog='moniker-api')
+config_files.append('./etc/moniker-api.conf')
cfg.CONF(sys.argv[1:], project='moniker', prog='moniker-api',
default_config_files=config_files)
logging.setup('moniker')
-if cfg.CONF.verbose or cfg.CONF.debug:
- app.debug = True
-
-if cfg.CONF.enable_keystone:
- # Add Keystone Middleware
- middleware_conf = {'delay_auth_decision': False}
- app.wsgi_app = auth_token.AuthProtocol(app.wsgi_app, middleware_conf)
+serv = api_service.Service(host=cfg.CONF.api_host, port=cfg.CONF.api_port)
-app.run(host=cfg.CONF.api_host, port=cfg.CONF.api_port)
+launcher = service.launch(serv)
+launcher.wait()
diff --git a/etc/moniker-api-paste.ini.sample b/etc/moniker-api-paste.ini.sample
new file mode 100644
index 00000000..b9d1c5ca
--- /dev/null
+++ b/etc/moniker-api-paste.ini.sample
@@ -0,0 +1,29 @@
+[composite:osapi_dns]
+use = egg:Paste#urlmap
+/v1: osapi_dns_api_v1
+
+[composite:osapi_dns_api_v1]
+use = call:moniker.api.auth:pipeline_factory
+noauth = noauth osapi_dns_app_v1
+keystone = authtoken keystonecontext osapi_dns_app_v1
+
+[app:osapi_dns_app_v1]
+paste.app_factory = moniker.api.v1:factory
+
+[filter:noauth]
+paste.filter_factory = moniker.api.auth:NoAuthMiddleware.factory
+
+[filter:keystonecontext]
+paste.filter_factory = moniker.api.auth:KeystoneContextMiddleware.factory
+
+[filter:authtoken]
+paste.filter_factory = keystone.middleware.auth_token:filter_factory
+service_protocol = http
+service_host = 127.0.0.1
+service_port = 5000
+auth_host = 127.0.0.1
+auth_port = 35357
+auth_protocol = http
+admin_tenant_name = %SERVICE_TENANT_NAME%
+admin_user = %SERVICE_USER%
+admin_password = %SERVICE_PASSWORD%
diff --git a/etc/moniker-api.conf.sample b/etc/moniker-api.conf.sample
index 3de866c4..e9763731 100644
--- a/etc/moniker-api.conf.sample
+++ b/etc/moniker-api.conf.sample
@@ -20,10 +20,5 @@ control_exchange = moniker
#
allowed_rpc_exception_modules = moniker.exceptions, moniker.openstack.common.exception
-[keystone_authtoken]
-auth_host = 127.0.0.1
-auth_port = 35357
-auth_protocol = http
-admin_tenant_name = admin
-admin_user = admin
-admin_password = password
+#
+auth_strategy = noauth
diff --git a/moniker/api/__init__.py b/moniker/api/__init__.py
index e69de29b..d0dce6fc 100644
--- a/moniker/api/__init__.py
+++ b/moniker/api/__init__.py
@@ -0,0 +1,35 @@
+# Copyright 2012 Managed I.T.
+#
+# Author: Kiall Mac Innes <kiall@managedit.ie>
+#
+# 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 flask
+from moniker.openstack.common import cfg
+from moniker.openstack.common import jsonutils
+
+
+cfg.CONF.register_opts([
+ cfg.StrOpt('api_host', default='0.0.0.0',
+ help='API Host'),
+ cfg.IntOpt('api_port', default=9001,
+ help='API Port Number'),
+ cfg.StrOpt('api_paste_config', default='moniker-api-paste.ini',
+ help='File name for the paste.deploy config for moniker-api'),
+ cfg.StrOpt('auth_strategy', default='noauth',
+ help='The strategy to use for auth. Supports noauth or '
+ 'keystone'),
+])
+
+
+# Allows us to serialize datetime's etc
+flask.helpers.json = jsonutils
diff --git a/moniker/api/app.py b/moniker/api/app.py
deleted file mode 100644
index 20ca1da6..00000000
--- a/moniker/api/app.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# Copyright 2012 Managed I.T.
-#
-# Author: Kiall Mac Innes <kiall@managedit.ie>
-#
-# 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 flask
-from moniker.openstack.common import cfg
-from moniker.openstack.common import jsonutils
-from moniker.openstack.common.context import RequestContext
-from moniker import central
-from moniker.api import v1
-from moniker.api import debug
-
-# Allows us to serialize datetime's etc
-flask.helpers.json = jsonutils
-
-cfg.CONF.register_opts([
- cfg.StrOpt('api_host', default='0.0.0.0',
- help='API Host'),
- cfg.IntOpt('api_port', default=9001,
- help='API Port Number'),
-])
-
-app = flask.Flask('moniker.api')
-
-# Blueprints
-app.register_blueprint(v1.blueprint, url_prefix='/v1')
-app.register_blueprint(debug.blueprint, url_prefix='/debug')
-
-
-@app.before_request
-def attach_context():
- request = flask.request
- headers = request.headers
-
- if cfg.CONF.enable_keystone:
- request.context = RequestContext(auth_tok=headers.get('X-Auth-Token'),
- user=headers.get('X-User-ID'),
- tenant=headers.get('X-Tenant-ID'))
- else:
- request.context = RequestContext(user=cfg.CONF.default_user,
- tenant=cfg.CONF.default_tenant)
diff --git a/moniker/api/auth.py b/moniker/api/auth.py
new file mode 100644
index 00000000..27935c19
--- /dev/null
+++ b/moniker/api/auth.py
@@ -0,0 +1,53 @@
+# Copyright 2012 Managed I.T.
+#
+# Author: Kiall Mac Innes <kiall@managedit.ie>
+#
+# 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 moniker.openstack.common.context import RequestContext
+from moniker.openstack.common import cfg
+from moniker.openstack.common import log as logging
+from moniker import wsgi
+
+LOG = logging.getLogger(__name__)
+
+
+def pipeline_factory(loader, global_conf, **local_conf):
+ """
+ A paste pipeline replica that keys off of auth_strategy.
+
+ Code nabbed from cinder.
+ """
+ pipeline = local_conf[cfg.CONF.auth_strategy]
+ pipeline = pipeline.split()
+ filters = [loader.get_filter(n) for n in pipeline[:-1]]
+ app = loader.get_app(pipeline[-1])
+ filters.reverse()
+ for filter in filters:
+ app = filter(app)
+ return app
+
+
+class KeystoneContextMiddleware(wsgi.Middleware):
+ def process_request(self, request):
+ headers = request.headers
+ context = RequestContext(auth_tok=headers.get('X-Auth-Token'),
+ user=headers.get('X-User-ID'),
+ tenant=headers.get('X-Tenant-ID'))
+ request.environ['context'] = context
+
+
+class NoAuthMiddleware(wsgi.Middleware):
+ def process_request(self, request):
+ context = RequestContext(user=cfg.CONF.default_user,
+ tenant=cfg.CONF.default_tenant)
+ request.environ['context'] = context
diff --git a/moniker/api/service.py b/moniker/api/service.py
new file mode 100644
index 00000000..2cb37a03
--- /dev/null
+++ b/moniker/api/service.py
@@ -0,0 +1,44 @@
+# Copyright 2012 Managed I.T.
+#
+# Author: Kiall Mac Innes <kiall@managedit.ie>
+#
+# 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 os
+from paste import deploy
+from moniker.openstack.common import log as logging
+from moniker.openstack.common import wsgi
+from moniker.openstack.common import cfg
+from moniker import utils
+
+
+LOG = logging.getLogger(__name__)
+
+
+class Service(wsgi.Service):
+ def __init__(self, port, host='0.0.0.0', backlog=128, threads=1000):
+ super(Service, self).__init__(threads)
+
+ self.host = host
+ self.port = port
+ self.backlog = backlog
+
+ config_path = cfg.CONF.api_paste_config
+ config_path = utils.find_config(config_path)
+
+ self.application = deploy.loadapp("config:%s" % config_path,
+ name='osapi_dns')
+
+ def start(self):
+ return super(Service, self).start(application=self.application,
+ port=self.port, host=self.host,
+ backlog=self.backlog)
diff --git a/moniker/api/v1/__init__.py b/moniker/api/v1/__init__.py
index 1638196c..7ca0b9b8 100644
--- a/moniker/api/v1/__init__.py
+++ b/moniker/api/v1/__init__.py
@@ -20,3 +20,11 @@ blueprint = flask.Blueprint('v1', __name__)
import moniker.api.v1.servers
import moniker.api.v1.domains
import moniker.api.v1.records
+
+
+def factory(global_config, **local_conf):
+ import flask
+ app = flask.Flask('moniker.api.v1')
+ app.register_blueprint(blueprint)
+
+ return app
diff --git a/moniker/api/v1/domains.py b/moniker/api/v1/domains.py
index 7df2a51b..e240f19f 100644
--- a/moniker/api/v1/domains.py
+++ b/moniker/api/v1/domains.py
@@ -43,7 +43,7 @@ def get_domains_schema():
@blueprint.route('/domains', methods=['POST'])
def create_domain():
- context = flask.request.context
+ context = flask.request.environ.get('context')
values = flask.request.json
try:
@@ -67,7 +67,7 @@ def create_domain():
@blueprint.route('/domains', methods=['GET'])
def get_domains():
- context = flask.request.context
+ context = flask.request.environ.get('context')
domains = central_api.get_domains(context)
@@ -78,7 +78,7 @@ def get_domains():
@blueprint.route('/domains/<domain_id>', methods=['GET'])
def get_domain(domain_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
try:
domain = central_api.get_domain(context, domain_id)
@@ -94,7 +94,7 @@ def get_domain(domain_id):
@blueprint.route('/domains/<domain_id>', methods=['PUT'])
def update_domain(domain_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
values = flask.request.json
try:
@@ -116,7 +116,7 @@ def update_domain(domain_id):
@blueprint.route('/domains/<domain_id>', methods=['DELETE'])
def delete_domain(domain_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
try:
central_api.delete_domain(context, domain_id)
diff --git a/moniker/api/v1/records.py b/moniker/api/v1/records.py
index 443531cf..6019e8e1 100644
--- a/moniker/api/v1/records.py
+++ b/moniker/api/v1/records.py
@@ -44,7 +44,7 @@ def get_records_schema():
@blueprint.route('/domains/<domain_id>/records', methods=['POST'])
def create_record(domain_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
values = flask.request.json
try:
@@ -69,7 +69,7 @@ def create_record(domain_id):
@blueprint.route('/domains/<domain_id>/records', methods=['GET'])
def get_records(domain_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
records = central_api.get_records(context, domain_id)
@@ -78,7 +78,7 @@ def get_records(domain_id):
@blueprint.route('/domains/<domain_id>/records/<record_id>', methods=['GET'])
def get_record(domain_id, record_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
try:
record = central_api.get_record(context, domain_id, record_id)
@@ -94,7 +94,7 @@ def get_record(domain_id, record_id):
@blueprint.route('/domains/<domain_id>/records/<record_id>', methods=['PUT'])
def update_record(domain_id, record_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
values = flask.request.json
try:
@@ -118,7 +118,7 @@ def update_record(domain_id, record_id):
@blueprint.route('/domains/<domain_id>/records/<record_id>',
methods=['DELETE'])
def delete_record(domain_id, record_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
try:
central_api.delete_record(context, domain_id, record_id)
diff --git a/moniker/api/v1/servers.py b/moniker/api/v1/servers.py
index fe41cf91..bcd8a14f 100644
--- a/moniker/api/v1/servers.py
+++ b/moniker/api/v1/servers.py
@@ -42,7 +42,7 @@ def get_servers_schema():
@blueprint.route('/servers', methods=['POST'])
def create_server():
- context = flask.request.context
+ context = flask.request.environ.get('context')
values = flask.request.json
try:
@@ -66,7 +66,7 @@ def create_server():
@blueprint.route('/servers', methods=['GET'])
def get_servers():
- context = flask.request.context
+ context = flask.request.environ.get('context')
servers = central_api.get_servers(context)
@@ -77,7 +77,7 @@ def get_servers():
@blueprint.route('/servers/<server_id>', methods=['GET'])
def get_server(server_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
try:
server = central_api.get_server(context, server_id)
@@ -93,7 +93,7 @@ def get_server(server_id):
@blueprint.route('/servers/<server_id>', methods=['PUT'])
def update_server(server_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
values = flask.request.json
try:
@@ -115,7 +115,7 @@ def update_server(server_id):
@blueprint.route('/servers/<server_id>', methods=['DELETE'])
def delete_server(server_id):
- context = flask.request.context
+ context = flask.request.environ.get('context')
try:
central_api.delete_server(context, server_id)
diff --git a/moniker/exceptions.py b/moniker/exceptions.py
index 62f0659a..ee4a4518 100644
--- a/moniker/exceptions.py
+++ b/moniker/exceptions.py
@@ -19,6 +19,10 @@ class Base(Exception):
pass
+class ConfigNotFound(Base):
+ pass
+
+
class InvalidObject(Base):
pass
diff --git a/moniker/openstack/common/wsgi.py b/moniker/openstack/common/wsgi.py
index e7610723..84034d6c 100644
--- a/moniker/openstack/common/wsgi.py
+++ b/moniker/openstack/common/wsgi.py
@@ -56,7 +56,7 @@ class Service(service.Service):
"""
def __init__(self, threads=1000):
- super(Service, self).start()
+ super(Service, self).__init__()
self.pool = eventlet.GreenPool(threads)
def start(self, application, port, host='0.0.0.0', backlog=128):
diff --git a/moniker/utils.py b/moniker/utils.py
index e5ddfd3c..122bd945 100644
--- a/moniker/utils.py
+++ b/moniker/utils.py
@@ -13,8 +13,10 @@
# 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 moniker.openstack.common import rpc
+import os
+from moniker.openstack.common import cfg
from moniker.openstack.common.notifier import api as notifier_api
+from moniker import exceptions
def notify(context, service, event_type, payload):
@@ -24,10 +26,28 @@ def notify(context, service, event_type, payload):
notifier_api.notify(context, publisher_id, event_type, priority, payload)
-def fanout_cast(context, topic, method, **kwargs):
- msg = {
- 'method': method,
- 'args': kwargs
- }
+def find_config(config_path):
+ """ Find a configuration file using the given hint.
- rpc.fanout_cast(context, topic, msg)
+ Code nabbed from cinder.
+
+ :param config_path: Full or relative path to the config.
+ :returns: Full path of the config, if it exists.
+ :raises: `moniker.exceptions.ConfigNotFound`
+
+ """
+ possible_locations = [
+ config_path,
+ os.path.join("etc", "moniker", config_path),
+ os.path.join("etc", config_path),
+ os.path.join(cfg.CONF.state_path, "etc", "moniker", config_path),
+ os.path.join(cfg.CONF.state_path, "etc", config_path),
+ os.path.join(cfg.CONF.state_path, config_path),
+ "/etc/moniker/%s" % config_path,
+ ]
+
+ for path in possible_locations:
+ if os.path.exists(path):
+ return os.path.abspath(path)
+
+ raise exceptions.ConfigNotFound(os.path.abspath(config_path))
diff --git a/moniker/wsgi.py b/moniker/wsgi.py
new file mode 100644
index 00000000..94856d49
--- /dev/null
+++ b/moniker/wsgi.py
@@ -0,0 +1,27 @@
+# Copyright 2012 Managed I.T.
+#
+# Author: Kiall Mac Innes <kiall@managedit.ie>
+#
+# 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 moniker.openstack.common import wsgi
+
+
+class Middleware(wsgi.Middleware):
+ @classmethod
+ def factory(cls, global_config, **local_conf):
+ """ Used for paste app factories in paste.deploy config files """
+
+ def _factory(app):
+ return cls(app, **local_conf)
+
+ return _factory