Got a nice API shell working
* uses devstacks install for nova/keystone/et al * talks to nova via novaclient. * adds a few extensions to show how its done * has a single call to list instances * found a few minor bugs to discuss w/ nova crew ** Note in order to run this you have to mod the code downloaded by devstack or have local symlinks to nova & novaclient in your src tree running trunk This will get dealt with soon (it is a weekend!)
This commit is contained in:
parent
f66d94b3a1
commit
dc5a1bb8c3
70
bin/reddwarf-api-os-database
Executable file
70
bin/reddwarf-api-os-database
Executable file
@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
"""Starter script for Nova OS API."""
|
||||
|
||||
import eventlet
|
||||
eventlet.monkey_patch()
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(
|
||||
sys.argv[0]), os.pardir, os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, "nova", "__init__.py")):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova import service
|
||||
from nova import utils
|
||||
from nova import wsgi
|
||||
from nova.openstack.common import cfg
|
||||
|
||||
SERVICE_NAME="reddwarfapi_database"
|
||||
|
||||
reddwarf_opts = [
|
||||
cfg.StrOpt('reddwarf_api_paste_config',
|
||||
default='reddwarf-api-paste.ini',
|
||||
help='Reddwarf API paste config'),
|
||||
# port magic in service.WSGIService the name of the app has to be the same
|
||||
# as the name of the service
|
||||
cfg.IntOpt('%s_listen_port' % SERVICE_NAME,
|
||||
default=8779,
|
||||
help='Reddwarf API default port'),
|
||||
cfg.MultiStrOpt('reddwarf_api_extension',
|
||||
default=[
|
||||
'reddwarf.api.database.contrib.standard_extensions'
|
||||
],
|
||||
help='osapi compute extension to load'),
|
||||
]
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(reddwarf_opts)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
utils.default_flagfile()
|
||||
flags.FLAGS(sys.argv)
|
||||
logging.setup()
|
||||
utils.monkey_patch()
|
||||
loader = wsgi.Loader(config_path=FLAGS.reddwarf_api_paste_config)
|
||||
server = service.WSGIService(SERVICE_NAME, loader=loader)
|
||||
service.serve(server)
|
||||
service.wait()
|
304
bin/reddwarf-cli
Executable file
304
bin/reddwarf-cli
Executable file
@ -0,0 +1,304 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2011 OpenStack LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
Reddwarf Command line tool
|
||||
"""
|
||||
|
||||
import json
|
||||
import optparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
# If ../reddwarf/__init__.py exists, add ../ to Python search path, so that
|
||||
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||
possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||
os.pardir,
|
||||
os.pardir))
|
||||
if os.path.exists(os.path.join(possible_topdir, 'reddwarfclient',
|
||||
'__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
|
||||
sys.path.insert(0, possible_topdir)
|
||||
|
||||
|
||||
from reddwarfclient import common
|
||||
|
||||
|
||||
oparser = None
|
||||
|
||||
|
||||
def _pretty_print(info):
|
||||
print json.dumps(info, sort_keys=True, indent=4)
|
||||
|
||||
|
||||
class InstanceCommands(object):
|
||||
"""Commands to perform various instances operations and actions"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def create(self, name, volume_size,
|
||||
flavorRef="http://localhost:8775/v1.0/flavors/1"):
|
||||
"""Create a new instance"""
|
||||
dbaas = common.get_client()
|
||||
volume = {"size": volume_size}
|
||||
try:
|
||||
result = dbaas.instances.create(name, flavorRef, volume)
|
||||
_pretty_print(result._info)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def delete(self, id):
|
||||
"""Delete the specified instance"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
result = dbaas.instances.delete(id)
|
||||
if result:
|
||||
print result
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def get(self, id):
|
||||
"""Get details for the specified instance"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
_pretty_print(dbaas.instances.get(id)._info)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def list(self):
|
||||
"""List all instances for account"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
for instance in dbaas.instances.list():
|
||||
_pretty_print(instance._info)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def resize(self, id, size):
|
||||
"""Resize an instance volume"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
result = dbaas.instances.resize(id, size)
|
||||
if result:
|
||||
print result
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def restart(self, id):
|
||||
"""Restart the database"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
result = dbaas.instances.restart(id)
|
||||
if result:
|
||||
print result
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
|
||||
class FlavorsCommands(object):
|
||||
"""Commands for listing Flavors"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def list(self):
|
||||
"""List the available flavors"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
for flavor in dbaas.flavors.list():
|
||||
_pretty_print(flavor._info)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
|
||||
class DatabaseCommands(object):
|
||||
"""Database CRUD operations on an instance"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def create(self, id, dbname):
|
||||
"""Create a database"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
databases = [{'name': dbname}]
|
||||
dbaas.databases.create(id, databases)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def delete(self, id, dbname):
|
||||
"""Delete a database"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
dbaas.databases.delete(id, dbname)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def list(self, id):
|
||||
"""List the databases"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
for database in dbaas.databases.list(id):
|
||||
_pretty_print(database._info)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
|
||||
class UserCommands(object):
|
||||
"""User CRUD operations on an instance"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def create(self, id, username, password, dbname, *args):
|
||||
"""Create a user in instance, with access to one or more databases"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
databases = [{'name': dbname}]
|
||||
[databases.append({"name": db}) for db in args]
|
||||
users = [{'name': username, 'password': password,
|
||||
'databases': databases}]
|
||||
dbaas.users.create(id, users)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def delete(self, id, user):
|
||||
"""Delete the specified user"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
dbaas.users.delete(id, user)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def list(self, id):
|
||||
"""List all the users for an instance"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
for user in dbaas.users.list(id):
|
||||
_pretty_print(user._info)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
|
||||
class RootCommands(object):
|
||||
"""Root user related operations on an instance"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def create(self, id):
|
||||
"""Enable the instance's root user."""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
user, password = dbaas.root.create(id)
|
||||
print "User:\t\t%s\nPassword:\t%s" % (user, password)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
def enabled(self, id):
|
||||
"""Check the instance for root access"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
_pretty_print(dbaas.root.is_root_enabled(id))
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
|
||||
class VersionCommands(object):
|
||||
"""List available versions"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def list(self, url):
|
||||
"""List all the supported versions"""
|
||||
dbaas = common.get_client()
|
||||
try:
|
||||
versions = dbaas.versions.index(url)
|
||||
for version in versions:
|
||||
_pretty_print(version._info)
|
||||
except:
|
||||
print sys.exc_info()[1]
|
||||
|
||||
|
||||
def config_options():
|
||||
global oparser
|
||||
oparser.add_option("-u", "--url", default="http://localhost:5000/v1.1",
|
||||
help="Auth API endpoint URL with port and version. \
|
||||
Default: http://localhost:5000/v1.1")
|
||||
|
||||
|
||||
COMMANDS = {'auth': common.Auth,
|
||||
'instance': InstanceCommands,
|
||||
'flavor': FlavorsCommands,
|
||||
'database': DatabaseCommands,
|
||||
'user': UserCommands,
|
||||
'root': RootCommands,
|
||||
'version': VersionCommands}
|
||||
|
||||
|
||||
def main():
|
||||
# Parse arguments
|
||||
global oparser
|
||||
oparser = optparse.OptionParser("%prog [options] <cmd> <action> <args>",
|
||||
version='1.0')
|
||||
config_options()
|
||||
(options, args) = oparser.parse_args()
|
||||
|
||||
if not args:
|
||||
common.print_commands(COMMANDS)
|
||||
|
||||
# Pop the command and check if it's in the known commands
|
||||
cmd = args.pop(0)
|
||||
if cmd in COMMANDS:
|
||||
fn = COMMANDS.get(cmd)
|
||||
command_object = fn()
|
||||
|
||||
# Get a list of supported actions for the command
|
||||
actions = common.methods_of(command_object)
|
||||
|
||||
if len(args) < 1:
|
||||
common.print_actions(cmd, actions)
|
||||
|
||||
# Check for a valid action and perform that action
|
||||
action = args.pop(0)
|
||||
if action in actions:
|
||||
fn = actions.get(action)
|
||||
|
||||
try:
|
||||
fn(*args)
|
||||
sys.exit(0)
|
||||
except TypeError as err:
|
||||
print "Possible wrong number of arguments supplied."
|
||||
print "%s %s: %s" % (cmd, action, fn.__doc__)
|
||||
print "\t\t", [fn.func_code.co_varnames[i] for i in
|
||||
range(fn.func_code.co_argcount)]
|
||||
print "ERROR: %s" % err
|
||||
except Exception:
|
||||
print "Command failed, please check the log for more info."
|
||||
raise
|
||||
else:
|
||||
common.print_actions(cmd, actions)
|
||||
else:
|
||||
common.print_commands(COMMANDS)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
50
development/FunkyBugs.txt
Normal file
50
development/FunkyBugs.txt
Normal file
@ -0,0 +1,50 @@
|
||||
ONE:
|
||||
|
||||
Looks like the on-behalf-of functionality in python novaclient is borked in association w/ keystone
|
||||
*There are 2 different calls to keystone to get token info, and they return different hash's
|
||||
*The service catalog borks out if 'access' is not the main key, but in the 2nd case 'token' is the main key.
|
||||
|
||||
Modified Files
|
||||
* novaclient/service_catalog.py
|
||||
|
||||
Until this is fixed you need to mod python-novaclient after it gets downloaded via devstack
|
||||
###### BEGIN PATCH
|
||||
@@ -44,12 +44,17 @@ class ServiceCatalog(object):
|
||||
raise novaclient.exceptions.EndpointNotFound()
|
||||
|
||||
# We don't always get a service catalog back ...
|
||||
- if not 'serviceCatalog' in self.catalog['access']:
|
||||
+ try:
|
||||
+ if 'serviceCatalog' in self.catalog['access']:
|
||||
+ # Full catalog ...
|
||||
+ catalog = self.catalog['access']['serviceCatalog']
|
||||
+ except KeyError:
|
||||
+ if 'serviceCatalog' in self.catalog['token']:
|
||||
+ # Full catalog ...
|
||||
+ catalog = self.catalog['token']['serviceCatalog']
|
||||
+ if catalog is None:
|
||||
return None
|
||||
|
||||
- # Full catalog ...
|
||||
- catalog = self.catalog['access']['serviceCatalog']
|
||||
-
|
||||
for service in catalog:
|
||||
if service.get("type") != service_type:
|
||||
continue
|
||||
###### END PATCH
|
||||
|
||||
TWO:
|
||||
|
||||
Looks like there is a missing import in nova/api/openstack/auth.py
|
||||
from nova.auth import manager
|
||||
|
||||
Until this is fixed you need to mod nova after its gets downloaded via devstack
|
||||
###### BEGIN PATCH
|
||||
@@ -80,6 +80,7 @@ class AuthMiddleware(base_wsgi.Middleware):
|
||||
if not db_driver:
|
||||
db_driver = FLAGS.db_driver
|
||||
self.db = utils.import_object(db_driver)
|
||||
+ from nova.auth import manager
|
||||
self.auth = auth.manager.AuthManager()
|
||||
super(AuthMiddleware, self).__init__(application)
|
||||
###### END PATCH
|
40
development/development_enviroment.sh
Normal file
40
development/development_enviroment.sh
Normal file
@ -0,0 +1,40 @@
|
||||
# Steps
|
||||
# 1 install nova via devstack
|
||||
# 2 install reddwarf via this (or eventually mod devstack)
|
||||
# 3 run tempest tests
|
||||
cd ~
|
||||
git clone git://github.com/openstack-dev/devstack.git
|
||||
cd devstack
|
||||
# Make sure every devstack instance on a new vm will get the default params for novaclient, paste, etc..
|
||||
# We can change these to external flags in the future
|
||||
echo "MYSQL_PASSWORD=e1a2c042c828d3566d0a
|
||||
RABBIT_PASSWORD=f7999d1955c5014aa32c
|
||||
SERVICE_TOKEN=be19c524ddc92109a224
|
||||
ADMIN_PASSWORD=3de4922d8b6ac5a1aad9" > localrc
|
||||
|
||||
./stack.sh
|
||||
|
||||
# Now add a user to keystone that is reddwarf specific. This is what we will use in dev/test to authenticate against keystone
|
||||
# the get_id is stolen from devstack :D
|
||||
function get_id () {
|
||||
echo `$@ | grep id | awk '{print $4}'`
|
||||
}
|
||||
# NOTE THIS AUTH TOKEN NEEDS TO BE CHANGED
|
||||
REDDWARF_TENANT=`get_id keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 tenant-create --name=reddwarf`
|
||||
REDDWARF_USER=`get_id keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 user-create \
|
||||
--name=reddwarf --pass="REDDWARF-PASS" --email=reddwarf@example.com`
|
||||
REDDWARF_ROLE=`get_id keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 role-create --name=reddwarf`
|
||||
keystone --endpoint http://localhost:35357/v2.0 --token be19c524ddc92109a224 add-user-role $REDDWARF_USER $REDDWARF_ROLE $REDDWARF_TENANT
|
||||
|
||||
# Now attempt a login
|
||||
curl -d '{"auth":{"passwordCredentials":{"username": "reddwarf", "password": "REDDWARF-PASS"},"tenantName":"reddwarf"}}' \
|
||||
-H "Content-type: application/json" http://localhost:35357/v2.0/tokens
|
||||
|
||||
# now get a list of instances, which connects over python-novaclient to nova
|
||||
# NOTE THIS AUTH TOKEN NEEDS TO BE CHANGED
|
||||
# Also note that keystone uses the tenant id now and _not_ the name
|
||||
#curl -H"content-type:application/xml" -H"X-Auth-Project-Id:$REDDWARF_TENANT" -H"X-Auth-User:reddwarf" \
|
||||
# -H'X-Auth-Key:2a2c89c6a7284d32bcb94b4e56f0411c' http://0.0.0.0:8779/v2/$REDDWARF_TENANT/instances
|
||||
|
||||
# Also, you should start up the api node like this
|
||||
# bin/reddwarf-api-os-database --flagfile=etc/nova/nova.conf.template
|
@ -1,4 +0,0 @@
|
||||
# Steps
|
||||
# 1 install nova via devstack
|
||||
# 2 install reddwarf via this (or eventually mod devstack)
|
||||
# 3 run tempest tests
|
68
etc/nova/nova.conf.template
Normal file
68
etc/nova/nova.conf.template
Normal file
@ -0,0 +1,68 @@
|
||||
--reddwarf_api_paste_config=etc/nova/reddwarf-api-paste.ini
|
||||
--osapi_compute_listen_port=9000
|
||||
--sql_connection=mysql://root:e1a2c042c828d3566d0a@127.0.0.1/nova
|
||||
|
||||
|
||||
# Glance
|
||||
#--image_service=nova.image.glance.GlanceImageService
|
||||
#
|
||||
# Nova Network
|
||||
#--network_manager=reddwarf.network.manager.FlatManager
|
||||
#--fixed_range=10.0.0.0/26
|
||||
#--network_size=1
|
||||
#--flat_network_bridge=br100
|
||||
|
||||
# Nova Scheduler
|
||||
#--scheduler_driver=reddwarf.scheduler.simple.UnforgivingMemoryScheduler
|
||||
#--max_instance_memory_mb=6144
|
||||
#
|
||||
# Nova Volume
|
||||
# Volume configuration for vagrant vm
|
||||
#--volume_manager=reddwarf.volume.manager.ReddwarfVolumeManager
|
||||
#--volume_driver=reddwarf.tests.volume.driver.ISCSITestDriver
|
||||
#--use_local_volumes=False
|
||||
#--san_thin_provision=False
|
||||
#--san_ip=33.33.33.11
|
||||
#--san_login=vagrant
|
||||
#--san_privatekey=/home/vagrant/.ssh/id_rsa
|
||||
#
|
||||
# Reddwarf Compute Manager
|
||||
#--compute_manager=reddwarf.compute.manager.ReddwarfComputeManager
|
||||
#
|
||||
# Nova Compute
|
||||
#--connection_type=openvz
|
||||
#--ovz_ve_private_dir=/var/lib/vz/private/
|
||||
#--lock_path=/tmp
|
||||
#--resume_guests_state_on_host_boot=True
|
||||
##--start_guests_on_host_boot=True
|
||||
#
|
||||
## API
|
||||
#--api_paste_config=/home/vagrant/api-paste_keystone.ini
|
||||
|
||||
#--enabled_apis=osapi
|
||||
#--allow_admin_api=True
|
||||
#--reddwarf_auth_cache_expire_time=300
|
||||
#
|
||||
## Quota Limits
|
||||
#--quota_instances=10
|
||||
#--quota_cores=20
|
||||
#--quota_ram=51200
|
||||
#--quota_volumes=10
|
||||
#--quota_gigabytes=10000
|
||||
#--quota_floating_ips=10
|
||||
#
|
||||
## Infrastructure Services
|
||||
#--sql_connection=mysql://nova:novapass@10.0.4.15/nova
|
||||
#--sql_min_pool_size=1
|
||||
#--rabbit_host=10.0.4.15
|
||||
#
|
||||
## Logging
|
||||
#--logfile=/vagrant/nova.log
|
||||
#--verbose
|
||||
#--notification_driver=reddwarf.notifier.logfile_notifier
|
||||
#--notifier_logfile=/vagrant/notification.log
|
||||
#
|
||||
## Reaper config
|
||||
#--reaper_driver=reddwarf.reaper.driver.ReddwarfReaperDriver
|
||||
|
||||
#Extra ending line needed so that other conf files can be appended to this one.
|
133
etc/nova/reddwarf-api-paste.ini
Normal file
133
etc/nova/reddwarf-api-paste.ini
Normal file
@ -0,0 +1,133 @@
|
||||
############
|
||||
# Metadata #
|
||||
############
|
||||
[composite:metadata]
|
||||
use = egg:Paste#urlmap
|
||||
/: metaversions
|
||||
/latest: meta
|
||||
/2007-01-19: meta
|
||||
/2007-03-01: meta
|
||||
/2007-08-29: meta
|
||||
/2007-10-10: meta
|
||||
/2007-12-15: meta
|
||||
/2008-02-01: meta
|
||||
/2008-09-01: meta
|
||||
/2009-04-04: meta
|
||||
|
||||
[pipeline:metaversions]
|
||||
pipeline = ec2faultwrap logrequest metaverapp
|
||||
|
||||
[pipeline:meta]
|
||||
pipeline = ec2faultwrap logrequest metaapp
|
||||
|
||||
[app:metaverapp]
|
||||
paste.app_factory = nova.api.metadata.handler:Versions.factory
|
||||
|
||||
[app:metaapp]
|
||||
paste.app_factory = nova.api.metadata.handler:MetadataRequestHandler.factory
|
||||
|
||||
#######
|
||||
# EC2 #
|
||||
#######
|
||||
|
||||
[composite:ec2]
|
||||
use = egg:Paste#urlmap
|
||||
/services/Cloud: ec2cloud
|
||||
|
||||
[pipeline:ec2cloud]
|
||||
pipeline = ec2faultwrap logrequest ec2noauth cloudrequest authorizer validator ec2executor
|
||||
# NOTE(vish): use the following pipeline for deprecated auth
|
||||
# pipeline = ec2faultwrap logrequest authenticate cloudrequest authorizer validator ec2executor
|
||||
# NOTE(vish): use the following pipeline for keystone auth
|
||||
# pipeline = ec2faultwrap logrequest ec2keystoneauth cloudrequest authorizer validator ec2executor
|
||||
|
||||
[filter:ec2faultwrap]
|
||||
paste.filter_factory = nova.api.ec2:FaultWrapper.factory
|
||||
|
||||
[filter:logrequest]
|
||||
paste.filter_factory = nova.api.ec2:RequestLogging.factory
|
||||
|
||||
[filter:ec2lockout]
|
||||
paste.filter_factory = nova.api.ec2:Lockout.factory
|
||||
|
||||
[filter:totoken]
|
||||
paste.filter_factory = nova.api.ec2:EC2Token.factory
|
||||
|
||||
[filter:ec2keystoneauth]
|
||||
paste.filter_factory = nova.api.ec2:EC2KeystoneAuth.factory
|
||||
|
||||
[filter:ec2noauth]
|
||||
paste.filter_factory = nova.api.ec2:NoAuth.factory
|
||||
|
||||
[filter:authenticate]
|
||||
paste.filter_factory = nova.api.ec2:Authenticate.factory
|
||||
|
||||
[filter:cloudrequest]
|
||||
controller = nova.api.ec2.cloud.CloudController
|
||||
paste.filter_factory = nova.api.ec2:Requestify.factory
|
||||
|
||||
[filter:authorizer]
|
||||
paste.filter_factory = nova.api.ec2:Authorizer.factory
|
||||
|
||||
[filter:validator]
|
||||
paste.filter_factory = nova.api.ec2:Validator.factory
|
||||
|
||||
[app:ec2executor]
|
||||
paste.app_factory = nova.api.ec2:Executor.factory
|
||||
|
||||
#############
|
||||
# Openstack #
|
||||
#############
|
||||
|
||||
[composite:reddwarfapi_database]
|
||||
use = call:nova.api.openstack.urlmap:urlmap_factory
|
||||
/: reddwarfdatabaseversions
|
||||
#/v1.1: reddwarf_database_api_v2
|
||||
/v2: reddwarf_database_api_v2
|
||||
|
||||
[pipeline:reddwarf_database_api_v2]
|
||||
pipeline = faultwrap authtoken keystonecontext reddwarf_database_app_v2
|
||||
#pipeline = faultwrap noauth ratelimit reddwarf_database_app_v2
|
||||
# NOTE(vish): use the following pipeline for deprecated auth
|
||||
# pipeline = faultwrap auth ratelimit reddwarf_database_app_v2
|
||||
# NOTE(vish): use the following pipeline for keystone auth
|
||||
# pipeline = faultwrap authtoken keystonecontext ratelimit reddwarf_database_app_v2
|
||||
|
||||
[filter:faultwrap]
|
||||
paste.filter_factory = nova.api.openstack:FaultWrapper.factory
|
||||
|
||||
[filter:auth]
|
||||
paste.filter_factory = nova.api.openstack.auth:AuthMiddleware.factory
|
||||
|
||||
[filter:noauth]
|
||||
paste.filter_factory = nova.api.openstack.auth:NoAuthMiddleware.factory
|
||||
|
||||
[filter:ratelimit]
|
||||
paste.filter_factory = nova.api.openstack.compute.limits:RateLimitingMiddleware.factory
|
||||
|
||||
[app:reddwarf_database_app_v2]
|
||||
paste.app_factory = reddwarf.api.database:APIRouter.factory
|
||||
|
||||
[pipeline:reddwarfdatabaseversions]
|
||||
pipeline = faultwrap reddwarfdatabaseversionapp
|
||||
|
||||
[app:reddwarfdatabaseversionapp]
|
||||
paste.app_factory = reddwarf.api.database.versions:Versions.factory
|
||||
|
||||
##########
|
||||
# Shared #
|
||||
##########
|
||||
|
||||
[filter:keystonecontext]
|
||||
paste.filter_factory = nova.api.auth:NovaKeystoneContext.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
|
||||
auth_uri = http://127.0.0.1:5000/
|
||||
admin_token = be19c524ddc92109a224
|
32
reddwarf/__init__.py
Normal file
32
reddwarf/__init__.py
Normal file
@ -0,0 +1,32 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
:mod:`reddwarf` -- Cloud PaaS Database Platform
|
||||
===================================
|
||||
|
||||
.. automodule:: reddwarf
|
||||
:platform: Unix
|
||||
:synopsis: Platform-As-A-Service Database Cloud
|
||||
.. moduleauthor:: Michael Basnight <mbasnight@gmail.com>
|
||||
"""
|
||||
|
||||
import gettext
|
||||
|
||||
|
||||
gettext.install("reddwarf", unicode=1)
|
21
reddwarf/api/__init__.py
Normal file
21
reddwarf/api/__init__.py
Normal file
@ -0,0 +1,21 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
WSGI middleware for OpenStack API controllers.
|
||||
"""
|
55
reddwarf/api/database/__init__.py
Normal file
55
reddwarf/api/database/__init__.py
Normal file
@ -0,0 +1,55 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2010 United States Government as represented by the
|
||||
# Administrator of the National Aeronautics and Space Administration.
|
||||
# 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.
|
||||
|
||||
"""
|
||||
WSGI middleware for OpenStack Compute API.
|
||||
"""
|
||||
|
||||
import nova.api.openstack
|
||||
from reddwarf.api.database import extensions
|
||||
from reddwarf.api.database import instances
|
||||
from reddwarf.api.database import versions
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger('reddwarf.api.database')
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
class APIRouter(nova.api.openstack.APIRouter):
|
||||
"""
|
||||
Routes requests on the OpenStack API to the appropriate controller
|
||||
and method.
|
||||
"""
|
||||
ExtensionManager = extensions.ExtensionManager
|
||||
|
||||
def _setup_routes(self, mapper):
|
||||
self.resources['versions'] = versions.create_resource()
|
||||
mapper.connect("versions", "/",
|
||||
controller=self.resources['versions'],
|
||||
action='show')
|
||||
|
||||
mapper.redirect("", "/")
|
||||
|
||||
self.resources['instances'] = instances.create_resource()
|
||||
mapper.resource("instance", "instances",
|
||||
controller=self.resources['instances'],
|
||||
collection={'detail': 'GET'},
|
||||
member={'action': 'POST'})
|
||||
|
||||
|
39
reddwarf/api/database/contrib/__init__.py
Normal file
39
reddwarf/api/database/contrib/__init__.py
Normal file
@ -0,0 +1,39 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 Justin Santa Barbara
|
||||
# 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.
|
||||
|
||||
"""Contrib contains extensions that are shipped with nova.
|
||||
|
||||
It can't be called 'extensions' because that causes namespacing problems.
|
||||
|
||||
"""
|
||||
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
from nova.api.openstack import extensions
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
LOG = logging.getLogger('reddwarf.api.database.contrib')
|
||||
|
||||
|
||||
def standard_extensions(ext_mgr):
|
||||
extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__)
|
||||
|
||||
|
||||
def select_extensions(ext_mgr):
|
||||
extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__,
|
||||
FLAGS.osapi_compute_ext_list)
|
79
reddwarf/api/database/contrib/databases.py
Normal file
79
reddwarf/api/database/contrib/databases.py
Normal file
@ -0,0 +1,79 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# 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 webob.exc
|
||||
|
||||
from nova.api.openstack import extensions
|
||||
from nova import log as logging
|
||||
|
||||
LOG = logging.getLogger('reddwarf.api.database.contrib.databases')
|
||||
|
||||
class DatabasesController(object):
|
||||
|
||||
def index(self, req):
|
||||
LOG.info("index call databases")
|
||||
return "This is a index of databases"
|
||||
|
||||
class UsersController(object):
|
||||
|
||||
def index(self, req):
|
||||
LOG.info("index call users")
|
||||
return "This is a index of users"
|
||||
|
||||
|
||||
#class DatabasesControllerExtension(wsgi.Controller):
|
||||
# @wsgi.action('test_func')
|
||||
# def _test_func(self, req, id, body):
|
||||
#
|
||||
# return "Test Func called."
|
||||
|
||||
class Databases(extensions.ExtensionDescriptor):
|
||||
"""The Databases Extension"""
|
||||
|
||||
name = "Databases"
|
||||
alias = "DATABASES"
|
||||
namespace = "http://TBD"
|
||||
updated = "2011-01-22T13:25:27-06:00"
|
||||
|
||||
def __init__(self, ext_mgr):
|
||||
ext_mgr.register(self)
|
||||
|
||||
def get_resources(self):
|
||||
resources = []
|
||||
resource = extensions.ResourceExtension('databases',
|
||||
DatabasesController())
|
||||
resources.append(resource)
|
||||
resource = extensions.ResourceExtension('users',
|
||||
UsersController())
|
||||
resources.append(resource)
|
||||
|
||||
return resources
|
||||
|
||||
def get_controller_extensions(self):
|
||||
extension_list = []
|
||||
|
||||
extension_set = [
|
||||
# (DatabasesControllerExtension, 'instances'),
|
||||
# (FoxInSocksFlavorGooseControllerExtension, 'flavors'),
|
||||
# (FoxInSocksFlavorBandsControllerExtension, 'flavors'),
|
||||
]
|
||||
for klass, collection in extension_set:
|
||||
controller = klass()
|
||||
ext = extensions.ControllerExtension(self, collection, controller)
|
||||
extension_list.append(ext)
|
||||
|
||||
return extension_list
|
33
reddwarf/api/database/extensions.py
Normal file
33
reddwarf/api/database/extensions.py
Normal file
@ -0,0 +1,33 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# 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 nova.api.openstack import extensions as base_extensions
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
|
||||
LOG = logging.getLogger('reddwarf.api.database.extensions')
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
class ExtensionManager(base_extensions.ExtensionManager):
|
||||
def __init__(self):
|
||||
LOG.audit(_('Initializing extension manager.'))
|
||||
|
||||
self.cls_list = FLAGS.reddwarf_api_extension
|
||||
self.extensions = {}
|
||||
self._load_extensions()
|
59
reddwarf/api/database/instances.py
Normal file
59
reddwarf/api/database/instances.py
Normal file
@ -0,0 +1,59 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# 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 nova import flags
|
||||
from nova import log as logging
|
||||
from nova.api.openstack import wsgi
|
||||
from novaclient.v1_1.client import Client
|
||||
from nova.openstack.common import cfg
|
||||
|
||||
LOG = logging.getLogger('reddwarf.api.database.instances')
|
||||
|
||||
reddwarf_opts = [
|
||||
cfg.StrOpt('reddwarf_proxy_admin_user',
|
||||
default='admin',
|
||||
help='User by which you make proxy requests to the nova api with'),
|
||||
cfg.StrOpt('reddwarf_proxy_admin_pass',
|
||||
default='3de4922d8b6ac5a1aad9',
|
||||
help='Password for the admin user defined in reddwarf_proxy_admin_user'),
|
||||
cfg.StrOpt('reddwarf_proxy_admin_tenant_name',
|
||||
default='admin',
|
||||
help='Tenant name fro teh admin user defined in reddwarf_proxy_admin_user'),
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(reddwarf_opts)
|
||||
|
||||
class Controller(wsgi.Controller):
|
||||
|
||||
#_view_builder_class = views_servers.ViewBuilder
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super(Controller, self).__init__(**kwargs)
|
||||
|
||||
# @wsgi.response(202)
|
||||
def index(self, req):
|
||||
"""Return all servers."""
|
||||
client = Client(FLAGS.reddwarf_proxy_admin_user, FLAGS.reddwarf_proxy_admin_pass,
|
||||
FLAGS.reddwarf_proxy_admin_tenant_name, "http://0.0.0.0:5000/v2.0", token=req.headers["X-Auth-Token"])
|
||||
client.authenticate()
|
||||
servers = client.servers.list()
|
||||
LOG.info(servers)
|
||||
return "got the list of servers back! %s" % servers
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(Controller())
|
236
reddwarf/api/database/versions.py
Normal file
236
reddwarf/api/database/versions.py
Normal file
@ -0,0 +1,236 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2011 OpenStack LLC.
|
||||
# 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 datetime
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from nova.api.openstack.compute.views import versions as views_versions
|
||||
from nova.api.openstack import wsgi
|
||||
from nova.api.openstack import xmlutil
|
||||
|
||||
|
||||
VERSIONS = {
|
||||
"v2.0": {
|
||||
"id": "v2.0",
|
||||
"status": "CURRENT",
|
||||
"updated": "2012-01-01T11:33:21Z",
|
||||
"links": [
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/pdf",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"clouddatabase/api/v1.1/cs-devguide-20110125.pdf",
|
||||
},
|
||||
{
|
||||
"rel": "describedby",
|
||||
"type": "application/vnd.sun.wadl+xml",
|
||||
"href": "http://docs.rackspacecloud.com/"
|
||||
"clouddatabase/api/v1.1/application.wadl",
|
||||
},
|
||||
],
|
||||
"media-types": [
|
||||
{
|
||||
"base": "application/xml",
|
||||
"type": "application/vnd.openstack.reddwarf+xml;version=2",
|
||||
},
|
||||
{
|
||||
"base": "application/json",
|
||||
"type": "application/vnd.openstack.reddwarf+json;version=2",
|
||||
}
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MediaTypesTemplateElement(xmlutil.TemplateElement):
|
||||
def will_render(self, datum):
|
||||
return 'media-types' in datum
|
||||
|
||||
|
||||
def make_version(elem):
|
||||
elem.set('id')
|
||||
elem.set('status')
|
||||
elem.set('updated')
|
||||
|
||||
mts = MediaTypesTemplateElement('media-types')
|
||||
elem.append(mts)
|
||||
|
||||
mt = xmlutil.SubTemplateElement(mts, 'media-type', selector='media-types')
|
||||
mt.set('base')
|
||||
mt.set('type')
|
||||
|
||||
xmlutil.make_links(elem, 'links')
|
||||
|
||||
|
||||
version_nsmap = {None: xmlutil.XMLNS_V11, 'atom': xmlutil.XMLNS_ATOM}
|
||||
|
||||
|
||||
class VersionTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('version', selector='version')
|
||||
make_version(root)
|
||||
return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
|
||||
|
||||
|
||||
class VersionsTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('versions')
|
||||
elem = xmlutil.SubTemplateElement(root, 'version', selector='versions')
|
||||
make_version(elem)
|
||||
return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
|
||||
|
||||
|
||||
class ChoicesTemplate(xmlutil.TemplateBuilder):
|
||||
def construct(self):
|
||||
root = xmlutil.TemplateElement('choices')
|
||||
elem = xmlutil.SubTemplateElement(root, 'version', selector='choices')
|
||||
make_version(elem)
|
||||
return xmlutil.MasterTemplate(root, 1, nsmap=version_nsmap)
|
||||
|
||||
|
||||
class AtomSerializer(wsgi.XMLDictSerializer):
|
||||
|
||||
NSMAP = {None: xmlutil.XMLNS_ATOM}
|
||||
|
||||
def __init__(self, metadata=None, xmlns=None):
|
||||
self.metadata = metadata or {}
|
||||
if not xmlns:
|
||||
self.xmlns = wsgi.XMLNS_ATOM
|
||||
else:
|
||||
self.xmlns = xmlns
|
||||
|
||||
def _get_most_recent_update(self, versions):
|
||||
recent = None
|
||||
for version in versions:
|
||||
updated = datetime.datetime.strptime(version['updated'],
|
||||
'%Y-%m-%dT%H:%M:%SZ')
|
||||
if not recent:
|
||||
recent = updated
|
||||
elif updated > recent:
|
||||
recent = updated
|
||||
|
||||
return recent.strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||
|
||||
def _get_base_url(self, link_href):
|
||||
# Make sure no trailing /
|
||||
link_href = link_href.rstrip('/')
|
||||
return link_href.rsplit('/', 1)[0] + '/'
|
||||
|
||||
def _create_feed(self, versions, feed_title, feed_id):
|
||||
feed = etree.Element('feed', nsmap=self.NSMAP)
|
||||
title = etree.SubElement(feed, 'title')
|
||||
title.set('type', 'text')
|
||||
title.text = feed_title
|
||||
|
||||
# Set this updated to the most recently updated version
|
||||
recent = self._get_most_recent_update(versions)
|
||||
etree.SubElement(feed, 'updated').text = recent
|
||||
|
||||
etree.SubElement(feed, 'id').text = feed_id
|
||||
|
||||
link = etree.SubElement(feed, 'link')
|
||||
link.set('rel', 'self')
|
||||
link.set('href', feed_id)
|
||||
|
||||
author = etree.SubElement(feed, 'author')
|
||||
etree.SubElement(author, 'name').text = 'Rackspace'
|
||||
etree.SubElement(author, 'uri').text = 'http://www.rackspace.com/'
|
||||
|
||||
for version in versions:
|
||||
feed.append(self._create_version_entry(version))
|
||||
|
||||
return feed
|
||||
|
||||
def _create_version_entry(self, version):
|
||||
entry = etree.Element('entry')
|
||||
etree.SubElement(entry, 'id').text = version['links'][0]['href']
|
||||
title = etree.SubElement(entry, 'title')
|
||||
title.set('type', 'text')
|
||||
title.text = 'Version %s' % version['id']
|
||||
etree.SubElement(entry, 'updated').text = version['updated']
|
||||
|
||||
for link in version['links']:
|
||||
link_elem = etree.SubElement(entry, 'link')
|
||||
link_elem.set('rel', link['rel'])
|
||||
link_elem.set('href', link['href'])
|
||||
if 'type' in link:
|
||||
link_elem.set('type', link['type'])
|
||||
|
||||
content = etree.SubElement(entry, 'content')
|
||||
content.set('type', 'text')
|
||||
content.text = 'Version %s %s (%s)' % (version['id'],
|
||||
version['status'],
|
||||
version['updated'])
|
||||
return entry
|
||||
|
||||
|
||||
class VersionsAtomSerializer(AtomSerializer):
|
||||
def default(self, data):
|
||||
versions = data['versions']
|
||||
feed_id = self._get_base_url(versions[0]['links'][0]['href'])
|
||||
feed = self._create_feed(versions, 'Available API Versions', feed_id)
|
||||
return self._to_xml(feed)
|
||||
|
||||
|
||||
class VersionAtomSerializer(AtomSerializer):
|
||||
def default(self, data):
|
||||
version = data['version']
|
||||
feed_id = version['links'][0]['href']
|
||||
feed = self._create_feed([version], 'About This Version', feed_id)
|
||||
return self._to_xml(feed)
|
||||
|
||||
|
||||
class Versions(wsgi.Resource):
|
||||
def __init__(self):
|
||||
super(Versions, self).__init__(None)
|
||||
|
||||
@wsgi.serializers(xml=VersionsTemplate,
|
||||
atom=VersionsAtomSerializer)
|
||||
def index(self, req):
|
||||
"""Return all versions."""
|
||||
builder = views_versions.get_view_builder(req)
|
||||
return builder.build_versions(VERSIONS)
|
||||
|
||||
@wsgi.serializers(xml=ChoicesTemplate)
|
||||
@wsgi.response(300)
|
||||
def multi(self, req):
|
||||
"""Return multiple choices."""
|
||||
builder = views_versions.get_view_builder(req)
|
||||
return builder.build_choices(VERSIONS, req)
|
||||
|
||||
def get_action_args(self, request_environment):
|
||||
"""Parse dictionary created by routes library."""
|
||||
args = {}
|
||||
if request_environment['PATH_INFO'] == '/':
|
||||
args['action'] = 'index'
|
||||
else:
|
||||
args['action'] = 'multi'
|
||||
|
||||
return args
|
||||
|
||||
|
||||
class VersionV2(object):
|
||||
@wsgi.serializers(xml=VersionTemplate,
|
||||
atom=VersionAtomSerializer)
|
||||
def show(self, req):
|
||||
builder = views_versions.get_view_builder(req)
|
||||
return builder.build_version(VERSIONS['v2.0'])
|
||||
|
||||
|
||||
def create_resource():
|
||||
return wsgi.Resource(VersionV2())
|
Loading…
Reference in New Issue
Block a user