From e686f3d0d36a519a2efe80e4273749e08c80e521 Mon Sep 17 00:00:00 2001 From: Pedro Henrique Pereira Martins Date: Tue, 28 Sep 2021 14:15:41 -0300 Subject: [PATCH] Add default values in port forwarding port ranges definitions Also, add a new common type 'PortRanges' to validate port ranges fields to the specific format PORT_RANGE:PORT_RANGE. Implements: blueprint floatingips-portforwarding-ranges Related-Bug: #1885921 Change-Id: Idd257d6e64ef43ccf7afabd30003986628b0d25a --- .../api/definitions/fip_pf_port_range.py | 12 ++-- neutron_lib/objects/common_types.py | 65 +++++++++++++++++++ .../tests/unit/objects/test_common_types.py | 13 ++++ 3 files changed, 86 insertions(+), 4 deletions(-) diff --git a/neutron_lib/api/definitions/fip_pf_port_range.py b/neutron_lib/api/definitions/fip_pf_port_range.py index a7962bec9..cb19b1b32 100644 --- a/neutron_lib/api/definitions/fip_pf_port_range.py +++ b/neutron_lib/api/definitions/fip_pf_port_range.py @@ -30,21 +30,25 @@ SUB_RESOURCE_ATTRIBUTE_MAP = { 'validate': {'type:port_range': None}, 'is_visible': True, 'is_sort_key': True, - 'is_filter': True}, + 'is_filter': True, + 'default': None}, INTERNAL_PORT_RANGE: { 'allow_post': True, 'allow_put': True, 'validate': {'type:port_range': None}, - 'is_visible': True}, + 'is_visible': True, + 'default': None}, pfw.EXTERNAL_PORT: { 'allow_post': True, 'allow_put': True, 'validate': {'type:range_or_none': [1, 65535]}, 'is_visible': True, 'is_sort_key': True, - 'is_filter': True}, + 'is_filter': True, + 'default': None}, pfw.INTERNAL_PORT: { 'allow_post': True, 'allow_put': True, 'validate': {'type:range_or_none': [1, 65535]}, - 'is_visible': True}, + 'is_visible': True, + 'default': None}, } } } diff --git a/neutron_lib/objects/common_types.py b/neutron_lib/objects/common_types.py index 5cbe2435e..9f913f9f0 100644 --- a/neutron_lib/objects/common_types.py +++ b/neutron_lib/objects/common_types.py @@ -63,6 +63,71 @@ class IPNetworkPrefixLenField(obj_fields.AutoTypedField): AUTO_TYPE = IPNetworkPrefixLen() +class PortRanges(obj_fields.FieldType): + @staticmethod + def _is_port_acceptable(port): + start = lib_constants.PORT_RANGE_MIN + end = lib_constants.PORT_RANGE_MAX + return start <= port <= end + + def get_schema(self): + return {'type': ['string', 'integer']} + + def _validate_port(self, attr, value): + if self._is_port_acceptable(value): + return + raise ValueError( + _('The port %(value)s does not respect the ' + 'range (%(min)s, %(max)s) in field %(attr)s') + % {'attr': attr, + 'value': value, + 'min': lib_constants.PORT_RANGE_MIN, + 'max': lib_constants.PORT_RANGE_MAX}) + + def coerce(self, obj, attr, value): + if isinstance(value, int): + self._validate_port(attr, value) + return value + if isinstance(value, str): + if value.isnumeric(): + self._validate_port(attr, int(value)) + return value + values = value.split(':') + if len(values) == 2: + start, end = list(map(int, values)) + if start > end: + raise ValueError( + _('The first port %(start)s must be less or equals ' + 'than the second port %(end)s of the port range ' + 'configuration %(value)s' + 'in field %(attr)s.') % { + 'attr': attr, + 'value': value, + 'start': start, + 'end': end}) + self._validate_port(attr, start) + self._validate_port(attr, end) + return value + raise ValueError( + _('The field %(attr)s must be in the format PORT_RANGE or' + 'PORT_RANGE:PORT_RANGE (two numeric values separated ' + 'by a colon), and PORT_RANGE must be a numeric ' + 'value and respect the range ' + '(%(min)s, %(max)s).') % { + 'attr': attr, + 'min': lib_constants.PORT_RANGE_MIN, + 'max': lib_constants.PORT_RANGE_MAX}) + raise ValueError( + _('An string/int PORT_RANGE or a string with ' + 'PORT_RANGE:PORT_RANGE format is ' + 'expected in field %(attr)s, not a %(type)s') % { + 'attr': attr, 'type': value}) + + +class PortRangesField(obj_fields.AutoTypedField): + AUTO_TYPE = PortRanges() + + class PortRange(RangeConstrainedInteger): def __init__(self, start=lib_constants.PORT_RANGE_MIN, **kwargs): super().__init__(start=start, diff --git a/neutron_lib/tests/unit/objects/test_common_types.py b/neutron_lib/tests/unit/objects/test_common_types.py index 8748c9e7e..67665ab32 100644 --- a/neutron_lib/tests/unit/objects/test_common_types.py +++ b/neutron_lib/tests/unit/objects/test_common_types.py @@ -199,6 +199,19 @@ class FlowDirectionAndAnyEnumFieldTest(test_base.BaseTestCase, TestField): self.assertEqual("'%s'" % in_val, self.field.stringify(in_val)) +class PortRangesFieldTest(test_base.BaseTestCase, TestField): + def setUp(self): + super(PortRangesFieldTest, self).setUp() + self.field = common_types.PortRangesField() + self.coerce_good_values = [(val, val) for val in ( + '80:80', '80:90', '80', 80)] + self.coerce_bad_values = ('x', 0, 99999, '99999', + '9999:', ':9999', + '99999:100000', '80:70') + self.to_primitive_values = self.coerce_good_values + self.from_primitive_values = self.coerce_good_values + + class DomainNameFieldTest(test_base.BaseTestCase, TestField): def setUp(self): super(DomainNameFieldTest, self).setUp()