From 1446be28653466becea1208a17fb3e2da4edf8db Mon Sep 17 00:00:00 2001 From: Ethan Lynn Date: Tue, 12 May 2015 17:16:30 +0800 Subject: [PATCH] Add CIDR format validation Add a new constraint for CIDR format validation. Implemented: blueprint enhance-property-constraints Change-Id: Ica70147b713992c8523b92b12db0172a2966719c --- .../rackspace/resources/cloudnetworks.py | 11 +++----- .../rackspace/tests/test_cloudnetworks.py | 9 ++++--- heat/engine/clients/os/neutron.py | 19 +++++++++++++ .../resources/openstack/neutron/firewall.py | 10 +++++-- .../openstack/neutron/security_group.py | 5 +++- .../resources/openstack/neutron/subnet.py | 5 +++- .../resources/openstack/neutron/vpnservice.py | 8 +++++- heat/tests/neutron/test_neutron_client.py | 27 +++++++++++++++++++ setup.cfg | 1 + 9 files changed, 80 insertions(+), 15 deletions(-) diff --git a/contrib/rackspace/rackspace/resources/cloudnetworks.py b/contrib/rackspace/rackspace/resources/cloudnetworks.py index 30dd6aa837..c78478181b 100644 --- a/contrib/rackspace/rackspace/resources/cloudnetworks.py +++ b/contrib/rackspace/rackspace/resources/cloudnetworks.py @@ -11,10 +11,8 @@ # License for the specific language governing permissions and limitations # under the License. -import netaddr from oslo_log import log as logging -from heat.common import exception from heat.common.i18n import _ from heat.common.i18n import _LW from heat.engine import attributes @@ -79,7 +77,10 @@ class CloudNetwork(resource.Resource): properties.Schema.STRING, _("The IP block from which to allocate the network. For example, " "172.16.0.0/24 or 2001:DB8::/64."), - required=True + required=True, + constraints=[ + constraints.CustomConstraint('net_cidr') + ] ) } @@ -154,10 +155,6 @@ class CloudNetwork(resource.Resource): def validate(self): super(CloudNetwork, self).validate() - try: - netaddr.IPNetwork(self.properties[self.CIDR]) - except netaddr.core.AddrFormatError: - raise exception.StackValidationFailed(message=_("Invalid cidr")) def _resolve_attribute(self, name): net = self.network() diff --git a/contrib/rackspace/rackspace/tests/test_cloudnetworks.py b/contrib/rackspace/rackspace/tests/test_cloudnetworks.py index 1e1324ebd6..80623918bb 100644 --- a/contrib/rackspace/rackspace/tests/test_cloudnetworks.py +++ b/contrib/rackspace/rackspace/tests/test_cloudnetworks.py @@ -116,12 +116,15 @@ class CloudNetworkTest(common.HeatTestCase): self.assertEqual(expect_label, res.FnGetAtt('label')) self.assertEqual(expect_cidr, res.FnGetAtt('cidr')) - def test_create_bad_cider(self, mock_client): - self._template['resources']['cnw']['properties']['cidr'] = "bad cidr" + def test_create_bad_cidr(self, mock_client): + prop = self._template['resources']['cnw']['properties'] + prop['cidr'] = "bad cidr" self._parse_stack() exc = self.assertRaises(exception.StackValidationFailed, self.stack.validate) - self.assertIn("Invalid cidr", six.text_type(exc)) + self.assertIn("Invalid net cidr", six.text_type(exc)) + # reset property + prop['cidr'] = "172.16.0.0/24" def test_check(self, mock_client): self._setup_stack(mock_client) diff --git a/heat/engine/clients/os/neutron.py b/heat/engine/clients/os/neutron.py index 189a2b4fdc..399801e32b 100644 --- a/heat/engine/clients/os/neutron.py +++ b/heat/engine/clients/os/neutron.py @@ -12,6 +12,7 @@ # under the License. import netaddr +import six from neutronclient.common import exceptions from neutronclient.neutron import v2_0 as neutronV20 @@ -189,3 +190,21 @@ class MACConstraint(constraints.BaseCustomConstraint): def validate(self, value, context): self._error_message = 'Invalid MAC address.' return netaddr.valid_mac(value) + + +class CIDRConstraint(constraints.BaseCustomConstraint): + + def _validate_whitespace(self, data): + self._error_message = ("Invalid net cidr '%s' contains " + "whitespace" % data) + if len(data.split()) > 1: + return False + return True + + def validate(self, value, context): + try: + netaddr.IPNetwork(value) + return self._validate_whitespace(value) + except Exception as ex: + self._error_message = 'Invalid net cidr %s ' % six.text_type(ex) + return False diff --git a/heat/engine/resources/openstack/neutron/firewall.py b/heat/engine/resources/openstack/neutron/firewall.py index ba9a8943da..ac8eaaf49a 100644 --- a/heat/engine/resources/openstack/neutron/firewall.py +++ b/heat/engine/resources/openstack/neutron/firewall.py @@ -298,12 +298,18 @@ class FirewallRule(neutron.NeutronResource): SOURCE_IP_ADDRESS: properties.Schema( properties.Schema.STRING, _('Source IP address or CIDR.'), - update_allowed=True + update_allowed=True, + constraints=[ + constraints.CustomConstraint('net_cidr') + ] ), DESTINATION_IP_ADDRESS: properties.Schema( properties.Schema.STRING, _('Destination IP address or CIDR.'), - update_allowed=True + update_allowed=True, + constraints=[ + constraints.CustomConstraint('net_cidr') + ] ), SOURCE_PORT: properties.Schema( properties.Schema.STRING, diff --git a/heat/engine/resources/openstack/neutron/security_group.py b/heat/engine/resources/openstack/neutron/security_group.py index 99db1d860d..0e7d3b6aac 100644 --- a/heat/engine/resources/openstack/neutron/security_group.py +++ b/heat/engine/resources/openstack/neutron/security_group.py @@ -99,7 +99,10 @@ class SecurityGroup(neutron.NeutronResource): RULE_REMOTE_IP_PREFIX: properties.Schema( properties.Schema.STRING, _('The remote IP prefix (CIDR) to be associated with this ' - 'security group rule.') + 'security group rule.'), + constraints=[ + constraints.CustomConstraint('net_cidr') + ] ), } diff --git a/heat/engine/resources/openstack/neutron/subnet.py b/heat/engine/resources/openstack/neutron/subnet.py index 8b281f6080..e72ccd6a03 100644 --- a/heat/engine/resources/openstack/neutron/subnet.py +++ b/heat/engine/resources/openstack/neutron/subnet.py @@ -84,7 +84,10 @@ class Subnet(neutron.NeutronResource): CIDR: properties.Schema( properties.Schema.STRING, _('The CIDR.'), - required=True + required=True, + constraints=[ + constraints.CustomConstraint('net_cidr') + ] ), VALUE_SPECS: properties.Schema( properties.Schema.MAP, diff --git a/heat/engine/resources/openstack/neutron/vpnservice.py b/heat/engine/resources/openstack/neutron/vpnservice.py index 07a0e1291b..f7ba93c420 100644 --- a/heat/engine/resources/openstack/neutron/vpnservice.py +++ b/heat/engine/resources/openstack/neutron/vpnservice.py @@ -219,7 +219,13 @@ class IPsecSiteConnection(neutron.NeutronResource): PEER_CIDRS: properties.Schema( properties.Schema.LIST, _('Remote subnet(s) in CIDR format.'), - required=True + required=True, + schema=properties.Schema( + properties.Schema.STRING, + constraints=[ + constraints.CustomConstraint('net_cidr') + ] + ) ), MTU: properties.Schema( properties.Schema.INTEGER, diff --git a/heat/tests/neutron/test_neutron_client.py b/heat/tests/neutron/test_neutron_client.py index 82339f99e4..7d33c2a042 100644 --- a/heat/tests/neutron/test_neutron_client.py +++ b/heat/tests/neutron/test_neutron_client.py @@ -254,3 +254,30 @@ class TestMACConstraint(common.HeatTestCase): ] for mac in invalidate_format: self.assertFalse(self.constraint.validate(mac, None)) + + +class TestCIDRConstraint(common.HeatTestCase): + + def setUp(self): + super(TestCIDRConstraint, self).setUp() + self.constraint = neutron.CIDRConstraint() + + def test_valid_cidr_format(self): + validate_format = [ + '10.0.0.0/24', + '6000::/64', + '8.8.8.8' + ] + for cidr in validate_format: + self.assertTrue(self.constraint.validate(cidr, None)) + + def test_invalid_cidr_format(self): + invalidate_format = [ + '::/129', + 'Invalid cidr', + '300.0.0.0/24', + '10.0.0.0/33', + '8.8.8.0/ 24' + ] + for cidr in invalidate_format: + self.assertFalse(self.constraint.validate(cidr, None)) diff --git a/setup.cfg b/setup.cfg index 87305aa4c4..10797eee86 100644 --- a/setup.cfg +++ b/setup.cfg @@ -74,6 +74,7 @@ heat.constraints = trove.flavor = heat.engine.clients.os.trove:FlavorConstraint ip_addr = heat.engine.clients.os.neutron:IPConstraint mac_addr = heat.engine.clients.os.neutron:MACConstraint + net_cidr = heat.engine.clients.os.neutron:CIDRConstraint heat.stack_lifecycle_plugins =