From e26a5997b071293391dd639758ddf4b740d188c5 Mon Sep 17 00:00:00 2001 From: Matt Riedemann Date: Fri, 25 May 2018 20:46:24 -0400 Subject: [PATCH] Add granular policy rules for traits in placement This adds the granular policy rule defaults for traits-related APIs in the placement service. By default these are backward-compatible and require the admin role. They also specify the system scope type since traits are defined and used by the system, but scope enforcement is disabled by default in oslo.policy. Part of blueprint granular-placement-policy Change-Id: I9a8af3cae34e32438df12125b9bf32ccdde3b792 --- nova/api/openstack/placement/handler.py | 5 + .../api/openstack/placement/handlers/trait.py | 8 ++ .../openstack/placement/policies/__init__.py | 2 + .../api/openstack/placement/policies/trait.py | 120 ++++++++++++++++++ .../placement/gabbits/traits-policy.yaml | 55 ++++++++ 5 files changed, 190 insertions(+) create mode 100644 nova/api/openstack/placement/policies/trait.py create mode 100644 nova/tests/functional/api/openstack/placement/gabbits/traits-policy.yaml diff --git a/nova/api/openstack/placement/handler.py b/nova/api/openstack/placement/handler.py index dfb62cfd95b9..5d3fdfb9f1dc 100644 --- a/nova/api/openstack/placement/handler.py +++ b/nova/api/openstack/placement/handler.py @@ -151,6 +151,11 @@ PER_ROUTE_POLICY = [ '/resource_classes', # /resource_providers/{uuid}/usages '/resource_providers/[A-Za-z0-9-]+/usages$', + # /traits + # /traits/{name} + '/traits', + # /resource_providers/{uuid}/traits + '/resource_providers/[A-Za-z0-9-]+/traits', # /usages '/usages' ] diff --git a/nova/api/openstack/placement/handlers/trait.py b/nova/api/openstack/placement/handlers/trait.py index 57ccc557fe93..27dc66bbc1ac 100644 --- a/nova/api/openstack/placement/handlers/trait.py +++ b/nova/api/openstack/placement/handlers/trait.py @@ -20,6 +20,7 @@ import webob from nova.api.openstack.placement import exception from nova.api.openstack.placement import microversion from nova.api.openstack.placement.objects import resource_provider as rp_obj +from nova.api.openstack.placement.policies import trait as policies from nova.api.openstack.placement.schemas import trait as schema from nova.api.openstack.placement import util from nova.api.openstack.placement import wsgi_wrapper @@ -64,6 +65,7 @@ def _serialize_traits(traits, want_version): @microversion.version_handler('1.6') def put_trait(req): context = req.environ['placement.context'] + context.can(policies.TRAITS_UPDATE) want_version = req.environ[microversion.MICROVERSION_ENVIRON] name = util.wsgi_path_item(req.environ, 'name') @@ -99,6 +101,7 @@ def put_trait(req): @microversion.version_handler('1.6') def get_trait(req): context = req.environ['placement.context'] + context.can(policies.TRAITS_SHOW) want_version = req.environ[microversion.MICROVERSION_ENVIRON] name = util.wsgi_path_item(req.environ, 'name') @@ -119,6 +122,7 @@ def get_trait(req): @microversion.version_handler('1.6') def delete_trait(req): context = req.environ['placement.context'] + context.can(policies.TRAITS_DELETE) name = util.wsgi_path_item(req.environ, 'name') try: @@ -141,6 +145,7 @@ def delete_trait(req): @util.check_accept('application/json') def list_traits(req): context = req.environ['placement.context'] + context.can(policies.TRAITS_LIST) want_version = req.environ[microversion.MICROVERSION_ENVIRON] filters = {} @@ -172,6 +177,7 @@ def list_traits(req): @util.check_accept('application/json') def list_traits_for_resource_provider(req): context = req.environ['placement.context'] + context.can(policies.RP_TRAIT_LIST) want_version = req.environ[microversion.MICROVERSION_ENVIRON] uuid = util.wsgi_path_item(req.environ, 'uuid') @@ -206,6 +212,7 @@ def list_traits_for_resource_provider(req): @util.require_content('application/json') def update_traits_for_resource_provider(req): context = req.environ['placement.context'] + context.can(policies.RP_TRAIT_UPDATE) want_version = req.environ[microversion.MICROVERSION_ENVIRON] uuid = util.wsgi_path_item(req.environ, 'uuid') data = util.extract_json(req.body, schema.SET_TRAITS_FOR_RP_SCHEMA) @@ -246,6 +253,7 @@ def update_traits_for_resource_provider(req): @microversion.version_handler('1.6') def delete_traits_for_resource_provider(req): context = req.environ['placement.context'] + context.can(policies.RP_TRAIT_DELETE) uuid = util.wsgi_path_item(req.environ, 'uuid') resource_provider = rp_obj.ResourceProvider.get_by_uuid(context, uuid) diff --git a/nova/api/openstack/placement/policies/__init__.py b/nova/api/openstack/placement/policies/__init__.py index bee9ce14cd8b..e0e09bbede2e 100644 --- a/nova/api/openstack/placement/policies/__init__.py +++ b/nova/api/openstack/placement/policies/__init__.py @@ -17,6 +17,7 @@ from nova.api.openstack.placement.policies import base from nova.api.openstack.placement.policies import inventory from nova.api.openstack.placement.policies import resource_class from nova.api.openstack.placement.policies import resource_provider +from nova.api.openstack.placement.policies import trait from nova.api.openstack.placement.policies import usage @@ -28,4 +29,5 @@ def list_rules(): inventory.list_rules(), aggregate.list_rules(), usage.list_rules(), + trait.list_rules(), ) diff --git a/nova/api/openstack/placement/policies/trait.py b/nova/api/openstack/placement/policies/trait.py new file mode 100644 index 000000000000..6b35a703de5c --- /dev/null +++ b/nova/api/openstack/placement/policies/trait.py @@ -0,0 +1,120 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +from oslo_policy import policy + +from nova.api.openstack.placement.policies import base + + +RP_TRAIT_PREFIX = 'placement:resource_providers:traits:%s' +RP_TRAIT_LIST = RP_TRAIT_PREFIX % 'list' +RP_TRAIT_UPDATE = RP_TRAIT_PREFIX % 'update' +RP_TRAIT_DELETE = RP_TRAIT_PREFIX % 'delete' + +TRAITS_PREFIX = 'placement:traits:%s' +TRAITS_LIST = TRAITS_PREFIX % 'list' +TRAITS_SHOW = TRAITS_PREFIX % 'show' +TRAITS_UPDATE = TRAITS_PREFIX % 'update' +TRAITS_DELETE = TRAITS_PREFIX % 'delete' + + +rules = [ + policy.DocumentedRuleDefault( + TRAITS_LIST, + base.RULE_ADMIN_API, + "List traits.", + [ + { + 'method': 'GET', + 'path': '/traits' + } + ], + scope_types=['system'] + ), + policy.DocumentedRuleDefault( + TRAITS_SHOW, + base.RULE_ADMIN_API, + "Show trait.", + [ + { + 'method': 'GET', + 'path': '/traits/{name}' + } + ], + scope_types=['system'], + ), + policy.DocumentedRuleDefault( + TRAITS_UPDATE, + base.RULE_ADMIN_API, + "Update trait.", + [ + { + 'method': 'PUT', + 'path': '/traits/{name}' + } + ], + scope_types=['system'], + ), + policy.DocumentedRuleDefault( + TRAITS_DELETE, + base.RULE_ADMIN_API, + "Delete trait.", + [ + { + 'method': 'DELETE', + 'path': '/traits/{name}' + } + ], + scope_types=['system'], + ), + policy.DocumentedRuleDefault( + RP_TRAIT_LIST, + base.RULE_ADMIN_API, + "List resource provider traits.", + [ + { + 'method': 'GET', + 'path': '/resource_providers/{uuid}/traits' + } + ], + scope_types=['system'], + ), + policy.DocumentedRuleDefault( + RP_TRAIT_UPDATE, + base.RULE_ADMIN_API, + "Update resource provider traits.", + [ + { + 'method': 'PUT', + 'path': '/resource_providers/{uuid}/traits' + } + ], + scope_types=['system'], + ), + policy.DocumentedRuleDefault( + RP_TRAIT_DELETE, + base.RULE_ADMIN_API, + "Delete resource provider traits.", + [ + { + 'method': 'DELETE', + 'path': '/resource_providers/{uuid}/traits' + } + ], + scope_types=['system'], + ), +] + + +def list_rules(): + return rules diff --git a/nova/tests/functional/api/openstack/placement/gabbits/traits-policy.yaml b/nova/tests/functional/api/openstack/placement/gabbits/traits-policy.yaml new file mode 100644 index 000000000000..03cc1544406c --- /dev/null +++ b/nova/tests/functional/api/openstack/placement/gabbits/traits-policy.yaml @@ -0,0 +1,55 @@ +# This tests the individual CRUD operations on +# /traits* and /resource_providers/{uuid}/traits using a non-admin user with an +# open policy configuration. The response validation is intentionally minimal. +fixtures: + - OpenPolicyFixture + +defaults: + request_headers: + x-auth-token: user + accept: application/json + content-type: application/json + openstack-api-version: placement latest + +tests: + +- name: list traits + GET: /traits + status: 200 + +- name: create a trait + PUT: /traits/CUSTOM_TRAIT_X + status: 201 + +- name: show trait + GET: /traits/CUSTOM_TRAIT_X + status: 204 + +- name: create resource provider + POST: /resource_providers + data: + name: $ENVIRON['RP_NAME'] + uuid: $ENVIRON['RP_UUID'] + status: 200 + +- name: list resource provider traits + GET: /resource_providers/$ENVIRON['RP_UUID']/traits + status: 200 + +- name: update resource provider traits + PUT: /resource_providers/$ENVIRON['RP_UUID']/traits + request_headers: + content-type: application/json + status: 200 + data: + traits: + - CUSTOM_TRAIT_X + resource_provider_generation: 0 + +- name: delete resource provider traits + DELETE: /resource_providers/$ENVIRON['RP_UUID']/traits + status: 204 + +- name: delete trait + DELETE: /traits/CUSTOM_TRAIT_X + status: 204