Add in multi-tenant support in openstack api.

This commit is contained in:
Monsyne Dragon
2011-03-03 15:41:45 +00:00
parent bb7c1b8c63
commit 6797c5acc4
24 changed files with 688 additions and 103 deletions

View File

@@ -433,6 +433,8 @@ class ProjectCommands(object):
"been created.\nPlease create a database by running a "
"nova-api server on this host.")
AccountCommands = ProjectCommands
class FixedIpCommands(object):
"""Class for managing fixed ip."""
@@ -663,6 +665,7 @@ class VolumeCommands(object):
CATEGORIES = [
('user', UserCommands),
('account', AccountCommands),
('project', ProjectCommands),
('role', RoleCommands),
('shell', ShellCommands),

View File

@@ -27,6 +27,7 @@ import webob.exc
from nova import flags
from nova import log as logging
from nova import wsgi
from nova.api.openstack import accounts
from nova.api.openstack import faults
from nova.api.openstack import backup_schedules
from nova.api.openstack import consoles
@@ -34,6 +35,7 @@ from nova.api.openstack import flavors
from nova.api.openstack import images
from nova.api.openstack import servers
from nova.api.openstack import shared_ip_groups
from nova.api.openstack import users
from nova.api.openstack import zones
@@ -71,6 +73,18 @@ class APIRouter(wsgi.Router):
def __init__(self):
mapper = routes.Mapper()
accounts_controller = accounts.Controller()
mapper.connect("account", "/{id}",
controller=accounts_controller, action="show",
conditions=dict(method=["GET"]))
if FLAGS.allow_admin_api:
mapper.connect("/{id}",
controller=accounts_controller, action="update",
conditions=dict(method=["PUT"]))
mapper.connect("/{id}",
controller=accounts_controller, action="delete",
conditions=dict(method=["DELETE"]))
server_members = {'action': 'POST'}
if FLAGS.allow_admin_api:
LOG.debug(_("Including admin operations in API."))
@@ -84,27 +98,38 @@ class APIRouter(wsgi.Router):
server_members['inject_network_info'] = 'POST'
mapper.resource("zone", "zones", controller=zones.Controller(),
path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("user", "users", controller=users.Controller(),
path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("server", "servers", controller=servers.Controller(),
collection={'detail': 'GET'},
path_prefix="{account_id}/",
member=server_members)
mapper.resource("backup_schedule", "backup_schedule",
controller=backup_schedules.Controller(),
path_prefix="{account_id}/servers/{server_id}/",
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("console", "consoles",
controller=consoles.Controller(),
path_prefix="{account_id}/servers/{server_id}/",
parent_resource=dict(member_name='server',
collection_name='servers'))
mapper.resource("image", "images", controller=images.Controller(),
path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("flavor", "flavors", controller=flavors.Controller(),
path_prefix="{account_id}/",
collection={'detail': 'GET'})
mapper.resource("shared_ip_group", "shared_ip_groups",
path_prefix="{account_id}/",
collection={'detail': 'GET'},
controller=shared_ip_groups.Controller())

View File

@@ -0,0 +1,73 @@
# 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 common
from nova import exception
from nova import flags
from nova import log as logging
from nova import wsgi
from nova.auth import manager
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack')
def _translate_keys(account):
return dict(id=account.id,
name=account.name,
description=account.description,
manager=account.project_manager_id)
class Controller(wsgi.Controller):
_serialization_metadata = {
'application/xml': {
"attributes": {
"account": ["id", "name", "description", "manager"]}}}
def __init__(self):
self.manager = manager.AuthManager()
def _check_admin(self, context):
""" We cannot depend on the db layer to check for admin access
for the auth manager, so we do it here """
if not context.is_admin:
raise exception.NotAuthorized("Not admin user.")
def show(self, req, id):
"""Return data about the given account id"""
account = self.manager.get_project(id)
return dict(account=_translate_keys(account))
def delete(self, req, id):
self._check_admin(req.environ['nova.context'])
self.manager.delete_project(id)
return {}
def update(self, req, id):
""" This is really create or update. """
self._check_admin(req.environ['nova.context'])
env = self._deserialize(req.body, req)
description = env['account'].get('description')
manager = env['account'].get('manager')
try:
account = self.manager.get_project(id)
self.manager.modify_project(id, manager, description)
except exception.NotFound:
account = self.manager.create_project(id, manager, description)
return dict(account=_translate_keys(account))

View File

@@ -28,11 +28,13 @@ from nova import context
from nova import db
from nova import exception
from nova import flags
from nova import log as logging
from nova import manager
from nova import utils
from nova import wsgi
from nova.api.openstack import faults
LOG = logging.getLogger('nova.api.openstack')
FLAGS = flags.FLAGS
@@ -50,14 +52,27 @@ class AuthMiddleware(wsgi.Middleware):
def __call__(self, req):
if not self.has_authentication(req):
return self.authenticate(req)
user = self.get_user_by_authentication(req)
account_name = req.path_info_peek()
if not user:
return faults.Fault(webob.exc.HTTPUnauthorized())
project = self.auth.get_project(FLAGS.default_project)
req.environ['nova.context'] = context.RequestContext(user, project)
if not account_name:
if self.auth.is_admin(user):
account_name = FLAGS.default_project
else:
return faults.Fault(webob.exc.HTTPUnauthorized())
try:
account = self.auth.get_project(account_name)
except exception.NotFound:
return faults.Fault(webob.exc.HTTPUnauthorized())
if not self.auth.is_admin(user) and \
not self.auth.is_project_member(user, account):
return faults.Fault(webob.exc.HTTPUnauthorized())
req.environ['nova.context'] = context.RequestContext(user, account)
return self.application
def has_authentication(self, req):
@@ -70,6 +85,7 @@ class AuthMiddleware(wsgi.Middleware):
# Unless the request is explicitly made against /<version>/ don't
# honor it
path_info = req.path_info
account_name = None
if len(path_info) > 1:
return faults.Fault(webob.exc.HTTPUnauthorized())
@@ -79,7 +95,10 @@ class AuthMiddleware(wsgi.Middleware):
except KeyError:
return faults.Fault(webob.exc.HTTPUnauthorized())
token, user = self._authorize_user(username, key, req)
if ':' in username:
account_name, username = username.rsplit(':', 1)
token, user = self._authorize_user(username, account_name, key, req)
if user and token:
res = webob.Response()
res.headers['X-Auth-Token'] = token.token_hash
@@ -116,23 +135,44 @@ class AuthMiddleware(wsgi.Middleware):
return self.auth.get_user(token.user_id)
return None
def _authorize_user(self, username, key, req):
def _authorize_user(self, username, account_name, key, req):
"""Generates a new token and assigns it to a user.
username - string
account_name - string
key - string API key
req - webob.Request object
"""
ctxt = context.get_admin_context()
user = self.auth.get_user_from_access_key(key)
if account_name:
try:
account = self.auth.get_project(account_name)
except exception.NotFound:
return None, None
else:
# (dragondm) punt and try to determine account.
# this is something of a hack, but a user on 1 account is a
# common case, and is the way the current RS code works.
accounts = self.auth.get_projects(user=user)
if len(accounts) == 1:
account = accounts[0]
else:
#we can't tell what account they are logging in for.
return None, None
if user and user.name == username:
token_hash = hashlib.sha1('%s%s%f' % (username, key,
time.time())).hexdigest()
token_dict = {}
token_dict['token_hash'] = token_hash
token_dict['cdn_management_url'] = ''
# Same as auth url, e.g. http://foo.org:8774/baz/v1.0
token_dict['server_management_url'] = req.url
# auth url + project (account) id, e.g.
# http://foo.org:8774/baz/v1.0/myacct/
os_url = '%s%s%s/' % (req.url,
'' if req.url.endswith('/') else '/',
account.id)
token_dict['server_management_url'] = os_url
token_dict['storage_url'] = ''
token_dict['user_id'] = user.id
token = self.db.auth_token_create(ctxt, token_dict)

View File

@@ -40,15 +40,15 @@ class Controller(wsgi.Controller):
def __init__(self):
pass
def index(self, req, server_id):
def index(self, req, server_id, **kw):
""" Returns the list of backup schedules for a given instance """
return _translate_keys({})
def create(self, req, server_id):
def create(self, req, server_id, **kw):
""" No actual update method required, since the existing API allows
both create and update through a POST """
return faults.Fault(exc.HTTPNotImplemented())
def delete(self, req, server_id, id):
def delete(self, req, server_id, id, **kw):
""" Deletes an existing backup schedule """
return faults.Fault(exc.HTTPNotImplemented())

View File

@@ -55,7 +55,7 @@ class Controller(wsgi.Controller):
self.console_api = console.API()
super(Controller, self).__init__()
def index(self, req, server_id):
def index(self, req, server_id, **kw):
"""Returns a list of consoles for this instance"""
consoles = self.console_api.get_consoles(
req.environ['nova.context'],
@@ -63,14 +63,14 @@ class Controller(wsgi.Controller):
return dict(consoles=[_translate_keys(console)
for console in consoles])
def create(self, req, server_id):
def create(self, req, server_id, **kw):
"""Creates a new console"""
#info = self._deserialize(req.body, req)
self.console_api.create_console(
req.environ['nova.context'],
int(server_id))
def show(self, req, server_id, id):
def show(self, req, server_id, id, **kw):
"""Shows in-depth information on a specific console"""
try:
console = self.console_api.get_console(
@@ -81,11 +81,11 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return _translate_detail_keys(console)
def update(self, req, server_id, id):
def update(self, req, server_id, id, **kw):
"""You can't update a console"""
raise faults.Fault(exc.HTTPNotImplemented())
def delete(self, req, server_id, id):
def delete(self, req, server_id, id, **kw):
"""Deletes a console"""
try:
self.console_api.delete_console(req.environ['nova.context'],

View File

@@ -32,18 +32,18 @@ class Controller(wsgi.Controller):
"attributes": {
"flavor": ["id", "name", "ram", "disk"]}}}
def index(self, req):
def index(self, req, **kw):
"""Return all flavors in brief."""
return dict(flavors=[dict(id=flavor['id'], name=flavor['name'])
for flavor in self.detail(req)['flavors']])
def detail(self, req):
def detail(self, req, **kw):
"""Return all flavors in detail."""
items = [self.show(req, id)['flavor'] for id in self._all_ids()]
items = common.limited(items, req)
return dict(flavors=items)
def show(self, req, id):
def show(self, req, id, **kw):
"""Return data about the given flavor id."""
for name, val in instance_types.INSTANCE_TYPES.iteritems():
if val['flavorid'] == int(id):

View File

@@ -115,14 +115,14 @@ class Controller(wsgi.Controller):
def __init__(self):
self._service = utils.import_object(FLAGS.image_service)
def index(self, req):
def index(self, req, **kw):
"""Return all public images in brief"""
items = self._service.index(req.environ['nova.context'])
items = common.limited(items, req)
items = [_filter_keys(item, ('id', 'name')) for item in items]
return dict(images=items)
def detail(self, req):
def detail(self, req, **kw):
"""Return all public images in detail"""
try:
items = self._service.detail(req.environ['nova.context'])
@@ -136,7 +136,7 @@ class Controller(wsgi.Controller):
items = [_translate_status(item) for item in items]
return dict(images=items)
def show(self, req, id):
def show(self, req, id, **kw):
"""Return data about the given image id"""
image_id = common.get_image_id_from_image_hash(self._service,
req.environ['nova.context'], id)
@@ -145,11 +145,11 @@ class Controller(wsgi.Controller):
_convert_image_id_to_hash(image)
return dict(image=image)
def delete(self, req, id):
def delete(self, req, id, **kw):
# Only public images are supported for now.
raise faults.Fault(exc.HTTPNotFound())
def create(self, req):
def create(self, req, **kw):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
instance_id = env["image"]["serverId"]
@@ -160,7 +160,7 @@ class Controller(wsgi.Controller):
return dict(image=image_meta)
def update(self, req, id):
def update(self, req, id, **kw):
# Users may not modify public images, and that's all that
# we support for now.
raise faults.Fault(exc.HTTPNotFound())

View File

@@ -105,11 +105,11 @@ class Controller(wsgi.Controller):
self._image_service = utils.import_object(FLAGS.image_service)
super(Controller, self).__init__()
def index(self, req):
def index(self, req, **kw):
""" Returns a list of server names and ids for a given user """
return self._items(req, entity_maker=_translate_keys)
def detail(self, req):
def detail(self, req, **kw):
""" Returns a list of server details for a given user """
return self._items(req, entity_maker=_translate_detail_keys)
@@ -123,7 +123,7 @@ class Controller(wsgi.Controller):
res = [entity_maker(inst)['server'] for inst in limited_list]
return dict(servers=res)
def show(self, req, id):
def show(self, req, id, **kw):
""" Returns server details by server id """
try:
instance = self.compute_api.get(req.environ['nova.context'], id)
@@ -131,7 +131,7 @@ class Controller(wsgi.Controller):
except exception.NotFound:
return faults.Fault(exc.HTTPNotFound())
def delete(self, req, id):
def delete(self, req, id, **kw):
""" Destroys a server """
try:
self.compute_api.delete(req.environ['nova.context'], id)
@@ -139,7 +139,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
def create(self, req):
def create(self, req, **kw):
""" Creates a new server for a given user """
env = self._deserialize(req.body, req)
if not env:
@@ -180,7 +180,7 @@ class Controller(wsgi.Controller):
onset_files=env.get('onset_files', []))
return _translate_keys(instances[0])
def update(self, req, id):
def update(self, req, id, **kw):
""" Updates the server name or password """
inst_dict = self._deserialize(req.body, req)
if not inst_dict:
@@ -202,7 +202,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPNoContent()
def action(self, req, id):
def action(self, req, id, **kw):
""" Multi-purpose method used to reboot, rebuild, and
resize a server """
input_dict = self._deserialize(req.body, req)
@@ -219,7 +219,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def lock(self, req, id):
def lock(self, req, id, **kw):
"""
lock the instance with id
admin only operation
@@ -234,7 +234,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def unlock(self, req, id):
def unlock(self, req, id, **kw):
"""
unlock the instance with id
admin only operation
@@ -249,7 +249,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def get_lock(self, req, id):
def get_lock(self, req, id, **kw):
"""
return the boolean state of (instance with id)'s lock
@@ -263,7 +263,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def reset_network(self, req, id):
def reset_network(self, req, id, **kw):
"""
Reset networking on an instance (admin only).
@@ -277,7 +277,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def inject_network_info(self, req, id):
def inject_network_info(self, req, id, **kw):
"""
Inject network info for an instance (admin only).
@@ -291,7 +291,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def pause(self, req, id):
def pause(self, req, id, **kw):
""" Permit Admins to Pause the server. """
ctxt = req.environ['nova.context']
try:
@@ -302,7 +302,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def unpause(self, req, id):
def unpause(self, req, id, **kw):
""" Permit Admins to Unpause the server. """
ctxt = req.environ['nova.context']
try:
@@ -313,7 +313,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def suspend(self, req, id):
def suspend(self, req, id, **kw):
"""permit admins to suspend the server"""
context = req.environ['nova.context']
try:
@@ -324,7 +324,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def resume(self, req, id):
def resume(self, req, id, **kw):
"""permit admins to resume the server from suspend"""
context = req.environ['nova.context']
try:
@@ -335,7 +335,7 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPUnprocessableEntity())
return exc.HTTPAccepted()
def get_ajax_console(self, req, id):
def get_ajax_console(self, req, id, **kw):
""" Returns a url to an instance's ajaxterm console. """
try:
self.compute_api.get_ajax_console(req.environ['nova.context'],
@@ -344,12 +344,12 @@ class Controller(wsgi.Controller):
return faults.Fault(exc.HTTPNotFound())
return exc.HTTPAccepted()
def diagnostics(self, req, id):
def diagnostics(self, req, id, **kw):
"""Permit Admins to retrieve server diagnostics."""
ctxt = req.environ["nova.context"]
return self.compute_api.get_diagnostics(ctxt, id)
def actions(self, req, id):
def actions(self, req, id, **kw):
"""Permit Admins to retrieve server actions."""
ctxt = req.environ["nova.context"]
items = self.compute_api.get_actions(ctxt, id)

View File

@@ -40,26 +40,26 @@ class Controller(wsgi.Controller):
'attributes': {
'sharedIpGroup': []}}}
def index(self, req):
def index(self, req, **kw):
""" Returns a list of Shared IP Groups for the user """
return dict(sharedIpGroups=[])
def show(self, req, id):
def show(self, req, id, **kw):
""" Shows in-depth information on a specific Shared IP Group """
return _translate_keys({})
def update(self, req, id):
def update(self, req, id, **kw):
""" You can't update a Shared IP Group """
raise faults.Fault(exc.HTTPNotImplemented())
def delete(self, req, id):
def delete(self, req, id, **kw):
""" Deletes a Shared IP Group """
raise faults.Fault(exc.HTTPNotImplemented())
def detail(self, req):
def detail(self, req, **kw):
""" Returns a complete list of Shared IP Groups """
return _translate_detail_keys({})
def create(self, req):
def create(self, req, **kw):
""" Creates a new Shared IP group """
raise faults.Fault(exc.HTTPNotImplemented())

View File

@@ -0,0 +1,93 @@
# 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 common
from nova import exception
from nova import flags
from nova import log as logging
from nova import wsgi
from nova.auth import manager
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack')
def _translate_keys(user):
return dict(id=user.id,
name=user.name,
access=user.access,
secret=user.secret,
admin=user.admin)
class Controller(wsgi.Controller):
_serialization_metadata = {
'application/xml': {
"attributes": {
"user": ["id", "name", "access", "secret", "admin"]}}}
def __init__(self):
self.manager = manager.AuthManager()
def _check_admin(self, context):
""" We cannot depend on the db layer to check for admin access
for the auth manager, so we do it here """
if not context.is_admin:
raise exception.NotAuthorized("Not admin user")
def index(self, req, **kw):
"""Return all users in brief"""
users = self.manager.get_users()
users = common.limited(users, req)
users = [_translate_keys(user) for user in users]
return dict(users=users)
def detail(self, req, **kw):
"""Return all users in detail"""
return self.index(req)
def show(self, req, id, **kw):
"""Return data about the given user id"""
user = self.manager.get_user(id)
return dict(user=_translate_keys(user))
def delete(self, req, id, **kw):
self._check_admin(req.environ['nova.context'])
self.manager.delete_user(id)
return {}
def create(self, req, **kw):
self._check_admin(req.environ['nova.context'])
env = self._deserialize(req.body, req)
is_admin = env['user'].get('admin') in ('T', 'True', True)
name = env['user'].get('name')
access = env['user'].get('access')
secret = env['user'].get('secret')
user = self.manager.create_user(name, access, secret, is_admin)
return dict(user=_translate_keys(user))
def update(self, req, id, **kw):
self._check_admin(req.environ['nova.context'])
env = self._deserialize(req.body, req)
is_admin = env['user'].get('admin')
if is_admin is not None:
is_admin = is_admin in ('T', 'True', True)
access = env['user'].get('access')
secret = env['user'].get('secret')
self.manager.modify_user(id, access, secret, is_admin)
return dict(user=_translate_keys(self.manager.get_user(id)))

View File

@@ -43,35 +43,35 @@ class Controller(wsgi.Controller):
"attributes": {
"zone": ["id", "api_url"]}}}
def index(self, req):
def index(self, req, **kw):
"""Return all zones in brief"""
items = db.zone_get_all(req.environ['nova.context'])
items = common.limited(items, req)
items = [_scrub_zone(item) for item in items]
return dict(zones=items)
def detail(self, req):
def detail(self, req, **kw):
"""Return all zones in detail"""
return self.index(req)
def show(self, req, id):
def show(self, req, id, **kw):
"""Return data about the given zone id"""
zone_id = int(id)
zone = db.zone_get(req.environ['nova.context'], zone_id)
return dict(zone=_scrub_zone(zone))
def delete(self, req, id):
def delete(self, req, id, **kw):
zone_id = int(id)
db.zone_delete(req.environ['nova.context'], zone_id)
return {}
def create(self, req):
def create(self, req, **kw):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone = db.zone_create(context, env["zone"])
return dict(zone=_scrub_zone(zone))
def update(self, req, id):
def update(self, req, id, **kw):
context = req.environ['nova.context']
env = self._deserialize(req.body, req)
zone_id = int(id)

View File

@@ -11,5 +11,5 @@ export EUCALYPTUS_CERT=${NOVA_CERT} # euca-bundle-image seems to require this se
alias ec2-bundle-image="ec2-bundle-image --cert ${EC2_CERT} --privatekey ${EC2_PRIVATE_KEY} --user 42 --ec2cert ${NOVA_CERT}"
alias ec2-upload-bundle="ec2-upload-bundle -a ${EC2_ACCESS_KEY} -s ${EC2_SECRET_KEY} --url ${S3_URL} --ec2cert ${NOVA_CERT}"
export NOVA_API_KEY="%(access)s"
export NOVA_USERNAME="%(user)s"
export NOVA_USERNAME="%(project)s:%(user)s"
export NOVA_URL="%(os)s"

View File

@@ -1861,8 +1861,11 @@ def project_get_by_user(context, user_id):
session = get_session()
user = session.query(models.User).\
filter_by(deleted=can_read_deleted(context)).\
filter_by(id=user_id).\
options(joinedload_all('projects')).\
first()
if not user:
raise exception.NotFound(_('Invalid user_id %s') % user_id)
return user.projects

View File

@@ -26,7 +26,6 @@ from paste import urlmap
from glance import client as glance_client
from nova import auth
from nova import context
from nova import exception as exc
from nova import flags
@@ -35,6 +34,7 @@ import nova.api.openstack.auth
from nova.api import openstack
from nova.api.openstack import auth
from nova.api.openstack import ratelimiting
from nova.auth.manager import User, Project
from nova.image import glance
from nova.image import local
from nova.image import service
@@ -227,19 +227,97 @@ class FakeAuthDatabase(object):
class FakeAuthManager(object):
auth_data = {}
projects = {}
@classmethod
def clear_fakes(cls):
cls.auth_data = {}
cls.projects = {}
@classmethod
def reset_fake_data(cls):
cls.auth_data = dict(acc1=User('guy1', 'guy1', 'acc1',
'fortytwo!', False))
cls.projects = dict(testacct=Project('testacct',
'testacct',
'guy1',
'test',
[]))
def add_user(self, key, user):
FakeAuthManager.auth_data[key] = user
def get_users(self):
return FakeAuthManager.auth_data.values()
def get_user(self, uid):
for k, v in FakeAuthManager.auth_data.iteritems():
if v.id == uid:
return v
return None
def get_project(self, pid):
def delete_user(self, uid):
for k, v in FakeAuthManager.auth_data.items():
if v.id == uid:
del FakeAuthManager.auth_data[k]
return None
def create_user(self, name, access=None, secret=None, admin=False):
u = User(name, name, access, secret, admin)
FakeAuthManager.auth_data[access] = u
return u
def modify_user(self, user_id, access=None, secret=None, admin=None):
user = None
for k, v in FakeAuthManager.auth_data.iteritems():
if v.id == user_id:
user = v
if user:
user.access = access
user.secret = secret
if admin is not None:
user.admin = admin
def is_admin(self, user):
return user.admin
def is_project_member(self, user, project):
return ((user.id in project.member_ids) or
(user.id == project.project_manager_id))
def create_project(self, name, manager_user, description=None,
member_users=None):
member_ids = [User.safe_id(m) for m in member_users] \
if member_users else []
p = Project(name, name, User.safe_id(manager_user),
description, member_ids)
FakeAuthManager.projects[name] = p
return p
def delete_project(self, pid):
if pid in FakeAuthManager.projects:
del FakeAuthManager.projects[pid]
def modify_project(self, project, manager_user=None, description=None):
p = FakeAuthManager.projects.get(project)
p.project_manager_id = User.safe_id(manager_user)
p.description = description
def get_project(self, pid):
p = FakeAuthManager.projects.get(pid)
if p:
return p
else:
raise exc.NotFound
def get_projects(self, user=None):
if not user:
return FakeAuthManager.projects.values()
else:
return [p for p in FakeAuthManager.projects.values()
if (user.id in p.member_ids) or
(user.id == p.project_manager_id)]
def get_user_from_access_key(self, key):
return FakeAuthManager.auth_data.get(key, None)

View File

@@ -0,0 +1,123 @@
# Copyright 2010 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 stubout
import webob
import json
import nova.api
import nova.api.openstack.auth
from nova import context
from nova import flags
from nova import test
from nova.auth.manager import User
from nova.tests.api.openstack import fakes
FLAGS = flags.FLAGS
FLAGS.verbose = True
def fake_init(self):
self.manager = fakes.FakeAuthManager()
def fake_admin_check(self, req):
return True
class AccountsTest(test.TestCase):
def setUp(self):
super(AccountsTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
self.stubs.Set(nova.api.openstack.accounts.Controller, '__init__',
fake_init)
self.stubs.Set(nova.api.openstack.accounts.Controller, '_check_admin',
fake_admin_check)
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthManager.projects = {}
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
self.allow_admin = FLAGS.allow_admin_api
FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
joeuser = User('guy1', 'guy1', 'acc1', 'fortytwo!', False)
superuser = User('guy2', 'guy2', 'acc2', 'swordfish', True)
fakemgr.add_user(joeuser.access, joeuser)
fakemgr.add_user(superuser.access, superuser)
fakemgr.create_project('test1', joeuser)
fakemgr.create_project('test2', superuser)
def tearDown(self):
self.stubs.UnsetAll()
FLAGS.allow_admin_api = self.allow_admin
super(AccountsTest, self).tearDown()
def test_get_account(self):
req = webob.Request.blank('/v1.0/test1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['account']['id'], 'test1')
self.assertEqual(res_dict['account']['name'], 'test1')
self.assertEqual(res_dict['account']['manager'], 'guy1')
self.assertEqual(res.status_int, 200)
def test_account_delete(self):
req = webob.Request.blank('/v1.0/test1')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertTrue('test1' not in fakes.FakeAuthManager.projects)
self.assertEqual(res.status_int, 200)
def test_account_create(self):
body = dict(account=dict(description='test account',
manager='guy1'))
req = webob.Request.blank('/v1.0/newacct')
req.method = 'PUT'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
self.assertEqual(res_dict['account']['id'], 'newacct')
self.assertEqual(res_dict['account']['name'], 'newacct')
self.assertEqual(res_dict['account']['description'], 'test account')
self.assertEqual(res_dict['account']['manager'], 'guy1')
self.assertTrue('newacct' in
fakes.FakeAuthManager.projects)
self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 3)
def test_account_update(self):
body = dict(account=dict(description='test account',
manager='guy2'))
req = webob.Request.blank('/v1.0/test1')
req.method = 'PUT'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
self.assertEqual(res_dict['account']['id'], 'test1')
self.assertEqual(res_dict['account']['name'], 'test1')
self.assertEqual(res_dict['account']['description'], 'test account')
self.assertEqual(res_dict['account']['manager'], 'guy2')
self.assertEqual(len(fakes.FakeAuthManager.projects.values()), 2)

View File

@@ -35,7 +35,7 @@ class AdminAPITest(test.TestCase):
def setUp(self):
super(AdminAPITest, self).setUp()
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -50,7 +50,7 @@ class AdminAPITest(test.TestCase):
def test_admin_enabled(self):
FLAGS.allow_admin_api = True
# We should still be able to access public operations.
req = webob.Request.blank('/v1.0/flavors')
req = webob.Request.blank('/v1.0/testacct/flavors')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
# TODO: Confirm admin operations are available.
@@ -58,7 +58,7 @@ class AdminAPITest(test.TestCase):
def test_admin_disabled(self):
FLAGS.allow_admin_api = False
# We should still be able to access public operations.
req = webob.Request.blank('/v1.0/flavors')
req = webob.Request.blank('/v1.0/testacct/flavors')
res = req.get_response(fakes.wsgi_app())
# TODO: Confirm admin operations are unavailable.
self.assertEqual(res.status_int, 200)

View File

@@ -51,7 +51,9 @@ class Test(test.TestCase):
def test_authorize_user(self):
f = fakes.FakeAuthManager()
f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
u = nova.auth.manager.User(1, 'herp', None, None, None)
f.add_user('derp', u)
f.create_project('test', u)
req = webob.Request.blank('/v1.0/')
req.headers['X-Auth-User'] = 'herp'
@@ -65,7 +67,9 @@ class Test(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
u = nova.auth.manager.User(1, 'herp', None, None, None)
f.add_user('derp', u)
f.create_project('test', u)
req = webob.Request.blank('/v1.0/', {'HTTP_HOST': 'foo'})
req.headers['X-Auth-User'] = 'herp'
@@ -74,7 +78,7 @@ class Test(test.TestCase):
self.assertEqual(result.status, '204 No Content')
self.assertEqual(len(result.headers['X-Auth-Token']), 40)
self.assertEqual(result.headers['X-Server-Management-Url'],
"http://foo/v1.0/")
"http://foo/v1.0/test/")
self.assertEqual(result.headers['X-CDN-Management-Url'],
"")
self.assertEqual(result.headers['X-Storage-Url'], "")
@@ -82,7 +86,7 @@ class Test(test.TestCase):
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouter',
fakes.FakeRouter)
req = webob.Request.blank('/v1.0/fake')
req = webob.Request.blank('/v1.0/test/fake')
req.headers['X-Auth-Token'] = token
result = req.get_response(fakes.wsgi_app())
self.assertEqual(result.status, '200 OK')
@@ -176,6 +180,9 @@ class TestLimiter(test.TestCase):
def test_authorize_token(self):
f = fakes.FakeAuthManager()
u = nova.auth.manager.User(1, 'herp', None, None, None)
f.add_user('derp', u)
f.create_project('test', u)
f.add_user('derp', nova.auth.manager.User(1, 'herp', None, None, None))
req = webob.Request.blank('/v1.0/')
@@ -187,7 +194,7 @@ class TestLimiter(test.TestCase):
token = result.headers['X-Auth-Token']
self.stubs.Set(nova.api.openstack, 'APIRouter',
fakes.FakeRouter)
req = webob.Request.blank('/v1.0/fake')
req = webob.Request.blank('/v1.0/test/fake')
req.method = 'POST'
req.headers['X-Auth-Token'] = token
result = req.get_response(fakes.wsgi_app())

View File

@@ -28,7 +28,7 @@ class FlavorsTest(test.TestCase):
def setUp(self):
super(FlavorsTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -39,7 +39,7 @@ class FlavorsTest(test.TestCase):
super(FlavorsTest, self).tearDown()
def test_get_flavor_list(self):
req = webob.Request.blank('/v1.0/flavors')
req = webob.Request.blank('/v1.0/testacct/flavors')
res = req.get_response(fakes.wsgi_app())
def test_get_flavor_by_id(self):

View File

@@ -202,7 +202,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
self.orig_image_service = FLAGS.image_service
FLAGS.image_service = 'nova.image.glance.GlanceImageService'
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -216,7 +216,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
super(ImageControllerWithGlanceServiceTest, self).tearDown()
def test_get_image_index(self):
req = webob.Request.blank('/v1.0/images')
req = webob.Request.blank('/v1.0/testacct/images')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -228,7 +228,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase):
"image %s not in fixture index!" % str(image))
def test_get_image_details(self):
req = webob.Request.blank('/v1.0/images/detail')
req = webob.Request.blank('/v1.0/testacct/images/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)

View File

@@ -118,7 +118,7 @@ class ServersTest(test.TestCase):
def setUp(self):
super(ServersTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -150,7 +150,7 @@ class ServersTest(test.TestCase):
super(ServersTest, self).tearDown()
def test_get_server_by_id(self):
req = webob.Request.blank('/v1.0/servers/1')
req = webob.Request.blank('/v1.0/testacct/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -161,7 +161,7 @@ class ServersTest(test.TestCase):
public = ["1.2.3.4"]
new_return_server = return_server_with_addresses(private, public)
self.stubs.Set(nova.db.api, 'instance_get', new_return_server)
req = webob.Request.blank('/v1.0/servers/1')
req = webob.Request.blank('/v1.0/testacct/servers/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['server']['id'], '1')
@@ -173,7 +173,7 @@ class ServersTest(test.TestCase):
self.assertEqual(addresses["private"][0], private)
def test_get_server_list(self):
req = webob.Request.blank('/v1.0/servers')
req = webob.Request.blank('/v1.0/testacct/servers')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -224,7 +224,7 @@ class ServersTest(test.TestCase):
name='server_test', imageId=2, flavorId=2,
metadata={'hello': 'world', 'open': 'stack'},
personality={}))
req = webob.Request.blank('/v1.0/servers')
req = webob.Request.blank('/v1.0/testacct/servers')
req.method = 'POST'
req.body = json.dumps(body)
@@ -233,7 +233,7 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_update_no_body(self):
req = webob.Request.blank('/v1.0/servers/1')
req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'PUT'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 422)
@@ -251,7 +251,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
req = webob.Request.blank('/v1.0/servers/1')
req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(fakes.wsgi_app())
@@ -267,30 +267,30 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_update',
server_update)
req = webob.Request.blank('/v1.0/servers/1')
req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'PUT'
req.body = self.body
req.get_response(fakes.wsgi_app())
def test_create_backup_schedules(self):
req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
req.method = 'POST'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_delete_backup_schedules(self):
req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_get_server_backup_schedules(self):
req = webob.Request.blank('/v1.0/servers/1/backup_schedules')
req = webob.Request.blank('/v1.0/testacct/servers/1/backup_schedules')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status, '404 Not Found')
def test_get_all_server_details(self):
req = webob.Request.blank('/v1.0/servers/detail')
req = webob.Request.blank('/v1.0/testacct/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -321,7 +321,7 @@ class ServersTest(test.TestCase):
self.stubs.Set(nova.db.api, 'instance_get_all_by_user',
return_servers_with_host)
req = webob.Request.blank('/v1.0/servers/detail')
req = webob.Request.blank('/v1.0/testacct/servers/detail')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -341,7 +341,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/pause')
req = webob.Request.blank('/v1.0/testacct/servers/1/pause')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -353,7 +353,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/unpause')
req = webob.Request.blank('/v1.0/testacct/servers/1/unpause')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -365,7 +365,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/suspend')
req = webob.Request.blank('/v1.0/testacct/servers/1/suspend')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -377,7 +377,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/resume')
req = webob.Request.blank('/v1.0/testacct/servers/1/resume')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -389,7 +389,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/reset_network')
req = webob.Request.blank('/v1.0/testacct/servers/1/reset_network')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -401,7 +401,8 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/inject_network_info')
req = webob.Request.blank(
'/v1.0/testacct/servers/1/inject_network_info')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -409,13 +410,13 @@ class ServersTest(test.TestCase):
self.assertEqual(res.status_int, 202)
def test_server_diagnostics(self):
req = webob.Request.blank("/v1.0/servers/1/diagnostics")
req = webob.Request.blank("/v1.0/testacct/servers/1/diagnostics")
req.method = "GET"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
def test_server_actions(self):
req = webob.Request.blank("/v1.0/servers/1/actions")
req = webob.Request.blank("/v1.0/testacct/servers/1/actions")
req.method = "GET"
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 404)
@@ -424,7 +425,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/action')
req = webob.Request.blank('/v1.0/testacct/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -434,7 +435,7 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/action')
req = webob.Request.blank('/v1.0/testacct/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
@@ -444,14 +445,14 @@ class ServersTest(test.TestCase):
body = dict(server=dict(
name='server_test', imageId=2, flavorId=2, metadata={},
personality={}))
req = webob.Request.blank('/v1.0/servers/1/action')
req = webob.Request.blank('/v1.0/testacct/servers/1/action')
req.method = 'POST'
req.content_type = 'application/json'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
def test_delete_server_instance(self):
req = webob.Request.blank('/v1.0/servers/1')
req = webob.Request.blank('/v1.0/testacct/servers/1')
req.method = 'DELETE'
self.server_delete_called = False

View File

@@ -0,0 +1,139 @@
# Copyright 2010 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 stubout
import webob
import json
import nova.api
import nova.api.openstack.auth
from nova import context
from nova import flags
from nova import test
from nova.auth.manager import User, Project
from nova.tests.api.openstack import fakes
FLAGS = flags.FLAGS
FLAGS.verbose = True
def fake_init(self):
self.manager = fakes.FakeAuthManager()
def fake_admin_check(self, req):
return True
class UsersTest(test.TestCase):
def setUp(self):
super(UsersTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
self.stubs.Set(nova.api.openstack.users.Controller, '__init__',
fake_init)
self.stubs.Set(nova.api.openstack.users.Controller, '_check_admin',
fake_admin_check)
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthManager.projects = dict(testacct=Project('testacct',
'testacct',
'guy1',
'test',
[]))
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
fakes.stub_out_auth(self.stubs)
self.allow_admin = FLAGS.allow_admin_api
FLAGS.allow_admin_api = True
fakemgr = fakes.FakeAuthManager()
fakemgr.add_user('acc1', User('guy1', 'guy1', 'acc1',
'fortytwo!', False))
fakemgr.add_user('acc2', User('guy2', 'guy2', 'acc2',
'swordfish', True))
def tearDown(self):
self.stubs.UnsetAll()
FLAGS.allow_admin_api = self.allow_admin
super(UsersTest, self).tearDown()
def test_get_user_list(self):
req = webob.Request.blank('/v1.0/testacct/users')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
self.assertEqual(len(res_dict['users']), 2)
def test_get_user_by_id(self):
req = webob.Request.blank('/v1.0/testacct/users/guy2')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res_dict['user']['id'], 'guy2')
self.assertEqual(res_dict['user']['name'], 'guy2')
self.assertEqual(res_dict['user']['secret'], 'swordfish')
self.assertEqual(res_dict['user']['admin'], True)
self.assertEqual(res.status_int, 200)
def test_user_delete(self):
req = webob.Request.blank('/v1.0/testacct/users/guy1')
req.method = 'DELETE'
res = req.get_response(fakes.wsgi_app())
self.assertTrue('guy1' not in [u.id for u in
fakes.FakeAuthManager.auth_data.values()])
self.assertEqual(res.status_int, 200)
def test_user_create(self):
body = dict(user=dict(name='test_guy',
access='acc3',
secret='invasionIsInNormandy',
admin=True))
req = webob.Request.blank('/v1.0/testacct/users')
req.method = 'POST'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
self.assertEqual(res_dict['user']['id'], 'test_guy')
self.assertEqual(res_dict['user']['name'], 'test_guy')
self.assertEqual(res_dict['user']['access'], 'acc3')
self.assertEqual(res_dict['user']['secret'], 'invasionIsInNormandy')
self.assertEqual(res_dict['user']['admin'], True)
self.assertTrue('test_guy' in [u.id for u in
fakes.FakeAuthManager.auth_data.values()])
self.assertEqual(len(fakes.FakeAuthManager.auth_data.values()), 3)
def test_user_update(self):
body = dict(user=dict(name='guy2',
access='acc2',
secret='invasionIsInNormandy'))
req = webob.Request.blank('/v1.0/testacct/users/guy2')
req.method = 'PUT'
req.body = json.dumps(body)
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
self.assertEqual(res.status_int, 200)
self.assertEqual(res_dict['user']['id'], 'guy2')
self.assertEqual(res_dict['user']['name'], 'guy2')
self.assertEqual(res_dict['user']['access'], 'acc2')
self.assertEqual(res_dict['user']['secret'], 'invasionIsInNormandy')
self.assertEqual(res_dict['user']['admin'], True)

View File

@@ -64,7 +64,7 @@ class ZonesTest(test.TestCase):
def setUp(self):
super(ZonesTest, self).setUp()
self.stubs = stubout.StubOutForTesting()
fakes.FakeAuthManager.auth_data = {}
fakes.FakeAuthManager.reset_fake_data()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)
@@ -85,7 +85,7 @@ class ZonesTest(test.TestCase):
super(ZonesTest, self).tearDown()
def test_get_zone_list(self):
req = webob.Request.blank('/v1.0/zones')
req = webob.Request.blank('/v1.0/testacct/zones')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -93,7 +93,7 @@ class ZonesTest(test.TestCase):
self.assertEqual(len(res_dict['zones']), 2)
def test_get_zone_by_id(self):
req = webob.Request.blank('/v1.0/zones/1')
req = webob.Request.blank('/v1.0/testacct/zones/1')
res = req.get_response(fakes.wsgi_app())
res_dict = json.loads(res.body)
@@ -103,7 +103,7 @@ class ZonesTest(test.TestCase):
self.assertEqual(res.status_int, 200)
def test_zone_delete(self):
req = webob.Request.blank('/v1.0/zones/1')
req = webob.Request.blank('/v1.0/testacct/zones/1')
res = req.get_response(fakes.wsgi_app())
self.assertEqual(res.status_int, 200)
@@ -111,7 +111,7 @@ class ZonesTest(test.TestCase):
def test_zone_create(self):
body = dict(zone=dict(api_url='http://blah.zoo', username='fred',
password='fubar'))
req = webob.Request.blank('/v1.0/zones')
req = webob.Request.blank('/v1.0/testacct/zones')
req.method = 'POST'
req.body = json.dumps(body)
@@ -125,7 +125,7 @@ class ZonesTest(test.TestCase):
def test_zone_update(self):
body = dict(zone=dict(username='zeb', password='sneaky'))
req = webob.Request.blank('/v1.0/zones/1')
req = webob.Request.blank('/v1.0/testacct/zones/1')
req.method = 'PUT'
req.body = json.dumps(body)

View File

@@ -207,7 +207,7 @@ def _upload_tarball(staging_path, image_id, glance_host, glance_port):
'transfer-encoding': 'chunked',
'x-image-meta-is_public': 'True',
'x-image-meta-status': 'queued',
'x-image-meta-type': 'vhd'
'x-image-meta-type': 'vhd',
}
for header, value in headers.iteritems():
conn.putheader(header, value)