Merge "Support filtering port with IP address substring"

This commit is contained in:
Zuul 2018-01-17 05:30:38 +00:00 committed by Gerrit Code Review
commit 1813f7c497
6 changed files with 169 additions and 1 deletions

View 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

View 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 = [
]

View File

@ -46,6 +46,7 @@ from oslo_utils import excutils
from oslo_utils import importutils
from oslo_utils import uuidutils
import sqlalchemy
from sqlalchemy import or_
from sqlalchemy.orm import exc as sa_exc
from neutron._i18n import _
@ -153,7 +154,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
"availability_zone",
"network_availability_zone",
"default-subnetpools",
"subnet-service-types"]
"subnet-service-types",
"ip-substring-filtering"]
@property
def supported_extension_aliases(self):
@ -1849,6 +1851,19 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
return port.id
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(
self, context, network_id, candidate_hosts):
segments = segments_db.get_network_segments(context, network_id)

View File

@ -15,6 +15,7 @@ NETWORK_API_EXTENSIONS+=",external-net"
NETWORK_API_EXTENSIONS+=",extra_dhcp_opt"
NETWORK_API_EXTENSIONS+=",extraroute"
NETWORK_API_EXTENSIONS+=",flavors"
NETWORK_API_EXTENSIONS+=",ip-substring-filtering"
NETWORK_API_EXTENSIONS+=",l3-flavors"
NETWORK_API_EXTENSIONS+=",l3-ha"
NETWORK_API_EXTENSIONS+=",l3_agent_scheduler"

View File

@ -1217,6 +1217,67 @@ class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
# make sure that the grenade went off during the commit
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):

View File

@ -0,0 +1,4 @@
---
features:
- |
Support substring matching when filtering ports by IP address.