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
This commit is contained in:

committed by
Pedro Henrique

parent
f9b428667b
commit
e686f3d0d3
@@ -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},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -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()
|
||||
|
Reference in New Issue
Block a user