8ecb28dd09
This patch is dependent on commit I8d03528f8f45f5f50fa467b39245a513a37c5d89. It integrates the VersionedObject with the existing code. Integration revealed that using IPAddress is not correct for allowed address pairs, because the address can also represent a subnet. Another issue revealed by the integration is that we must retain the original string format passed by users through API for MAC addresses. Neither we can use IPNetworkField from oslo.versionedobjects for ip_address field because it will then always append prefix length to base network address, even if prefix length is maximum for the type of IP network (meaning, the address actually represents a single host), which is contradictory to how API currently behaves (returning mask-less addresses for /32 - for ipv4 - and /128 - for ipv6 - prefix lengths). To solve those issues, 'authentic' flavors for netaddr.EUI and netaddr.IPNetwork types are introduced. Those 'authentic' flavors attempt to retain the original string representation, as passed by the caller. Since base IPNetworkField recreates network object on coerce(), and hence looses information about the original string representation, we introduce our custom flavor of the field type that reuses the network object passed by the caller. The change for the type of ip_address field triggers hash change. Anyway, we are safe to change it without considering backwards compatibility, because the object is not used anywhere yet. Co-Authored-By: Ihar Hrachyshka <ihrachys@redhat.com> Change-Id: I3c937267ce789ed510373616713b3fa9517c18ac Partial-Bug: #1541928
200 lines
8.1 KiB
Python
200 lines
8.1 KiB
Python
# Copyright 2016 OpenStack Foundation
|
|
# 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.
|
|
|
|
import abc
|
|
|
|
from neutron_lib import constants as const
|
|
|
|
from neutron.common import constants
|
|
from neutron.objects import common_types
|
|
from neutron.tests import base as test_base
|
|
from neutron.tests import tools
|
|
|
|
|
|
class TestField(object):
|
|
|
|
def test_coerce_good_values(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual(out_val, self.field.coerce('obj', 'attr', in_val))
|
|
|
|
def test_coerce_bad_values(self):
|
|
for in_val in self.coerce_bad_values:
|
|
self.assertRaises((TypeError, ValueError),
|
|
self.field.coerce, 'obj', 'attr', in_val)
|
|
|
|
def test_to_primitive(self):
|
|
for in_val, prim_val in self.to_primitive_values:
|
|
self.assertEqual(prim_val, self.field.to_primitive('obj', 'attr',
|
|
in_val))
|
|
|
|
def test_from_primitive(self):
|
|
class ObjectLikeThing(object):
|
|
_context = 'context'
|
|
|
|
for prim_val, out_val in self.from_primitive_values:
|
|
self.assertEqual(out_val, self.field.from_primitive(
|
|
ObjectLikeThing, 'attr', prim_val))
|
|
|
|
@abc.abstractmethod
|
|
def test_stringify(self):
|
|
'''This test should validate stringify() format for new field types.'''
|
|
|
|
|
|
class IPV6ModeEnumFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(IPV6ModeEnumFieldTest, self).setUp()
|
|
self.field = common_types.IPV6ModeEnumField()
|
|
self.coerce_good_values = [(mode, mode)
|
|
for mode in constants.IPV6_MODES]
|
|
self.coerce_bad_values = ['6', 4, 'type', 'slaacc']
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual("'%s'" % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class DscpMarkFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(DscpMarkFieldTest, self).setUp()
|
|
self.field = common_types.DscpMarkField()
|
|
self.coerce_good_values = [(val, val)
|
|
for val in constants.VALID_DSCP_MARKS]
|
|
self.coerce_bad_values = ['6', 'str', [], {}, object()]
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual("%s" % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class IPNetworkPrefixLenFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(IPNetworkPrefixLenFieldTest, self).setUp()
|
|
self.field = common_types.IPNetworkPrefixLenField()
|
|
self.coerce_good_values = [(x, x) for x in (0, 32, 128, 42)]
|
|
self.coerce_bad_values = ['len', '1', 129, -1]
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual("%s" % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class MACAddressFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(MACAddressFieldTest, self).setUp()
|
|
self.field = common_types.MACAddressField()
|
|
mac1 = tools.get_random_EUI()
|
|
mac2 = tools.get_random_EUI()
|
|
self.coerce_good_values = [(mac1, mac1), (mac2, mac2)]
|
|
self.coerce_bad_values = [
|
|
'XXXX', 'ypp', 'g3:vvv',
|
|
# the field type is strict and does not allow to pass strings, even
|
|
# if they represent a valid MAC address
|
|
tools.get_random_mac(),
|
|
]
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual('%s' % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class IPNetworkFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(IPNetworkFieldTest, self).setUp()
|
|
self.field = common_types.IPNetworkField()
|
|
addrs = [
|
|
tools.get_random_ip_network(version=ip_version)
|
|
for ip_version in constants.IP_ALLOWED_VERSIONS
|
|
]
|
|
self.coerce_good_values = [(addr, addr) for addr in addrs]
|
|
self.coerce_bad_values = [
|
|
'ypp', 'g3:vvv',
|
|
# the field type is strict and does not allow to pass strings, even
|
|
# if they represent a valid IP network
|
|
'10.0.0.0/24',
|
|
]
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual('%s' % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class IPVersionEnumFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(IPVersionEnumFieldTest, self).setUp()
|
|
self.field = common_types.IPVersionEnumField()
|
|
self.coerce_good_values = [(val, val)
|
|
for val in constants.IP_ALLOWED_VERSIONS]
|
|
self.coerce_bad_values = [5, 0, -1, 'str']
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual("%s" % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class FlowDirectionEnumFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(FlowDirectionEnumFieldTest, self).setUp()
|
|
self.field = common_types.FlowDirectionEnumField()
|
|
self.coerce_good_values = [(val, val)
|
|
for val in constants.VALID_DIRECTIONS]
|
|
self.coerce_bad_values = ['test', '8', 10, []]
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual("'%s'" % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class EtherTypeEnumFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(EtherTypeEnumFieldTest, self).setUp()
|
|
self.field = common_types.EtherTypeEnumField()
|
|
self.coerce_good_values = [(val, val)
|
|
for val in constants.VALID_ETHERTYPES]
|
|
self.coerce_bad_values = ['IpV4', 8, 'str', 'ipv6']
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual("'%s'" % in_val, self.field.stringify(in_val))
|
|
|
|
|
|
class IpProtocolEnumFieldTest(test_base.BaseTestCase, TestField):
|
|
def setUp(self):
|
|
super(IpProtocolEnumFieldTest, self).setUp()
|
|
self.field = common_types.IpProtocolEnumField()
|
|
self.coerce_good_values = [(val, val)
|
|
for val in
|
|
list(const.IP_PROTOCOL_MAP.keys())]
|
|
self.coerce_bad_values = ['test', '8', 10, 'Udp']
|
|
self.to_primitive_values = self.coerce_good_values
|
|
self.from_primitive_values = self.coerce_good_values
|
|
|
|
def test_stringify(self):
|
|
for in_val, out_val in self.coerce_good_values:
|
|
self.assertEqual("'%s'" % in_val, self.field.stringify(in_val))
|