From 4bd1c82213e063f2e6a39e36d828bd800fd81ac6 Mon Sep 17 00:00:00 2001 From: Hang Yang Date: Mon, 27 Sep 2021 17:29:33 -0500 Subject: [PATCH] Add shared field to SG API response and filter Add the shared field to security group API responses and support using shared as a query filter. A follow-up patch will remove the temporary api def once it is merged and released in neutron-lib. Related-Bug: #1942615 Depends-On: https://review.opendev.org/c/openstack/neutron-lib/+/812617 Change-Id: Ic04be8f0b7097c8aed19365f06089aa7af333eb9 --- neutron/agent/securitygroups_rpc.py | 3 + neutron/common/ovn/extensions.py | 5 ++ neutron/db/securitygroups_db.py | 1 + .../security_groups_shared_filtering.py | 21 ++++++ .../security_groups_shared_filtering_lib.py | 67 +++++++++++++++++++ neutron/objects/securitygroup.py | 5 +- neutron/plugins/ml2/plugin.py | 2 + .../tests/contrib/hooks/api_all_extensions | 1 + .../tests/unit/db/test_securitygroups_db.py | 1 + .../unit/extensions/test_securitygroup.py | 27 +++++++- neutron/tests/unit/objects/test_objects.py | 2 +- ...ed-field-to-response-9ff6b49d36f4af4d.yaml | 7 ++ 12 files changed, 137 insertions(+), 5 deletions(-) create mode 100644 neutron/extensions/security_groups_shared_filtering.py create mode 100644 neutron/extensions/security_groups_shared_filtering_lib.py create mode 100644 releasenotes/notes/add-sg-shared-field-to-response-9ff6b49d36f4af4d.yaml diff --git a/neutron/agent/securitygroups_rpc.py b/neutron/agent/securitygroups_rpc.py index a65904c6e97..2f7c4a3d922 100644 --- a/neutron/agent/securitygroups_rpc.py +++ b/neutron/agent/securitygroups_rpc.py @@ -28,6 +28,8 @@ from oslo_log import log as logging from neutron.agent import firewall from neutron.conf.agent import securitygroups_rpc as sc_cfg +from neutron.extensions import security_groups_shared_filtering_lib \ + as sg_shared_filtering LOG = logging.getLogger(__name__) @@ -53,6 +55,7 @@ def disable_security_group_extension_by_config(aliases): _disable_extension(stateful_sg.ALIAS, aliases) _disable_extension(sgag_def.ALIAS, aliases) _disable_extension(security_groups_normalized_cidr.ALIAS, aliases) + _disable_extension(sg_shared_filtering.ALIAS, aliases) LOG.info('Disabled allowed-address-pairs extension.') _disable_extension('allowed-address-pairs', aliases) LOG.info('Disabled address-group extension.') diff --git a/neutron/common/ovn/extensions.py b/neutron/common/ovn/extensions.py index ad2504db16e..c6a23b4d2fc 100644 --- a/neutron/common/ovn/extensions.py +++ b/neutron/common/ovn/extensions.py @@ -52,6 +52,7 @@ from neutron_lib.api.definitions import qos_default from neutron_lib.api.definitions import qos_rule_type_details from neutron_lib.api.definitions import qos_rules_alias from neutron_lib.api.definitions import rbac_address_scope +from neutron_lib.api.definitions import rbac_security_groups from neutron_lib.api.definitions import router_availability_zone as raz_def from neutron_lib.api.definitions import security_groups_normalized_cidr from neutron_lib.api.definitions import security_groups_remote_address_group @@ -62,6 +63,8 @@ from neutron_lib.api.definitions import trunk from neutron_lib.api.definitions import vlantransparent from neutron_lib import constants +from neutron.extensions import security_groups_shared_filtering_lib + # NOTE(russellb) This remains in its own file (vs constants.py) because we want # to be able to easily import it and export the info without any dependencies # on external imports. @@ -116,10 +119,12 @@ ML2_SUPPORTED_API_EXTENSIONS = [ 'quotas', rbac_address_scope.ALIAS, 'rbac-policies', + rbac_security_groups.ALIAS, 'standard-attr-revisions', 'security-group', security_groups_normalized_cidr.ALIAS, security_groups_remote_address_group.ALIAS, + security_groups_shared_filtering_lib.ALIAS, 'standard-attr-description', constants.SUBNET_ALLOCATION_EXT_ALIAS, 'standard-attr-tag', diff --git a/neutron/db/securitygroups_db.py b/neutron/db/securitygroups_db.py index cbb0c8c0193..5577ea09bab 100644 --- a/neutron/db/securitygroups_db.py +++ b/neutron/db/securitygroups_db.py @@ -342,6 +342,7 @@ class SecurityGroupDbMixin(ext_sg.SecurityGroupPluginBase, 'tenant_id': security_group['tenant_id'], 'description': security_group['description'], 'standard_attr_id': security_group.db_obj.standard_attr.id, + 'shared': security_group['shared'], } if security_group.rules: res['security_group_rules'] = [ diff --git a/neutron/extensions/security_groups_shared_filtering.py b/neutron/extensions/security_groups_shared_filtering.py new file mode 100644 index 00000000000..0a00516b555 --- /dev/null +++ b/neutron/extensions/security_groups_shared_filtering.py @@ -0,0 +1,21 @@ +# 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 import extensions + +from neutron.extensions import security_groups_shared_filtering_lib + + +class Security_groups_shared_filtering(extensions.APIExtensionDescriptor): + """Extension class supporting filtering SGs depend on the shared field.""" + + api_definition = security_groups_shared_filtering_lib diff --git a/neutron/extensions/security_groups_shared_filtering_lib.py b/neutron/extensions/security_groups_shared_filtering_lib.py new file mode 100644 index 00000000000..67862e62607 --- /dev/null +++ b/neutron/extensions/security_groups_shared_filtering_lib.py @@ -0,0 +1,67 @@ +# 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. + +# NOTE(hangyang): This file can be removed once the api def is merged +# to neutron-lib https://review.opendev.org/c/openstack/neutron-lib/+/812617 + +from neutron_lib.api import converters +from neutron_lib import constants + + +# The alias of the extension. +ALIAS = 'security-groups-shared-filtering' + +IS_SHIM_EXTENSION = False + +IS_STANDARD_ATTR_EXTENSION = False + +# The name of the extension. +NAME = 'Security group filtering on the shared field' + +# The description of the extension. +DESCRIPTION = "Support filtering security groups on the shared field" + +# A timestamp of when the extension was introduced. +UPDATED_TIMESTAMP = "2021-10-05T09:00:00-00:00" + +# The resource attribute map for the extension. +RESOURCE_ATTRIBUTE_MAP = { + 'security_groups': { + constants.SHARED: { + 'allow_post': False, + 'allow_put': False, + 'convert_to': converters.convert_to_boolean, + 'is_visible': True, + 'is_filter': True, + 'required_by_policy': True, + 'enforce_policy': True} + } +} + +# The subresource attribute map for the extension. +SUB_RESOURCE_ATTRIBUTE_MAP = { +} + +# The action map. +ACTION_MAP = { +} + +# The action status. +ACTION_STATUS = { +} + +# The list of required extensions. +REQUIRED_EXTENSIONS = ['rbac-security-groups'] + +# The list of optional extensions. +OPTIONAL_EXTENSIONS = [ +] diff --git a/neutron/objects/securitygroup.py b/neutron/objects/securitygroup.py index 59c3c60b4e4..d7bc07ac682 100644 --- a/neutron/objects/securitygroup.py +++ b/neutron/objects/securitygroup.py @@ -40,7 +40,8 @@ class SecurityGroup(rbac_db.NeutronRbacObject): # Version 1.2: Added stateful support # Version 1.3: Added support for remote_address_group_id in rules # Version 1.4: Added support for normalized_cidr in rules - VERSION = '1.4' + # Version 1.5: Make the shared field nullable + VERSION = '1.5' # required by RbacNeutronMetaclass rbac_db_cls = SecurityGroupRBAC @@ -50,7 +51,7 @@ class SecurityGroup(rbac_db.NeutronRbacObject): 'id': common_types.UUIDField(), 'name': obj_fields.StringField(nullable=True), 'project_id': obj_fields.StringField(nullable=True), - 'shared': obj_fields.BooleanField(default=False), + 'shared': obj_fields.BooleanField(nullable=True), 'stateful': obj_fields.BooleanField(default=True), 'is_default': obj_fields.BooleanField(default=False), 'rules': obj_fields.ListOfObjectsField( diff --git a/neutron/plugins/ml2/plugin.py b/neutron/plugins/ml2/plugin.py index ab7d56c7cae..99ec8c01cd3 100644 --- a/neutron/plugins/ml2/plugin.py +++ b/neutron/plugins/ml2/plugin.py @@ -121,6 +121,7 @@ from neutron.db import subnet_service_type_mixin from neutron.db import vlantransparent_db from neutron.extensions import dhcpagentscheduler as dhcp_ext from neutron.extensions import filter_validation +from neutron.extensions import security_groups_shared_filtering_lib from neutron.extensions import vlantransparent from neutron.ipam import exceptions as ipam_exc from neutron.objects import base as base_obj @@ -216,6 +217,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2, security_groups_normalized_cidr.ALIAS, security_groups_port_filtering.ALIAS, security_groups_remote_address_group.ALIAS, + security_groups_shared_filtering_lib.ALIAS, empty_string_filtering.ALIAS, filter_apidef.ALIAS, port_mac_address_regenerate.ALIAS, diff --git a/neutron/tests/contrib/hooks/api_all_extensions b/neutron/tests/contrib/hooks/api_all_extensions index f1e538e397d..9cd772e06ce 100644 --- a/neutron/tests/contrib/hooks/api_all_extensions +++ b/neutron/tests/contrib/hooks/api_all_extensions @@ -55,6 +55,7 @@ NETWORK_API_EXTENSIONS+=",router-admin-state-down-before-update" NETWORK_API_EXTENSIONS+=",router_availability_zone" NETWORK_API_EXTENSIONS+=",security-group" NETWORK_API_EXTENSIONS+=",security-groups-remote-address-group" +NETWORK_API_EXTENSIONS+=",security-groups-shared-filtering" NETWORK_API_EXTENSIONS+=",port-device-profile" NETWORK_API_EXTENSIONS+=",port-mac-address-regenerate" NETWORK_API_EXTENSIONS+=",port-numa-affinity-policy" diff --git a/neutron/tests/unit/db/test_securitygroups_db.py b/neutron/tests/unit/db/test_securitygroups_db.py index 64518243dc8..1afa65a46c7 100644 --- a/neutron/tests/unit/db/test_securitygroups_db.py +++ b/neutron/tests/unit/db/test_securitygroups_db.py @@ -292,6 +292,7 @@ class SecurityGroupDbMixinTestCase(testlib_api.SqlTestCase): 'description': 'Default security group', 'stateful': mock.ANY, 'standard_attr_id': mock.ANY, + 'shared': False, 'security_group_rules': [ # Four rules for egress/ingress and ipv4/ipv6 mock.ANY, mock.ANY, mock.ANY, mock.ANY, diff --git a/neutron/tests/unit/extensions/test_securitygroup.py b/neutron/tests/unit/extensions/test_securitygroup.py index c41903a107f..a6b7e276ea8 100644 --- a/neutron/tests/unit/extensions/test_securitygroup.py +++ b/neutron/tests/unit/extensions/test_securitygroup.py @@ -17,6 +17,7 @@ import contextlib import copy from unittest import mock +from neutron_lib.api.definitions import rbac_security_groups as rbac_sg_def from neutron_lib.api.definitions import security_groups_remote_address_group \ as sgag_def from neutron_lib.api import validators @@ -35,6 +36,8 @@ from neutron.db import address_group_db from neutron.db import db_base_plugin_v2 from neutron.db import securitygroups_db from neutron.extensions import address_group as ext_ag +from neutron.extensions import security_groups_shared_filtering_lib \ + as sg_shared_filter_def from neutron.extensions import securitygroup as ext_sg from neutron.extensions import standardattrdescription from neutron.tests import base @@ -68,6 +71,9 @@ class SecurityGroupTestExtensionManager(object): # update with the remote address group api definition ext_sg.Securitygroup().update_attributes_map( sgag_def.RESOURCE_ATTRIBUTE_MAP) + # update with the shared field api definition + ext_sg.Securitygroup().update_attributes_map( + sg_shared_filter_def.RESOURCE_ATTRIBUTE_MAP) return (ext_sg.Securitygroup.get_resources() + ext_ag.Address_group().get_resources()) @@ -225,7 +231,9 @@ class SecurityGroupTestPlugin(db_base_plugin_v2.NeutronDbPluginV2, __native_pagination_support = True __native_sorting_support = True - supported_extension_aliases = ["security-group"] + supported_extension_aliases = [ + "security-group", sgag_def.ALIAS, + rbac_sg_def.ALIAS, sg_shared_filter_def.ALIAS] def create_port(self, context, port): tenant_id = port['port']['tenant_id'] @@ -289,7 +297,8 @@ class TestSecurityGroups(SecurityGroupDBTestCase): def test_create_security_group(self): name = 'webservers' description = 'my webservers' - keys = [('name', name,), ('description', description)] + keys = [('name', name,), ('description', description), + ('shared', False)] with self.security_group(name, description) as security_group: for k, v, in keys: self.assertEqual(v, security_group['security_group'][k]) @@ -498,6 +507,20 @@ class TestSecurityGroups(SecurityGroupDBTestCase): security_groups, query_params='description=sg') + def test_list_security_groups_with_shared_filter_true(self): + with self.security_group(name='sg1', description='sg'): + security_groups = () + self._test_list_resources('security-group', + security_groups, + query_params='name=sg1&shared=True') + + def test_list_security_groups_with_shared_filter_false(self): + with self.security_group(name='sg1', description='sg') as sg1: + security_groups = (sg1, ) + self._test_list_resources('security-group', + security_groups, + query_params='name=sg1&shared=False') + def test_list_security_groups_with_sort(self): with self.security_group(name='sg1', description='sg') as sg1,\ self.security_group(name='sg2', description='sg') as sg2,\ diff --git a/neutron/tests/unit/objects/test_objects.py b/neutron/tests/unit/objects/test_objects.py index 511b18e3447..5ee52c2b9a2 100644 --- a/neutron/tests/unit/objects/test_objects.py +++ b/neutron/tests/unit/objects/test_objects.py @@ -105,7 +105,7 @@ object_data = { 'RouterL3AgentBinding': '1.0-c5ba6c95e3a4c1236a55f490cd67da82', 'RouterPort': '1.0-c8c8f499bcdd59186fcd83f323106908', 'RouterRoute': '1.0-07fc5337c801fb8c6ccfbcc5afb45907', - 'SecurityGroup': '1.4-7b63b834e511856f54a09282d6843ecc', + 'SecurityGroup': '1.5-7eb8e44c327512e7bb1759ab41ede44b', 'SecurityGroupPortBinding': '1.0-6879d5c0af80396ef5a72934b6a6ef20', 'SecurityGroupRBAC': '1.0-192845c5ed0718e1c54fac36936fcd7d', 'SecurityGroupRule': '1.2-27793368d4ac35f2ed6e0bb653c6aaad', diff --git a/releasenotes/notes/add-sg-shared-field-to-response-9ff6b49d36f4af4d.yaml b/releasenotes/notes/add-sg-shared-field-to-response-9ff6b49d36f4af4d.yaml new file mode 100644 index 00000000000..5294d1c56de --- /dev/null +++ b/releasenotes/notes/add-sg-shared-field-to-response-9ff6b49d36f4af4d.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Add the shared field to security group API responses and support + using shared as a query filter. + For more information see bug + `1942615 `_.