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:
parent
19a6e8e626
commit
5a558b7d13
@ -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_external_network
|
||||||
from neutron_lib.api.definitions import subnet_service_types
|
from neutron_lib.api.definitions import subnet_service_types
|
||||||
from neutron_lib.api.definitions import subnetpool_prefix_ops
|
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 tap_mirror
|
||||||
from neutron_lib.api.definitions import trunk
|
from neutron_lib.api.definitions import trunk
|
||||||
from neutron_lib.api.definitions import uplink_status_propagation
|
from neutron_lib.api.definitions import uplink_status_propagation
|
||||||
@ -189,6 +190,7 @@ ML2_SUPPORTED_API_EXTENSIONS = [
|
|||||||
subnetpool_prefix_ops.ALIAS,
|
subnetpool_prefix_ops.ALIAS,
|
||||||
subnet_external_network.ALIAS,
|
subnet_external_network.ALIAS,
|
||||||
subnet_service_types.ALIAS,
|
subnet_service_types.ALIAS,
|
||||||
|
tag_creation.ALIAS,
|
||||||
trunk.ALIAS,
|
trunk.ALIAS,
|
||||||
seg_def.ALIAS,
|
seg_def.ALIAS,
|
||||||
expose_port_forwarding_in_fip.ALIAS,
|
expose_port_forwarding_in_fip.ALIAS,
|
||||||
|
@ -30,6 +30,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -73,6 +76,13 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATION_REASON,
|
deprecated_reason=DEPRECATION_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_floatingip',
|
name='get_floatingip',
|
||||||
check_str=base.ADMIN_OR_PROJECT_READER,
|
check_str=base.ADMIN_OR_PROJECT_READER,
|
||||||
|
@ -46,6 +46,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -177,6 +180,13 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_network',
|
name='get_network',
|
||||||
|
@ -36,6 +36,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -60,6 +63,13 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_network_segment_range',
|
name='get_network_segment_range',
|
||||||
|
@ -46,6 +46,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -306,6 +309,16 @@ rules = [
|
|||||||
),
|
),
|
||||||
operations=ACTION_POST,
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_port',
|
name='get_port',
|
||||||
|
@ -45,6 +45,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -157,6 +160,13 @@ rules = [
|
|||||||
' creating a router'),
|
' creating a router'),
|
||||||
operations=ACTION_POST,
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_router',
|
name='get_router',
|
||||||
|
@ -41,6 +41,9 @@ SG_ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': SG_TAGS_PATH},
|
{'method': 'PUT', 'path': SG_TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': SG_TAG_PATH},
|
{'method': 'PUT', 'path': SG_TAG_PATH},
|
||||||
]
|
]
|
||||||
|
SG_ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': SG_TAGS_PATH},
|
||||||
|
]
|
||||||
SG_ACTION_DELETE_TAGS = [
|
SG_ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': SG_TAGS_PATH},
|
{'method': 'DELETE', 'path': SG_TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': SG_TAG_PATH},
|
{'method': 'DELETE', 'path': SG_TAG_PATH},
|
||||||
@ -92,6 +95,13 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_security_group',
|
name='get_security_group',
|
||||||
check_str=neutron_policy.policy_or(
|
check_str=neutron_policy.policy_or(
|
||||||
|
@ -32,6 +32,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -56,6 +59,13 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_segment',
|
name='get_segment',
|
||||||
check_str=base.ADMIN,
|
check_str=base.ADMIN,
|
||||||
|
@ -45,6 +45,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -98,6 +101,16 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_subnet',
|
name='get_subnet',
|
||||||
check_str=neutron_policy.policy_or(
|
check_str=neutron_policy.policy_or(
|
||||||
|
@ -35,6 +35,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -100,6 +103,13 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_subnetpool',
|
name='get_subnetpool',
|
||||||
check_str=neutron_policy.policy_or(
|
check_str=neutron_policy.policy_or(
|
||||||
|
@ -30,6 +30,9 @@ ACTION_PUT_TAGS = [
|
|||||||
{'method': 'PUT', 'path': TAGS_PATH},
|
{'method': 'PUT', 'path': TAGS_PATH},
|
||||||
{'method': 'PUT', 'path': TAG_PATH},
|
{'method': 'PUT', 'path': TAG_PATH},
|
||||||
]
|
]
|
||||||
|
ACTION_POST_TAGS = [
|
||||||
|
{'method': 'POST', 'path': TAGS_PATH},
|
||||||
|
]
|
||||||
ACTION_DELETE_TAGS = [
|
ACTION_DELETE_TAGS = [
|
||||||
{'method': 'DELETE', 'path': TAGS_PATH},
|
{'method': 'DELETE', 'path': TAGS_PATH},
|
||||||
{'method': 'DELETE', 'path': TAG_PATH},
|
{'method': 'DELETE', 'path': TAG_PATH},
|
||||||
@ -57,6 +60,13 @@ rules = [
|
|||||||
deprecated_reason=DEPRECATED_REASON,
|
deprecated_reason=DEPRECATED_REASON,
|
||||||
deprecated_since=versionutils.deprecated.WALLABY)
|
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(
|
policy.DocumentedRuleDefault(
|
||||||
name='get_trunk',
|
name='get_trunk',
|
||||||
check_str=base.ADMIN_OR_PROJECT_READER,
|
check_str=base.ADMIN_OR_PROJECT_READER,
|
||||||
|
20
neutron/extensions/tag_creation.py
Normal file
20
neutron/extensions/tag_creation.py
Normal 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
|
@ -24,7 +24,6 @@ from neutron_lib import exceptions
|
|||||||
from neutron_lib.plugins import directory
|
from neutron_lib.plugins import directory
|
||||||
from neutron_lib import rpc as n_rpc
|
from neutron_lib import rpc as n_rpc
|
||||||
from neutron_lib.services import base as service_base
|
from neutron_lib.services import base as service_base
|
||||||
import webob.exc
|
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
from neutron.api import extensions
|
from neutron.api import extensions
|
||||||
@ -158,10 +157,20 @@ class TaggingController(object):
|
|||||||
policy.enforce(ctx, 'get_%s_%s' % (res, TAGS), target)
|
policy.enforce(ctx, 'get_%s_%s' % (res, TAGS), target)
|
||||||
return self.plugin.get_tag(ctx, res, res_id, id)
|
return self.plugin.get_tag(ctx, res, res_id, id)
|
||||||
|
|
||||||
def create(self, request, **kwargs):
|
@_policy_init
|
||||||
# not supported
|
def create(self, request, body, **kwargs):
|
||||||
# POST /v2.0/{parent_resource}/{parent_resource_id}/tags
|
# 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
|
@_policy_init
|
||||||
def update(self, request, id, **kwargs):
|
def update(self, request, id, **kwargs):
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# under the License.
|
# 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 api as db_api
|
||||||
from neutron_lib.db import model_query
|
from neutron_lib.db import model_query
|
||||||
from neutron_lib.db import resource_extend
|
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):
|
class TagPlugin(tagging.TagPluginBase):
|
||||||
"""Implementation of the Neutron Tag Service Plugin."""
|
"""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
|
__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):
|
if not any(tag == tag_db.tag for tag_db in res.standard_attr.tags):
|
||||||
raise tagging.TagNotFound(tag=tag)
|
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
|
@log_helpers.log_method_call
|
||||||
@db_api.retry_if_session_inactive()
|
@db_api.retry_if_session_inactive()
|
||||||
def update_tags(self, context, resource, resource_id, body):
|
def update_tags(self, context, resource, resource_id, body):
|
||||||
|
@ -59,6 +59,18 @@ class SystemAdminTests(FloatingIPAPITestCase):
|
|||||||
self.context, "create_floatingip:floating_ip_address",
|
self.context, "create_floatingip:floating_ip_address",
|
||||||
self.alt_target)
|
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):
|
def test_get_floatingip(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -146,6 +158,14 @@ class AdminTests(FloatingIPAPITestCase):
|
|||||||
self.context,
|
self.context,
|
||||||
"create_floatingip:floating_ip_address", self.alt_target))
|
"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):
|
def test_get_floatingip(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, "get_floatingip", self.target))
|
policy.enforce(self.context, "get_floatingip", self.target))
|
||||||
@ -203,6 +223,15 @@ class ProjectManagerTests(AdminTests):
|
|||||||
self.context, "create_floatingip:floating_ip_address",
|
self.context, "create_floatingip:floating_ip_address",
|
||||||
self.alt_target)
|
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):
|
def test_get_floatingip(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, "get_floatingip", self.target))
|
policy.enforce(self.context, "get_floatingip", self.target))
|
||||||
@ -277,6 +306,16 @@ class ProjectReaderTests(ProjectMemberTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, "create_floatingip", self.alt_target)
|
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):
|
def test_update_floatingip(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -327,6 +366,12 @@ class ServiceRoleTests(FloatingIPAPITestCase):
|
|||||||
self.context, "create_floatingip:floating_ip_address",
|
self.context, "create_floatingip:floating_ip_address",
|
||||||
self.target)
|
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):
|
def test_get_floatingip(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -128,6 +128,15 @@ class SystemAdminTests(NetworkAPITestCase):
|
|||||||
self.context, 'create_network:provider:segmentation_id',
|
self.context, 'create_network:provider:segmentation_id',
|
||||||
self.alt_target)
|
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):
|
def test_get_network(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -415,6 +424,13 @@ class AdminTests(NetworkAPITestCase):
|
|||||||
'create_network:provider:segmentation_id',
|
'create_network:provider:segmentation_id',
|
||||||
self.alt_target))
|
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):
|
def test_get_network(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_network', self.target))
|
policy.enforce(self.context, 'get_network', self.target))
|
||||||
@ -655,6 +671,14 @@ class ProjectManagerTests(AdminTests):
|
|||||||
self.context, 'create_network:provider:segmentation_id',
|
self.context, 'create_network:provider:segmentation_id',
|
||||||
self.alt_target)
|
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):
|
def test_get_network(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_network', self.target))
|
policy.enforce(self.context, 'get_network', self.target))
|
||||||
@ -867,6 +891,15 @@ class ProjectReaderTests(ProjectMemberTests):
|
|||||||
self.context, 'create_network:port_security_enabled',
|
self.context, 'create_network:port_security_enabled',
|
||||||
self.alt_target)
|
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):
|
def test_update_network(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -976,6 +1009,11 @@ class ServiceRoleTests(NetworkAPITestCase):
|
|||||||
self.context, 'create_network:provider:segmentation_id',
|
self.context, 'create_network:provider:segmentation_id',
|
||||||
self.target)
|
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):
|
def test_get_network(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_network', self.target))
|
policy.enforce(self.context, 'get_network', self.target))
|
||||||
|
@ -38,6 +38,12 @@ class SystemAdminTests(NetworkSegmentRangeAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_network_segment_range', self.target)
|
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):
|
def test_get_network_segment_range(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -100,6 +106,11 @@ class AdminTests(NetworkSegmentRangeAPITestCase):
|
|||||||
policy.enforce(self.context,
|
policy.enforce(self.context,
|
||||||
'create_network_segment_range', self.target))
|
'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):
|
def test_get_network_segment_range(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context,
|
policy.enforce(self.context,
|
||||||
@ -143,6 +154,12 @@ class ProjectManagerTests(AdminTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_network_segment_range', self.target)
|
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):
|
def test_get_network_segment_range(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -206,6 +223,12 @@ class ServiceRoleTests(NetworkSegmentRangeAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_network_segment_range', self.target)
|
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):
|
def test_get_network_segment_range(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -188,6 +188,14 @@ class SystemAdminTests(PortAPITestCase):
|
|||||||
self.context, 'create_port:allowed_address_pairs:ip_address',
|
self.context, 'create_port:allowed_address_pairs:ip_address',
|
||||||
self.alt_target)
|
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):
|
def test_get_port(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -562,6 +570,12 @@ class AdminTests(PortAPITestCase):
|
|||||||
'create_port:trusted',
|
'create_port:trusted',
|
||||||
self.alt_target))
|
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):
|
def test_get_port(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_port', self.target))
|
policy.enforce(self.context, 'get_port', self.target))
|
||||||
@ -939,6 +953,13 @@ class ProjectManagerTests(AdminTests):
|
|||||||
self.context, 'create_port:trusted',
|
self.context, 'create_port:trusted',
|
||||||
self.alt_target)
|
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):
|
def test_get_port(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_port', self.target))
|
policy.enforce(self.context, 'get_port', self.target))
|
||||||
@ -1426,6 +1447,14 @@ class ProjectReaderTests(ProjectMemberTests):
|
|||||||
policy.enforce, self.context, 'create_port:binding:vnic_type',
|
policy.enforce, self.context, 'create_port:binding:vnic_type',
|
||||||
self.alt_target)
|
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):
|
def test_update_port(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -1538,6 +1567,13 @@ class ServiceRoleTests(PortAPITestCase):
|
|||||||
self.context, 'create_port:allowed_address_pairs:ip_address',
|
self.context, 'create_port:allowed_address_pairs:ip_address',
|
||||||
self.target)
|
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):
|
def test_get_port(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_port', self.target))
|
policy.enforce(self.context, 'get_port', self.target))
|
||||||
|
@ -138,6 +138,16 @@ class SystemAdminTests(RouterAPITestCase):
|
|||||||
self.context, 'create_router:enable_default_route_ecmp',
|
self.context, 'create_router:enable_default_route_ecmp',
|
||||||
self.alt_target)
|
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):
|
def test_get_router(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -415,6 +425,13 @@ class AdminTests(RouterAPITestCase):
|
|||||||
'create_router:external_gateway_info:external_fixed_ips',
|
'create_router:external_gateway_info:external_fixed_ips',
|
||||||
self.alt_target))
|
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):
|
def test_update_router_enable_default_route_bfd(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(
|
policy.enforce(
|
||||||
@ -646,6 +663,14 @@ class ProjectManagerTests(AdminTests):
|
|||||||
'create_router:external_gateway_info:external_fixed_ips',
|
'create_router:external_gateway_info:external_fixed_ips',
|
||||||
self.alt_target)
|
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):
|
def test_update_router_enable_default_route_bfd(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -876,6 +901,16 @@ class ProjectReaderTests(ProjectMemberTests):
|
|||||||
self.context, 'create_router:external_gateway_info:network_id',
|
self.context, 'create_router:external_gateway_info:network_id',
|
||||||
self.alt_target)
|
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):
|
def test_update_router(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -1143,6 +1178,12 @@ class ServiceRoleTests(RouterAPITestCase):
|
|||||||
'create_router:external_gateway_info:external_fixed_ips',
|
'create_router:external_gateway_info:external_fixed_ips',
|
||||||
self.target)
|
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):
|
def test_get_router(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -46,6 +46,16 @@ class SystemAdminSecurityGroupTests(SecurityGroupAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_security_group', self.alt_target)
|
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):
|
def test_get_security_group(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -134,6 +144,14 @@ class AdminSecurityGroupTests(SecurityGroupAPITestCase):
|
|||||||
policy.enforce(
|
policy.enforce(
|
||||||
self.context, 'create_security_group', self.alt_target))
|
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):
|
def test_get_security_group(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_security_group', self.target))
|
policy.enforce(self.context, 'get_security_group', self.target))
|
||||||
@ -194,6 +212,15 @@ class ProjectManagerSecurityGroupTests(AdminSecurityGroupTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_security_group', self.alt_target)
|
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):
|
def test_get_security_group(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_security_group', self.target))
|
policy.enforce(self.context, 'get_security_group', self.target))
|
||||||
@ -267,6 +294,16 @@ class ProjectReaderSecurityGroupTests(ProjectMemberSecurityGroupTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_security_group', self.alt_target)
|
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):
|
def test_update_security_group(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -320,6 +357,12 @@ class ServiceRoleSecurityGroupTests(SecurityGroupAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_security_group', self.target)
|
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):
|
def test_get_security_group(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -38,6 +38,12 @@ class SystemAdminTests(SegmentAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_segment', self.target)
|
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):
|
def test_get_segment(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -99,6 +105,10 @@ class AdminTests(SegmentAPITestCase):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'create_segment', self.target))
|
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):
|
def test_get_segment(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_segment', self.target))
|
policy.enforce(self.context, 'get_segment', self.target))
|
||||||
@ -136,6 +146,12 @@ class ProjectManagerTests(AdminTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_segment', self.target)
|
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):
|
def test_get_segment(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -199,6 +215,12 @@ class ServiceRoleTests(SegmentAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_segment', self.target)
|
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):
|
def test_get_segment(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -140,6 +140,20 @@ class SystemAdminTests(SubnetAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnet:service_types', self.alt_target)
|
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):
|
def test_get_subnet(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -330,6 +344,16 @@ class AdminTests(SubnetAPITestCase):
|
|||||||
policy.enforce(
|
policy.enforce(
|
||||||
self.context, 'create_subnet:service_types', self.alt_target))
|
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):
|
def test_get_subnet(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_subnet', self.target))
|
policy.enforce(self.context, 'get_subnet', self.target))
|
||||||
@ -475,6 +499,17 @@ class ProjectManagerTests(AdminTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnet:service_types', self.alt_target)
|
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):
|
def test_get_subnet(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_subnet', self.target))
|
policy.enforce(self.context, 'get_subnet', self.target))
|
||||||
@ -619,6 +654,20 @@ class ProjectReaderTests(ProjectMemberTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnet', self.alt_target)
|
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):
|
def test_update_subnet(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -700,6 +749,12 @@ class ServiceRoleTests(SubnetAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnet:service_types', self.target)
|
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):
|
def test_get_subnet(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -63,6 +63,16 @@ class SystemAdminTests(SubnetpoolAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnetpool:is_default', self.alt_target)
|
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):
|
def test_get_subnetpool(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -206,6 +216,13 @@ class AdminTests(SubnetpoolAPITestCase):
|
|||||||
policy.enforce(
|
policy.enforce(
|
||||||
self.context, 'create_subnetpool:default', self.alt_target))
|
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):
|
def test_get_subnetpool(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_subnetpool', self.target))
|
policy.enforce(self.context, 'get_subnetpool', self.target))
|
||||||
@ -310,6 +327,15 @@ class ProjectManagerTests(AdminTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnetpool:is_default', self.alt_target)
|
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):
|
def test_get_subnetpool(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_subnetpool', self.target))
|
policy.enforce(self.context, 'get_subnetpool', self.target))
|
||||||
@ -419,6 +445,16 @@ class ProjectReaderTests(ProjectMemberTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnetpool', self.alt_target)
|
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):
|
def test_update_subnetpool(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -502,6 +538,12 @@ class ServiceRoleTests(SubnetpoolAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_subnetpool', self.target)
|
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):
|
def test_create_subnetpool_shared(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -43,6 +43,16 @@ class SystemAdminTests(TrunkAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_trunk', self.alt_target)
|
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):
|
def test_get_trunk(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.InvalidScope,
|
base_policy.InvalidScope,
|
||||||
@ -160,6 +170,13 @@ class AdminTests(TrunkAPITestCase):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'create_trunk', self.alt_target))
|
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):
|
def test_get_trunk(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_trunk', self.target))
|
policy.enforce(self.context, 'get_trunk', self.target))
|
||||||
@ -211,6 +228,14 @@ class ProjectManagerTests(AdminTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_trunk', self.alt_target)
|
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):
|
def test_get_trunk(self):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
policy.enforce(self.context, 'get_trunk', self.target))
|
policy.enforce(self.context, 'get_trunk', self.target))
|
||||||
@ -283,6 +308,16 @@ class ProjectReaderTests(ProjectMemberTests):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_trunk', self.alt_target)
|
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):
|
def test_update_trunk(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
@ -336,6 +371,12 @@ class ServiceRoleTests(TrunkAPITestCase):
|
|||||||
policy.enforce,
|
policy.enforce,
|
||||||
self.context, 'create_trunk', self.target)
|
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):
|
def test_get_trunk(self):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
base_policy.PolicyNotAuthorized,
|
base_policy.PolicyNotAuthorized,
|
||||||
|
@ -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.
|
@ -15,7 +15,7 @@ requests>=2.18.0 # Apache-2.0
|
|||||||
Jinja2>=2.10 # BSD License (3 clause)
|
Jinja2>=2.10 # BSD License (3 clause)
|
||||||
keystonemiddleware>=5.1.0 # Apache-2.0
|
keystonemiddleware>=5.1.0 # Apache-2.0
|
||||||
netaddr>=0.7.18 # BSD
|
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
|
python-neutronclient>=7.8.0 # Apache-2.0
|
||||||
tenacity>=6.0.0 # Apache-2.0
|
tenacity>=6.0.0 # Apache-2.0
|
||||||
SQLAlchemy>=1.4.23 # MIT
|
SQLAlchemy>=1.4.23 # MIT
|
||||||
|
Loading…
Reference in New Issue
Block a user