Add policy checks to Compute.API
* Second step of blueprint interim-nova-authz-service * Adds policy.json to define policy * Add nova.policy.wrap_enforce decorator * wrap majority of compute api functions with wrap_enforce Change-Id: If6702873db3249921f931a42e889ee7d0338e4b8
This commit is contained in:
parent
e0680250c0
commit
ace0252d75
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"admin_or_owner": [["role:admin"], ["project_id:%(project_id)s"]],
|
||||||
|
|
||||||
|
|
||||||
|
"compute:create": [["rule:admin_or_owner"]],
|
||||||
|
"compute:create:attach_network": [["rule:admin_or_owner"]],
|
||||||
|
"compute:create:attach_volume": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:get": [["rule:admin_or_owner"]],
|
||||||
|
"compute:get_all" :[],
|
||||||
|
|
||||||
|
"compute:update": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:get_instance_metadata": [["rule:admin_or_owner"]],
|
||||||
|
"compute:update_instance_metadata": [["rule:admin_or_owner"]],
|
||||||
|
"compute:delete_instance_metadata": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:get_instance_faults": [["rule:admin_or_owner"]],
|
||||||
|
"compute:get_actions": [["rule:admin_or_owner"]],
|
||||||
|
"compute:get_diagnostics": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:get_lock": [["rule:admin_or_owner"]],
|
||||||
|
"compute:lock": [["rule:admin_or_owner"]],
|
||||||
|
"compute:unlock": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:get_ajax_console": [["rule:admin_or_owner"]],
|
||||||
|
"compute:get_vnc_console": [["rule:admin_or_owner"]],
|
||||||
|
"compute:get_console_output": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:associate_floating_ip": [["rule:admin_or_owner"]],
|
||||||
|
"compute:reset_network": [["rule:admin_or_owner"]],
|
||||||
|
"compute:inject_network_info": [["rule:admin_or_owner"]],
|
||||||
|
"compute:add_fixed_ip": [["rule:admin_or_owner"]],
|
||||||
|
"compute:remove_fixed_ip": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:attach_volume": [["rule:admin_or_owner"]],
|
||||||
|
"compute:detach_volume": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:inject_file": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:set_admin_password": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:rescue": [["rule:admin_or_owner"]],
|
||||||
|
"compute:unrescue": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:suspend": [["rule:admin_or_owner"]],
|
||||||
|
"compute:resume": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:pause": [["rule:admin_or_owner"]],
|
||||||
|
"compute:unpause": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:start": [["rule:admin_or_owner"]],
|
||||||
|
"compute:stop": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:resize": [["rule:admin_or_owner"]],
|
||||||
|
"compute:confirm_resize": [["rule:admin_or_owner"]],
|
||||||
|
"compute:revert_resize": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:rebuild": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:reboot": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:snapshot": [["rule:admin_or_owner"]],
|
||||||
|
"compute:backup": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:add_security_group": [["rule:admin_or_owner"]],
|
||||||
|
"compute:remove_security_group": [["rule:admin_or_owner"]],
|
||||||
|
|
||||||
|
"compute:delete": [["rule:admin_or_owner"]],
|
||||||
|
"compute:soft_delete": [["rule:admin_or_owner"]],
|
||||||
|
"compute:force_delete": [["rule:admin_or_owner"]],
|
||||||
|
"compute:restore": [["rule:admin_or_owner"]]
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ import urllib
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
|
|
||||||
class NotAllowed(Exception):
|
class NotAuthorized(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -91,14 +91,14 @@ def enforce(match_list, target_dict, credentials_dict):
|
||||||
Credentials dicts contain as much information as we can about the user
|
Credentials dicts contain as much information as we can about the user
|
||||||
performing the action.
|
performing the action.
|
||||||
|
|
||||||
:raises NotAllowed if the check fails
|
:raises NotAuthorized if the check fails
|
||||||
|
|
||||||
"""
|
"""
|
||||||
global _BRAIN
|
global _BRAIN
|
||||||
if not _BRAIN:
|
if not _BRAIN:
|
||||||
_BRAIN = Brain()
|
_BRAIN = Brain()
|
||||||
if not _BRAIN.check(match_list, target_dict, credentials_dict):
|
if not _BRAIN.check(match_list, target_dict, credentials_dict):
|
||||||
raise NotAllowed()
|
raise NotAuthorized()
|
||||||
|
|
||||||
|
|
||||||
class Brain(object):
|
class Brain(object):
|
||||||
|
|
|
@ -37,6 +37,7 @@ from nova import flags
|
||||||
import nova.image
|
import nova.image
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
from nova import network
|
from nova import network
|
||||||
|
from nova import policy
|
||||||
from nova import quota
|
from nova import quota
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
from nova.scheduler import api as scheduler_api
|
from nova.scheduler import api as scheduler_api
|
||||||
|
@ -88,6 +89,20 @@ def check_instance_state(vm_state=None, task_state=None):
|
||||||
return outer
|
return outer
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_check_policy(func):
|
||||||
|
"""Check corresponding policy prior of wrapped method to execution"""
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapped(self, context, target, *args, **kwargs):
|
||||||
|
check_policy(context, func.__name__, target)
|
||||||
|
return func(self, context, target, *args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
def check_policy(context, action, target):
|
||||||
|
_action = 'compute:%s' % action
|
||||||
|
nova.policy.enforce(context, _action, target)
|
||||||
|
|
||||||
|
|
||||||
class API(base.Base):
|
class API(base.Base):
|
||||||
"""API for interacting with the compute manager."""
|
"""API for interacting with the compute manager."""
|
||||||
|
|
||||||
|
@ -426,6 +441,8 @@ class API(base.Base):
|
||||||
self.db.block_device_mapping_update_or_create(elevated_context,
|
self.db.block_device_mapping_update_or_create(elevated_context,
|
||||||
values)
|
values)
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): No policy check since this is only used by scheduler and
|
||||||
|
# the compute api. That should probably be cleaned up, though.
|
||||||
def create_db_entry_for_new_instance(self, context, instance_type, image,
|
def create_db_entry_for_new_instance(self, context, instance_type, image,
|
||||||
base_options, security_group, block_device_mapping, num=1):
|
base_options, security_group, block_device_mapping, num=1):
|
||||||
"""Create an entry in the DB for this new instance,
|
"""Create an entry in the DB for this new instance,
|
||||||
|
@ -552,6 +569,16 @@ class API(base.Base):
|
||||||
could be 'None' or a list of instance dicts depending on if
|
could be 'None' or a list of instance dicts depending on if
|
||||||
we waited for information from the scheduler or not.
|
we waited for information from the scheduler or not.
|
||||||
"""
|
"""
|
||||||
|
target = {'project_id': context.project_id,
|
||||||
|
'user_id': context.user_id,
|
||||||
|
'availability_zone': availability_zone}
|
||||||
|
check_policy(context, 'create', target)
|
||||||
|
|
||||||
|
if requested_networks:
|
||||||
|
check_policy(context, 'create:attach_network', target)
|
||||||
|
|
||||||
|
if block_device_mapping:
|
||||||
|
check_policy(context, 'create:attach_volume', target)
|
||||||
|
|
||||||
# We can create the DB entry for the instance here if we're
|
# We can create the DB entry for the instance here if we're
|
||||||
# only going to create 1 instance and we're in a single
|
# only going to create 1 instance and we're in a single
|
||||||
|
@ -588,16 +615,6 @@ class API(base.Base):
|
||||||
|
|
||||||
return (inst_ret_list, reservation_id)
|
return (inst_ret_list, reservation_id)
|
||||||
|
|
||||||
def has_finished_migration(self, context, instance_uuid):
|
|
||||||
"""Returns true if an instance has a finished migration."""
|
|
||||||
try:
|
|
||||||
self.db.migration_get_by_instance_and_status(context,
|
|
||||||
instance_uuid,
|
|
||||||
'finished')
|
|
||||||
return True
|
|
||||||
except exception.NotFound:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def ensure_default_security_group(self, context):
|
def ensure_default_security_group(self, context):
|
||||||
"""Ensure that a context has a security group.
|
"""Ensure that a context has a security group.
|
||||||
|
|
||||||
|
@ -705,6 +722,7 @@ class API(base.Base):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def add_security_group(self, context, instance, security_group_name):
|
def add_security_group(self, context, instance, security_group_name):
|
||||||
"""Add security group to the instance"""
|
"""Add security group to the instance"""
|
||||||
security_group = self.db.security_group_get_by_name(context,
|
security_group = self.db.security_group_get_by_name(context,
|
||||||
|
@ -733,6 +751,7 @@ class API(base.Base):
|
||||||
{"method": "refresh_security_group_rules",
|
{"method": "refresh_security_group_rules",
|
||||||
"args": {"security_group_id": security_group['id']}})
|
"args": {"security_group_id": security_group['id']}})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def remove_security_group(self, context, instance, security_group_name):
|
def remove_security_group(self, context, instance, security_group_name):
|
||||||
"""Remove the security group associated with the instance"""
|
"""Remove the security group associated with the instance"""
|
||||||
security_group = self.db.security_group_get_by_name(context,
|
security_group = self.db.security_group_get_by_name(context,
|
||||||
|
@ -761,6 +780,7 @@ class API(base.Base):
|
||||||
{"method": "refresh_security_group_rules",
|
{"method": "refresh_security_group_rules",
|
||||||
"args": {"security_group_id": security_group['id']}})
|
"args": {"security_group_id": security_group['id']}})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@scheduler_api.reroute_compute("update")
|
@scheduler_api.reroute_compute("update")
|
||||||
def update(self, context, instance, **kwargs):
|
def update(self, context, instance, **kwargs):
|
||||||
"""Updates the instance in the datastore.
|
"""Updates the instance in the datastore.
|
||||||
|
@ -776,6 +796,7 @@ class API(base.Base):
|
||||||
rv = self.db.instance_update(context, instance["id"], kwargs)
|
rv = self.db.instance_update(context, instance["id"], kwargs)
|
||||||
return dict(rv.iteritems())
|
return dict(rv.iteritems())
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||||
vm_states.ERROR])
|
vm_states.ERROR])
|
||||||
@scheduler_api.reroute_compute("soft_delete")
|
@scheduler_api.reroute_compute("soft_delete")
|
||||||
|
@ -821,6 +842,7 @@ class API(base.Base):
|
||||||
# NOTE(jerdfelt): The API implies that only ACTIVE and ERROR are
|
# NOTE(jerdfelt): The API implies that only ACTIVE and ERROR are
|
||||||
# allowed but the EC2 API appears to allow from RESCUED and STOPPED
|
# allowed but the EC2 API appears to allow from RESCUED and STOPPED
|
||||||
# too
|
# too
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||||
vm_states.ERROR, vm_states.RESCUED,
|
vm_states.ERROR, vm_states.RESCUED,
|
||||||
vm_states.STOPPED])
|
vm_states.STOPPED])
|
||||||
|
@ -834,6 +856,7 @@ class API(base.Base):
|
||||||
|
|
||||||
self._delete(context, instance)
|
self._delete(context, instance)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
|
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
|
||||||
@scheduler_api.reroute_compute("restore")
|
@scheduler_api.reroute_compute("restore")
|
||||||
def restore(self, context, instance):
|
def restore(self, context, instance):
|
||||||
|
@ -852,12 +875,14 @@ class API(base.Base):
|
||||||
self._cast_compute_message('power_on_instance', context,
|
self._cast_compute_message('power_on_instance', context,
|
||||||
instance['uuid'], host)
|
instance['uuid'], host)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
|
@check_instance_state(vm_state=[vm_states.SOFT_DELETE])
|
||||||
@scheduler_api.reroute_compute("force_delete")
|
@scheduler_api.reroute_compute("force_delete")
|
||||||
def force_delete(self, context, instance):
|
def force_delete(self, context, instance):
|
||||||
"""Force delete a previously deleted (but not reclaimed) instance."""
|
"""Force delete a previously deleted (but not reclaimed) instance."""
|
||||||
self._delete(context, instance)
|
self._delete(context, instance)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||||
vm_states.RESCUED],
|
vm_states.RESCUED],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
|
@ -884,6 +909,7 @@ class API(base.Base):
|
||||||
else:
|
else:
|
||||||
self._call_compute_message('stop_instance', context, instance)
|
self._call_compute_message('stop_instance', context, instance)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.STOPPED, vm_states.SHUTOFF])
|
@check_instance_state(vm_state=[vm_states.STOPPED, vm_states.SHUTOFF])
|
||||||
def start(self, context, instance):
|
def start(self, context, instance):
|
||||||
"""Start an instance."""
|
"""Start an instance."""
|
||||||
|
@ -915,11 +941,14 @@ class API(base.Base):
|
||||||
"args": {"topic": FLAGS.compute_topic,
|
"args": {"topic": FLAGS.compute_topic,
|
||||||
"instance_uuid": instance_uuid}})
|
"instance_uuid": instance_uuid}})
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): no policy check here since it should be rolled in to
|
||||||
|
# search_opts in get_all
|
||||||
def get_active_by_window(self, context, begin, end=None, project_id=None):
|
def get_active_by_window(self, context, begin, end=None, project_id=None):
|
||||||
"""Get instances that were continuously active over a window."""
|
"""Get instances that were continuously active over a window."""
|
||||||
return self.db.instance_get_active_by_window(context, begin, end,
|
return self.db.instance_get_active_by_window(context, begin, end,
|
||||||
project_id)
|
project_id)
|
||||||
|
|
||||||
|
#NOTE(bcwaldon): this doesn't really belong in this class
|
||||||
def get_instance_type(self, context, instance_type_id):
|
def get_instance_type(self, context, instance_type_id):
|
||||||
"""Get an instance type by instance type id."""
|
"""Get an instance type by instance type id."""
|
||||||
return instance_types.get_instance_type(instance_type_id)
|
return instance_types.get_instance_type(instance_type_id)
|
||||||
|
@ -932,6 +961,8 @@ class API(base.Base):
|
||||||
else:
|
else:
|
||||||
instance = self.db.instance_get(context, instance_id)
|
instance = self.db.instance_get(context, instance_id)
|
||||||
|
|
||||||
|
check_policy(context, 'get', instance)
|
||||||
|
|
||||||
inst = dict(instance.iteritems())
|
inst = dict(instance.iteritems())
|
||||||
# NOTE(comstud): Doesn't get returned with iteritems
|
# NOTE(comstud): Doesn't get returned with iteritems
|
||||||
inst['name'] = instance['name']
|
inst['name'] = instance['name']
|
||||||
|
@ -957,6 +988,14 @@ class API(base.Base):
|
||||||
search option that says otherwise.
|
search option that says otherwise.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#TODO(bcwaldon): determine the best argument for target here
|
||||||
|
target = {
|
||||||
|
'project_id': context.project_id,
|
||||||
|
'user_id': context.user_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
check_policy(context, "get_all", target)
|
||||||
|
|
||||||
if search_opts is None:
|
if search_opts is None:
|
||||||
search_opts = {}
|
search_opts = {}
|
||||||
|
|
||||||
|
@ -1101,6 +1140,7 @@ class API(base.Base):
|
||||||
raise exception.Error(_("Unable to find host for Instance %s")
|
raise exception.Error(_("Unable to find host for Instance %s")
|
||||||
% instance_uuid)
|
% instance_uuid)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
@scheduler_api.reroute_compute("backup")
|
@scheduler_api.reroute_compute("backup")
|
||||||
|
@ -1120,6 +1160,7 @@ class API(base.Base):
|
||||||
extra_properties=extra_properties)
|
extra_properties=extra_properties)
|
||||||
return recv_meta
|
return recv_meta
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
@scheduler_api.reroute_compute("snapshot")
|
@scheduler_api.reroute_compute("snapshot")
|
||||||
|
@ -1199,6 +1240,7 @@ class API(base.Base):
|
||||||
|
|
||||||
return min_ram, min_disk
|
return min_ram, min_disk
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||||
vm_states.RESCUED],
|
vm_states.RESCUED],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
|
@ -1216,6 +1258,7 @@ class API(base.Base):
|
||||||
instance['uuid'],
|
instance['uuid'],
|
||||||
params={'reboot_type': reboot_type})
|
params={'reboot_type': reboot_type})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
@scheduler_api.reroute_compute("rebuild")
|
@scheduler_api.reroute_compute("rebuild")
|
||||||
|
@ -1246,6 +1289,7 @@ class API(base.Base):
|
||||||
instance["uuid"],
|
instance["uuid"],
|
||||||
params=rebuild_params)
|
params=rebuild_params)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
||||||
task_state=[task_states.RESIZE_VERIFY])
|
task_state=[task_states.RESIZE_VERIFY])
|
||||||
@scheduler_api.reroute_compute("revert_resize")
|
@scheduler_api.reroute_compute("revert_resize")
|
||||||
|
@ -1272,6 +1316,7 @@ class API(base.Base):
|
||||||
self.db.migration_update(context, migration_ref['id'],
|
self.db.migration_update(context, migration_ref['id'],
|
||||||
{'status': 'reverted'})
|
{'status': 'reverted'})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
||||||
task_state=[task_states.RESIZE_VERIFY])
|
task_state=[task_states.RESIZE_VERIFY])
|
||||||
@scheduler_api.reroute_compute("confirm_resize")
|
@scheduler_api.reroute_compute("confirm_resize")
|
||||||
|
@ -1300,6 +1345,7 @@ class API(base.Base):
|
||||||
self.db.instance_update(context, instance['uuid'],
|
self.db.instance_update(context, instance['uuid'],
|
||||||
{'host': migration_ref['dest_compute'], })
|
{'host': migration_ref['dest_compute'], })
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF],
|
||||||
task_state=[None])
|
task_state=[None])
|
||||||
@scheduler_api.reroute_compute("resize")
|
@scheduler_api.reroute_compute("resize")
|
||||||
|
@ -1355,6 +1401,7 @@ class API(base.Base):
|
||||||
"instance_type_id": new_instance_type['id'],
|
"instance_type_id": new_instance_type['id'],
|
||||||
"request_spec": request_spec}})
|
"request_spec": request_spec}})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@scheduler_api.reroute_compute("add_fixed_ip")
|
@scheduler_api.reroute_compute("add_fixed_ip")
|
||||||
def add_fixed_ip(self, context, instance, network_id):
|
def add_fixed_ip(self, context, instance, network_id):
|
||||||
"""Add fixed_ip from specified network to given instance."""
|
"""Add fixed_ip from specified network to given instance."""
|
||||||
|
@ -1364,6 +1411,7 @@ class API(base.Base):
|
||||||
instance_uuid,
|
instance_uuid,
|
||||||
params=dict(network_id=network_id))
|
params=dict(network_id=network_id))
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@scheduler_api.reroute_compute("remove_fixed_ip")
|
@scheduler_api.reroute_compute("remove_fixed_ip")
|
||||||
def remove_fixed_ip(self, context, instance, address):
|
def remove_fixed_ip(self, context, instance, address):
|
||||||
"""Remove fixed_ip from specified network to given instance."""
|
"""Remove fixed_ip from specified network to given instance."""
|
||||||
|
@ -1383,6 +1431,7 @@ class API(base.Base):
|
||||||
# didn't raise so this is the correct zone
|
# didn't raise so this is the correct zone
|
||||||
self.network_api.add_network_to_project(context, project_id)
|
self.network_api.add_network_to_project(context, project_id)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||||
vm_states.RESCUED],
|
vm_states.RESCUED],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
|
@ -1396,6 +1445,7 @@ class API(base.Base):
|
||||||
task_state=task_states.PAUSING)
|
task_state=task_states.PAUSING)
|
||||||
self._cast_compute_message('pause_instance', context, instance_uuid)
|
self._cast_compute_message('pause_instance', context, instance_uuid)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.PAUSED])
|
@check_instance_state(vm_state=[vm_states.PAUSED])
|
||||||
@scheduler_api.reroute_compute("unpause")
|
@scheduler_api.reroute_compute("unpause")
|
||||||
def unpause(self, context, instance):
|
def unpause(self, context, instance):
|
||||||
|
@ -1423,6 +1473,7 @@ class API(base.Base):
|
||||||
return self._call_compute_message_for_host("host_power_action",
|
return self._call_compute_message_for_host("host_power_action",
|
||||||
context, host=host, params={"action": action})
|
context, host=host, params={"action": action})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@scheduler_api.reroute_compute("diagnostics")
|
@scheduler_api.reroute_compute("diagnostics")
|
||||||
def get_diagnostics(self, context, instance):
|
def get_diagnostics(self, context, instance):
|
||||||
"""Retrieve diagnostics for the given instance."""
|
"""Retrieve diagnostics for the given instance."""
|
||||||
|
@ -1430,10 +1481,12 @@ class API(base.Base):
|
||||||
context,
|
context,
|
||||||
instance)
|
instance)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def get_actions(self, context, instance):
|
def get_actions(self, context, instance):
|
||||||
"""Retrieve actions for the given instance."""
|
"""Retrieve actions for the given instance."""
|
||||||
return self.db.instance_get_actions(context, instance['uuid'])
|
return self.db.instance_get_actions(context, instance['uuid'])
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||||
vm_states.RESCUED],
|
vm_states.RESCUED],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
|
@ -1447,6 +1500,7 @@ class API(base.Base):
|
||||||
task_state=task_states.SUSPENDING)
|
task_state=task_states.SUSPENDING)
|
||||||
self._cast_compute_message('suspend_instance', context, instance_uuid)
|
self._cast_compute_message('suspend_instance', context, instance_uuid)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.SUSPENDED])
|
@check_instance_state(vm_state=[vm_states.SUSPENDED])
|
||||||
@scheduler_api.reroute_compute("resume")
|
@scheduler_api.reroute_compute("resume")
|
||||||
def resume(self, context, instance):
|
def resume(self, context, instance):
|
||||||
|
@ -1458,6 +1512,7 @@ class API(base.Base):
|
||||||
task_state=task_states.RESUMING)
|
task_state=task_states.RESUMING)
|
||||||
self._cast_compute_message('resume_instance', context, instance_uuid)
|
self._cast_compute_message('resume_instance', context, instance_uuid)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.SHUTOFF,
|
||||||
vm_states.STOPPED],
|
vm_states.STOPPED],
|
||||||
task_state=[None, task_states.RESIZE_VERIFY])
|
task_state=[None, task_states.RESIZE_VERIFY])
|
||||||
|
@ -1476,6 +1531,7 @@ class API(base.Base):
|
||||||
instance['uuid'],
|
instance['uuid'],
|
||||||
params=rescue_params)
|
params=rescue_params)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@check_instance_state(vm_state=[vm_states.RESCUED])
|
@check_instance_state(vm_state=[vm_states.RESCUED])
|
||||||
@scheduler_api.reroute_compute("unrescue")
|
@scheduler_api.reroute_compute("unrescue")
|
||||||
def unrescue(self, context, instance):
|
def unrescue(self, context, instance):
|
||||||
|
@ -1487,6 +1543,7 @@ class API(base.Base):
|
||||||
self._cast_compute_message('unrescue_instance', context,
|
self._cast_compute_message('unrescue_instance', context,
|
||||||
instance['uuid'])
|
instance['uuid'])
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@scheduler_api.reroute_compute("set_admin_password")
|
@scheduler_api.reroute_compute("set_admin_password")
|
||||||
def set_admin_password(self, context, instance, password=None):
|
def set_admin_password(self, context, instance, password=None):
|
||||||
"""Set the root/admin password for the given instance."""
|
"""Set the root/admin password for the given instance."""
|
||||||
|
@ -1504,6 +1561,7 @@ class API(base.Base):
|
||||||
"args": {
|
"args": {
|
||||||
"instance_uuid": instance_uuid, "new_pass": password}})
|
"instance_uuid": instance_uuid, "new_pass": password}})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
@scheduler_api.reroute_compute("inject_file")
|
@scheduler_api.reroute_compute("inject_file")
|
||||||
def inject_file(self, context, instance, path, file_contents):
|
def inject_file(self, context, instance, path, file_contents):
|
||||||
"""Write a file to the given instance."""
|
"""Write a file to the given instance."""
|
||||||
|
@ -1511,6 +1569,7 @@ class API(base.Base):
|
||||||
self._cast_compute_message('inject_file', context,
|
self._cast_compute_message('inject_file', context,
|
||||||
instance['uuid'], params=params)
|
instance['uuid'], params=params)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def get_ajax_console(self, context, instance):
|
def get_ajax_console(self, context, instance):
|
||||||
"""Get a url to an AJAX Console."""
|
"""Get a url to an AJAX Console."""
|
||||||
output = self._call_compute_message('get_ajax_console',
|
output = self._call_compute_message('get_ajax_console',
|
||||||
|
@ -1523,6 +1582,7 @@ class API(base.Base):
|
||||||
return {'url': '%s/?token=%s' % (FLAGS.ajax_console_proxy_url,
|
return {'url': '%s/?token=%s' % (FLAGS.ajax_console_proxy_url,
|
||||||
output['token'])}
|
output['token'])}
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def get_vnc_console(self, context, instance):
|
def get_vnc_console(self, context, instance):
|
||||||
"""Get a url to a VNC Console."""
|
"""Get a url to a VNC Console."""
|
||||||
output = self._call_compute_message('get_vnc_console',
|
output = self._call_compute_message('get_vnc_console',
|
||||||
|
@ -1541,6 +1601,7 @@ class API(base.Base):
|
||||||
'hostignore',
|
'hostignore',
|
||||||
'portignore')}
|
'portignore')}
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def get_console_output(self, context, instance, tail_length=None):
|
def get_console_output(self, context, instance, tail_length=None):
|
||||||
"""Get console output for an an instance."""
|
"""Get console output for an an instance."""
|
||||||
return self._call_compute_message('get_console_output',
|
return self._call_compute_message('get_console_output',
|
||||||
|
@ -1548,29 +1609,35 @@ class API(base.Base):
|
||||||
instance,
|
instance,
|
||||||
{'tail_length': tail_length})
|
{'tail_length': tail_length})
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def lock(self, context, instance):
|
def lock(self, context, instance):
|
||||||
"""Lock the given instance."""
|
"""Lock the given instance."""
|
||||||
self._cast_compute_message('lock_instance', context, instance['uuid'])
|
self._cast_compute_message('lock_instance', context, instance['uuid'])
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def unlock(self, context, instance):
|
def unlock(self, context, instance):
|
||||||
"""Unlock the given instance."""
|
"""Unlock the given instance."""
|
||||||
self._cast_compute_message('unlock_instance',
|
self._cast_compute_message('unlock_instance',
|
||||||
context,
|
context,
|
||||||
instance['uuid'])
|
instance['uuid'])
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def get_lock(self, context, instance):
|
def get_lock(self, context, instance):
|
||||||
"""Return the boolean state of given instance's lock."""
|
"""Return the boolean state of given instance's lock."""
|
||||||
return self.get(context, instance['uuid'])['locked']
|
return self.get(context, instance['uuid'])['locked']
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def reset_network(self, context, instance):
|
def reset_network(self, context, instance):
|
||||||
"""Reset networking on the instance."""
|
"""Reset networking on the instance."""
|
||||||
self._cast_compute_message('reset_network', context, instance['uuid'])
|
self._cast_compute_message('reset_network', context, instance['uuid'])
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def inject_network_info(self, context, instance):
|
def inject_network_info(self, context, instance):
|
||||||
"""Inject network info for the instance."""
|
"""Inject network info for the instance."""
|
||||||
self._cast_compute_message('inject_network_info', context,
|
self._cast_compute_message('inject_network_info', context,
|
||||||
instance['uuid'])
|
instance['uuid'])
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def attach_volume(self, context, instance, volume_id, device):
|
def attach_volume(self, context, instance, volume_id, device):
|
||||||
"""Attach an existing volume to an existing instance."""
|
"""Attach an existing volume to an existing instance."""
|
||||||
if not re.match("^/dev/x{0,1}[a-z]d[a-z]+$", device):
|
if not re.match("^/dev/x{0,1}[a-z]d[a-z]+$", device):
|
||||||
|
@ -1590,6 +1657,9 @@ class API(base.Base):
|
||||||
instance = self.db.volume_get_instance(context.elevated(), volume_id)
|
instance = self.db.volume_get_instance(context.elevated(), volume_id)
|
||||||
if not instance:
|
if not instance:
|
||||||
raise exception.ApiError(_("Volume isn't attached to anything!"))
|
raise exception.ApiError(_("Volume isn't attached to anything!"))
|
||||||
|
|
||||||
|
check_policy(context, 'detach_volume', instance)
|
||||||
|
|
||||||
self.volume_api.check_detach(context, volume_id=volume_id)
|
self.volume_api.check_detach(context, volume_id=volume_id)
|
||||||
host = instance['host']
|
host = instance['host']
|
||||||
rpc.cast(context,
|
rpc.cast(context,
|
||||||
|
@ -1599,6 +1669,7 @@ class API(base.Base):
|
||||||
"volume_id": volume_id}})
|
"volume_id": volume_id}})
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def associate_floating_ip(self, context, instance, address):
|
def associate_floating_ip(self, context, instance, address):
|
||||||
"""Makes calls to network_api to associate_floating_ip.
|
"""Makes calls to network_api to associate_floating_ip.
|
||||||
|
|
||||||
|
@ -1630,15 +1701,18 @@ class API(base.Base):
|
||||||
floating_address=address,
|
floating_address=address,
|
||||||
fixed_address=fixed_ip_addrs[0])
|
fixed_address=fixed_ip_addrs[0])
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def get_instance_metadata(self, context, instance):
|
def get_instance_metadata(self, context, instance):
|
||||||
"""Get all metadata associated with an instance."""
|
"""Get all metadata associated with an instance."""
|
||||||
rv = self.db.instance_metadata_get(context, instance['id'])
|
rv = self.db.instance_metadata_get(context, instance['id'])
|
||||||
return dict(rv.iteritems())
|
return dict(rv.iteritems())
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def delete_instance_metadata(self, context, instance, key):
|
def delete_instance_metadata(self, context, instance, key):
|
||||||
"""Delete the given metadata item from an instance."""
|
"""Delete the given metadata item from an instance."""
|
||||||
self.db.instance_metadata_delete(context, instance['id'], key)
|
self.db.instance_metadata_delete(context, instance['id'], key)
|
||||||
|
|
||||||
|
@wrap_check_policy
|
||||||
def update_instance_metadata(self, context, instance,
|
def update_instance_metadata(self, context, instance,
|
||||||
metadata, delete=False):
|
metadata, delete=False):
|
||||||
"""Updates or creates instance metadata.
|
"""Updates or creates instance metadata.
|
||||||
|
@ -1660,5 +1734,9 @@ class API(base.Base):
|
||||||
|
|
||||||
def get_instance_faults(self, context, instances):
|
def get_instance_faults(self, context, instances):
|
||||||
"""Get all faults for a list of instance uuids."""
|
"""Get all faults for a list of instance uuids."""
|
||||||
|
|
||||||
|
for instance in instances:
|
||||||
|
check_policy(context, 'get_instance_faults', instance)
|
||||||
|
|
||||||
uuids = [instance['uuid'] for instance in instances]
|
uuids = [instance['uuid'] for instance in instances]
|
||||||
return self.db.instance_fault_get_by_instance_uuids(context, uuids)
|
return self.db.instance_fault_get_by_instance_uuids(context, uuids)
|
||||||
|
|
|
@ -203,7 +203,7 @@ class AdminRequired(NotAuthorized):
|
||||||
message = _("User does not have admin privileges")
|
message = _("User does not have admin privileges")
|
||||||
|
|
||||||
|
|
||||||
class PolicyNotAllowed(NotAuthorized):
|
class PolicyNotAuthorized(NotAuthorized):
|
||||||
message = _("Policy Doesn't allow %(action)s to be performed.")
|
message = _("Policy Doesn't allow %(action)s to be performed.")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,10 +17,10 @@
|
||||||
|
|
||||||
"""Policy Engine For Nova"""
|
"""Policy Engine For Nova"""
|
||||||
|
|
||||||
|
from nova.common import policy
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import utils
|
from nova import utils
|
||||||
from nova.common import policy
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
FLAGS = flags.FLAGS
|
||||||
flags.DEFINE_string('policy_file', 'policy.json',
|
flags.DEFINE_string('policy_file', 'policy.json',
|
||||||
|
@ -43,8 +43,8 @@ def init():
|
||||||
global _POLICY_CACHE
|
global _POLICY_CACHE
|
||||||
if not _POLICY_PATH:
|
if not _POLICY_PATH:
|
||||||
_POLICY_PATH = utils.find_config(FLAGS.policy_file)
|
_POLICY_PATH = utils.find_config(FLAGS.policy_file)
|
||||||
data = utils.read_cached_file(_POLICY_PATH, _POLICY_CACHE,
|
utils.read_cached_file(_POLICY_PATH, _POLICY_CACHE,
|
||||||
reload_func=_set_brain)
|
reload_func=_set_brain)
|
||||||
|
|
||||||
|
|
||||||
def _set_brain(data):
|
def _set_brain(data):
|
||||||
|
@ -74,5 +74,5 @@ def enforce(context, action, target):
|
||||||
credentials_dict = context.to_dict()
|
credentials_dict = context.to_dict()
|
||||||
try:
|
try:
|
||||||
policy.enforce(match_list, target_dict, credentials_dict)
|
policy.enforce(match_list, target_dict, credentials_dict)
|
||||||
except policy.NotAllowed:
|
except policy.NotAuthorized:
|
||||||
raise exception.PolicyNotAllowed(action=action)
|
raise exception.PolicyNotAuthorized(action=action)
|
||||||
|
|
|
@ -1,11 +1,70 @@
|
||||||
{
|
{
|
||||||
"true" : [],
|
"compute:create": [],
|
||||||
"compute:create_instance" : [],
|
"compute:create:attach_network": [],
|
||||||
"compute:attach_network" : [],
|
"compute:create:attach_volume": [],
|
||||||
"compute:attach_volume" : [],
|
|
||||||
"compute:list_instances": [],
|
"compute:get": [],
|
||||||
"compute:get_instance": [],
|
"compute:get_all" :[],
|
||||||
"network:attach_network" : [],
|
|
||||||
"volume:create_volume": [],
|
"compute:update": [],
|
||||||
"volume:attach_volume": []
|
|
||||||
|
"compute:get_instance_metadata": [],
|
||||||
|
"compute:update_instance_metadata": [],
|
||||||
|
"compute:delete_instance_metadata": [],
|
||||||
|
|
||||||
|
"compute:get_instance_faults": [],
|
||||||
|
"compute:get_actions": [],
|
||||||
|
"compute:get_diagnostics": [],
|
||||||
|
|
||||||
|
"compute:get_lock": [],
|
||||||
|
"compute:lock": [],
|
||||||
|
"compute:unlock": [],
|
||||||
|
|
||||||
|
"compute:get_ajax_console": [],
|
||||||
|
"compute:get_vnc_console": [],
|
||||||
|
"compute:get_console_output": [],
|
||||||
|
|
||||||
|
"compute:associate_floating_ip": [],
|
||||||
|
"compute:reset_network": [],
|
||||||
|
"compute:inject_network_info": [],
|
||||||
|
"compute:add_fixed_ip": [],
|
||||||
|
"compute:remove_fixed_ip": [],
|
||||||
|
|
||||||
|
"compute:attach_volume": [],
|
||||||
|
"compute:detach_volume": [],
|
||||||
|
|
||||||
|
"compute:inject_file": [],
|
||||||
|
|
||||||
|
"compute:set_admin_password": [],
|
||||||
|
|
||||||
|
"compute:rescue": [],
|
||||||
|
"compute:unrescue": [],
|
||||||
|
|
||||||
|
"compute:suspend": [],
|
||||||
|
"compute:resume": [],
|
||||||
|
|
||||||
|
"compute:pause": [],
|
||||||
|
"compute:unpause": [],
|
||||||
|
|
||||||
|
"compute:start": [],
|
||||||
|
"compute:stop": [],
|
||||||
|
|
||||||
|
"compute:resize": [],
|
||||||
|
"compute:confirm_resize": [],
|
||||||
|
"compute:revert_resize": [],
|
||||||
|
|
||||||
|
"compute:rebuild": [],
|
||||||
|
|
||||||
|
"compute:reboot": [],
|
||||||
|
|
||||||
|
"compute:snapshot": [],
|
||||||
|
"compute:backup": [],
|
||||||
|
|
||||||
|
"compute:add_security_group": [],
|
||||||
|
"compute:remove_security_group": [],
|
||||||
|
|
||||||
|
"compute:delete": [],
|
||||||
|
"compute:soft_delete": [],
|
||||||
|
"compute:force_delete": [],
|
||||||
|
"compute:restore": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,9 @@ import mox
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
import nova
|
import nova
|
||||||
|
import nova.common.policy
|
||||||
from nova import compute
|
from nova import compute
|
||||||
|
import nova.compute.api
|
||||||
from nova.compute import instance_types
|
from nova.compute import instance_types
|
||||||
from nova.compute import manager as compute_manager
|
from nova.compute import manager as compute_manager
|
||||||
from nova.compute import power_state
|
from nova.compute import power_state
|
||||||
|
@ -42,8 +44,9 @@ from nova.image import fake as fake_image
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
from nova.network.quantum import client as quantum_client
|
from nova.network.quantum import client as quantum_client
|
||||||
from nova.notifier import test_notifier
|
from nova.notifier import test_notifier
|
||||||
from nova.scheduler import driver as scheduler_driver
|
import nova.policy
|
||||||
from nova import rpc
|
from nova import rpc
|
||||||
|
from nova.scheduler import driver as scheduler_driver
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova.tests import fake_network
|
from nova.tests import fake_network
|
||||||
from nova import utils
|
from nova import utils
|
||||||
|
@ -111,7 +114,8 @@ class BaseTestCase(test.TestCase):
|
||||||
self.compute = utils.import_object(FLAGS.compute_manager)
|
self.compute = utils.import_object(FLAGS.compute_manager)
|
||||||
self.user_id = 'fake'
|
self.user_id = 'fake'
|
||||||
self.project_id = 'fake'
|
self.project_id = 'fake'
|
||||||
self.context = context.RequestContext(self.user_id, self.project_id)
|
self.context = context.RequestContext(self.user_id,
|
||||||
|
self.project_id)
|
||||||
test_notifier.NOTIFICATIONS = []
|
test_notifier.NOTIFICATIONS = []
|
||||||
self.mox = mox.Mox()
|
self.mox = mox.Mox()
|
||||||
self.total_waits = 0
|
self.total_waits = 0
|
||||||
|
@ -878,7 +882,9 @@ class ComputeTestCase(BaseTestCase):
|
||||||
instance_uuid = instance['uuid']
|
instance_uuid = instance['uuid']
|
||||||
self.compute.run_instance(self.context, instance_uuid)
|
self.compute.run_instance(self.context, instance_uuid)
|
||||||
|
|
||||||
non_admin_context = context.RequestContext(None, None, is_admin=False)
|
non_admin_context = context.RequestContext(None,
|
||||||
|
None,
|
||||||
|
is_admin=False)
|
||||||
|
|
||||||
# decorator should return False (fail) with locked nonadmin context
|
# decorator should return False (fail) with locked nonadmin context
|
||||||
self.compute.lock_instance(self.context, instance_uuid)
|
self.compute.lock_instance(self.context, instance_uuid)
|
||||||
|
@ -2815,7 +2821,7 @@ class ComputeAPITestCase(BaseTestCase):
|
||||||
def test_attach_volume_invalid(self):
|
def test_attach_volume_invalid(self):
|
||||||
self.assertRaises(exception.ApiError,
|
self.assertRaises(exception.ApiError,
|
||||||
self.compute_api.attach_volume,
|
self.compute_api.attach_volume,
|
||||||
None,
|
self.context,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
'/dev/invalid')
|
'/dev/invalid')
|
||||||
|
@ -2966,3 +2972,112 @@ class ComputeAPITestCase(BaseTestCase):
|
||||||
self.compute_api.inject_file(self.context, instance,
|
self.compute_api.inject_file(self.context, instance,
|
||||||
"/tmp/test", "File Contents")
|
"/tmp/test", "File Contents")
|
||||||
db.instance_destroy(self.context, instance['id'])
|
db.instance_destroy(self.context, instance['id'])
|
||||||
|
|
||||||
|
|
||||||
|
class ComputePolicyTestCase(BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(ComputePolicyTestCase, self).setUp()
|
||||||
|
nova.policy.reset()
|
||||||
|
nova.policy.init()
|
||||||
|
|
||||||
|
self.compute_api = compute.API()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(ComputePolicyTestCase, self).tearDown()
|
||||||
|
nova.policy.reset()
|
||||||
|
|
||||||
|
def _set_rules(self, rules):
|
||||||
|
nova.common.policy.set_brain(nova.common.policy.HttpBrain(rules))
|
||||||
|
|
||||||
|
def test_actions_are_prefixed(self):
|
||||||
|
self.mox.StubOutWithMock(nova.policy, 'enforce')
|
||||||
|
nova.policy.enforce(self.context, 'compute:reboot', {})
|
||||||
|
self.mox.ReplayAll()
|
||||||
|
nova.compute.api.check_policy(self.context, 'reboot', {})
|
||||||
|
self.mox.UnsetStubs()
|
||||||
|
self.mox.VerifyAll()
|
||||||
|
|
||||||
|
def test_wrapped_method(self):
|
||||||
|
instance = self._create_fake_instance()
|
||||||
|
self.compute.run_instance(self.context, instance['uuid'])
|
||||||
|
|
||||||
|
# force delete to fail
|
||||||
|
rules = {"compute:delete": [["false:false"]]}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
self.compute_api.delete, self.context, instance)
|
||||||
|
|
||||||
|
# reset rules to allow deletion
|
||||||
|
rules = {"compute:delete": []}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.compute_api.delete(self.context, instance)
|
||||||
|
|
||||||
|
def test_create_fail(self):
|
||||||
|
rules = {"compute:create": [["false:false"]]}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
self.compute_api.create, self.context, '1', '1')
|
||||||
|
|
||||||
|
def test_create_attach_volume_fail(self):
|
||||||
|
rules = {
|
||||||
|
"compute:create": [],
|
||||||
|
"compute:create:attach_network": [["false:false"]],
|
||||||
|
"compute:create:attach_volume": [],
|
||||||
|
}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
self.compute_api.create, self.context, '1', '1',
|
||||||
|
requested_networks='blah',
|
||||||
|
block_device_mapping='blah')
|
||||||
|
|
||||||
|
def test_create_attach_network_fail(self):
|
||||||
|
rules = {
|
||||||
|
"compute:create": [],
|
||||||
|
"compute:create:attach_network": [],
|
||||||
|
"compute:create:attach_volume": [["false:false"]],
|
||||||
|
}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
self.compute_api.create, self.context, '1', '1',
|
||||||
|
requested_networks='blah',
|
||||||
|
block_device_mapping='blah')
|
||||||
|
|
||||||
|
def test_get_fail(self):
|
||||||
|
instance = self._create_fake_instance()
|
||||||
|
|
||||||
|
rules = {
|
||||||
|
"compute:get": [["false:false"]],
|
||||||
|
}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
self.compute_api.get, self.context, instance['uuid'])
|
||||||
|
|
||||||
|
def test_get_all_fail(self):
|
||||||
|
rules = {
|
||||||
|
"compute:get_all": [["false:false"]],
|
||||||
|
}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
self.compute_api.get_all, self.context)
|
||||||
|
|
||||||
|
def test_get_instance_faults(self):
|
||||||
|
instance1 = self._create_fake_instance()
|
||||||
|
instance2 = self._create_fake_instance()
|
||||||
|
instances = [instance1, instance2]
|
||||||
|
|
||||||
|
rules = {
|
||||||
|
"compute:get_instance_faults": [["false:false"]],
|
||||||
|
}
|
||||||
|
self._set_rules(rules)
|
||||||
|
|
||||||
|
self.assertRaises(exception.PolicyNotAuthorized,
|
||||||
|
self.compute_api.get_instance_faults,
|
||||||
|
self.context, instances)
|
||||||
|
|
|
@ -53,7 +53,7 @@ class PolicyFileTestCase(test.TestCase):
|
||||||
policyfile.write("""{"example:test": ["false:false"]}""")
|
policyfile.write("""{"example:test": ["false:false"]}""")
|
||||||
# NOTE(vish): reset stored policy cache so we don't have to sleep(1)
|
# NOTE(vish): reset stored policy cache so we don't have to sleep(1)
|
||||||
policy._POLICY_CACHE = {}
|
policy._POLICY_CACHE = {}
|
||||||
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
self.context, action, self.target)
|
self.context, action, self.target)
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,12 +89,12 @@ class PolicyTestCase(test.TestCase):
|
||||||
|
|
||||||
def test_enforce_nonexistent_action_throws(self):
|
def test_enforce_nonexistent_action_throws(self):
|
||||||
action = "example:noexist"
|
action = "example:noexist"
|
||||||
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
self.context, action, self.target)
|
self.context, action, self.target)
|
||||||
|
|
||||||
def test_enforce_bad_action_throws(self):
|
def test_enforce_bad_action_throws(self):
|
||||||
action = "example:denied"
|
action = "example:denied"
|
||||||
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
self.context, action, self.target)
|
self.context, action, self.target)
|
||||||
|
|
||||||
def test_enforce_good_action(self):
|
def test_enforce_good_action(self):
|
||||||
|
@ -118,7 +118,7 @@ class PolicyTestCase(test.TestCase):
|
||||||
self.stubs.Set(urllib2, 'urlopen', fakeurlopen)
|
self.stubs.Set(urllib2, 'urlopen', fakeurlopen)
|
||||||
action = "example:get_http"
|
action = "example:get_http"
|
||||||
target = {}
|
target = {}
|
||||||
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
self.context, action, target)
|
self.context, action, target)
|
||||||
|
|
||||||
def test_templatized_enforcement(self):
|
def test_templatized_enforcement(self):
|
||||||
|
@ -126,12 +126,12 @@ class PolicyTestCase(test.TestCase):
|
||||||
target_not_mine = {'project_id': 'another'}
|
target_not_mine = {'project_id': 'another'}
|
||||||
action = "example:my_file"
|
action = "example:my_file"
|
||||||
policy.enforce(self.context, action, target_mine)
|
policy.enforce(self.context, action, target_mine)
|
||||||
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
self.context, action, target_not_mine)
|
self.context, action, target_not_mine)
|
||||||
|
|
||||||
def test_early_AND_enforcement(self):
|
def test_early_AND_enforcement(self):
|
||||||
action = "example:early_and_fail"
|
action = "example:early_and_fail"
|
||||||
self.assertRaises(exception.PolicyNotAllowed, policy.enforce,
|
self.assertRaises(exception.PolicyNotAuthorized, policy.enforce,
|
||||||
self.context, action, self.target)
|
self.context, action, self.target)
|
||||||
|
|
||||||
def test_early_OR_enforcement(self):
|
def test_early_OR_enforcement(self):
|
||||||
|
|
Loading…
Reference in New Issue