Remove admin_only ext attr in favor of authz

Working on blueprint separate-nova-adminapi. This removes the
admin_only extension attribute and the allow_admin_api flag. The
approach we're going for now is to load all extensions, but
to set an admin-only rule in our policy file for those extensions
that should be limited to just admin users. Now that all of our
admin api code has been moved to extensions, in order to
prevent admin api code from being loaded, simply remove it from
the extension list.

Change-Id: Ic574e06af44922ba764013b769077fc5099fd1a2
This commit is contained in:
Brian Waldon
2012-01-19 15:30:55 -08:00
parent 3ad3292efd
commit 9cb5f547dc
51 changed files with 325 additions and 232 deletions

View File

@@ -164,14 +164,12 @@ All this Zone and Distributed Scheduler stuff can seem a little daunting to conf
::
--allow_admin_api=true
--enable_zone_routing=true
--zone_name=zone1
--build_plan_encryption_key=c286696d887c9aa0611bbb3e2025a45b
--scheduler_driver=nova.scheduler.base_scheduler.BaseScheduler
--default_host_filter=nova.scheduler.filters.AllHostsFilter
`--allow_admin_api` must be set for OS API to enable the new `/zones/*` commands.
`--enable_zone_routing` must be set for OS API commands such as `create()`, `pause()` and `delete()` to get routed from Zone to Zone when looking for instances.
`--zone_name` is only required in child Zones. The default Zone name is `nova`, but you may want to name your child Zones something useful. Duplicate Zone names are not an issue.
`build_plan_encryption_key` is the SHA-256 key for encrypting/decrypting the Host information when it leaves a Zone. Be sure to change this key for each Zone you create. Do not duplicate keys.

View File

@@ -55,8 +55,6 @@ Zone administrative operations are usually done using python-novaclient_
.. _python-novaclient: https://github.com/rackspace/python-novaclient
In order to use the Zone operations, be sure to enable administrator operations in OpenStack API by setting the `--allow_admin_api=true` flag.
Finally you need to enable Zone Forwarding. This will be used by the Distributed Scheduler initiative currently underway. Set `--enable_zone_routing=true` to enable this feature.
Find out about this Zone

View File

@@ -9,6 +9,38 @@
"compute:get_all": [],
"admin_api": [["role:admin"]],
"compute_extension:accounts": [["rule:admin_api"]],
"compute_extension:admin_actions": [["rule:admin_api"]],
"compute_extension:cloudpipe": [],
"compute_extension:console_output": [],
"compute_extension:consoles": [],
"compute_extension:createserverext": [],
"compute_extension:deferred_delete": [],
"compute_extension:disk_config": [],
"compute_extension:extended_status": [["rule:admin_api"]],
"compute_extension:flavorextraspecs": [],
"compute_extension:floating_ip_dns": [],
"compute_extension:floating_ip_pools": [],
"compute_extension:floating_ips": [],
"compute_extension:hosts": [["rule:admin_api"]],
"compute_extension:keypairs": [],
"compute_extension:multinic": [],
"compute_extension:networks": [["rule:admin_api"]],
"compute_extension:quotas": [],
"compute_extension:rescue": [],
"compute_extension:security_groups": [],
"compute_extension:server_action_list": [["rule:admin_api"]],
"compute_extension:server_diagnostics": [["rule:admin_api"]],
"compute_extension:simple_tenant_usage": [["rule:admin_api"]],
"compute_extension:users": [["rule:admin_api"]],
"compute_extension:virtual_interfaces": [],
"compute_extension:virtual_storage_arrays": [],
"compute_extension:volumes": [],
"compute_extension:volumetypes": [],
"compute_extension:zones": [],
"volume:create": [],
"volume:get_all": [],
"volume:get_volume_metadata": [],

View File

@@ -32,9 +32,6 @@ from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.compute')
FLAGS = flags.FLAGS
flags.DEFINE_bool('allow_admin_api',
False,
'When True, this API service will accept admin operations.')
flags.DEFINE_bool('allow_instance_snapshots',
True,
'When True, this API service will permit instance snapshot operations.')

View File

@@ -42,9 +42,6 @@ from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.compute')
FLAGS = flags.FLAGS
flags.DEFINE_bool('allow_admin_api',
False,
'When True, this API service will accept admin operations.')
flags.DEFINE_bool('allow_instance_snapshots',
True,
'When True, this API service will permit instance snapshot operations.')

View File

@@ -26,6 +26,7 @@ from nova import log as logging
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack.compute.contrib.accounts')
authorize = extensions.extension_authorizer('compute', 'accounts')
class AccountTemplate(xmlutil.TemplateBuilder):
@@ -51,23 +52,18 @@ class Controller(object):
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.AdminRequired()
def index(self, req):
raise webob.exc.HTTPNotImplemented()
@wsgi.serializers(xml=AccountTemplate)
def show(self, req, id):
"""Return data about the given account id"""
authorize(req.environ['nova.context'])
account = self.manager.get_project(id)
return dict(account=_translate_keys(account))
def delete(self, req, id):
self._check_admin(req.environ['nova.context'])
authorize(req.environ['nova.context'])
self.manager.delete_project(id)
return {}
@@ -79,7 +75,7 @@ class Controller(object):
@wsgi.serializers(xml=AccountTemplate)
def update(self, req, id, body):
"""This is really create or update."""
self._check_admin(req.environ['nova.context'])
authorize(req.environ['nova.context'])
description = body['account'].get('description')
manager = body['account'].get('manager')
try:
@@ -97,11 +93,8 @@ class Accounts(extensions.ExtensionDescriptor):
alias = "os-accounts"
namespace = "http://docs.openstack.org/compute/ext/accounts/api/v1.1"
updated = "2011-12-23T00:00:00+00:00"
admin_only = True
def get_resources(self):
#TODO(bcwaldon): This should be prefixed with 'os-'
res = extensions.ResourceExtension('accounts',
Controller())
res = extensions.ResourceExtension('accounts', Controller())
return [res]

View File

@@ -30,6 +30,7 @@ from nova.scheduler import api as scheduler_api
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.compute.contrib.admin_actions")
authorize = extensions.extension_authorizer('compute', 'admin_actions')
class AdminActionsController(wsgi.Controller):
@@ -45,6 +46,7 @@ class AdminActionsController(wsgi.Controller):
def _pause(self, req, id, body):
"""Permit Admins to pause the server"""
ctxt = req.environ['nova.context']
authorize(ctxt)
try:
server = self.compute_api.get(ctxt, id)
self.compute_api.pause(ctxt, server)
@@ -63,6 +65,7 @@ class AdminActionsController(wsgi.Controller):
def _unpause(self, req, id, body):
"""Permit Admins to unpause the server"""
ctxt = req.environ['nova.context']
authorize(ctxt)
try:
server = self.compute_api.get(ctxt, id)
self.compute_api.unpause(ctxt, server)
@@ -81,6 +84,7 @@ class AdminActionsController(wsgi.Controller):
def _suspend(self, req, id, body):
"""Permit admins to suspend the server"""
context = req.environ['nova.context']
authorize(context)
try:
server = self.compute_api.get(context, id)
self.compute_api.suspend(context, server)
@@ -99,6 +103,7 @@ class AdminActionsController(wsgi.Controller):
def _resume(self, req, id, body):
"""Permit admins to resume the server from suspend"""
context = req.environ['nova.context']
authorize(context)
try:
server = self.compute_api.get(context, id)
self.compute_api.resume(context, server)
@@ -117,6 +122,7 @@ class AdminActionsController(wsgi.Controller):
def _migrate(self, req, id, body):
"""Permit admins to migrate a server to a new host"""
context = req.environ['nova.context']
authorize(context)
try:
instance = self.compute_api.get(context, id)
self.compute_api.resize(req.environ['nova.context'], instance)
@@ -134,6 +140,7 @@ class AdminActionsController(wsgi.Controller):
def _reset_network(self, req, id, body):
"""Permit admins to reset networking on an server"""
context = req.environ['nova.context']
authorize(context)
try:
instance = self.compute_api.get(context, id)
self.compute_api.reset_network(context, instance)
@@ -149,6 +156,7 @@ class AdminActionsController(wsgi.Controller):
def _inject_network_info(self, req, id, body):
"""Permit admins to inject network info into a server"""
context = req.environ['nova.context']
authorize(context)
try:
instance = self.compute_api.get(context, id)
self.compute_api.inject_network_info(context, instance)
@@ -166,6 +174,7 @@ class AdminActionsController(wsgi.Controller):
def _lock(self, req, id, body):
"""Permit admins to lock a server"""
context = req.environ['nova.context']
authorize(context)
try:
instance = self.compute_api.get(context, id)
self.compute_api.lock(context, instance)
@@ -183,6 +192,7 @@ class AdminActionsController(wsgi.Controller):
def _unlock(self, req, id, body):
"""Permit admins to lock a server"""
context = req.environ['nova.context']
authorize(context)
try:
instance = self.compute_api.get(context, id)
self.compute_api.unlock(context, instance)
@@ -207,6 +217,7 @@ class AdminActionsController(wsgi.Controller):
"""
context = req.environ["nova.context"]
authorize(context)
try:
entity = body["createBackup"]
@@ -273,7 +284,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
alias = "os-admin-actions"
namespace = "http://docs.openstack.org/compute/ext/admin-actions/api/v1.1"
updated = "2011-09-20T00:00:00+00:00"
admin_only = True
def get_controller_extensions(self):
controller = AdminActionsController()

View File

@@ -32,6 +32,7 @@ from nova import utils
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.compute.contrib.cloudpipe")
authorize = extensions.extension_authorizer('compute', 'cloudpipe')
class CloudpipeTemplate(xmlutil.TemplateBuilder):
@@ -120,6 +121,7 @@ class CloudpipeController(object):
"""
ctxt = req.environ['nova.context']
authorize(ctxt)
params = body.get('cloudpipe', {})
project_id = params.get('project_id', ctxt.project_id)
instance = self._get_cloudpipe_for_project(ctxt, project_id)
@@ -137,8 +139,9 @@ class CloudpipeController(object):
@wsgi.serializers(xml=CloudpipesTemplate)
def index(self, req):
"""Show admins the list of running cloudpipe instances."""
"""List running cloudpipe instances."""
context = req.environ['nova.context']
authorize(context)
vpns = []
# TODO(todd): could use compute_api.get_all with admin context?
for project in self.auth_manager.get_projects():
@@ -162,7 +165,6 @@ class Cloudpipe(extensions.ExtensionDescriptor):
alias = "os-cloudpipe"
namespace = "http://docs.openstack.org/compute/ext/cloudpipe/api/v1.1"
updated = "2011-12-16T00:00:00+00:00"
admin_only = True
def get_resources(self):
resources = []

View File

@@ -26,6 +26,7 @@ from nova.api.openstack import wsgi
LOG = logging.getLogger('nova.api.openstack.compute.contrib.console_output')
authorize = extensions.extension_authorizer('compute', 'console_output')
class ConsoleOutputController(wsgi.Controller):
@@ -37,6 +38,7 @@ class ConsoleOutputController(wsgi.Controller):
def get_console_output(self, req, id, body):
"""Get text console output."""
context = req.environ['nova.context']
authorize(context)
try:
instance = self.compute_api.routing_get(context, id)
@@ -54,8 +56,6 @@ class ConsoleOutputController(wsgi.Controller):
length)
except exception.ApiError, e:
raise webob.exc.HTTPBadRequest(explanation=e.message)
except exception.NotAuthorized, e:
raise webob.exc.HTTPUnauthorized()
return {'output': output}

View File

@@ -24,6 +24,7 @@ from nova.api.openstack import wsgi
LOG = logging.getLogger('nova.api.openstack.compute.contrib.console')
authorize = extensions.extension_authorizer('compute', 'consoles')
class ConsolesController(wsgi.Controller):
@@ -35,6 +36,7 @@ class ConsolesController(wsgi.Controller):
def get_vnc_console(self, req, id, body):
"""Get text console output."""
context = req.environ['nova.context']
authorize(context)
console_type = body['os-getVNCConsole'].get('type')

View File

@@ -19,13 +19,18 @@ from nova.api.openstack.compute import servers
from nova.api.openstack.compute import views
authorize = extensions.soft_extension_authorizer('compute', 'createserverext')
class ViewBuilder(views.servers.ViewBuilder):
"""Adds security group output when viewing server details."""
def show(self, request, instance):
"""Detailed view of a single instance."""
server = super(ViewBuilder, self).show(request, instance)
server["server"]["security_groups"] = self._get_groups(instance)
context = request.environ['nova.context']
if authorize(context):
server["server"]["security_groups"] = self._get_groups(instance)
return server
def _get_groups(self, instance):

View File

@@ -26,6 +26,7 @@ from nova import log as logging
LOG = logging.getLogger("nova.api.openstack.compute.contrib.deferred-delete")
authorize = extensions.extension_authorizer('compute', 'deferred_delete')
class DeferredDeleteController(wsgi.Controller):
@@ -36,8 +37,8 @@ class DeferredDeleteController(wsgi.Controller):
@wsgi.action('restore')
def _restore(self, req, id, body):
"""Restore a previously deleted instance."""
context = req.environ["nova.context"]
authorize(context)
instance = self.compute_api.get(context, id)
try:
self.compute_api.restore(context, instance)
@@ -49,8 +50,8 @@ class DeferredDeleteController(wsgi.Controller):
@wsgi.action('forceDelete')
def _force_delete(self, req, id, body):
"""Force delete of instance before deferred cleanup."""
context = req.environ["nova.context"]
authorize(context)
instance = self.compute_api.get(context, id)
try:
self.compute_api.force_delete(context, instance)

View File

@@ -28,6 +28,7 @@ ALIAS = 'OS-DCF'
XMLNS_DCF = "http://docs.openstack.org/compute/ext/disk_config/api/v1.1"
API_DISK_CONFIG = "%s:diskConfig" % ALIAS
INTERNAL_DISK_CONFIG = "auto_disk_config"
authorize = extensions.soft_extension_authorizer('compute', 'disk_config')
def disk_config_to_api(value):
@@ -70,16 +71,16 @@ class ImageDiskConfigController(wsgi.Controller):
@wsgi.extends
def show(self, req, resp_obj, id):
if 'image' in resp_obj.obj:
context = req.environ['nova.context']
context = req.environ['nova.context']
if 'image' in resp_obj.obj and authorize(context):
resp_obj.attach(xml=ImageDiskConfigTemplate())
image = resp_obj.obj['image']
self._add_disk_config(context, [image])
@wsgi.extends
def detail(self, req, resp_obj):
if 'images' in resp_obj.obj:
context = req.environ['nova.context']
context = req.environ['nova.context']
if 'images' in resp_obj.obj and authorize(context):
resp_obj.attach(xml=ImagesDiskConfigTemplate())
images = resp_obj.obj['images']
self._add_disk_config(context, images)
@@ -102,13 +103,14 @@ class ServersDiskConfigTemplate(xmlutil.TemplateBuilder):
class ServerDiskConfigController(wsgi.Controller):
def _add_disk_config(self, context, servers):
# Filter out any servers that already have the key set (most likely
# from a remote zone)
# Filter out any servers that already have the key set
# (most likely from a remote zone)
servers = [s for s in servers if API_DISK_CONFIG not in s]
# Get DB information for servers
uuids = [server['id'] for server in servers]
db_servers = db.instance_get_all_by_filters(context, {'uuid': uuids})
db_servers = db.instance_get_all_by_filters(context,
{'uuid': uuids})
db_servers_by_uuid = dict((s['uuid'], s) for s in db_servers)
for server in servers:
@@ -117,21 +119,22 @@ class ServerDiskConfigController(wsgi.Controller):
value = db_server[INTERNAL_DISK_CONFIG]
server[API_DISK_CONFIG] = disk_config_to_api(value)
def _show(self, req, resp_obj):
def _show(self, context, resp_obj):
if 'server' in resp_obj.obj:
context = req.environ['nova.context']
resp_obj.attach(xml=ServerDiskConfigTemplate())
server = resp_obj.obj['server']
self._add_disk_config(context, [server])
@wsgi.extends
def show(self, req, resp_obj, id):
self._show(req, resp_obj)
context = req.environ['nova.context']
if authorize(context):
self._show(context, resp_obj)
@wsgi.extends
def detail(self, req, resp_obj):
if 'servers' in resp_obj.obj:
context = req.environ['nova.context']
context = req.environ['nova.context']
if 'servers' in resp_obj.obj and authorize(context):
resp_obj.attach(xml=ServersDiskConfigTemplate())
servers = resp_obj.obj['servers']
self._add_disk_config(context, servers)
@@ -144,26 +147,34 @@ class ServerDiskConfigController(wsgi.Controller):
@wsgi.extends
def create(self, req, body):
self._set_disk_config(body['server'])
resp_obj = (yield)
self._show(req, resp_obj)
context = req.environ['nova.context']
if authorize(context):
self._set_disk_config(body['server'])
resp_obj = (yield)
self._show(context, resp_obj)
@wsgi.extends
def update(self, req, id, body):
self._set_disk_config(body['server'])
resp_obj = (yield)
self._show(req, resp_obj)
context = req.environ['nova.context']
if authorize(context):
self._set_disk_config(body['server'])
resp_obj = (yield)
self._show(context, resp_obj)
@wsgi.extends(action='rebuild')
def _action_rebuild(self, req, id, body):
self._set_disk_config(body['rebuild'])
resp_obj = (yield)
self._show(req, resp_obj)
context = req.environ['nova.context']
if authorize(context):
self._set_disk_config(body['rebuild'])
resp_obj = (yield)
self._show(context, resp_obj)
@wsgi.extends(action='resize')
def _action_resize(self, req, id, body):
self._set_disk_config(body['resize'])
yield
context = req.environ['nova.context']
if authorize(context):
self._set_disk_config(body['resize'])
yield
class Disk_config(extensions.ExtensionDescriptor):

View File

@@ -27,6 +27,7 @@ from nova import log as logging
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.compute.contrib.extendedstatus")
authorize = extensions.soft_extension_authorizer('compute', 'extended_status')
class ExtendedStatusController(wsgi.Controller):
@@ -48,34 +49,34 @@ class ExtendedStatusController(wsgi.Controller):
@wsgi.extends
def show(self, req, resp_obj, id):
context = req.environ['nova.context']
if authorize(context):
# Attach our slave template to the response object
resp_obj.attach(xml=ExtendedStatusTemplate())
# Attach our slave template to the response object
resp_obj.attach(xml=ExtendedStatusTemplate())
try:
self._get_and_extend_one(context, id, resp_obj.obj['server'])
except exception.NotFound:
explanation = _("Server not found.")
raise exc.HTTPNotFound(explanation=explanation)
try:
self._get_and_extend_one(context, id, resp_obj.obj['server'])
except exception.NotFound:
explanation = _("Server not found.")
raise exc.HTTPNotFound(explanation=explanation)
@wsgi.extends
def detail(self, req, resp_obj):
context = req.environ['nova.context']
if authorize(context):
# Attach our slave template to the response object
resp_obj.attach(xml=ExtendedStatusesTemplate())
# Attach our slave template to the response object
resp_obj.attach(xml=ExtendedStatusesTemplate())
for server in list(resp_obj.obj['servers']):
try:
self._get_and_extend_one(context, server['id'], server)
except exception.NotFound:
# NOTE(dtroyer): A NotFound exception at this point
# happens because a delete was in progress and the
# server that was present in the original call to
# compute.api.get_all() is no longer present.
# Delete it from the response and move on.
resp_obj.obj['servers'].remove(server)
continue
for server in list(resp_obj.obj['servers']):
try:
self._get_and_extend_one(context, server['id'], server)
except exception.NotFound:
# NOTE(dtroyer): A NotFound exception at this point
# happens because a delete was in progress and the
# server that was present in the original call to
# compute.api.get_all() is no longer present.
# Delete it from the response and move on.
resp_obj.obj['servers'].remove(server)
continue
class Extended_status(extensions.ExtensionDescriptor):
@@ -86,7 +87,6 @@ class Extended_status(extensions.ExtensionDescriptor):
namespace = "http://docs.openstack.org/compute/ext/" \
"extended_status/api/v1.1"
updated = "2011-11-03T00:00:00+00:00"
admin_only = True
def get_controller_extensions(self):
controller = ExtendedStatusController()

View File

@@ -26,6 +26,9 @@ from nova import db
from nova import exception
authorize = extensions.extension_authorizer('compute', 'flavorextraspecs')
class ExtraSpecsTemplate(xmlutil.TemplateBuilder):
def construct(self):
return xmlutil.MasterTemplate(xmlutil.make_flat_dict('extra_specs'), 1)
@@ -50,12 +53,14 @@ class FlavorExtraSpecsController(object):
def index(self, req, flavor_id):
""" Returns the list of extra specs for a givenflavor """
context = req.environ['nova.context']
authorize(context)
return self._get_extra_specs(context, flavor_id)
@wsgi.serializers(xml=ExtraSpecsTemplate)
def create(self, req, flavor_id, body):
self._check_body(body)
context = req.environ['nova.context']
authorize(context)
self._check_body(body)
specs = body.get('extra_specs')
try:
db.instance_type_extra_specs_update_or_create(context,
@@ -67,8 +72,9 @@ class FlavorExtraSpecsController(object):
@wsgi.serializers(xml=ExtraSpecsTemplate)
def update(self, req, flavor_id, id, body):
self._check_body(body)
context = req.environ['nova.context']
authorize(context)
self._check_body(body)
if not id in body:
expl = _('Request body and URI mismatch')
raise exc.HTTPBadRequest(explanation=expl)
@@ -88,6 +94,7 @@ class FlavorExtraSpecsController(object):
def show(self, req, flavor_id, id):
""" Return a single extra spec item """
context = req.environ['nova.context']
authorize(context)
specs = self._get_extra_specs(context, flavor_id)
if id in specs['extra_specs']:
return {id: specs['extra_specs'][id]}
@@ -97,6 +104,7 @@ class FlavorExtraSpecsController(object):
def delete(self, req, flavor_id, id):
""" Deletes an existing extra spec """
context = req.environ['nova.context']
authorize(context)
db.instance_type_extra_specs_delete(context, flavor_id, id)
def _handle_quota_error(self, error):

View File

@@ -27,6 +27,7 @@ from nova import network
LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_dns')
authorize = extensions.extension_authorizer('compute', 'floating_ip_dns')
def make_dns_entry(elem):
@@ -138,6 +139,7 @@ class FloatingIPDNSDomainController(object):
def index(self, req):
"""Return a list of available DNS domains."""
context = req.environ['nova.context']
authorize(context)
domains = self.network_api.get_dns_domains(context)
domainlist = [_create_domain_entry(domain['domain'],
domain.get('scope'),
@@ -151,6 +153,7 @@ class FloatingIPDNSDomainController(object):
def update(self, req, id, body):
"""Add or modify domain entry"""
context = req.environ['nova.context']
authorize(context)
fqdomain = _unquote_domain(id)
try:
entry = body['domain_entry']
@@ -185,6 +188,7 @@ class FloatingIPDNSDomainController(object):
def delete(self, req, id):
"""Delete the domain identified by id. """
context = req.environ['nova.context']
authorize(context)
params = req.str_GET
domain = _unquote_domain(id)
@@ -210,6 +214,7 @@ class FloatingIPDNSEntryController(object):
def show(self, req, domain_id, id):
"""Return the DNS entry that corresponds to domain_id and id."""
context = req.environ['nova.context']
authorize(context)
domain = _unquote_domain(domain_id)
name = id
@@ -222,6 +227,7 @@ class FloatingIPDNSEntryController(object):
def index(self, req, domain_id):
"""Return a list of dns entries for the specified domain and ip."""
context = req.environ['nova.context']
authorize(context)
params = req.GET
floating_ip = params.get('ip')
domain = _unquote_domain(domain_id)
@@ -241,6 +247,7 @@ class FloatingIPDNSEntryController(object):
def update(self, req, domain_id, id, body):
"""Add or modify dns entry"""
context = req.environ['nova.context']
authorize(context)
domain = _unquote_domain(domain_id)
name = id
try:
@@ -268,6 +275,7 @@ class FloatingIPDNSEntryController(object):
def delete(self, req, domain_id, id):
"""Delete the entry identified by req and id. """
context = req.environ['nova.context']
authorize(context)
domain = _unquote_domain(domain_id)
name = id

View File

@@ -22,6 +22,7 @@ from nova import network
LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ip_pools')
authorize = extensions.extension_authorizer('compute', 'floating_ip_pools')
def _translate_floating_ip_view(pool):
@@ -69,6 +70,7 @@ class FloatingIPPoolsController(object):
def index(self, req):
"""Return a list of pools."""
context = req.environ['nova.context']
authorize(context)
pools = self.network_api.get_floating_ip_pools(context)
return _translate_floating_ip_pools_view(pools)

View File

@@ -30,6 +30,7 @@ from nova import rpc
LOG = logging.getLogger('nova.api.openstack.compute.contrib.floating_ips')
authorize = extensions.extension_authorizer('compute', 'floating_ips')
def make_float_ip(elem):
@@ -116,6 +117,7 @@ class FloatingIPController(object):
def show(self, req, id):
"""Return data about the given floating ip."""
context = req.environ['nova.context']
authorize(context)
try:
floating_ip = self.network_api.get_floating_ip(context, id)
@@ -130,6 +132,7 @@ class FloatingIPController(object):
def index(self, req):
"""Return a list of floating ips allocated to a project."""
context = req.environ['nova.context']
authorize(context)
floating_ips = self.network_api.get_floating_ips_by_project(context)
@@ -141,6 +144,7 @@ class FloatingIPController(object):
@wsgi.serializers(xml=FloatingIPTemplate)
def create(self, req, body=None):
context = req.environ['nova.context']
authorize(context)
pool = None
if body and 'pool' in body:
@@ -163,6 +167,7 @@ class FloatingIPController(object):
def delete(self, req, id):
context = req.environ['nova.context']
authorize(context)
floating_ip = self.network_api.get_floating_ip(context, id)
if floating_ip.get('fixed_ip_id'):
@@ -188,6 +193,7 @@ class FloatingIPActionController(wsgi.Controller):
def _add_floating_ip(self, req, id, body):
"""Associate floating_ip to an instance."""
context = req.environ['nova.context']
authorize(context)
try:
address = body['addFloatingIp']['address']
@@ -213,6 +219,7 @@ class FloatingIPActionController(wsgi.Controller):
def _remove_floating_ip(self, req, id, body):
"""Dissociate floating_ip from an instance."""
context = req.environ['nova.context']
authorize(context)
try:
address = body['removeFloatingIp']['address']

View File

@@ -31,6 +31,7 @@ from nova.scheduler import api as scheduler_api
LOG = logging.getLogger("nova.api.openstack.compute.contrib.hosts")
FLAGS = flags.FLAGS
authorize = extensions.extension_authorizer('compute', 'hosts')
class HostIndexTemplate(xmlutil.TemplateBuilder):
@@ -112,12 +113,14 @@ class HostController(object):
@wsgi.serializers(xml=HostIndexTemplate)
def index(self, req):
authorize(req.environ['nova.context'])
return {'hosts': _list_hosts(req)}
@wsgi.serializers(xml=HostUpdateTemplate)
@wsgi.deserializers(xml=HostDeserializer)
@check_host
def update(self, req, id, body):
authorize(req.environ['nova.context'])
for raw_key, raw_val in body.iteritems():
key = raw_key.lower().strip()
val = raw_val.lower().strip()
@@ -149,6 +152,7 @@ class HostController(object):
def _host_power_action(self, req, host, action):
"""Reboots, shuts down or powers up the host."""
context = req.environ['nova.context']
authorize(context)
try:
result = self.compute_api.host_power_action(context, host=host,
action=action)
@@ -176,7 +180,6 @@ class Hosts(extensions.ExtensionDescriptor):
alias = "os-hosts"
namespace = "http://docs.openstack.org/compute/ext/hosts/api/v1.1"
updated = "2011-06-29T00:00:00+00:00"
admin_only = True
def get_resources(self):
resources = [extensions.ResourceExtension('os-hosts',

View File

@@ -31,6 +31,9 @@ from nova import db
from nova import exception
authorize = extensions.extension_authorizer('compute', 'keypairs')
class KeypairTemplate(xmlutil.TemplateBuilder):
def construct(self):
return xmlutil.MasterTemplate(xmlutil.make_flat_dict('keypair'), 1)
@@ -77,6 +80,7 @@ class KeypairController(object):
"""
context = req.environ['nova.context']
authorize(context)
params = body['keypair']
name = params['name']
@@ -109,6 +113,7 @@ class KeypairController(object):
Delete a keypair with a given name
"""
context = req.environ['nova.context']
authorize(context)
db.key_pair_destroy(context, context.user_id, id)
return webob.Response(status_int=202)
@@ -118,6 +123,7 @@ class KeypairController(object):
List of keypairs for a user
"""
context = req.environ['nova.context']
authorize(context)
key_pairs = db.key_pair_get_all_by_user(context, context.user_id)
rval = []
for key_pair in key_pairs:

View File

@@ -26,6 +26,7 @@ from nova import log as logging
LOG = logging.getLogger("nova.api.openstack.compute.contrib.multinic")
authorize = extensions.extension_authorizer('compute', 'multinic')
class MultinicController(wsgi.Controller):
@@ -43,13 +44,14 @@ class MultinicController(wsgi.Controller):
@wsgi.action('addFixedIp')
def _add_fixed_ip(self, req, id, body):
"""Adds an IP on a given network to an instance."""
context = req.environ['nova.context']
authorize(context)
# Validate the input entity
if 'networkId' not in body['addFixedIp']:
msg = _("Missing 'networkId' argument for addFixedIp")
raise exc.HTTPUnprocessableEntity(explanation=msg)
context = req.environ['nova.context']
instance = self._get_instance(context, id)
network_id = body['addFixedIp']['networkId']
self.compute_api.add_fixed_ip(context, instance, network_id)
@@ -58,13 +60,14 @@ class MultinicController(wsgi.Controller):
@wsgi.action('removeFixedIp')
def _remove_fixed_ip(self, req, id, body):
"""Removes an IP from an instance."""
context = req.environ['nova.context']
authorize(context)
# Validate the input entity
if 'address' not in body['removeFixedIp']:
msg = _("Missing 'address' argument for removeFixedIp")
raise exc.HTTPUnprocessableEntity(explanation=msg)
context = req.environ['nova.context']
instance = self._get_instance(context, id)
address = body['removeFixedIp']['address']

View File

@@ -28,6 +28,7 @@ import nova.network.api
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack.compute.contrib.networks')
authorize = extensions.extension_authorizer('compute', 'networks')
def network_dict(network):
@@ -65,6 +66,7 @@ class NetworkController(object):
def _disassociate(self, request, network_id, body):
context = request.environ['nova.context']
authorize(context)
LOG.debug(_("Disassociating network with id %s" % network_id))
try:
self.network_api.disassociate(context, network_id)
@@ -74,12 +76,14 @@ class NetworkController(object):
def index(self, req):
context = req.environ['nova.context']
authorize(context)
networks = self.network_api.get_all(context)
result = [network_dict(net_ref) for net_ref in networks]
return {'networks': result}
def show(self, req, id):
context = req.environ['nova.context']
authorize(context)
LOG.debug(_("Showing network with id %s") % id)
try:
network = self.network_api.get(context, id)
@@ -89,6 +93,7 @@ class NetworkController(object):
def delete(self, req, id):
context = req.environ['nova.context']
authorize(context)
LOG.info(_("Deleting network with id %s") % id)
try:
self.network_api.delete(context, id)
@@ -107,7 +112,6 @@ class Networks(extensions.ExtensionDescriptor):
alias = "os-networks"
namespace = "http://docs.openstack.org/compute/ext/networks/api/v1.1"
updated = "2011-12-23 00:00:00"
admin_only = True
def get_resources(self):
member_actions = {'action': 'POST'}

View File

@@ -25,6 +25,9 @@ from nova import exception
from nova import quota
authorize = extensions.extension_authorizer('compute', 'quotas')
quota_resources = ['metadata_items', 'injected_file_content_bytes',
'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances',
'injected_files', 'cores']
@@ -57,6 +60,7 @@ class QuotaSetsController(object):
@wsgi.serializers(xml=QuotaTemplate)
def show(self, req, id):
context = req.environ['nova.context']
authorize(context)
try:
db.sqlalchemy.api.authorize_project_context(context, id)
return self._format_quota_set(id,
@@ -67,6 +71,7 @@ class QuotaSetsController(object):
@wsgi.serializers(xml=QuotaTemplate)
def update(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
project_id = id
for key in body['quota_set'].keys():
if key in quota_resources:
@@ -80,6 +85,7 @@ class QuotaSetsController(object):
return {'quota_set': quota.get_project_quotas(context, project_id)}
def defaults(self, req, id):
authorize(req.environ['nova.context'])
return self._format_quota_set(id, quota._get_default_quotas())

View File

@@ -28,6 +28,7 @@ from nova import utils
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.compute.contrib.rescue")
authorize = exts.extension_authorizer('compute', 'rescue')
class RescueController(wsgi.Controller):
@@ -47,6 +48,7 @@ class RescueController(wsgi.Controller):
def _rescue(self, req, id, body):
"""Rescue an instance."""
context = req.environ["nova.context"]
authorize(context)
if body['rescue'] and 'adminPass' in body['rescue']:
password = body['rescue']['adminPass']
@@ -62,6 +64,7 @@ class RescueController(wsgi.Controller):
def _unrescue(self, req, id, body):
"""Unrescue an instance."""
context = req.environ["nova.context"]
authorize(context)
instance = self._get_instance(context, id)
self.compute_api.unrescue(context, instance)
return webob.Response(status_int=202)

View File

@@ -35,6 +35,7 @@ from nova import utils
LOG = logging.getLogger("nova.api.openstack.compute.contrib.security_groups")
FLAGS = flags.FLAGS
authorize = extensions.extension_authorizer('compute', 'security_groups')
def make_rule(elem):
@@ -223,6 +224,7 @@ class SecurityGroupController(object):
def show(self, req, id):
"""Return data about the given security group."""
context = req.environ['nova.context']
authorize(context)
security_group = self._get_security_group(context, id)
return {'security_group': self._format_security_group(context,
security_group)}
@@ -230,6 +232,7 @@ class SecurityGroupController(object):
def delete(self, req, id):
"""Delete a security group."""
context = req.environ['nova.context']
authorize(context)
security_group = self._get_security_group(context, id)
LOG.audit(_("Delete security group %s"), id, context=context)
db.security_group_destroy(context, security_group.id)
@@ -240,6 +243,7 @@ class SecurityGroupController(object):
def index(self, req):
"""Returns a list of security groups"""
context = req.environ['nova.context']
authorize(context)
self.compute_api.ensure_default_security_group(context)
groups = db.security_group_get_by_project(context,
@@ -257,6 +261,7 @@ class SecurityGroupController(object):
def create(self, req, body):
"""Creates a new security group."""
context = req.environ['nova.context']
authorize(context)
if not body:
raise exc.HTTPUnprocessableEntity()
@@ -313,6 +318,7 @@ class SecurityGroupRulesController(SecurityGroupController):
@wsgi.deserializers(xml=SecurityGroupRulesXMLDeserializer)
def create(self, req, body):
context = req.environ['nova.context']
authorize(context)
if not body:
raise exc.HTTPUnprocessableEntity()
@@ -471,6 +477,7 @@ class SecurityGroupRulesController(SecurityGroupController):
def delete(self, req, id):
context = req.environ['nova.context']
authorize(context)
self.compute_api.ensure_default_security_group(context)
try:
@@ -505,6 +512,7 @@ class SecurityGroupActionController(wsgi.Controller):
@wsgi.action('addSecurityGroup')
def _addSecurityGroup(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
try:
body = body['addSecurityGroup']
@@ -535,6 +543,7 @@ class SecurityGroupActionController(wsgi.Controller):
@wsgi.action('removeSecurityGroup')
def _removeSecurityGroup(self, req, id, body):
context = req.environ['nova.context']
authorize(context)
try:
body = body['removeSecurityGroup']

View File

@@ -23,6 +23,7 @@ from nova import exception
sa_nsmap = {None: wsgi.XMLNS_V11}
authorize = extensions.extension_authorizer('compute', 'server_action_list')
class ServerActionsTemplate(xmlutil.TemplateBuilder):
@@ -39,6 +40,7 @@ class ServerActionListController(object):
@wsgi.serializers(xml=ServerActionsTemplate)
def index(self, req, server_id):
context = req.environ["nova.context"]
authorize(context)
compute_api = compute.API()
try:
@@ -66,7 +68,6 @@ class Server_action_list(extensions.ExtensionDescriptor):
namespace = "http://docs.openstack.org/compute/ext/" \
"server-actions-list/api/v1.1"
updated = "2011-12-21T00:00:00+00:00"
admin_only = True
def get_resources(self):
parent_def = {'member_name': 'server', 'collection_name': 'servers'}

View File

@@ -23,6 +23,7 @@ from nova import exception
from nova.scheduler import api as scheduler_api
authorize = extensions.extension_authorizer('compute', 'server_diagnostics')
sd_nsmap = {None: wsgi.XMLNS_V11}
@@ -41,6 +42,7 @@ class ServerDiagnosticsController(object):
@scheduler_api.redirect_handler
def index(self, req, server_id):
context = req.environ["nova.context"]
authorize(context)
compute_api = compute.API()
try:
instance = compute_api.get(context, id)
@@ -58,7 +60,6 @@ class Server_diagnostics(extensions.ExtensionDescriptor):
namespace = "http://docs.openstack.org/compute/ext/" \
"server-diagnostics/api/v1.1"
updated = "2011-12-21T00:00:00+00:00"
admin_only = True
def get_resources(self):
parent_def = {'member_name': 'server', 'collection_name': 'servers'}

View File

@@ -29,6 +29,7 @@ from nova import flags
FLAGS = flags.FLAGS
authorize = extensions.extension_authorizer('compute', 'simple_tenant_usage')
def make_usage(elem):
@@ -211,6 +212,7 @@ class SimpleTenantUsageController(object):
def index(self, req):
"""Retrive tenant_usage for all tenants"""
context = req.environ['nova.context']
authorize(context)
if not context.is_admin:
return webob.Response(status_int=403)
@@ -227,6 +229,7 @@ class SimpleTenantUsageController(object):
"""Retrive tenant_usage for a specified tenant"""
tenant_id = id
context = req.environ['nova.context']
authorize(context)
if not context.is_admin:
if tenant_id != context.project_id:
@@ -253,7 +256,6 @@ class Simple_tenant_usage(extensions.ExtensionDescriptor):
namespace = "http://docs.openstack.org/compute/ext/" \
"os-simple-tenant-usage/api/v1.1"
updated = "2011-08-19T00:00:00+00:00"
admin_only = True
def get_resources(self):
resources = []

View File

@@ -27,6 +27,7 @@ from nova import log as logging
FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.api.openstack.users')
authorize = extensions.extension_authorizer('compute', 'users')
def make_user(elem):
@@ -65,15 +66,10 @@ class Controller(object):
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.AdminRequired()
@wsgi.serializers(xml=UsersTemplate)
def index(self, req):
"""Return all users in brief"""
authorize(req.environ['nova.context'])
users = self.manager.get_users()
users = common.limited(users, req)
users = [_translate_keys(user) for user in users]
@@ -87,6 +83,7 @@ class Controller(object):
@wsgi.serializers(xml=UserTemplate)
def show(self, req, id):
"""Return data about the given user id"""
authorize(req.environ['nova.context'])
#NOTE(justinsb): The drivers are a little inconsistent in how they
# deal with "NotFound" - some throw, some return None.
@@ -101,13 +98,13 @@ class Controller(object):
return dict(user=_translate_keys(user))
def delete(self, req, id):
self._check_admin(req.environ['nova.context'])
authorize(req.environ['nova.context'])
self.manager.delete_user(id)
return {}
@wsgi.serializers(xml=UserTemplate)
def create(self, req, body):
self._check_admin(req.environ['nova.context'])
authorize(req.environ['nova.context'])
is_admin = body['user'].get('admin') in ('T', 'True', True)
name = body['user'].get('name')
access = body['user'].get('access')
@@ -117,7 +114,7 @@ class Controller(object):
@wsgi.serializers(xml=UserTemplate)
def update(self, req, id, body):
self._check_admin(req.environ['nova.context'])
authorize(req.environ['nova.context'])
is_admin = body['user'].get('admin')
if is_admin is not None:
is_admin = is_admin in ('T', 'True', True)
@@ -134,7 +131,6 @@ class Users(extensions.ExtensionDescriptor):
alias = "os-users"
namespace = "http://docs.openstack.org/compute/ext/users/api/v1.1"
updated = "2011-08-08T00:00:00+00:00"
admin_only = True
def get_resources(self):
coll_actions = {'detail': 'GET'}

View File

@@ -25,6 +25,7 @@ from nova import network
LOG = logging.getLogger("nova.api.openstack.compute."
"contrib.virtual_interfaces")
authorize = extensions.extension_authorizer('compute', 'virtual_interfaces')
vif_nsmap = {None: wsgi.XMLNS_V11}
@@ -68,6 +69,7 @@ class ServerVirtualInterfaceController(object):
@wsgi.serializers(xml=VirtualInterfaceTemplate)
def index(self, req, server_id):
"""Returns the list of VIFs for a given instance."""
authorize(req.environ['nova.context'])
return self._items(req, server_id,
entity_maker=_translate_vif_summary_view)

View File

@@ -36,9 +36,11 @@ from nova import log as logging
from nova import vsa
from nova import volume
FLAGS = flags.FLAGS
FLAGS = flags.FLAGS
LOG = logging.getLogger("nova.api.openstack.compute.contrib.vsa")
authorize = extensions.extension_authorizer('compute',
'virtual_storage_arrays')
def _vsa_view(context, vsa, details=False, instances=None):
@@ -124,6 +126,7 @@ class VsaController(object):
def _items(self, req, details):
"""Return summary or detailed list of VSAs."""
context = req.environ['nova.context']
authorize(context)
vsas = self.vsa_api.get_all(context)
limited_list = common.limited(vsas, req)
@@ -147,6 +150,7 @@ class VsaController(object):
def show(self, req, id):
"""Return data about the given VSA."""
context = req.environ['nova.context']
authorize(context)
try:
vsa = self.vsa_api.get(context, vsa_id=id)
@@ -160,6 +164,7 @@ class VsaController(object):
def create(self, req, body):
"""Create a new VSA."""
context = req.environ['nova.context']
authorize(context)
if not body or 'vsa' not in body:
LOG.debug(_("No body provided"), context=context)
@@ -193,6 +198,7 @@ class VsaController(object):
def delete(self, req, id):
"""Delete a VSA."""
context = req.environ['nova.context']
authorize(context)
LOG.audit(_("Delete VSA with id: %s"), id, context=context)
@@ -206,6 +212,7 @@ class VsaController(object):
auto or manually associate an IP to VSA
"""
context = req.environ['nova.context']
authorize(context)
if body is None:
ip = 'auto'
@@ -234,6 +241,7 @@ class VsaController(object):
auto or manually associate an IP to VSA
"""
context = req.environ['nova.context']
authorize(context)
if body is None:
ip = 'auto'
@@ -293,6 +301,7 @@ class VsaVolumeDriveController(volumes.VolumeController):
def _items(self, req, vsa_id, details):
"""Return summary or detailed list of volumes for particular VSA."""
context = req.environ['nova.context']
authorize(context)
vols = self.volume_api.get_all(context,
search_opts={'metadata': {self.direction: str(vsa_id)}})
@@ -317,6 +326,7 @@ class VsaVolumeDriveController(volumes.VolumeController):
"""Create a new volume from VSA."""
LOG.audit(_("Create. vsa_id=%(vsa_id)s, body=%(body)s"), locals())
context = req.environ['nova.context']
authorize(context)
if not body:
raise exc.HTTPUnprocessableEntity()
@@ -345,6 +355,7 @@ class VsaVolumeDriveController(volumes.VolumeController):
def update(self, req, vsa_id, id, body):
"""Update a volume."""
context = req.environ['nova.context']
authorize(context)
try:
self._check_volume_ownership(context, vsa_id, id)
@@ -380,6 +391,7 @@ class VsaVolumeDriveController(volumes.VolumeController):
def delete(self, req, vsa_id, id):
"""Delete a volume."""
context = req.environ['nova.context']
authorize(context)
LOG.audit(_("Delete. vsa_id=%(vsa_id)s, id=%(id)s"), locals())
@@ -395,6 +407,7 @@ class VsaVolumeDriveController(volumes.VolumeController):
def show(self, req, vsa_id, id):
"""Return data about the given volume."""
context = req.environ['nova.context']
authorize(context)
LOG.audit(_("Show. vsa_id=%(vsa_id)s, id=%(id)s"), locals())

View File

@@ -32,9 +32,8 @@ from nova.volume import volume_types
LOG = logging.getLogger("nova.api.openstack.compute.contrib.volumes")
FLAGS = flags.FLAGS
authorize = extensions.extension_authorizer('compute', 'volumes')
def _translate_volume_detail_view(context, vol):
@@ -130,6 +129,7 @@ class VolumeController(object):
def show(self, req, id):
"""Return data about the given volume."""
context = req.environ['nova.context']
authorize(context)
try:
vol = self.volume_api.get(context, id)
@@ -141,6 +141,7 @@ class VolumeController(object):
def delete(self, req, id):
"""Delete a volume."""
context = req.environ['nova.context']
authorize(context)
LOG.audit(_("Delete volume with id: %s"), id, context=context)
@@ -164,6 +165,7 @@ class VolumeController(object):
def _items(self, req, entity_maker):
"""Returns a list of volumes, transformed through entity_maker."""
context = req.environ['nova.context']
authorize(context)
volumes = self.volume_api.get_all(context)
limited_list = common.limited(volumes, req)
@@ -174,6 +176,7 @@ class VolumeController(object):
def create(self, req, body):
"""Creates a new volume."""
context = req.environ['nova.context']
authorize(context)
if not body:
raise exc.HTTPUnprocessableEntity()
@@ -289,6 +292,7 @@ class VolumeAttachmentController(object):
def show(self, req, server_id, id):
"""Return data about the given volume attachment."""
context = req.environ['nova.context']
authorize(context)
volume_id = id
try:
@@ -309,6 +313,7 @@ class VolumeAttachmentController(object):
def create(self, req, server_id, body):
"""Attach a volume to an instance."""
context = req.environ['nova.context']
authorize(context)
if not body:
raise exc.HTTPUnprocessableEntity()
@@ -350,6 +355,7 @@ class VolumeAttachmentController(object):
def delete(self, req, server_id, id):
"""Detach a volume from an instance."""
context = req.environ['nova.context']
authorize(context)
volume_id = id
LOG.audit(_("Detach volume %s"), volume_id, context=context)
@@ -372,6 +378,7 @@ class VolumeAttachmentController(object):
def _items(self, req, server_id, entity_maker):
"""Returns a list of attachments, transformed through entity_maker."""
context = req.environ['nova.context']
authorize(context)
try:
instance = self.compute_api.get(context, server_id)
@@ -452,6 +459,7 @@ class SnapshotController(object):
def show(self, req, id):
"""Return data about the given snapshot."""
context = req.environ['nova.context']
authorize(context)
try:
vol = self.volume_api.get_snapshot(context, id)
@@ -463,6 +471,7 @@ class SnapshotController(object):
def delete(self, req, id):
"""Delete a snapshot."""
context = req.environ['nova.context']
authorize(context)
LOG.audit(_("Delete snapshot with id: %s"), id, context=context)
@@ -485,6 +494,7 @@ class SnapshotController(object):
def _items(self, req, entity_maker):
"""Returns a list of snapshots, transformed through entity_maker."""
context = req.environ['nova.context']
authorize(context)
snapshots = self.volume_api.get_all_snapshots(context)
limited_list = common.limited(snapshots, req)
@@ -495,6 +505,7 @@ class SnapshotController(object):
def create(self, req, body):
"""Creates a new snapshot."""
context = req.environ['nova.context']
authorize(context)
if not body:
return exc.HTTPUnprocessableEntity()

View File

@@ -27,6 +27,9 @@ from nova import exception
from nova.volume import volume_types
authorize = extensions.extension_authorizer('compute', 'volumetypes')
def make_voltype(elem):
elem.set('id')
elem.set('name')
@@ -57,12 +60,14 @@ class VolumeTypesController(object):
def index(self, req):
""" Returns the list of volume types """
context = req.environ['nova.context']
authorize(context)
return volume_types.get_all_types(context)
@wsgi.serializers(xml=VolumeTypeTemplate)
def create(self, req, body):
"""Creates a new volume type."""
context = req.environ['nova.context']
authorize(context)
if not body or body == "":
raise exc.HTTPUnprocessableEntity()
@@ -91,6 +96,7 @@ class VolumeTypesController(object):
def show(self, req, id):
""" Return a single volume type item """
context = req.environ['nova.context']
authorize(context)
try:
vol_type = volume_types.get_volume_type(context, id)
@@ -102,6 +108,7 @@ class VolumeTypesController(object):
def delete(self, req, id):
""" Deletes an existing volume type """
context = req.environ['nova.context']
authorize(context)
try:
vol_type = volume_types.get_volume_type(context, id)
@@ -155,12 +162,14 @@ class VolumeTypeExtraSpecsController(object):
def index(self, req, vol_type_id):
""" Returns the list of extra specs for a given volume type """
context = req.environ['nova.context']
authorize(context)
return self._get_extra_specs(context, vol_type_id)
@wsgi.serializers(xml=VolumeTypeExtraSpecsTemplate)
def create(self, req, vol_type_id, body):
self._check_body(body)
context = req.environ['nova.context']
authorize(context)
self._check_body(body)
specs = body.get('extra_specs')
try:
db.volume_type_extra_specs_update_or_create(context,
@@ -172,8 +181,9 @@ class VolumeTypeExtraSpecsController(object):
@wsgi.serializers(xml=VolumeTypeExtraSpecTemplate)
def update(self, req, vol_type_id, id, body):
self._check_body(body)
context = req.environ['nova.context']
authorize(context)
self._check_body(body)
if not id in body:
expl = _('Request body and URI mismatch')
raise exc.HTTPBadRequest(explanation=expl)
@@ -193,6 +203,7 @@ class VolumeTypeExtraSpecsController(object):
def show(self, req, vol_type_id, id):
""" Return a single extra spec item """
context = req.environ['nova.context']
authorize(context)
specs = self._get_extra_specs(context, vol_type_id)
if id in specs['extra_specs']:
return {id: specs['extra_specs'][id]}
@@ -202,6 +213,7 @@ class VolumeTypeExtraSpecsController(object):
def delete(self, req, vol_type_id, id):
""" Deletes an existing extra spec """
context = req.environ['nova.context']
authorize(context)
db.volume_type_extra_specs_delete(context, vol_type_id, id)
def _handle_quota_error(self, error):

View File

@@ -34,6 +34,7 @@ import nova.scheduler.api
LOG = logging.getLogger("nova.api.openstack.compute.contrib.zones")
FLAGS = flags.FLAGS
authorize = extensions.extension_authorizer('compute', 'zones')
class CapabilitySelector(object):
@@ -117,6 +118,7 @@ class Controller(object):
@wsgi.serializers(xml=ZonesTemplate)
def index(self, req):
"""Return all zones in brief"""
authorize(req.environ['nova.context'])
# Ask the ZoneManager in the Scheduler for most recent data,
# or fall-back to the database ...
items = nova.scheduler.api.get_zone_list(req.environ['nova.context'])
@@ -133,6 +135,7 @@ class Controller(object):
def info(self, req):
"""Return name and capabilities for this zone."""
context = req.environ['nova.context']
authorize(context)
zone_capabs = nova.scheduler.api.get_zone_capabilities(context)
# NOTE(comstud): This should probably return, instead:
# {'zone': {'name': FLAGS.zone_name,
@@ -143,13 +146,15 @@ class Controller(object):
@wsgi.serializers(xml=ZoneTemplate)
def show(self, req, id):
"""Return data about the given zone id"""
zone_id = int(id)
context = req.environ['nova.context']
authorize(context)
zone_id = int(id)
zone = nova.scheduler.api.zone_get(context, zone_id)
return dict(zone=_scrub_zone(zone))
def delete(self, req, id):
"""Delete a child zone entry."""
authorize(req.environ['nova.context'])
zone_id = int(id)
nova.scheduler.api.zone_delete(req.environ['nova.context'], zone_id)
return {}
@@ -159,6 +164,7 @@ class Controller(object):
def create(self, req, body):
"""Create a child zone entry."""
context = req.environ['nova.context']
authorize(context)
zone = nova.scheduler.api.zone_create(context, body["zone"])
return dict(zone=_scrub_zone(zone))
@@ -166,6 +172,7 @@ class Controller(object):
def update(self, req, id, body):
"""Update a child zone entry."""
context = req.environ['nova.context']
authorize(context)
zone_id = int(id)
zone = nova.scheduler.api.zone_update(context, zone_id, body["zone"])
return dict(zone=_scrub_zone(zone))
@@ -175,9 +182,10 @@ class Controller(object):
def select(self, req, body):
"""Returns a weighted list of costs to create instances
of desired capabilities."""
ctx = req.environ['nova.context']
context = req.environ['nova.context']
authorize(context)
specs = json.loads(body)
build_plan = nova.scheduler.api.select(ctx, specs=specs)
build_plan = nova.scheduler.api.select(context, specs=specs)
cooked = self._scrub_build_plan(build_plan)
return {"weights": cooked}
@@ -205,7 +213,6 @@ class Zones(extensions.ExtensionDescriptor):
alias = "os-zones"
namespace = "http://docs.openstack.org/compute/ext/zones/api/v1.1"
updated = "2011-09-21T00:00:00+00:00"
admin_only = True
def get_resources(self):
#NOTE(bcwaldon): This resource should be prefixed with 'os-'

View File

@@ -1170,7 +1170,7 @@ def create_resource():
def remove_invalid_options(context, search_options, allowed_search_options):
"""Remove search options that are not valid for non-admin API/context"""
if FLAGS.allow_admin_api and context.is_admin:
if context.is_admin:
# Allow all options
return
# Otherwise, strip out all unknown options

View File

@@ -16,8 +16,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
import os
import routes
import webob.dec
import webob.exc
@@ -27,13 +29,12 @@ from nova.api.openstack import xmlutil
from nova import exception
from nova import flags
from nova import log as logging
import nova.policy
from nova import utils
from nova import wsgi as base_wsgi
LOG = logging.getLogger('nova.api.openstack.extensions')
FLAGS = flags.FLAGS
@@ -61,10 +62,6 @@ class ExtensionDescriptor(object):
# '2011-01-22T13:25:27-06:00'
updated = None
# This attribute causes the extension to load only when
# the admin api is enabled
admin_only = False
def __init__(self, ext_mgr):
"""Register extension with the extension manager."""
@@ -246,15 +243,10 @@ class ExtensionManager(object):
' '.join(extension.__doc__.strip().split()))
LOG.debug(_('Ext namespace: %s'), extension.namespace)
LOG.debug(_('Ext updated: %s'), extension.updated)
LOG.debug(_('Ext admin_only: %s'), extension.admin_only)
except AttributeError as ex:
LOG.exception(_("Exception loading extension: %s"), unicode(ex))
return False
# Don't load admin api extensions if the admin api isn't enabled
if not FLAGS.allow_admin_api and extension.admin_only:
return False
return True
def load_extension(self, ext_factory):
@@ -384,3 +376,22 @@ def load_standard_extensions(ext_mgr, logger, path, package):
# Update the list of directories we'll explore...
dirnames[:] = subdirs
def extension_authorizer(api_name, extension_name):
def authorize(context):
action = '%s_extension:%s' % (api_name, extension_name)
nova.policy.enforce(context, action, {})
return authorize
def soft_extension_authorizer(api_name, extension_name):
hard_authorize = extension_authorizer(api_name, extension_name)
def authorize(context):
try:
hard_authorize(context)
return True
except exception.NotAuthorized:
return False
return authorize

View File

@@ -29,18 +29,12 @@ 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.flags(verbose=True, allow_admin_api=True)
self.flags(verbose=True)
self.stubs.Set(accounts.Controller, '__init__',
fake_init)
self.stubs.Set(accounts.Controller, '_check_admin',
fake_admin_check)
fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthDatabase.data = {}
fakes.stub_out_networking(self.stubs)

View File

@@ -79,7 +79,6 @@ class AdminActionsTest(test.TestCase):
super(AdminActionsTest, self).setUp()
self.stubs.Set(compute.API, 'get', fake_compute_api_get)
self.UUID = utils.gen_uuid()
self.flags(allow_admin_api=True)
for _method in self._methods:
self.stubs.Set(compute.API, _method, fake_compute_api)
@@ -122,8 +121,9 @@ class CreateBackupTests(test.TestCase):
self.stubs.Set(compute.API, 'get', fake_compute_api_get)
self.backup_stubs = fakes.stub_out_compute_api_backup(self.stubs)
self.flags(allow_admin_api=True)
self.app = compute_api.APIRouter()
router = compute_api.APIRouter()
ext_middleware = extensions.ExtensionMiddleware(router)
self.app = wsgi.LazySerializationMiddleware(ext_middleware)
self.uuid = utils.gen_uuid()

View File

@@ -107,7 +107,6 @@ class CloudpipeTest(test.TestCase):
def setUp(self):
super(CloudpipeTest, self).setUp()
self.flags(allow_admin_api=True)
self.app = fakes.wsgi_app()
inner_app = compute.APIRouter()
self.context = context.RequestContext('fake', 'fake', is_admin=True)

View File

@@ -18,6 +18,7 @@
import webob
from nova.api.openstack.compute.contrib import deferred_delete
import nova.context
from nova import compute
from nova import exception
from nova import test
@@ -34,7 +35,7 @@ class DeferredDeleteExtensionTest(test.TestCase):
self.extension = deferred_delete.DeferredDeleteController()
self.fake_input_dict = {}
self.fake_uuid = 'fake_uuid'
self.fake_context = 'fake_context'
self.fake_context = nova.context.RequestContext('fake', 'fake')
self.fake_req = FakeRequest(self.fake_context)
def test_force_delete(self):

View File

@@ -41,7 +41,6 @@ class ExtendedStatusTest(test.TestCase):
self.uuid = '70f6db34-de8d-4fbd-aafb-4065bdfa6114'
self.url = '/v2/fake/servers/%s' % self.uuid
fakes.stub_out_nw_api(self.stubs)
self.flags(allow_admin_api=True)
self.stubs.Set(compute.api.API, 'routing_get', fake_compute_get)
def _make_request(self):

View File

@@ -94,17 +94,14 @@ class HostTestCase(test.TestCase):
self.assertEqual(result_c2["status"], "disabled")
def test_host_startup(self):
self.flags(allow_admin_api=True)
result = self.controller.startup(self.req, "host_c1")
self.assertEqual(result["power_action"], "startup")
def test_host_shutdown(self):
self.flags(allow_admin_api=True)
result = self.controller.shutdown(self.req, "host_c1")
self.assertEqual(result["power_action"], "shutdown")
def test_host_reboot(self):
self.flags(allow_admin_api=True)
result = self.controller.reboot(self.req, "host_c1")
self.assertEqual(result["power_action"], "reboot")

View File

@@ -92,7 +92,6 @@ class NetworksTest(test.TestCase):
def setUp(self):
super(NetworksTest, self).setUp()
self.flags(allow_admin_api=True)
self.fake_network_api = FakeNetworkAPI()
self.controller = networks.NetworkController(self.fake_network_api)
fakes.stub_out_networking(self.stubs)

View File

@@ -47,7 +47,6 @@ class ServerActionsTest(test.TestCase):
def setUp(self):
super(ServerActionsTest, self).setUp()
self.flags(allow_admin_api=True)
self.flags(verbose=True)
self.stubs.Set(nova.compute.API, 'get_actions', fake_get_actions)
self.stubs.Set(nova.compute.API, 'get', fake_instance_get)

View File

@@ -40,7 +40,6 @@ class ServerDiagnosticsTest(test.TestCase):
def setUp(self):
super(ServerDiagnosticsTest, self).setUp()
self.flags(allow_admin_api=True)
self.flags(verbose=True)
self.stubs.Set(nova.compute.API, 'get_diagnostics',
fake_get_diagnostics)

View File

@@ -88,7 +88,6 @@ class SimpleTenantUsageTest(test.TestCase):
self.alt_user_context = context.RequestContext('fakeadmin_0',
'faketenant_1',
is_admin=False)
FLAGS.allow_admin_api = True
def test_verify_index(self):
req = webob.Request.blank(

View File

@@ -26,18 +26,12 @@ 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.flags(verbose=True, allow_admin_api=True)
self.flags(verbose=True)
self.stubs.Set(users.Controller, '__init__',
fake_init)
self.stubs.Set(users.Controller, '_check_admin',
fake_admin_check)
fakes.FakeAuthManager.clear_fakes()
fakes.FakeAuthManager.projects = dict(testacct=Project('testacct',
'testacct',

View File

@@ -95,7 +95,6 @@ def zone_select(context, specs):
class ZonesTest(test.TestCase):
def setUp(self):
super(ZonesTest, self).setUp()
self.flags(verbose=True, allow_admin_api=True)
fakes.stub_out_networking(self.stubs)
fakes.stub_out_rate_limiting(self.stubs)

View File

@@ -150,7 +150,6 @@ class ExtensionControllerTest(ExtensionTestCase):
def setUp(self):
super(ExtensionControllerTest, self).setUp()
self.flags(allow_admin_api=True)
self.ext_list = [
"Accounts",
"AdminActions",
@@ -355,19 +354,6 @@ class InvalidExtension(object):
alias = "THIRD"
class AdminExtension(base_extensions.ExtensionDescriptor):
"""Admin-only extension"""
name = "Admin Ext"
alias = "ADMIN"
namespace = "http://www.example.com/"
updated = "2011-01-22T13:25:27-06:00"
admin_only = True
def __init__(self, *args, **kwargs):
pass
class ExtensionManagerTest(ExtensionTestCase):
response_body = "Try to say this Mr. Knox, sir..."
@@ -388,22 +374,6 @@ class ExtensionManagerTest(ExtensionTestCase):
self.assertTrue('FOXNSOX' in ext_mgr.extensions)
self.assertTrue('THIRD' not in ext_mgr.extensions)
def test_admin_extensions(self):
self.flags(allow_admin_api=True)
app = compute.APIRouter()
ext_mgr = compute_extensions.ExtensionManager()
ext_mgr.register(AdminExtension())
self.assertTrue('FOXNSOX' in ext_mgr.extensions)
self.assertTrue('ADMIN' in ext_mgr.extensions)
def test_admin_extensions_no_admin_api(self):
self.flags(allow_admin_api=False)
app = compute.APIRouter()
ext_mgr = compute_extensions.ExtensionManager()
ext_mgr.register(AdminExtension())
self.assertTrue('FOXNSOX' in ext_mgr.extensions)
self.assertTrue('ADMIN' not in ext_mgr.extensions)
class ActionExtensionTest(ExtensionTestCase):

View File

@@ -861,7 +861,6 @@ class ServersControllerTest(test.TestCase):
return [fakes.stub_instance(100, uuid=server_uuid)]
self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
self.flags(allow_admin_api=False)
req = fakes.HTTPRequest.blank('/v2/fake/servers?image=12345')
servers = self.controller.index(req)['servers']
@@ -878,7 +877,6 @@ class ServersControllerTest(test.TestCase):
self.stubs.Set(nova.db, 'instance_get_all_by_filters',
fake_get_all)
self.flags(allow_admin_api=True)
req = fakes.HTTPRequest.blank('/v2/fake/servers?tenant_id=fake',
use_admin_context=True)
@@ -897,7 +895,6 @@ class ServersControllerTest(test.TestCase):
return [fakes.stub_instance(100, uuid=server_uuid)]
self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
self.flags(allow_admin_api=False)
req = fakes.HTTPRequest.blank('/v2/fake/servers?flavor=12345')
servers = self.controller.index(req)['servers']
@@ -915,7 +912,6 @@ class ServersControllerTest(test.TestCase):
return [fakes.stub_instance(100, uuid=server_uuid)]
self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
self.flags(allow_admin_api=False)
req = fakes.HTTPRequest.blank('/v2/fake/servers?status=active')
servers = self.controller.index(req)['servers']
@@ -925,8 +921,8 @@ class ServersControllerTest(test.TestCase):
def test_get_servers_invalid_status(self):
"""Test getting servers by invalid status"""
self.flags(allow_admin_api=False)
req = fakes.HTTPRequest.blank('/v2/fake/servers?status=unknown')
req = fakes.HTTPRequest.blank('/v2/fake/servers?status=unknown',
use_admin_context=False)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req)
def test_get_servers_allows_name(self):
@@ -939,7 +935,6 @@ class ServersControllerTest(test.TestCase):
return [fakes.stub_instance(100, uuid=server_uuid)]
self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
self.flags(allow_admin_api=False)
req = fakes.HTTPRequest.blank('/v2/fake/servers?name=whee.*')
servers = self.controller.index(req)['servers']
@@ -972,47 +967,11 @@ class ServersControllerTest(test.TestCase):
req = fakes.HTTPRequest.blank('/v2/fake/servers?%s' % params)
self.assertRaises(webob.exc.HTTPBadRequest, self.controller.index, req)
def test_get_servers_unknown_or_admin_options1(self):
"""Test getting servers by admin-only or unknown options.
This tests when admin_api is off. Make sure the admin and
unknown options are stripped before they get to
compute_api.get_all()
def test_get_servers_admin_filters_as_user(self):
"""Test getting servers by admin-only or unknown options when
context is not admin. Make sure the admin and unknown options
are stripped before they get to compute_api.get_all()
"""
self.flags(allow_admin_api=False)
server_uuid = str(utils.gen_uuid())
def fake_get_all(compute_self, context, search_opts=None):
self.assertNotEqual(search_opts, None)
# Allowed by user
self.assertTrue('name' in search_opts)
self.assertTrue('status' in search_opts)
# Allowed only by admins with admin API on
self.assertFalse('ip' in search_opts)
self.assertFalse('unknown_option' in search_opts)
return [fakes.stub_instance(100, uuid=server_uuid)]
self.stubs.Set(nova.compute.API, 'get_all', fake_get_all)
query_str = "name=foo&ip=10.*&status=active&unknown_option=meow"
req = fakes.HTTPRequest.blank('/v2/fake/servers?%s' % query_str,
use_admin_context=True)
res = self.controller.index(req)
servers = res['servers']
self.assertEqual(len(servers), 1)
self.assertEqual(servers[0]['id'], server_uuid)
def test_get_servers_unknown_or_admin_options2(self):
"""Test getting servers by admin-only or unknown options.
This tests when admin_api is on, but context is a user.
Make sure the admin and unknown options are stripped before
they get to compute_api.get_all()
"""
self.flags(allow_admin_api=True)
server_uuid = str(utils.gen_uuid())
def fake_get_all(compute_self, context, search_opts=None):
@@ -1035,14 +994,10 @@ class ServersControllerTest(test.TestCase):
self.assertEqual(len(servers), 1)
self.assertEqual(servers[0]['id'], server_uuid)
def test_get_servers_unknown_or_admin_options3(self):
"""Test getting servers by admin-only or unknown options.
This tests when admin_api is on and context is admin.
All options should be passed through to compute_api.get_all()
def test_get_servers_admin_options_as_admin(self):
"""Test getting servers by admin-only or unknown options when
context is admin. All options should be passed
"""
self.flags(allow_admin_api=True)
server_uuid = str(utils.gen_uuid())
def fake_get_all(compute_self, context, search_opts=None):
@@ -1069,8 +1024,6 @@ class ServersControllerTest(test.TestCase):
"""Test getting servers by ip with admin_api enabled and
admin context
"""
self.flags(allow_admin_api=True)
server_uuid = str(utils.gen_uuid())
def fake_get_all(compute_self, context, search_opts=None):
@@ -1092,8 +1045,6 @@ class ServersControllerTest(test.TestCase):
"""Test getting servers by ip6 with admin_api enabled and
admin context
"""
self.flags(allow_admin_api=True)
server_uuid = str(utils.gen_uuid())
def fake_get_all(compute_self, context, search_opts=None):

View File

@@ -69,6 +69,38 @@
"compute:restore": [],
"compute_extension:accounts": [],
"compute_extension:admin_actions": [],
"compute_extension:cloudpipe": [],
"compute_extension:console_output": [],
"compute_extension:consoles": [],
"compute_extension:createserverext": [],
"compute_extension:deferred_delete": [],
"compute_extension:disk_config": [],
"compute_extension:extended_status": [],
"compute_extension:flavorextraspecs": [],
"compute_extension:floating_ip_dns": [],
"compute_extension:floating_ip_pools": [],
"compute_extension:floating_ips": [],
"compute_extension:hosts": [],
"compute_extension:keypairs": [],
"compute_extension:multinic": [],
"compute_extension:networks": [],
"compute_extension:quotas": [],
"compute_extension:rescue": [],
"compute_extension:security_groups": [],
"compute_extension:server_action_list": [],
"compute_extension:server_diagnostics": [],
"compute_extension:simple_tenant_usage": [],
"compute_extension:users": [],
"compute_extension:virtual_interfaces": [],
"compute_extension:virtual_storage_arrays": [],
"compute_extension:volumes": [],
"compute_extension:volumetypes": [],
"compute_extension:zones": [],
"volume:create": [],
"volume:get": [],
"volume:get_all": [],