Add new "tagging" API method: create (POST)

This new method allows to create multiple tags for a single resource.
The tags are passed as arguments in the ``POST`` call. That solves
the issue with the usage of URI reserved characters in the name of
the tags.

Bumped neutron-lib library to version 3.15.0, that contains [1].

[1]https://review.opendev.org/c/openstack/neutron-lib/+/924700

APIImpact add create method for service pluging "tagging"
Closes-Bug: #2073836

Change-Id: I9709da13c321695f324fe8d6c1cdc03756660a03
This commit is contained in:
Rodolfo Alonso Hernandez 2024-07-20 20:01:40 +00:00
parent 19a6e8e626
commit 5a558b7d13
26 changed files with 556 additions and 6 deletions

View File

@ -88,6 +88,7 @@ from neutron_lib.api.definitions import subnet_dns_publish_fixed_ip
from neutron_lib.api.definitions import subnet_external_network
from neutron_lib.api.definitions import subnet_service_types
from neutron_lib.api.definitions import subnetpool_prefix_ops
from neutron_lib.api.definitions import tag_creation
from neutron_lib.api.definitions import tap_mirror
from neutron_lib.api.definitions import trunk
from neutron_lib.api.definitions import uplink_status_propagation
@ -189,6 +190,7 @@ ML2_SUPPORTED_API_EXTENSIONS = [
subnetpool_prefix_ops.ALIAS,
subnet_external_network.ALIAS,
subnet_service_types.ALIAS,
tag_creation.ALIAS,
trunk.ALIAS,
seg_def.ALIAS,
expose_port_forwarding_in_fip.ALIAS,

View File

@ -30,6 +30,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -73,6 +76,13 @@ rules = [
deprecated_reason=DEPRECATION_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_floatingips_tags',
check_str=base.ADMIN_OR_PROJECT_MEMBER,
description='Create the floating IP tags',
operations=ACTION_POST_TAGS,
scope_types=['project'],
),
policy.DocumentedRuleDefault(
name='get_floatingip',
check_str=base.ADMIN_OR_PROJECT_READER,

View File

@ -46,6 +46,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -177,6 +180,13 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_networks_tags',
check_str=base.ADMIN_OR_PROJECT_MEMBER,
scope_types=['project'],
description='Create the network tags',
operations=ACTION_POST_TAGS,
),
policy.DocumentedRuleDefault(
name='get_network',

View File

@ -36,6 +36,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -60,6 +63,13 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_network_segment_ranges_tags',
check_str=base.ADMIN,
scope_types=['project'],
description='Create the network segment range tags',
operations=ACTION_POST_TAGS,
),
policy.DocumentedRuleDefault(
name='get_network_segment_range',

View File

@ -46,6 +46,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -306,6 +309,16 @@ rules = [
),
operations=ACTION_POST,
),
policy.DocumentedRuleDefault(
name='create_ports_tags',
check_str=neutron_policy.policy_or(
base.ADMIN_OR_PROJECT_MEMBER,
neutron_policy.RULE_ADVSVC
),
scope_types=['project'],
description='Create the port tags',
operations=ACTION_POST_TAGS,
),
policy.DocumentedRuleDefault(
name='get_port',

View File

@ -45,6 +45,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -157,6 +160,13 @@ rules = [
' creating a router'),
operations=ACTION_POST,
),
policy.DocumentedRuleDefault(
name='create_routers_tags',
check_str=base.ADMIN_OR_PROJECT_MEMBER,
scope_types=['project'],
description='Create the router tags',
operations=ACTION_POST_TAGS,
),
policy.DocumentedRuleDefault(
name='get_router',

View File

@ -41,6 +41,9 @@ SG_ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': SG_TAGS_PATH},
{'method': 'PUT', 'path': SG_TAG_PATH},
]
SG_ACTION_POST_TAGS = [
{'method': 'POST', 'path': SG_TAGS_PATH},
]
SG_ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': SG_TAGS_PATH},
{'method': 'DELETE', 'path': SG_TAG_PATH},
@ -92,6 +95,13 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_security_groups_tags',
check_str=base.ADMIN_OR_PROJECT_MEMBER,
scope_types=['project'],
description='Create the security group tags',
operations=SG_ACTION_POST_TAGS,
),
policy.DocumentedRuleDefault(
name='get_security_group',
check_str=neutron_policy.policy_or(

View File

@ -32,6 +32,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -56,6 +59,13 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_segments_tags',
check_str=base.ADMIN,
scope_types=['project'],
description='Create the segment tags',
operations=ACTION_POST_TAGS,
),
policy.DocumentedRuleDefault(
name='get_segment',
check_str=base.ADMIN,

View File

@ -45,6 +45,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -98,6 +101,16 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_subnets_tags',
check_str=neutron_policy.policy_or(
base.PROJECT_MEMBER,
base.ADMIN_OR_NET_OWNER_MEMBER,
),
scope_types=['project'],
description='Create the subnet tags',
operations=ACTION_POST_TAGS,
),
policy.DocumentedRuleDefault(
name='get_subnet',
check_str=neutron_policy.policy_or(

View File

@ -35,6 +35,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -100,6 +103,13 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_subnetpools_tags',
check_str=base.ADMIN_OR_PROJECT_MEMBER,
scope_types=['project'],
description='Create the subnetpool tags',
operations=ACTION_POST_TAGS
),
policy.DocumentedRuleDefault(
name='get_subnetpool',
check_str=neutron_policy.policy_or(

View File

@ -30,6 +30,9 @@ ACTION_PUT_TAGS = [
{'method': 'PUT', 'path': TAGS_PATH},
{'method': 'PUT', 'path': TAG_PATH},
]
ACTION_POST_TAGS = [
{'method': 'POST', 'path': TAGS_PATH},
]
ACTION_DELETE_TAGS = [
{'method': 'DELETE', 'path': TAGS_PATH},
{'method': 'DELETE', 'path': TAG_PATH},
@ -57,6 +60,13 @@ rules = [
deprecated_reason=DEPRECATED_REASON,
deprecated_since=versionutils.deprecated.WALLABY)
),
policy.DocumentedRuleDefault(
name='create_trunks_tags',
check_str=base.ADMIN_OR_PROJECT_MEMBER,
scope_types=['project'],
description='Create the trunk tags',
operations=ACTION_POST_TAGS
),
policy.DocumentedRuleDefault(
name='get_trunk',
check_str=base.ADMIN_OR_PROJECT_READER,

View File

@ -0,0 +1,20 @@
# Copyright (c) 2024 Red Hat, Inc.
#
# 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 neutron_lib.api.definitions import tag_creation
from neutron_lib.api import extensions as api_extensions
class Tag_creation(api_extensions.APIExtensionDescriptor):
api_definition = tag_creation

View File

@ -24,7 +24,6 @@ from neutron_lib import exceptions
from neutron_lib.plugins import directory
from neutron_lib import rpc as n_rpc
from neutron_lib.services import base as service_base
import webob.exc
from neutron._i18n import _
from neutron.api import extensions
@ -158,10 +157,20 @@ class TaggingController(object):
policy.enforce(ctx, 'get_%s_%s' % (res, TAGS), target)
return self.plugin.get_tag(ctx, res, res_id, id)
def create(self, request, **kwargs):
# not supported
@_policy_init
def create(self, request, body, **kwargs):
# POST /v2.0/{parent_resource}/{parent_resource_id}/tags
raise webob.exc.HTTPNotFound("not supported")
# body: {"tags": ["aaa", "bbb"]}
validate_tags(body)
ctx = request.context
res, res_id, p_res, p_res_id = self._get_parent_resource_and_id(
ctx, kwargs)
target = self._get_target(ctx, res_id, p_res, p_res_id)
policy.enforce(ctx, 'create_%s_%s' % (res, TAGS), target)
notify_tag_action(ctx, 'create.start', res, res_id, body['tags'])
result = self.plugin.create_tags(ctx, res, res_id, body)
notify_tag_action(ctx, 'create.end', res, res_id, body['tags'])
return result
@_policy_init
def update(self, request, id, **kwargs):

View File

@ -12,6 +12,7 @@
# under the License.
#
from neutron_lib.api.definitions import tag_creation
from neutron_lib.db import api as db_api
from neutron_lib.db import model_query
from neutron_lib.db import resource_extend
@ -33,7 +34,9 @@ resource_model_map = standard_attr.get_standard_attr_resource_model_map()
class TagPlugin(tagging.TagPluginBase):
"""Implementation of the Neutron Tag Service Plugin."""
supported_extension_aliases = ['standard-attr-tag']
supported_extension_aliases = ['standard-attr-tag',
tag_creation.ALIAS,
]
__filter_validation_support = True
@ -81,6 +84,22 @@ class TagPlugin(tagging.TagPluginBase):
if not any(tag == tag_db.tag for tag_db in res.standard_attr.tags):
raise tagging.TagNotFound(tag=tag)
@log_helpers.log_method_call
@db_api.retry_if_session_inactive()
@db_api.CONTEXT_WRITER
def create_tags(self, context, resource, resource_id, body):
"""Create new tags for a resource
This method will create the non-existent tags of a resource. If
present, the tags will be omitted. This method is idempotent.
"""
res = self._get_resource(context, resource, resource_id)
new_tags = set(body['tags'])
old_tags = {tag_db.tag for tag_db in res.standard_attr.tags}
tags_added = new_tags - old_tags
self.add_tags(context, res.standard_attr_id, tags_added)
return body
@log_helpers.log_method_call
@db_api.retry_if_session_inactive()
def update_tags(self, context, resource, resource_id, body):

View File

@ -59,6 +59,18 @@ class SystemAdminTests(FloatingIPAPITestCase):
self.context, "create_floatingip:floating_ip_address",
self.alt_target)
def test_create_floatingips_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, "create_floatingips_tags",
self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, "create_floatingips_tags",
self.alt_target)
def test_get_floatingip(self):
self.assertRaises(
base_policy.InvalidScope,
@ -146,6 +158,14 @@ class AdminTests(FloatingIPAPITestCase):
self.context,
"create_floatingip:floating_ip_address", self.alt_target))
def test_create_floatingips_tags(self):
self.assertTrue(
policy.enforce(self.context, "create_floatingips_tags",
self.target))
self.assertTrue(
policy.enforce(self.context, "create_floatingips_tags",
self.alt_target))
def test_get_floatingip(self):
self.assertTrue(
policy.enforce(self.context, "get_floatingip", self.target))
@ -203,6 +223,15 @@ class ProjectManagerTests(AdminTests):
self.context, "create_floatingip:floating_ip_address",
self.alt_target)
def test_create_floatingips_tags(self):
self.assertTrue(
policy.enforce(self.context, "create_floatingips_tags",
self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, "create_floatingips_tags", self.alt_target)
def test_get_floatingip(self):
self.assertTrue(
policy.enforce(self.context, "get_floatingip", self.target))
@ -277,6 +306,16 @@ class ProjectReaderTests(ProjectMemberTests):
policy.enforce,
self.context, "create_floatingip", self.alt_target)
def test_create_floatingips_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, "create_floatingips_tags", self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, "create_floatingips_tags", self.alt_target)
def test_update_floatingip(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -327,6 +366,12 @@ class ServiceRoleTests(FloatingIPAPITestCase):
self.context, "create_floatingip:floating_ip_address",
self.target)
def test_create_floatingips_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, "create_floatingips_tags", self.target)
def test_get_floatingip(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -128,6 +128,15 @@ class SystemAdminTests(NetworkAPITestCase):
self.context, 'create_network:provider:segmentation_id',
self.alt_target)
def test_create_networks_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'create_networks_tags', self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'create_networks_tags',
self.alt_target)
def test_get_network(self):
self.assertRaises(
base_policy.InvalidScope,
@ -415,6 +424,13 @@ class AdminTests(NetworkAPITestCase):
'create_network:provider:segmentation_id',
self.alt_target))
def test_create_networks_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_networks_tags', self.target))
self.assertTrue(
policy.enforce(self.context, 'create_networks_tags',
self.alt_target))
def test_get_network(self):
self.assertTrue(
policy.enforce(self.context, 'get_network', self.target))
@ -655,6 +671,14 @@ class ProjectManagerTests(AdminTests):
self.context, 'create_network:provider:segmentation_id',
self.alt_target)
def test_create_networks_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_networks_tags', self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_networks_tags', self.alt_target)
def test_get_network(self):
self.assertTrue(
policy.enforce(self.context, 'get_network', self.target))
@ -867,6 +891,15 @@ class ProjectReaderTests(ProjectMemberTests):
self.context, 'create_network:port_security_enabled',
self.alt_target)
def test_create_networks_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_networks_tags', self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_networks_tags',
self.alt_target)
def test_update_network(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -976,6 +1009,11 @@ class ServiceRoleTests(NetworkAPITestCase):
self.context, 'create_network:provider:segmentation_id',
self.target)
def test_create_networks_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_networks_tags', self.target)
def test_get_network(self):
self.assertTrue(
policy.enforce(self.context, 'get_network', self.target))

View File

@ -38,6 +38,12 @@ class SystemAdminTests(NetworkSegmentRangeAPITestCase):
policy.enforce,
self.context, 'create_network_segment_range', self.target)
def test_create_network_segment_ranges_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_network_segment_ranges_tags', self.target)
def test_get_network_segment_range(self):
self.assertRaises(
base_policy.InvalidScope,
@ -100,6 +106,11 @@ class AdminTests(NetworkSegmentRangeAPITestCase):
policy.enforce(self.context,
'create_network_segment_range', self.target))
def test_create_network_segment_ranges_tags(self):
self.assertTrue(
policy.enforce(self.context,
'create_network_segment_ranges_tags', self.target))
def test_get_network_segment_range(self):
self.assertTrue(
policy.enforce(self.context,
@ -143,6 +154,12 @@ class ProjectManagerTests(AdminTests):
policy.enforce,
self.context, 'create_network_segment_range', self.target)
def test_create_network_segment_ranges_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_network_segment_ranges_tags', self.target)
def test_get_network_segment_range(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -206,6 +223,12 @@ class ServiceRoleTests(NetworkSegmentRangeAPITestCase):
policy.enforce,
self.context, 'create_network_segment_range', self.target)
def test_create_network_segment_ranges_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_network_segment_ranges_tags', self.target)
def test_get_network_segment_range(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -188,6 +188,14 @@ class SystemAdminTests(PortAPITestCase):
self.context, 'create_port:allowed_address_pairs:ip_address',
self.alt_target)
def test_create_ports_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'create_ports_tags', self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce, self.context, 'create_ports_tags', self.alt_target)
def test_get_port(self):
self.assertRaises(
base_policy.InvalidScope,
@ -562,6 +570,12 @@ class AdminTests(PortAPITestCase):
'create_port:trusted',
self.alt_target))
def test_create_ports_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_ports_tags', self.target))
self.assertTrue(
policy.enforce(self.context, 'create_ports_tags', self.alt_target))
def test_get_port(self):
self.assertTrue(
policy.enforce(self.context, 'get_port', self.target))
@ -939,6 +953,13 @@ class ProjectManagerTests(AdminTests):
self.context, 'create_port:trusted',
self.alt_target)
def test_create_ports_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_ports_tags', self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_ports_tags', self.alt_target)
def test_get_port(self):
self.assertTrue(
policy.enforce(self.context, 'get_port', self.target))
@ -1426,6 +1447,14 @@ class ProjectReaderTests(ProjectMemberTests):
policy.enforce, self.context, 'create_port:binding:vnic_type',
self.alt_target)
def test_create_ports_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_ports_tags', self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce, self.context, 'create_ports_tags', self.alt_target)
def test_update_port(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -1538,6 +1567,13 @@ class ServiceRoleTests(PortAPITestCase):
self.context, 'create_port:allowed_address_pairs:ip_address',
self.target)
def test_create_ports_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_ports_tags',
self.target)
def test_get_port(self):
self.assertTrue(
policy.enforce(self.context, 'get_port', self.target))

View File

@ -138,6 +138,16 @@ class SystemAdminTests(RouterAPITestCase):
self.context, 'create_router:enable_default_route_ecmp',
self.alt_target)
def test_create_routers_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_routers_tags', self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_routers_tags', self.alt_target)
def test_get_router(self):
self.assertRaises(
base_policy.InvalidScope,
@ -415,6 +425,13 @@ class AdminTests(RouterAPITestCase):
'create_router:external_gateway_info:external_fixed_ips',
self.alt_target))
def test_create_routers_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_routers_tags', self.target))
self.assertTrue(
policy.enforce(self.context, 'create_routers_tags',
self.alt_target))
def test_update_router_enable_default_route_bfd(self):
self.assertTrue(
policy.enforce(
@ -646,6 +663,14 @@ class ProjectManagerTests(AdminTests):
'create_router:external_gateway_info:external_fixed_ips',
self.alt_target)
def test_create_routers_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_routers_tags', self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_routers_tags', self.alt_target)
def test_update_router_enable_default_route_bfd(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -876,6 +901,16 @@ class ProjectReaderTests(ProjectMemberTests):
self.context, 'create_router:external_gateway_info:network_id',
self.alt_target)
def test_create_routers_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_routers_tags', self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_routers_tags', self.alt_target)
def test_update_router(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -1143,6 +1178,12 @@ class ServiceRoleTests(RouterAPITestCase):
'create_router:external_gateway_info:external_fixed_ips',
self.target)
def test_create_routers_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_routers_tags', self.target)
def test_get_router(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -46,6 +46,16 @@ class SystemAdminSecurityGroupTests(SecurityGroupAPITestCase):
policy.enforce,
self.context, 'create_security_group', self.alt_target)
def test_create_security_groups_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_security_groups_tags', self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_security_groups_tags', self.alt_target)
def test_get_security_group(self):
self.assertRaises(
base_policy.InvalidScope,
@ -134,6 +144,14 @@ class AdminSecurityGroupTests(SecurityGroupAPITestCase):
policy.enforce(
self.context, 'create_security_group', self.alt_target))
def test_create_security_groups_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_security_groups_tags',
self.target))
self.assertTrue(
policy.enforce(self.context, 'create_security_groups_tags',
self.alt_target))
def test_get_security_group(self):
self.assertTrue(
policy.enforce(self.context, 'get_security_group', self.target))
@ -194,6 +212,15 @@ class ProjectManagerSecurityGroupTests(AdminSecurityGroupTests):
policy.enforce,
self.context, 'create_security_group', self.alt_target)
def test_create_security_groups_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_security_groups_tags',
self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_security_groups_tags', self.alt_target)
def test_get_security_group(self):
self.assertTrue(
policy.enforce(self.context, 'get_security_group', self.target))
@ -267,6 +294,16 @@ class ProjectReaderSecurityGroupTests(ProjectMemberSecurityGroupTests):
policy.enforce,
self.context, 'create_security_group', self.alt_target)
def test_create_security_groups_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_security_groups_tags', self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_security_groups_tags', self.alt_target)
def test_update_security_group(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -320,6 +357,12 @@ class ServiceRoleSecurityGroupTests(SecurityGroupAPITestCase):
policy.enforce,
self.context, 'create_security_group', self.target)
def test_create_security_groups_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_security_groups_tags', self.target)
def test_get_security_group(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -38,6 +38,12 @@ class SystemAdminTests(SegmentAPITestCase):
policy.enforce,
self.context, 'create_segment', self.target)
def test_create_segments_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_segments_tags', self.target)
def test_get_segment(self):
self.assertRaises(
base_policy.InvalidScope,
@ -99,6 +105,10 @@ class AdminTests(SegmentAPITestCase):
self.assertTrue(
policy.enforce(self.context, 'create_segment', self.target))
def test_create_segments_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_segments_tags', self.target))
def test_get_segment(self):
self.assertTrue(
policy.enforce(self.context, 'get_segment', self.target))
@ -136,6 +146,12 @@ class ProjectManagerTests(AdminTests):
policy.enforce,
self.context, 'create_segment', self.target)
def test_create_segments_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_segments_tags', self.target)
def test_get_segment(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -199,6 +215,12 @@ class ServiceRoleTests(SegmentAPITestCase):
policy.enforce,
self.context, 'create_segment', self.target)
def test_create_segments_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_segments_tags', self.target)
def test_get_segment(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -140,6 +140,20 @@ class SystemAdminTests(SubnetAPITestCase):
policy.enforce,
self.context, 'create_subnet:service_types', self.alt_target)
def test_create_subnets_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_subnets_tags', self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_subnets_tags', self.target_net_alt_target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_subnets_tags', self.alt_target)
def test_get_subnet(self):
self.assertRaises(
base_policy.InvalidScope,
@ -330,6 +344,16 @@ class AdminTests(SubnetAPITestCase):
policy.enforce(
self.context, 'create_subnet:service_types', self.alt_target))
def test_create_subnets_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_subnets_tags', self.target))
self.assertTrue(
policy.enforce(self.context, 'create_subnets_tags',
self.target_net_alt_target))
self.assertTrue(
policy.enforce(self.context, 'create_subnets_tags',
self.alt_target))
def test_get_subnet(self):
self.assertTrue(
policy.enforce(self.context, 'get_subnet', self.target))
@ -475,6 +499,17 @@ class ProjectManagerTests(AdminTests):
policy.enforce,
self.context, 'create_subnet:service_types', self.alt_target)
def test_create_subnets_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_subnets_tags', self.target))
self.assertTrue(
policy.enforce(self.context, 'create_subnets_tags',
self.target_net_alt_target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnets_tags', self.alt_target)
def test_get_subnet(self):
self.assertTrue(
policy.enforce(self.context, 'get_subnet', self.target))
@ -619,6 +654,20 @@ class ProjectReaderTests(ProjectMemberTests):
policy.enforce,
self.context, 'create_subnet', self.alt_target)
def test_create_subnets_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnets_tags', self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnets_tags', self.target_net_alt_target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnets_tags', self.alt_target)
def test_update_subnet(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -700,6 +749,12 @@ class ServiceRoleTests(SubnetAPITestCase):
policy.enforce,
self.context, 'create_subnet:service_types', self.target)
def test_create_subnets_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnets_tags', self.target)
def test_get_subnet(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -63,6 +63,16 @@ class SystemAdminTests(SubnetpoolAPITestCase):
policy.enforce,
self.context, 'create_subnetpool:is_default', self.alt_target)
def test_create_subnetpools_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_subnetpools_tags', self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_subnetpools_tags', self.alt_target)
def test_get_subnetpool(self):
self.assertRaises(
base_policy.InvalidScope,
@ -206,6 +216,13 @@ class AdminTests(SubnetpoolAPITestCase):
policy.enforce(
self.context, 'create_subnetpool:default', self.alt_target))
def test_create_subnetpools_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_subnetpools_tags',
self.target))
self.assertTrue(policy.enforce(self.context, 'create_subnetpools_tags',
self.alt_target))
def test_get_subnetpool(self):
self.assertTrue(
policy.enforce(self.context, 'get_subnetpool', self.target))
@ -310,6 +327,15 @@ class ProjectManagerTests(AdminTests):
policy.enforce,
self.context, 'create_subnetpool:is_default', self.alt_target)
def test_create_subnetpools_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_subnetpools_tags',
self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnetpools_tags', self.alt_target)
def test_get_subnetpool(self):
self.assertTrue(
policy.enforce(self.context, 'get_subnetpool', self.target))
@ -419,6 +445,16 @@ class ProjectReaderTests(ProjectMemberTests):
policy.enforce,
self.context, 'create_subnetpool', self.alt_target)
def test_create_subnetpools_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnetpools_tags', self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnetpools_tags', self.alt_target)
def test_update_subnetpool(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -502,6 +538,12 @@ class ServiceRoleTests(SubnetpoolAPITestCase):
policy.enforce,
self.context, 'create_subnetpool', self.target)
def test_create_subnetpools_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_subnetpools_tags', self.target)
def test_create_subnetpool_shared(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -43,6 +43,16 @@ class SystemAdminTests(TrunkAPITestCase):
policy.enforce,
self.context, 'create_trunk', self.alt_target)
def test_create_trunks_tags(self):
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_trunks_tags', self.target)
self.assertRaises(
base_policy.InvalidScope,
policy.enforce,
self.context, 'create_trunks_tags', self.alt_target)
def test_get_trunk(self):
self.assertRaises(
base_policy.InvalidScope,
@ -160,6 +170,13 @@ class AdminTests(TrunkAPITestCase):
self.assertTrue(
policy.enforce(self.context, 'create_trunk', self.alt_target))
def test_create_trunks_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_trunks_tags', self.target))
self.assertTrue(
policy.enforce(self.context, 'create_trunks_tags',
self.alt_target))
def test_get_trunk(self):
self.assertTrue(
policy.enforce(self.context, 'get_trunk', self.target))
@ -211,6 +228,14 @@ class ProjectManagerTests(AdminTests):
policy.enforce,
self.context, 'create_trunk', self.alt_target)
def test_create_trunks_tags(self):
self.assertTrue(
policy.enforce(self.context, 'create_trunks_tags', self.target))
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_trunks_tags', self.alt_target)
def test_get_trunk(self):
self.assertTrue(
policy.enforce(self.context, 'get_trunk', self.target))
@ -283,6 +308,16 @@ class ProjectReaderTests(ProjectMemberTests):
policy.enforce,
self.context, 'create_trunk', self.alt_target)
def test_create_trunks_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_trunks_tags', self.target)
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_trunks_tags', self.alt_target)
def test_update_trunk(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
@ -336,6 +371,12 @@ class ServiceRoleTests(TrunkAPITestCase):
policy.enforce,
self.context, 'create_trunk', self.target)
def test_create_trunks_tags(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,
policy.enforce,
self.context, 'create_trunks_tags', self.target)
def test_get_trunk(self):
self.assertRaises(
base_policy.PolicyNotAuthorized,

View File

@ -0,0 +1,8 @@
---
features:
- |
Added a new shim extension ``tag-creation``. This extension informs about
a new API definition in the tag plugin that allows to send ``POST``
requests. Now it is possible to create new tags, passing the value of the
tags as an argument of the call. That resolves formatting issues when
using URI reserved characters.

View File

@ -15,7 +15,7 @@ requests>=2.18.0 # Apache-2.0
Jinja2>=2.10 # BSD License (3 clause)
keystonemiddleware>=5.1.0 # Apache-2.0
netaddr>=0.7.18 # BSD
neutron-lib>=3.14.0 # Apache-2.0
neutron-lib>=3.15.0 # Apache-2.0
python-neutronclient>=7.8.0 # Apache-2.0
tenacity>=6.0.0 # Apache-2.0
SQLAlchemy>=1.4.23 # MIT