From 39c04fdc2c94b8ee48600f3148c5f850645b7c4e Mon Sep 17 00:00:00 2001 From: Kevin Benton Date: Mon, 17 Mar 2014 17:06:46 -0700 Subject: [PATCH] Allow CIDRs with non-zero masked portions Allow users to specify CIDRs with bits other than zeros in the masked portion of the subnet. e.g. 192.168.1.5/24 is accepted and converted to 192.168.1.0/24. Closes-Bug: #1204173 Change-Id: I7ddff41e6988feb6e2a87e40a4d99db7174415b1 --- neutron/api/v2/attributes.py | 2 +- neutron/db/db_base_plugin_v2.py | 5 ++++- neutron/tests/unit/test_attributes.py | 12 +++++------ neutron/tests/unit/test_db_plugin.py | 30 ++++++++++++++++++--------- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/neutron/api/v2/attributes.py b/neutron/api/v2/attributes.py index 1b90bb73a..8a2f74cf2 100644 --- a/neutron/api/v2/attributes.py +++ b/neutron/api/v2/attributes.py @@ -291,7 +291,7 @@ def _validate_subnet(data, valid_values=None): msg = None try: net = netaddr.IPNetwork(_validate_no_whitespace(data)) - if ('/' not in data or net.network != net.ip): + if '/' not in data: msg = _("'%(data)s' isn't a recognized IP subnet cidr," " '%(cidr)s' is recommended") % {"data": data, "cidr": net.cidr} diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index b7ad1ecff..875582f15 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -1062,8 +1062,11 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, def create_subnet(self, context, subnet): + net = netaddr.IPNetwork(subnet['subnet']['cidr']) + # turn the CIDR into a proper subnet + subnet['subnet']['cidr'] = '%s/%s' % (net.network, net.prefixlen) + s = subnet['subnet'] - net = netaddr.IPNetwork(s['cidr']) if s['gateway_ip'] is attributes.ATTR_NOT_SPECIFIED: s['gateway_ip'] = str(netaddr.IPAddress(net.first + 1)) diff --git a/neutron/tests/unit/test_attributes.py b/neutron/tests/unit/test_attributes.py index b4a44a6ed..53edc2693 100644 --- a/neutron/tests/unit/test_attributes.py +++ b/neutron/tests/unit/test_attributes.py @@ -459,13 +459,11 @@ class TestAttributes(base.BaseTestCase): "cidr": "10.0.2.0/32"} self.assertEqual(msg, error) - # Invalid - IPv4 with final octets - cidr = "192.168.1.1/24" - msg = validator(cidr, None) - error = _("'%(data)s' isn't a recognized IP subnet cidr," - " '%(cidr)s' is recommended") % {"data": cidr, - "cidr": "192.168.1.0/24"} - self.assertEqual(msg, error) + # Valid - IPv4 with non-zero masked bits is ok + for i in range(1, 255): + cidr = "192.168.1.%s/24" % i + msg = validator(cidr, None) + self.assertIsNone(msg) # Invalid - IPv6 without final octets, missing mask cidr = "fe80::" diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py index c7e780ac0..0194c1716 100644 --- a/neutron/tests/unit/test_db_plugin.py +++ b/neutron/tests/unit/test_db_plugin.py @@ -2503,16 +2503,26 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): device_owner='fake_owner', set_context=False) - def test_create_subnet_bad_cidr(self): - with self.network() as network: - data = {'subnet': {'network_id': network['network']['id'], - 'cidr': '10.0.2.5/24', - 'ip_version': 4, - 'tenant_id': network['network']['tenant_id']}} - - subnet_req = self.new_create_request('subnets', data) - res = subnet_req.get_response(self.api) - self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) + def test_create_subnet_nonzero_cidr(self): + with contextlib.nested( + self.subnet(cidr='10.129.122.5/8'), + self.subnet(cidr='11.129.122.5/15'), + self.subnet(cidr='12.129.122.5/16'), + self.subnet(cidr='13.129.122.5/18'), + self.subnet(cidr='14.129.122.5/22'), + self.subnet(cidr='15.129.122.5/24'), + self.subnet(cidr='16.129.122.5/28'), + self.subnet(cidr='17.129.122.5/32') + ) as subs: + # the API should accept and correct these for users + self.assertEqual(subs[0]['subnet']['cidr'], '10.0.0.0/8') + self.assertEqual(subs[1]['subnet']['cidr'], '11.128.0.0/15') + self.assertEqual(subs[2]['subnet']['cidr'], '12.129.0.0/16') + self.assertEqual(subs[3]['subnet']['cidr'], '13.129.64.0/18') + self.assertEqual(subs[4]['subnet']['cidr'], '14.129.120.0/22') + self.assertEqual(subs[5]['subnet']['cidr'], '15.129.122.0/24') + self.assertEqual(subs[6]['subnet']['cidr'], '16.129.122.0/28') + self.assertEqual(subs[7]['subnet']['cidr'], '17.129.122.5/32') def test_create_subnet_bad_ip_version(self): with self.network() as network: -- 2.45.2