diff --git a/shade/openstackcloud.py b/shade/openstackcloud.py index 1e9780838..95f961f34 100644 --- a/shade/openstackcloud.py +++ b/shade/openstackcloud.py @@ -2503,6 +2503,8 @@ class OpenStackCloud(object): def remove_router_interface(self, router, subnet_id=None, port_id=None): """Detach a subnet from an internal router interface. + At least one of subnet_id or port_id must be supplied. + If you specify both subnet and port ID, the subnet ID must correspond to the subnet ID of the first IP address on the port specified by the port ID. Otherwise an error occurs. @@ -2521,6 +2523,10 @@ class OpenStackCloud(object): if port_id: body['port_id'] = port_id + if not body: + raise ValueError( + "At least one of subnet_id or port_id must be supplied.") + with _utils.neutron_exceptions( "Error detaching interface from router {0}".format(router['id']) ): diff --git a/shade/tests/unit/test_shade.py b/shade/tests/unit/test_shade.py index 8ecdee2d7..3de161548 100644 --- a/shade/tests/unit/test_shade.py +++ b/shade/tests/unit/test_shade.py @@ -214,6 +214,11 @@ class TestShade(base.TestCase): router='123', body={'subnet_id': 'abc'} ) + @mock.patch.object(shade.OpenStackCloud, 'neutron_client') + def test_remove_router_interface_missing_argument(self, mock_client): + self.assertRaises(ValueError, self.cloud.remove_router_interface, + {'id': '123'}) + @mock.patch.object(shade.OpenStackCloud, 'get_router') @mock.patch.object(shade.OpenStackCloud, 'neutron_client') def test_update_router(self, mock_client, mock_get):