summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorMonsyne Dragon <mdragon@rackspace.com>2011-03-03 15:41:45 +0000
committerMonsyne Dragon <mdragon@rackspace.com>2011-03-03 15:41:45 +0000
commit6797c5acc47fb5111ef821d6b074cb635692a9fb (patch)
tree9aa5cffff1fd699ec93bfed765100ce4d58452a0 /nova/api
parentbb7c1b8c63632c789ed0cd3785a22b7baa90fd83 (diff)
Add in multi-tenant support in openstack api.
Diffstat (limited to 'nova/api')
-rw-r--r--nova/api/openstack/__init__.py25
-rw-r--r--nova/api/openstack/accounts.py73
-rw-r--r--nova/api/openstack/auth.py54
-rw-r--r--nova/api/openstack/backup_schedules.py6
-rw-r--r--nova/api/openstack/consoles.py10
-rw-r--r--nova/api/openstack/flavors.py6
-rw-r--r--nova/api/openstack/images.py12
-rw-r--r--nova/api/openstack/servers.py38
-rw-r--r--nova/api/openstack/shared_ip_groups.py12
-rw-r--r--nova/api/openstack/users.py93
-rw-r--r--nova/api/openstack/zones.py12
11 files changed, 286 insertions, 55 deletions
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index b1b38ed2d..73d52192e 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -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())
diff --git a/nova/api/openstack/accounts.py b/nova/api/openstack/accounts.py
new file mode 100644
index 000000000..264fdab99
--- /dev/null
+++ b/nova/api/openstack/accounts.py
@@ -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))
diff --git a/nova/api/openstack/auth.py b/nova/api/openstack/auth.py
index 6011e6115..e77910fed 100644
--- a/nova/api/openstack/auth.py
+++ b/nova/api/openstack/auth.py
@@ -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)
diff --git a/nova/api/openstack/backup_schedules.py b/nova/api/openstack/backup_schedules.py
index 7abb5f884..a4d5939df 100644
--- a/nova/api/openstack/backup_schedules.py
+++ b/nova/api/openstack/backup_schedules.py
@@ -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())
diff --git a/nova/api/openstack/consoles.py b/nova/api/openstack/consoles.py
index 9ebdbe710..85b2a4140 100644
--- a/nova/api/openstack/consoles.py
+++ b/nova/api/openstack/consoles.py
@@ -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'],
diff --git a/nova/api/openstack/flavors.py b/nova/api/openstack/flavors.py
index f620d4107..79c3e1ab3 100644
--- a/nova/api/openstack/flavors.py
+++ b/nova/api/openstack/flavors.py
@@ -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):
diff --git a/nova/api/openstack/images.py b/nova/api/openstack/images.py
index cf85a496f..5bc5b9978 100644
--- a/nova/api/openstack/images.py
+++ b/nova/api/openstack/images.py
@@ -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())
diff --git a/nova/api/openstack/servers.py b/nova/api/openstack/servers.py
index 69273ad7b..426de92be 100644
--- a/nova/api/openstack/servers.py
+++ b/nova/api/openstack/servers.py
@@ -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)
diff --git a/nova/api/openstack/shared_ip_groups.py b/nova/api/openstack/shared_ip_groups.py
index 5d78f9377..e3c917749 100644
--- a/nova/api/openstack/shared_ip_groups.py
+++ b/nova/api/openstack/shared_ip_groups.py
@@ -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())
diff --git a/nova/api/openstack/users.py b/nova/api/openstack/users.py
new file mode 100644
index 000000000..c0b7544f9
--- /dev/null
+++ b/nova/api/openstack/users.py
@@ -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)))
diff --git a/nova/api/openstack/zones.py b/nova/api/openstack/zones.py
index d5206da20..30bf2b67b 100644
--- a/nova/api/openstack/zones.py
+++ b/nova/api/openstack/zones.py
@@ -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)