From 3334a882490e7fe048fa73cdc4004ba02b455854 Mon Sep 17 00:00:00 2001 From: Jeff Peeler Date: Fri, 23 Aug 2013 16:44:37 -0400 Subject: [PATCH] Make security groups associated with Neutron port work The security group names are now translated to UUIDs which is required when using the python API. The lookup has been improved to find existing security groups not part of the stack as well. Fixes bug #1179481 Change-Id: I4f86bade87f89b867d12822c5f80aa5b075b7fbb --- heat/engine/resources/network_interface.py | 16 +--- heat/engine/resources/neutron/neutron.py | 30 ++++++ heat/engine/resources/neutron/port.py | 6 ++ heat/tests/test_vpc.py | 106 +++++++++++++++++---- 4 files changed, 129 insertions(+), 29 deletions(-) diff --git a/heat/engine/resources/network_interface.py b/heat/engine/resources/network_interface.py index 52c218c8..49f2bf8f 100644 --- a/heat/engine/resources/network_interface.py +++ b/heat/engine/resources/network_interface.py @@ -16,7 +16,7 @@ from heat.engine import clients from heat.openstack.common import log as logging from heat.engine import resource -from heat.common import exception +from heat.engine.resources.neutron import neutron logger = logging.getLogger(__name__) @@ -66,16 +66,10 @@ class NetworkInterface(resource.Resource): } if self.properties['GroupSet']: - props['security_groups'] = [] - - for sg in self.properties.get('GroupSet'): - resource = self.stack.resource_by_refid(sg) - if resource: - props['security_groups'].append(resource.resource_id) - else: - raise exception.InvalidTemplateAttribute( - resource=self.name, - key='GroupSet') + sgs = neutron.NeutronResource.get_secgroup_uuids( + self.stack, self.properties, 'GroupSet', self.name, + self.neutron()) + props['security_groups'] = sgs port = client.create_port({'port': props})['port'] self.resource_id_set(port['id']) diff --git a/heat/engine/resources/neutron/neutron.py b/heat/engine/resources/neutron/neutron.py index e14bd9c2..c580cee7 100644 --- a/heat/engine/resources/neutron/neutron.py +++ b/heat/engine/resources/neutron/neutron.py @@ -114,3 +114,33 @@ class NeutronResource(resource.Resource): def FnGetRefId(self): return unicode(self.resource_id) + + @staticmethod + def get_secgroup_uuids(stack, props, props_name, rsrc_name, client): + ''' + Returns security group names in UUID form. + + Args: + stack: stack associated with given resource + props: properties described in the template + props_name: name of security group property + rsrc_name: name of the given resource + client: reference to neutronclient + ''' + seclist = [] + for sg in props.get(props_name): + resource = stack.resource_by_refid(sg) + if resource is not None: + seclist.append(resource.resource_id) + else: + try: + client.show_security_group(sg) + seclist.append(sg) + except NeutronClientException as e: + if e.status_code == 404: + raise exception.InvalidTemplateAttribute( + resource=rsrc_name, + key=props_name) + else: + raise + return seclist diff --git a/heat/engine/resources/neutron/port.py b/heat/engine/resources/neutron/port.py index 1311f351..e5084d98 100644 --- a/heat/engine/resources/neutron/port.py +++ b/heat/engine/resources/neutron/port.py @@ -74,6 +74,12 @@ class Port(neutron.NeutronResource): props = self.prepare_properties( self.properties, self.physical_resource_name()) + + if self.properties['security_groups']: + props['security_groups'] = self.get_secgroup_uuids( + self.stack, self.properties, 'security_groups', self.name, + self.neutron()) + port = self.neutron().create_port({'port': props})['port'] self.resource_id_set(port['id']) diff --git a/heat/tests/test_vpc.py b/heat/tests/test_vpc.py index 2d94c1d7..2e53758b 100644 --- a/heat/tests/test_vpc.py +++ b/heat/tests/test_vpc.py @@ -214,26 +214,36 @@ class VPCTestBase(HeatTestCase): } }) - def mock_delete_security_group(self): + def mock_show_security_group(self, group='eeee'): sg_name = utils.PhysName('test_stack', 'the_sg') - neutronclient.Client.show_security_group('eeee').AndReturn({ - 'security_group': { - 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f', - 'name': sg_name, - 'description': '', - 'security_group_rules': [{ - 'direction': 'ingress', - 'protocol': 'tcp', - 'port_range_max': '22', - 'id': 'bbbb', - 'ethertype': 'IPv4', - 'security_group_id': 'eeee', - 'remote_group_id': None, - 'remote_ip_prefix': '0.0.0.0/0', + if group == 'eeee': + neutronclient.Client.show_security_group(group).AndReturn({ + 'security_group': { 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f', - 'port_range_min': '22' - }], - 'id': 'eeee'}}) + 'name': sg_name, + 'description': '', + 'security_group_rules': [{ + 'direction': 'ingress', + 'protocol': 'tcp', + 'port_range_max': '22', + 'id': 'bbbb', + 'ethertype': 'IPv4', + 'security_group_id': 'eeee', + 'remote_group_id': None, + 'remote_ip_prefix': '0.0.0.0/0', + 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f', + 'port_range_min': '22' + }], + 'id': 'eeee'}}) + elif group == 'INVALID-NO-REF': + neutronclient.Client.show_security_group(group).AndRaise( + NeutronClientException(status_code=404)) + elif group == 'RaiseException': + neutronclient.Client.show_security_group('eeee').AndRaise( + NeutronClientException(status_code=403)) + + def mock_delete_security_group(self): + self.mock_show_security_group() neutronclient.Client.delete_security_group_rule('bbbb').AndReturn(None) neutronclient.Client.delete_security_group('eeee').AndReturn(None) @@ -327,6 +337,9 @@ class VPCTestBase(HeatTestCase): self.assertEqual((resource.CREATE, resource.COMPLETE), resource.state) self.assertEqual(ref_id, resource.FnGetRefId()) + def mock_rsrc_by_refid(self, sg): + parser.Stack.resource_by_refid(sg).AndReturn(None) + class VPCTest(VPCTestBase): @@ -575,6 +588,38 @@ Resources: self.m.VerifyAll() + def test_network_interface_existing_groupset(self): + self.m.StubOutWithMock(parser.Stack, 'resource_by_refid') + self.mock_rsrc_by_refid(sg='eeee') + + self.mock_keystone() + self.mock_create_security_group() + self.mock_create_network() + self.mock_create_subnet() + self.mock_show_subnet() + self.mock_create_network_interface() + self.mock_show_security_group() + self.mock_delete_network_interface() + self.mock_delete_subnet() + self.mock_delete_network() + self.mock_delete_security_group() + + self.m.ReplayAll() + + stack = self.create_stack(self.test_template) + try: + self.assertEqual((stack.CREATE, stack.COMPLETE), stack.state) + rsrc = stack['the_nic'] + self.assertResourceState(rsrc, 'dddd') + + self.assertRaises(resource.UpdateReplace, + rsrc.handle_update, {}, {}, {}) + + finally: + stack.delete() + + self.m.VerifyAll() + def test_network_interface_no_groupset(self): self.mock_keystone() self.mock_create_network() @@ -592,6 +637,30 @@ Resources: self.m.VerifyAll() + def test_network_interface_exception(self): + self.m.StubOutWithMock(parser.Stack, 'resource_by_refid') + self.mock_rsrc_by_refid(sg='eeee') + + self.mock_keystone() + self.mock_create_security_group() + self.mock_create_network() + self.mock_create_subnet() + self.mock_show_subnet() + self.mock_show_security_group(group='RaiseException') + + self.m.ReplayAll() + + try: + stack = self.create_stack(self.test_template) + rsrc = stack['the_nic'] + self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state) + reason = rsrc.status_reason + self.assertTrue(reason.startswith('NeutronClientException:')) + finally: + stack.delete() + + self.m.VerifyAll() + def test_network_interface_error(self): real_exception = self.assertRaises( exception.InvalidTemplateReference, @@ -608,6 +677,7 @@ Resources: self.mock_create_network() self.mock_create_subnet() self.mock_show_subnet() + self.mock_show_security_group(group='INVALID-NO-REF') self.mock_delete_subnet() self.mock_delete_network() -- 2.45.2