Filter port-list based on security_groups.

This patch allows users to filter ports depending on security groups.
In addition to that I added a unit test to verify this change.
TODO: move security_groups_port_filtering_lib.py into neutron-lib.

Closes-Bug: 1405057
Change-Id: I528719d895fc1d89f74e2ee85b9ddc470323c601
This commit is contained in:
Ahmed Zaid 2018-01-17 09:32:59 -05:00 committed by Ahmed Zaid
parent 1813f7c497
commit 43d3e88a07
6 changed files with 142 additions and 1 deletions

View File

@ -0,0 +1,23 @@
# Copyright (c) 2018 Nokia. All rights reserved.
#
# 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_port_filtering_lib as apidef
class Security_groups_port_filtering(extensions.APIExtensionDescriptor):
"""Extension class supporting filtering port depend on security group."""
api_definition = apidef

View File

@ -0,0 +1,59 @@
# 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.
# The alias of the extension.
ALIAS = 'port-security-groups-filtering'
# Whether or not this extension is simply signaling behavior to the user
# or it actively modifies the attribute map.
IS_SHIM_EXTENSION = True
# Whether the extension is marking the adoption of standardattr model for
# legacy resources, or introducing new standardattr attributes. False or
# None if the standardattr model is adopted since the introduction of
# resource extension.
# If this is True, the alias for the extension should be prefixed with
# 'standard-attr-'.
IS_STANDARD_ATTR_EXTENSION = False
# The name of the extension.
NAME = 'Port filtering on security groups'
# The description of the extension.
DESCRIPTION = "Provides security groups filtering when listing ports"
# A timestamp of when the extension was introduced.
UPDATED_TIMESTAMP = "2018-01-09T09:00:00-00:00"
# The resource attribute map for the extension.
RESOURCE_ATTRIBUTE_MAP = {
}
# 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 = [
]
# The list of optional extensions.
OPTIONAL_EXTENSIONS = [
]

View File

@ -155,7 +155,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
"network_availability_zone",
"default-subnetpools",
"subnet-service-types",
"ip-substring-filtering"]
"ip-substring-filtering",
"port-security-groups-filtering"]
@property
def supported_extension_aliases(self):
@ -1853,6 +1854,17 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
def _get_ports_query(self, context, filters=None, *args, **kwargs):
filters = filters or {}
security_groups = filters.pop("security_groups", None)
if security_groups:
port_bindings = self._get_port_security_group_bindings(
context, filters={'security_group_id':
security_groups})
if 'id' in filters:
filters['id'] = [entry['port_id'] for
entry in port_bindings
if entry['port_id'] in filters['id']]
else:
filters['id'] = [entry['port_id'] for entry in port_bindings]
fixed_ips = filters.get('fixed_ips', {})
ip_addresses_s = fixed_ips.get('ip_address_substr')
query = super(Ml2Plugin, self)._get_ports_query(context, filters,

View File

@ -37,6 +37,7 @@ NETWORK_API_EXTENSIONS+=",rbac-policies"
NETWORK_API_EXTENSIONS+=",router"
NETWORK_API_EXTENSIONS+=",router_availability_zone"
NETWORK_API_EXTENSIONS+=",security-group"
NETWORK_API_EXTENSIONS+=",port-security-groups-filtering"
NETWORK_API_EXTENSIONS+=",segment"
NETWORK_API_EXTENSIONS+=",service-type"
NETWORK_API_EXTENSIONS+=",sorting"

View File

@ -44,6 +44,7 @@ from neutron.db import agents_db
from neutron.db import api as db_api
from neutron.db import models_v2
from neutron.db import provisioning_blocks
from neutron.db import securitygroups_db as sg_db
from neutron.db import segments_db
from neutron.extensions import multiprovidernet as mpnet
from neutron.objects import base as base_obj
@ -1278,6 +1279,45 @@ fixed_ips=ip_address_substr%%3D%s&fixed_ips=ip_address%%3D%s
self._delete('ports', port1['port']['id'])
self._delete('ports', port2['port']['id'])
def test_list_ports_filtered_by_security_groups(self):
# for this test we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
ctx = context.get_admin_context()
with self.port() as port1, self.port() as port2:
query_params = "security_groups=%s" % (
port1['port']['security_groups'][0])
ports_data = self._list('ports', query_params=query_params)
self.assertEqual(set([port1['port']['id'], port2['port']['id']]),
set([port['id'] for port in ports_data['ports']]))
self.assertEqual(2, len(ports_data['ports']))
query_params = "security_groups=%s&id=%s" % (
port1['port']['security_groups'][0],
port1['port']['id'])
ports_data = self._list('ports', query_params=query_params)
self.assertEqual(port1['port']['id'], ports_data['ports'][0]['id'])
self.assertEqual(1, len(ports_data['ports']))
temp_sg = {'security_group': {'tenant_id': 'some_tenant',
'name': '', 'description': 's'}}
sg_dbMixin = sg_db.SecurityGroupDbMixin()
sg = sg_dbMixin.create_security_group(ctx, temp_sg)
sg_dbMixin._delete_port_security_group_bindings(
ctx, port2['port']['id'])
sg_dbMixin._create_port_security_group_binding(
ctx, port2['port']['id'], sg['id'])
port2['port']['security_groups'][0] = sg['id']
query_params = "security_groups=%s&id=%s" % (
port1['port']['security_groups'][0],
port1['port']['id'])
ports_data = self._list('ports', query_params=query_params)
self.assertEqual(port1['port']['id'], ports_data['ports'][0]['id'])
self.assertEqual(1, len(ports_data['ports']))
query_params = "security_groups=%s&id=%s" % (
(port2['port']['security_groups'][0],
port2['port']['id']))
ports_data = self._list('ports', query_params=query_params)
self.assertEqual(port2['port']['id'], ports_data['ports'][0]['id'])
self.assertEqual(1, len(ports_data['ports']))
class TestMl2PortsV2WithRevisionPlugin(Ml2PluginV2TestCase):

View File

@ -0,0 +1,6 @@
---
features:
- |
Support port filtering on security group IDs.
The feature can be used if 'port-security-group-filtering' extension is available.