diff --git a/nova/api/openstack/compute/hypervisors.py b/nova/api/openstack/compute/hypervisors.py index 84432f7875..a6a8812416 100644 --- a/nova/api/openstack/compute/hypervisors.py +++ b/nova/api/openstack/compute/hypervisors.py @@ -122,7 +122,6 @@ class HypervisorsController(wsgi.Controller): :param links: If True, return links in the response for paging. """ context = req.environ['nova.context'] - context.can(hv_policies.BASE_POLICY_NAME) # The 2.53 microversion moves the search and servers routes into # GET /os-hypervisors and GET /os-hypervisors/detail with query @@ -221,6 +220,8 @@ class HypervisorsController(wsgi.Controller): return self._index(req) def _index(self, req, limit=None, marker=None, links=False): + context = req.environ['nova.context'] + context.can(hv_policies.BASE_POLICY_NAME % 'list') return self._get_hypervisors(req, detail=False, limit=limit, marker=marker, links=links) @@ -251,6 +252,8 @@ class HypervisorsController(wsgi.Controller): return self._detail(req) def _detail(self, req, limit=None, marker=None, links=False): + context = req.environ['nova.context'] + context.can(hv_policies.BASE_POLICY_NAME % 'list-detail') return self._get_hypervisors(req, detail=True, limit=limit, marker=marker, links=links) @@ -302,7 +305,7 @@ class HypervisorsController(wsgi.Controller): def _show(self, req, id, with_servers=False): context = req.environ['nova.context'] - context.can(hv_policies.BASE_POLICY_NAME) + context.can(hv_policies.BASE_POLICY_NAME % 'show') self._validate_id(req, id) @@ -324,7 +327,7 @@ class HypervisorsController(wsgi.Controller): @wsgi.expected_errors((400, 404, 501)) def uptime(self, req, id): context = req.environ['nova.context'] - context.can(hv_policies.BASE_POLICY_NAME) + context.can(hv_policies.BASE_POLICY_NAME % 'uptime') self._validate_id(req, id) @@ -362,7 +365,7 @@ class HypervisorsController(wsgi.Controller): index and detail methods. """ context = req.environ['nova.context'] - context.can(hv_policies.BASE_POLICY_NAME) + context.can(hv_policies.BASE_POLICY_NAME % 'search') hypervisors = self._get_compute_nodes_by_name_pattern(context, id) try: return dict(hypervisors=[ @@ -386,7 +389,7 @@ class HypervisorsController(wsgi.Controller): GET /os-hypervisors index and detail methods. """ context = req.environ['nova.context'] - context.can(hv_policies.BASE_POLICY_NAME) + context.can(hv_policies.BASE_POLICY_NAME % 'servers') compute_nodes = self._get_compute_nodes_by_name_pattern(context, id) hypervisors = [] for compute_node in compute_nodes: @@ -405,6 +408,6 @@ class HypervisorsController(wsgi.Controller): @wsgi.expected_errors(()) def statistics(self, req): context = req.environ['nova.context'] - context.can(hv_policies.BASE_POLICY_NAME) + context.can(hv_policies.BASE_POLICY_NAME % 'statistics') stats = self.host_api.compute_node_statistics(context) return dict(hypervisor_statistics=stats) diff --git a/nova/policies/hypervisors.py b/nova/policies/hypervisors.py index 2090642f83..6f52b65995 100644 --- a/nova/policies/hypervisors.py +++ b/nova/policies/hypervisors.py @@ -18,55 +18,121 @@ from oslo_policy import policy from nova.policies import base -BASE_POLICY_NAME = 'os_compute_api:os-hypervisors' +BASE_POLICY_NAME = 'os_compute_api:os-hypervisors:%s' +DEPRECATED_POLICY = policy.DeprecatedRule( + 'os_compute_api:os-hypervisors', + base.RULE_ADMIN_API, +) + +DEPRECATED_REASON = """ +Nova API policies are introducing new default roles with scope_type +capabilities. Old policies are deprecated and silently going to be ignored +in nova 23.0.0 release. +""" hypervisors_policies = [ policy.DocumentedRuleDefault( - name=BASE_POLICY_NAME, - check_str=base.RULE_ADMIN_API, - description="""Policy rule for hypervisor related APIs. - -This rule will be checked for the following APIs: - -List all hypervisors, list all hypervisors with details, show -summary statistics for all hypervisors over all compute nodes, -show details for a hypervisor, show the uptime of a hypervisor, -search hypervisor by hypervisor_hostname pattern and list all -servers on hypervisors that can match the provided -hypervisor_hostname pattern.""", + name=BASE_POLICY_NAME % 'list', + check_str=base.SYSTEM_READER, + description="List all hypervisors.", operations=[ { 'path': '/os-hypervisors', 'method': 'GET' }, + ], + scope_types=['system'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0'), + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'list-detail', + check_str=base.SYSTEM_READER, + description="List all hypervisors with details", + operations=[ { 'path': '/os-hypervisors/details', 'method': 'GET' }, + ], + scope_types=['system'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0'), + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'statistics', + check_str=base.SYSTEM_READER, + description="Show summary statistics for all hypervisors " + "over all compute nodes.", + operations=[ { 'path': '/os-hypervisors/statistics', 'method': 'GET' }, + ], + scope_types=['system'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0'), + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'show', + check_str=base.SYSTEM_READER, + description="Show details for a hypervisor.", + operations=[ { 'path': '/os-hypervisors/{hypervisor_id}', 'method': 'GET' }, + ], + scope_types=['system'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0'), + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'uptime', + check_str=base.SYSTEM_READER, + description="Show the uptime of a hypervisor.", + operations=[ { 'path': '/os-hypervisors/{hypervisor_id}/uptime', 'method': 'GET' }, + ], + scope_types=['system'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0'), + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'search', + check_str=base.SYSTEM_READER, + description="Search hypervisor by hypervisor_hostname pattern.", + operations=[ { 'path': '/os-hypervisors/{hypervisor_hostname_pattern}/search', 'method': 'GET' }, + ], + scope_types=['system'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0'), + policy.DocumentedRuleDefault( + name=BASE_POLICY_NAME % 'servers', + check_str=base.SYSTEM_READER, + description="List all servers on hypervisors that can match " + "the provided hypervisor_hostname pattern.", + operations=[ { 'path': '/os-hypervisors/{hypervisor_hostname_pattern}/servers', 'method': 'GET' } ], - scope_types=['system'] + scope_types=['system'], + deprecated_rule=DEPRECATED_POLICY, + deprecated_reason=DEPRECATED_REASON, + deprecated_since='21.0.0', ), ] diff --git a/nova/tests/unit/api/openstack/compute/test_hypervisors.py b/nova/tests/unit/api/openstack/compute/test_hypervisors.py index 8ba48fa3db..4a153b7a36 100644 --- a/nova/tests/unit/api/openstack/compute/test_hypervisors.py +++ b/nova/tests/unit/api/openstack/compute/test_hypervisors.py @@ -236,7 +236,6 @@ class HypervisorsTestV21(test.NoDBTestCase): def setUp(self): super(HypervisorsTestV21, self).setUp() self._set_up_controller() - self.rule_hyp_show = "os_compute_api:os-hypervisors" host_api = self.controller.host_api host_api.compute_node_get_all = mock.MagicMock( @@ -306,11 +305,6 @@ class HypervisorsTestV21(test.NoDBTestCase): self.assertEqual(dict(hypervisors=self.INDEX_HYPER_DICTS), result) - def test_index_non_admin(self): - req = self._get_request(False) - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.index, req) - def test_index_compute_host_not_found(self): """Tests that if a service is deleted but the compute node is not we don't fail when listing hypervisors. @@ -387,11 +381,6 @@ class HypervisorsTestV21(test.NoDBTestCase): self.assertEqual(dict(hypervisors=self.DETAIL_HYPERS_DICTS), result) - def test_detail_non_admin(self): - req = self._get_request(False) - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.detail, req) - def test_detail_compute_host_not_found(self): """Tests that if a service is deleted but the compute node is not we don't fail when listing hypervisors. @@ -514,12 +503,6 @@ class HypervisorsTestV21(test.NoDBTestCase): self.assertEqual(dict(hypervisor=self.DETAIL_HYPERS_DICTS[0]), result) - def test_show_non_admin(self): - req = self._get_request(False) - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.show, req, - self._get_hyper_id()) - def test_uptime_noid(self): req = self._get_request(True) hyper_id = uuids.hyper3 if self.expect_uuid_for_id else '3' @@ -553,12 +536,6 @@ class HypervisorsTestV21(test.NoDBTestCase): req = self._get_request(True) self.assertRaises(exc.HTTPNotFound, self.controller.uptime, req, 'abc') - def test_uptime_non_admin(self): - req = self._get_request(False) - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.uptime, req, - self.TEST_HYPERS_OBJ[0].id) - def test_uptime_hypervisor_down(self): with mock.patch.object(self.controller.host_api, 'get_host_uptime', side_effect=exception.ComputeServiceUnavailable(host='dummy') @@ -603,12 +580,6 @@ class HypervisorsTestV21(test.NoDBTestCase): self.assertEqual(dict(hypervisors=self.INDEX_HYPER_DICTS), result) - def test_search_non_admin(self): - req = self._get_request(False) - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.search, req, - self.TEST_HYPERS_OBJ[0].id) - def test_search_non_exist(self): with mock.patch.object(self.controller.host_api, 'compute_node_search_by_hypervisor', @@ -673,12 +644,6 @@ class HypervisorsTestV21(test.NoDBTestCase): req, '115') self.assertEqual(1, mock_node_search.call_count) - def test_servers_non_admin(self): - req = self._get_request(False) - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.servers, req, - self.TEST_HYPERS_OBJ[0].id) - def test_servers_with_non_integer_hypervisor_id(self): with mock.patch.object(self.controller.host_api, 'compute_node_search_by_hypervisor', @@ -716,11 +681,6 @@ class HypervisorsTestV21(test.NoDBTestCase): running_vms=4, disk_available_least=200)), result) - def test_statistics_non_admin(self): - req = self._get_request(False) - self.assertRaises(exception.PolicyNotAuthorized, - self.controller.statistics, req) - class HypervisorsTestV228(HypervisorsTestV21): api_version = '2.28' @@ -1080,12 +1040,6 @@ class HypervisorsTestV253(HypervisorsTestV252): self.assertRaises(exc.HTTPNotFound, self.controller.index, req) s.assert_called_once_with(req.environ['nova.context'], 'shenzhen') - def test_servers_non_admin(self): - """There is no reason to test this for 2.53 since the - /os-hypervisors/servers route is deprecated. - """ - pass - def test_servers_non_id(self): """There is no reason to test this for 2.53 since the /os-hypervisors/servers route is deprecated. @@ -1164,12 +1118,6 @@ class HypervisorsTestV253(HypervisorsTestV252): """ pass - def test_search_non_admin(self): - """There is no reason to test this for 2.53 since the - /os-hypervisors/search route is deprecated. - """ - pass - def test_search_unmapped(self): """This is already tested with test_index_compute_host_not_mapped.""" pass diff --git a/nova/tests/unit/fake_policy.py b/nova/tests/unit/fake_policy.py index e508a9a4c1..367f0bee4f 100644 --- a/nova/tests/unit/fake_policy.py +++ b/nova/tests/unit/fake_policy.py @@ -58,6 +58,13 @@ policy_data = """ "os_compute_api:os-instance-actions:events:details": "", "os_compute_api:os-instance-usage-audit-log:list": "", "os_compute_api:os-instance-usage-audit-log:show": "", + "os_compute_api:os-hypervisors:list": "", + "os_compute_api:os-hypervisors:list-detail": "", + "os_compute_api:os-hypervisors:statistics": "", + "os_compute_api:os-hypervisors:show": "", + "os_compute_api:os-hypervisors:uptime": "", + "os_compute_api:os-hypervisors:search": "", + "os_compute_api:os-hypervisors:servers": "", "os_compute_api:os-lock-server:lock": "", "os_compute_api:os-lock-server:unlock": "", diff --git a/nova/tests/unit/policies/test_hypervisors.py b/nova/tests/unit/policies/test_hypervisors.py index f512cb445f..abd5a50da1 100644 --- a/nova/tests/unit/policies/test_hypervisors.py +++ b/nova/tests/unit/policies/test_hypervisors.py @@ -13,6 +13,7 @@ import mock from nova.api.openstack.compute import hypervisors +from nova.policies import base as base_policy from nova.policies import hypervisors as hv_policies from nova.tests.unit.api.openstack import fakes from nova.tests.unit.policies import base @@ -35,68 +36,72 @@ class HypervisorsPolicyTest(base.BasePolicyTest): self.controller.host_api.service_get_by_compute_host = mock.MagicMock() self.controller.host_api.compute_node_get = mock.MagicMock() - # Check that admin is able to perform operations - # on hypervisors. - self.admin_authorized_contexts = [ - self.legacy_admin_context, self.system_admin_context, + # Check that system scoped admin, member and reader are able to + # perform operations on hypervisors. + # NOTE(gmann): Until old default rule which is admin_api is + # deprecated and not removed, project admin and legacy admin + # will be able to read the agent data. This make sure that existing + # tokens will keep working even we have changed this policy defaults + # to reader role. + self.reader_authorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context, self.legacy_admin_context, self.project_admin_context] - # Check that non-admin is not able to perform operations - # on hypervisors. - self.admin_unauthorized_contexts = [ - self.system_member_context, self.system_reader_context, - self.system_foo_context, self.project_member_context, - self.other_project_member_context, - self.project_foo_context, self.project_reader_context - ] + # Check that non-system-reader are not able to perform operations + # on hypervisors + self.reader_unauthorized_contexts = [ + self.system_foo_context, self.other_project_member_context, + self.project_foo_context, self.project_member_context, + self.project_reader_context] def test_list_hypervisors_policy(self): - rule_name = hv_policies.BASE_POLICY_NAME - self.common_policy_check(self.admin_authorized_contexts, - self.admin_unauthorized_contexts, + rule_name = hv_policies.BASE_POLICY_NAME % 'list' + self.common_policy_check(self.reader_authorized_contexts, + self.reader_unauthorized_contexts, rule_name, self.controller.index, self.req) def test_list_details_hypervisors_policy(self): - rule_name = hv_policies.BASE_POLICY_NAME - self.common_policy_check(self.admin_authorized_contexts, - self.admin_unauthorized_contexts, + rule_name = hv_policies.BASE_POLICY_NAME % 'list-detail' + self.common_policy_check(self.reader_authorized_contexts, + self.reader_unauthorized_contexts, rule_name, self.controller.detail, self.req) def test_show_hypervisors_policy(self): - rule_name = hv_policies.BASE_POLICY_NAME - self.common_policy_check(self.admin_authorized_contexts, - self.admin_unauthorized_contexts, + rule_name = hv_policies.BASE_POLICY_NAME % 'show' + self.common_policy_check(self.reader_authorized_contexts, + self.reader_unauthorized_contexts, rule_name, self.controller.show, self.req, 11111) @mock.patch('nova.compute.api.HostAPI.get_host_uptime') def test_uptime_hypervisors_policy(self, mock_uptime): - rule_name = hv_policies.BASE_POLICY_NAME - self.common_policy_check(self.admin_authorized_contexts, - self.admin_unauthorized_contexts, + rule_name = hv_policies.BASE_POLICY_NAME % 'uptime' + self.common_policy_check(self.reader_authorized_contexts, + self.reader_unauthorized_contexts, rule_name, self.controller.uptime, self.req, 11111) def test_search_hypervisors_policy(self): - rule_name = hv_policies.BASE_POLICY_NAME - self.common_policy_check(self.admin_authorized_contexts, - self.admin_unauthorized_contexts, + rule_name = hv_policies.BASE_POLICY_NAME % 'search' + self.common_policy_check(self.reader_authorized_contexts, + self.reader_unauthorized_contexts, rule_name, self.controller.search, self.req, 11111) def test_servers_hypervisors_policy(self): - rule_name = hv_policies.BASE_POLICY_NAME - self.common_policy_check(self.admin_authorized_contexts, - self.admin_unauthorized_contexts, + rule_name = hv_policies.BASE_POLICY_NAME % 'servers' + self.common_policy_check(self.reader_authorized_contexts, + self.reader_unauthorized_contexts, rule_name, self.controller.servers, self.req, 11111) @mock.patch('nova.compute.api.HostAPI.compute_node_statistics') def test_statistics_hypervisors_policy(self, mock_statistics): - rule_name = hv_policies.BASE_POLICY_NAME - self.common_policy_check(self.admin_authorized_contexts, - self.admin_unauthorized_contexts, + rule_name = hv_policies.BASE_POLICY_NAME % 'statistics' + self.common_policy_check(self.reader_authorized_contexts, + self.reader_unauthorized_contexts, rule_name, self.controller.statistics, self.req) @@ -115,16 +120,39 @@ class HypervisorsScopeTypePolicyTest(HypervisorsPolicyTest): super(HypervisorsScopeTypePolicyTest, self).setUp() self.flags(enforce_scope=True, group="oslo_policy") - # Check that system admin is able to perform operations + # Check that system reader is able to perform operations # on hypervisors. - self.admin_authorized_contexts = [ - self.system_admin_context] - # Check that non-system-admin is not able to perform operations + self.reader_authorized_contexts = [ + self.system_admin_context, self.system_member_context, + self.system_reader_context] + # Check that non-system-reader is not able to perform operations # on hypervisors. - self.admin_unauthorized_contexts = [ - self.legacy_admin_context, self.system_member_context, - self.system_reader_context, self.project_admin_context, + self.reader_unauthorized_contexts = [ + self.legacy_admin_context, self.project_admin_context, self.system_foo_context, self.project_member_context, self.other_project_member_context, self.project_foo_context, self.project_reader_context ] + + +class HypervisorsNoLegacyPolicyTest(HypervisorsScopeTypePolicyTest): + """Test Hypervisors APIs policies with system scope enabled, + and no more deprecated rules. + """ + without_deprecated_rules = True + rules_without_deprecation = { + hv_policies.BASE_POLICY_NAME % 'list': + base_policy.SYSTEM_READER, + hv_policies.BASE_POLICY_NAME % 'list-detail': + base_policy.SYSTEM_READER, + hv_policies.BASE_POLICY_NAME % 'show': + base_policy.SYSTEM_READER, + hv_policies.BASE_POLICY_NAME % 'statistics': + base_policy.SYSTEM_READER, + hv_policies.BASE_POLICY_NAME % 'uptime': + base_policy.SYSTEM_READER, + hv_policies.BASE_POLICY_NAME % 'search': + base_policy.SYSTEM_READER, + hv_policies.BASE_POLICY_NAME % 'servers': + base_policy.SYSTEM_READER, + } diff --git a/nova/tests/unit/test_policy.py b/nova/tests/unit/test_policy.py index 268a150a28..09fbe48e00 100644 --- a/nova/tests/unit/test_policy.py +++ b/nova/tests/unit/test_policy.py @@ -343,7 +343,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase): "os_compute_api:os-flavor-manage:update", "os_compute_api:os-flavor-manage:delete", "os_compute_api:os-hosts", -"os_compute_api:os-hypervisors", "os_compute_api:os-instance-actions:events", "os_compute_api:os-lock-server:unlock:unlock_override", "os_compute_api:os-migrate-server:migrate", @@ -461,6 +460,13 @@ class RealRolePolicyTestCase(test.NoDBTestCase): "os_compute_api:os-instance-usage-audit-log:list", "os_compute_api:os-instance-usage-audit-log:show", "os_compute_api:os-agents:list", +"os_compute_api:os-hypervisors:list", +"os_compute_api:os-hypervisors:list-detail", +"os_compute_api:os-hypervisors:show", +"os_compute_api:os-hypervisors:statistics", +"os_compute_api:os-hypervisors:uptime", +"os_compute_api:os-hypervisors:search", +"os_compute_api:os-hypervisors:servers", ) self.system_reader_or_owner_rules = (