Support filtering port with IP address substring
Neutron currently supports filtering ports by matching the exact IP address. This patch adds support for substring matching using "LIKE" SQL operator. This patch also added a new API extension to show whether or not the substring matching capability is available. APIImpact add IP address substring filtering on listing ports API-ref: I97259b85a2dce5a54bb6ea2cb9d9779ec0a25504 Co-Authored-By: Zhenyu Zheng <zhengzhenyu@huawei.com> Change-Id: I9549b2ba676e1bad0812682c3f3f3c97de15f5f6 Closes-Bug: #1718605
This commit is contained in:
parent
81b813fd46
commit
5c601bebeb
23
neutron/extensions/ip_substring_port_filtering.py
Normal file
23
neutron/extensions/ip_substring_port_filtering.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# Copyright (c) 2017 Huawei Technology, Inc. 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 ip_substring_port_filtering_lib as apidef
|
||||||
|
|
||||||
|
|
||||||
|
class Ip_substring_port_filtering(extensions.APIExtensionDescriptor):
|
||||||
|
"""Extension class supporting IP substring port filtering."""
|
||||||
|
|
||||||
|
api_definition = apidef
|
64
neutron/extensions/ip_substring_port_filtering_lib.py
Normal file
64
neutron/extensions/ip_substring_port_filtering_lib.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
TODO(hongbin): This module should be deleted once neutron-lib containing
|
||||||
|
https://review.openstack.org/#/c/525284/ change is released.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The alias of the extension.
|
||||||
|
ALIAS = 'ip-substring-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 = 'IP address substring filtering'
|
||||||
|
|
||||||
|
# The description of the extension.
|
||||||
|
DESCRIPTION = "Provides IP address substring filtering when listing ports"
|
||||||
|
|
||||||
|
# A timestamp of when the extension was introduced.
|
||||||
|
UPDATED_TIMESTAMP = "2017-11-28T09: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 = [
|
||||||
|
]
|
@ -46,6 +46,7 @@ from oslo_utils import excutils
|
|||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
from sqlalchemy import or_
|
||||||
from sqlalchemy.orm import exc as sa_exc
|
from sqlalchemy.orm import exc as sa_exc
|
||||||
|
|
||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
@ -153,7 +154,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
"availability_zone",
|
"availability_zone",
|
||||||
"network_availability_zone",
|
"network_availability_zone",
|
||||||
"default-subnetpools",
|
"default-subnetpools",
|
||||||
"subnet-service-types"]
|
"subnet-service-types",
|
||||||
|
"ip-substring-filtering"]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_extension_aliases(self):
|
def supported_extension_aliases(self):
|
||||||
@ -1849,6 +1851,19 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
return port.id
|
return port.id
|
||||||
return device
|
return device
|
||||||
|
|
||||||
|
def _get_ports_query(self, context, filters=None, *args, **kwargs):
|
||||||
|
filters = filters or {}
|
||||||
|
fixed_ips = filters.get('fixed_ips', {})
|
||||||
|
ip_addresses_s = fixed_ips.get('ip_address_substr')
|
||||||
|
query = super(Ml2Plugin, self)._get_ports_query(context, filters,
|
||||||
|
*args, **kwargs)
|
||||||
|
if ip_addresses_s:
|
||||||
|
substr_filter = or_(*[models_v2.Port.fixed_ips.any(
|
||||||
|
models_v2.IPAllocation.ip_address.like('%%%s%%' % ip))
|
||||||
|
for ip in ip_addresses_s])
|
||||||
|
query = query.filter(substr_filter)
|
||||||
|
return query
|
||||||
|
|
||||||
def filter_hosts_with_network_access(
|
def filter_hosts_with_network_access(
|
||||||
self, context, network_id, candidate_hosts):
|
self, context, network_id, candidate_hosts):
|
||||||
segments = segments_db.get_network_segments(context, network_id)
|
segments = segments_db.get_network_segments(context, network_id)
|
||||||
|
@ -15,6 +15,7 @@ NETWORK_API_EXTENSIONS+=",external-net"
|
|||||||
NETWORK_API_EXTENSIONS+=",extra_dhcp_opt"
|
NETWORK_API_EXTENSIONS+=",extra_dhcp_opt"
|
||||||
NETWORK_API_EXTENSIONS+=",extraroute"
|
NETWORK_API_EXTENSIONS+=",extraroute"
|
||||||
NETWORK_API_EXTENSIONS+=",flavors"
|
NETWORK_API_EXTENSIONS+=",flavors"
|
||||||
|
NETWORK_API_EXTENSIONS+=",ip-substring-filtering"
|
||||||
NETWORK_API_EXTENSIONS+=",l3-flavors"
|
NETWORK_API_EXTENSIONS+=",l3-flavors"
|
||||||
NETWORK_API_EXTENSIONS+=",l3-ha"
|
NETWORK_API_EXTENSIONS+=",l3-ha"
|
||||||
NETWORK_API_EXTENSIONS+=",l3_agent_scheduler"
|
NETWORK_API_EXTENSIONS+=",l3_agent_scheduler"
|
||||||
|
@ -1216,6 +1216,67 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
|
|||||||
# make sure that the grenade went off during the commit
|
# make sure that the grenade went off during the commit
|
||||||
self.assertTrue(listener.except_raised)
|
self.assertTrue(listener.except_raised)
|
||||||
|
|
||||||
|
def test_list_ports_filtered_by_fixed_ip_substring(self):
|
||||||
|
# for this test we need to enable overlapping ips
|
||||||
|
cfg.CONF.set_default('allow_overlapping_ips', True)
|
||||||
|
with self.port() as port1, self.port():
|
||||||
|
fixed_ips = port1['port']['fixed_ips'][0]
|
||||||
|
query_params = """
|
||||||
|
fixed_ips=ip_address_substr%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||||
|
""".strip() % (fixed_ips['ip_address'][:-1],
|
||||||
|
fixed_ips['subnet_id'])
|
||||||
|
self._test_list_resources('port', [port1],
|
||||||
|
query_params=query_params)
|
||||||
|
query_params = """
|
||||||
|
fixed_ips=ip_address_substr%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||||
|
""".strip() % (fixed_ips['ip_address'][1:],
|
||||||
|
fixed_ips['subnet_id'])
|
||||||
|
self._test_list_resources('port', [port1],
|
||||||
|
query_params=query_params)
|
||||||
|
query_params = """
|
||||||
|
fixed_ips=ip_address_substr%%3D%s&fixed_ips=subnet_id%%3D%s
|
||||||
|
""".strip() % ('192.168.',
|
||||||
|
fixed_ips['subnet_id'])
|
||||||
|
self._test_list_resources('port', [],
|
||||||
|
query_params=query_params)
|
||||||
|
|
||||||
|
def test_list_ports_filtered_by_fixed_ip_substring_dual_stack(self):
|
||||||
|
with self.subnet() as subnet:
|
||||||
|
# Get a IPv4 and IPv6 address
|
||||||
|
tenant_id = subnet['subnet']['tenant_id']
|
||||||
|
net_id = subnet['subnet']['network_id']
|
||||||
|
res = self._create_subnet(
|
||||||
|
self.fmt,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
net_id=net_id,
|
||||||
|
cidr='2607:f0d0:1002:51::/124',
|
||||||
|
ip_version=6,
|
||||||
|
gateway_ip=constants.ATTR_NOT_SPECIFIED)
|
||||||
|
subnet2 = self.deserialize(self.fmt, res)
|
||||||
|
kwargs = {"fixed_ips":
|
||||||
|
[{'subnet_id': subnet['subnet']['id']},
|
||||||
|
{'subnet_id': subnet2['subnet']['id']}]}
|
||||||
|
res = self._create_port(self.fmt, net_id=net_id, **kwargs)
|
||||||
|
port1 = self.deserialize(self.fmt, res)
|
||||||
|
res = self._create_port(self.fmt, net_id=net_id, **kwargs)
|
||||||
|
port2 = self.deserialize(self.fmt, res)
|
||||||
|
fixed_ips = port1['port']['fixed_ips']
|
||||||
|
self.assertEqual(2, len(fixed_ips))
|
||||||
|
query_params = """
|
||||||
|
fixed_ips=ip_address_substr%%3D%s&fixed_ips=ip_address%%3D%s
|
||||||
|
""".strip() % (fixed_ips[0]['ip_address'][:-1],
|
||||||
|
fixed_ips[1]['ip_address'])
|
||||||
|
self._test_list_resources('port', [port1],
|
||||||
|
query_params=query_params)
|
||||||
|
query_params = """
|
||||||
|
fixed_ips=ip_address_substr%%3D%s&fixed_ips=ip_address%%3D%s
|
||||||
|
""".strip() % ('192.168.',
|
||||||
|
fixed_ips[1]['ip_address'])
|
||||||
|
self._test_list_resources('port', [],
|
||||||
|
query_params=query_params)
|
||||||
|
self._delete('ports', port1['port']['id'])
|
||||||
|
self._delete('ports', port2['port']['id'])
|
||||||
|
|
||||||
|
|
||||||
class TestMl2PortsV2WithRevisionPlugin(Ml2PluginV2TestCase):
|
class TestMl2PortsV2WithRevisionPlugin(Ml2PluginV2TestCase):
|
||||||
|
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Support substring matching when filtering ports by IP address.
|
Loading…
Reference in New Issue
Block a user