diff --git a/heat/engine/resources/network_interface.py b/heat/engine/resources/network_interface.py index 03e5ac59a0..c1601e62c8 100644 --- a/heat/engine/resources/network_interface.py +++ b/heat/engine/resources/network_interface.py @@ -16,6 +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 logger = logging.getLogger(__name__) @@ -62,7 +63,16 @@ class NetworkInterface(resource.Resource): } if self.properties['GroupSet']: - props['security_groups'] = 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') port = client.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 c863634a7c..022039aa3a 100644 --- a/heat/tests/test_vpc.py +++ b/heat/tests/test_vpc.py @@ -49,6 +49,13 @@ class VPCTestBase(HeatTestCase): self.m.StubOutWithMock(quantumclient.Client, 'remove_interface_router') self.m.StubOutWithMock(quantumclient.Client, 'show_subnet') self.m.StubOutWithMock(quantumclient.Client, 'show_network') + self.m.StubOutWithMock(quantumclient.Client, 'create_security_group') + self.m.StubOutWithMock(quantumclient.Client, 'show_security_group') + self.m.StubOutWithMock(quantumclient.Client, 'delete_security_group') + self.m.StubOutWithMock( + quantumclient.Client, 'create_security_group_rule') + self.m.StubOutWithMock( + quantumclient.Client, 'delete_security_group_rule') def create_stack(self, template): t = template_format.parse(template) @@ -108,6 +115,66 @@ class VPCTestBase(HeatTestCase): u'bbbb', {'subnet_id': 'cccc'}).AndReturn(None) + def mock_create_security_group(self): + quantumclient.Client.create_security_group({ + 'security_group': { + 'name': 'test_stack.the_sg', + 'description': 'SSH access' + } + }).AndReturn({ + 'security_group': { + 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f', + 'name': 'test_stack.the_sg', + 'description': 'SSH access', + 'security_group_rules': [], + 'id': 'aaaa' + } + }) + + quantumclient.Client.create_security_group_rule({ + 'security_group_rule': { + 'direction': 'ingress', + 'remote_ip_prefix': '0.0.0.0/0', + 'port_range_min': 22, + 'ethertype': 'IPv4', + 'port_range_max': 22, + 'protocol': 'tcp', + 'security_group_id': 'aaaa' + } + }).AndReturn({ + 'security_group_rule': { + 'direction': 'ingress', + 'remote_ip_prefix': '0.0.0.0/0', + 'port_range_min': 22, + 'ethertype': 'IPv4', + 'port_range_max': 22, + 'protocol': 'tcp', + 'security_group_id': 'aaaa', + 'id': 'bbbb' + } + }) + + def mock_delete_security_group(self): + quantumclient.Client.show_security_group('aaaa').AndReturn({ + 'security_group': { + 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f', + 'name': 'sc1', + 'description': '', + 'security_group_rules': [{ + 'direction': 'ingress', + 'protocol': 'tcp', + 'port_range_max': 22, + 'id': 'bbbb', + 'ethertype': 'IPv4', + 'security_group_id': 'aaaa', + 'remote_ip_prefix': '0.0.0.0/0', + 'tenant_id': 'c1210485b2424d48804aad5d39c61b8f', + 'port_range_min': 22 + }], + 'id': 'aaaa'}}) + quantumclient.Client.delete_security_group_rule('bbbb').AndReturn(None) + quantumclient.Client.delete_security_group('aaaa').AndReturn(None) + def mock_delete_network(self): quantumclient.Client.delete_router('bbbb').AndReturn(None) quantumclient.Client.delete_network('aaaa').AndReturn(None) @@ -209,6 +276,16 @@ class NetworkInterfaceTest(VPCTestBase): test_template = ''' HeatTemplateFormatVersion: '2012-12-12' Resources: + the_sg: + Type: AWS::EC2::SecurityGroup + Properties: + VpcId: {Ref: the_vpc} + GroupDescription: SSH access + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: 0.0.0.0/0 the_vpc: Type: AWS::EC2::VPC Properties: {CidrBlock: '10.0.0.0/16'} @@ -223,6 +300,39 @@ Resources: Properties: PrivateIpAddress: 10.0.0.100 SubnetId: {Ref: the_subnet} + GroupSet: + - Ref: the_sg +''' + + test_template_error = ''' +HeatTemplateFormatVersion: '2012-12-12' +Resources: + the_sg: + Type: AWS::EC2::SecurityGroup + Properties: + VpcId: {Ref: the_vpc} + GroupDescription: SSH access + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 22 + ToPort: 22 + CidrIp: 0.0.0.0/0 + the_vpc: + Type: AWS::EC2::VPC + Properties: {CidrBlock: '10.0.0.0/16'} + the_subnet: + Type: AWS::EC2::Subnet + Properties: + CidrBlock: 10.0.0.0/24 + VpcId: {Ref: the_vpc} + AvailabilityZone: moon + the_nic: + Type: AWS::EC2::NetworkInterface + Properties: + PrivateIpAddress: 10.0.0.100 + SubnetId: {Ref: the_subnet} + GroupSet: + - Ref: INVALID-REF-IN-TEMPLATE ''' def mock_create_network_interface(self): @@ -233,7 +343,8 @@ Resources: 'ip_address': u'10.0.0.100' }], 'name': u'test_stack.the_nic', - 'admin_state_up': True + 'admin_state_up': True, + 'security_groups': ['aaaa'] }}).AndReturn({ 'port': { 'admin_state_up': True, @@ -258,12 +369,14 @@ Resources: quantumclient.Client.delete_port('dddd').AndReturn(None) def test_network_interface(self): + self.mock_create_security_group() self.mock_create_network() self.mock_create_subnet() self.mock_create_network_interface() self.mock_delete_network_interface() self.mock_delete_subnet() self.mock_delete_network() + self.mock_delete_security_group() self.m.ReplayAll() @@ -276,6 +389,12 @@ Resources: stack.delete() self.m.VerifyAll() + def test_network_interface_error(self): + self.assertRaises( + KeyError, + self.create_stack, + self.test_template_error) + class InternetGatewayTest(VPCTestBase):