From cbb15084912151ea39ec3817f047dcf3e6b7c820 Mon Sep 17 00:00:00 2001 From: Qin Zhao Date: Tue, 16 Sep 2014 15:17:44 +0800 Subject: [PATCH] Add InvalidIpForNetwork and InvalidIpForSubnet exception When posting an 'attach interface' request to Nova with an invalid ip for defined network, Nova returns an HTTP 500 error. In fact, Neutron returns 'InvalidInput' error, but Neutron client is not able to translate this error to a specific exception. So that a general 'BadRequest' exception is thrown. Neutron client and Nova need a more specific Neutron error type, in order to address and translate the error in to a proper Nova exception. APIImpact Change-Id: Id4d3108234a198c0eb75237ea8991a72bd3cc4bb Partial-Bug: 1369871 --- neutron/common/exceptions.py | 10 ++++++ neutron/db/db_base_plugin_v2.py | 13 ++++---- .../unit/opencontrail/test_contrail_plugin.py | 11 +++++-- neutron/tests/unit/test_db_plugin.py | 31 +++++++++++++++++++ 4 files changed, 56 insertions(+), 9 deletions(-) diff --git a/neutron/common/exceptions.py b/neutron/common/exceptions.py index 03f4e0777..e2b9f7bde 100644 --- a/neutron/common/exceptions.py +++ b/neutron/common/exceptions.py @@ -147,6 +147,16 @@ class DNSNameServersExhausted(BadRequest): "The number of DNS nameservers exceeds the limit %(quota)s.") +class InvalidIpForNetwork(BadRequest): + message = _("IP address %(ip_address)s is not a valid IP " + "for any of the subnets on the specified network.") + + +class InvalidIpForSubnet(BadRequest): + message = _("IP address %(ip_address)s is not a valid IP " + "for the specified subnet.") + + class IpAddressInUse(InUse): message = _("Unable to complete operation for network %(net_id)s. " "The IP address %(ip_address)s is in use.") diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index 03bbe948f..2145a86d3 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -358,7 +358,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, IPs. Include the subnet_id in the result if only an IP address is configured. - :raises: InvalidInput, IpAddressInUse + :raises: InvalidInput, IpAddressInUse, InvalidIpForNetwork, + InvalidIpForSubnet """ fixed_ip_set = [] for fixed in fixed_ips: @@ -377,9 +378,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, subnet_id = subnet['id'] break if not found: - msg = _('IP address %s is not a valid IP for the defined ' - 'networks subnets') % fixed['ip_address'] - raise n_exc.InvalidInput(error_message=msg) + raise n_exc.InvalidIpForNetwork( + ip_address=fixed['ip_address']) else: subnet = self._get_subnet(context, fixed['subnet_id']) if subnet['network_id'] != network_id: @@ -403,9 +403,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, if (not found and not self._check_subnet_ip(subnet['cidr'], fixed['ip_address'])): - msg = _('IP address %s is not a valid IP for the defined ' - 'subnet') % fixed['ip_address'] - raise n_exc.InvalidInput(error_message=msg) + raise n_exc.InvalidIpForSubnet( + ip_address=fixed['ip_address']) if (ipv6_utils.is_auto_address_subnet(subnet) and device_owner not in constants.ROUTER_INTERFACE_OWNERS): diff --git a/neutron/tests/unit/opencontrail/test_contrail_plugin.py b/neutron/tests/unit/opencontrail/test_contrail_plugin.py index 974fb7b46..f05407012 100644 --- a/neutron/tests/unit/opencontrail/test_contrail_plugin.py +++ b/neutron/tests/unit/opencontrail/test_contrail_plugin.py @@ -257,8 +257,15 @@ class TestContrailSubnetsV2(test_plugin.TestSubnetsV2, class TestContrailPortsV2(test_plugin.TestPortsV2, ContrailPluginTestCase): - def setUp(self): - super(TestContrailPortsV2, self).setUp() + def test_create_port_public_network_with_invalid_ip_no_subnet_id(self): + super(TestContrailPortsV2, self). \ + test_create_port_public_network_with_invalid_ip_no_subnet_id( + expected_error='ContrailBadRequestError') + + def test_create_port_public_network_with_invalid_ip_and_subnet_id(self): + super(TestContrailPortsV2, self). \ + test_create_port_public_network_with_invalid_ip_and_subnet_id( + expected_error='ContrailBadRequestError') def test_delete_ports_by_device_id(self): self.skipTest("This method tests rpc API of " diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py index ae88c86a0..b5dd98fd7 100644 --- a/neutron/tests/unit/test_db_plugin.py +++ b/neutron/tests/unit/test_db_plugin.py @@ -853,6 +853,37 @@ class TestPortsV2(NeutronDbPluginV2TestCase): self.assertIn('mac_address', port['port']) self._delete('ports', port['port']['id']) + def test_create_port_public_network_with_invalid_ip_no_subnet_id(self, + expected_error='InvalidIpForNetwork'): + with self.network(shared=True) as network: + with self.subnet(network=network, cidr='10.0.0.0/24'): + ips = [{'ip_address': '1.1.1.1'}] + res = self._create_port(self.fmt, + network['network']['id'], + webob.exc.HTTPBadRequest.code, + fixed_ips=ips, + set_context=True) + data = self.deserialize(self.fmt, res) + msg = str(n_exc.InvalidIpForNetwork(ip_address='1.1.1.1')) + self.assertEqual(expected_error, data['NeutronError']['type']) + self.assertEqual(msg, data['NeutronError']['message']) + + def test_create_port_public_network_with_invalid_ip_and_subnet_id(self, + expected_error='InvalidIpForSubnet'): + with self.network(shared=True) as network: + with self.subnet(network=network, cidr='10.0.0.0/24') as subnet: + ips = [{'subnet_id': subnet['subnet']['id'], + 'ip_address': '1.1.1.1'}] + res = self._create_port(self.fmt, + network['network']['id'], + webob.exc.HTTPBadRequest.code, + fixed_ips=ips, + set_context=True) + data = self.deserialize(self.fmt, res) + msg = str(n_exc.InvalidIpForSubnet(ip_address='1.1.1.1')) + self.assertEqual(expected_error, data['NeutronError']['type']) + self.assertEqual(msg, data['NeutronError']['message']) + def test_create_ports_bulk_native(self): if self._skip_native_bulk: self.skipTest("Plugin does not support native bulk port create") -- 2.45.2