diff --git a/nova/api/openstack/compute/security_groups.py b/nova/api/openstack/compute/security_groups.py index b10308393f43..f1c6d9aedb1d 100644 --- a/nova/api/openstack/compute/security_groups.py +++ b/nova/api/openstack/compute/security_groups.py @@ -439,7 +439,8 @@ class SecurityGroupActionController(wsgi.Controller): except (exception.SecurityGroupNotFound, exception.InstanceNotFound) as exp: raise exc.HTTPNotFound(explanation=exp.format_message()) - except exception.NoUniqueMatch as exp: + except (exception.NoUniqueMatch, + exception.SecurityGroupConnectionStateConflict) as exp: raise exc.HTTPConflict(explanation=exp.format_message()) except exception.SecurityGroupCannotBeApplied as exp: raise exc.HTTPBadRequest(explanation=exp.format_message()) diff --git a/nova/exception.py b/nova/exception.py index 20070efcc5b4..e59b8e3feba0 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -1056,6 +1056,12 @@ class SecurityGroupCannotBeApplied(Invalid): " in order to apply security groups.") +class SecurityGroupConnectionStateConflict(Invalid): + code = 409 + msg_fmt = _("Cannot apply both stateful and stateless security groups on " + "the same port.") + + class NoUniqueMatch(NovaException): msg_fmt = _("No Unique Match Found.") code = 409 diff --git a/nova/network/security_group_api.py b/nova/network/security_group_api.py index b36d96473da5..ab76ab8e67cd 100644 --- a/nova/network/security_group_api.py +++ b/nova/network/security_group_api.py @@ -649,6 +649,8 @@ def add_to_instance(context, instance, security_group_name): except n_exc.NeutronClientException as e: if e.status_code == 400: raise exception.SecurityGroupCannotBeApplied(str(e)) + elif e.status_code == 409: + raise exception.SecurityGroupConnectionStateConflict(str(e)) else: raise e except Exception: diff --git a/nova/tests/unit/network/test_security_group.py b/nova/tests/unit/network/test_security_group.py index a76dd4bf3c98..8c1a4a1eb4c4 100644 --- a/nova/tests/unit/network/test_security_group.py +++ b/nova/tests/unit/network/test_security_group.py @@ -373,6 +373,38 @@ class TestNeutronDriver(test.NoDBTestCase): self.mocked_client.update_port.assert_called_once_with( port_id, {'port': {'security_groups': [sg_id]}}) + def test_add_to_instance_with_conflicting_sg(self): + sg1_name = 'sg-stateful' + sg1_id = '85cc3048-abc3-43cc-89b3-377341426ac5' + port_id = 1 + port_list = {'ports': [{'id': port_id, 'device_id': uuids.instance, + 'fixed_ips': [{'ip_address': '10.0.0.1'}], + 'port_security_enabled': True, 'security_groups': []}]} + self.mocked_client.list_ports.return_value = port_list + with mock.patch.object(neutronv20, 'find_resourceid_by_name_or_id', + return_value=sg1_id): + sg_api.add_to_instance( + self.context, objects.Instance(uuid=uuids.instance), sg1_name) + self.mocked_client.list_ports.assert_called_once_with( + device_id=uuids.instance) + self.mocked_client.update_port.assert_called_once_with( + port_id, {'port': {'security_groups': [sg1_id]}}) + + sg2_name = 'sg-stateless' + sg2_id = '85cc3048-abc3-43cc-89b3-377341426ac5' + port_id = 1 + port_list = {'ports': [{'id': port_id, 'device_id': uuids.instance, + 'fixed_ips': [{'ip_address': '10.0.0.1'}], + 'port_security_enabled': True, 'security_groups': []}]} + self.mocked_client.list_ports.return_value = port_list + self.mocked_client.update_port.side_effect = ( + n_exc.Conflict(message='error')) + with mock.patch.object(neutronv20, 'find_resourceid_by_name_or_id', + return_value=sg2_id): + self.assertRaises(exception.SecurityGroupConnectionStateConflict, + sg_api.add_to_instance, self.context, + objects.Instance(uuid=uuids.instance), sg2_name) + def test_add_to_instance_duplicate_sg_name(self): sg_name = 'web_server' with mock.patch.object(neutronv20, 'find_resourceid_by_name_or_id',