from heat.engine import clients
from heat.engine import resource
+from heat.common import exception
from heat.openstack.common import log as logging
logger = logging.getLogger(__name__)
else:
self._handle_create_nova()
+ def _convert_to_neutron_rule(self, direction, sg_rule):
+ return {
+ 'direction': direction,
+ 'ethertype': 'IPv4',
+ 'remote_ip_prefix': sg_rule.get('CidrIp'),
+ 'port_range_min': sg_rule.get('FromPort'),
+ 'port_range_max': sg_rule.get('ToPort'),
+ 'protocol': sg_rule.get('IpProtocol'),
+ # Neutron understands both names and ids
+ 'remote_group_id': sg_rule.get('SourceSecurityGroupId') or
+ sg_rule.get('SourceSecurityGroupName'),
+ 'security_group_id': self.resource_id
+ }
+
def _handle_create_neutron(self):
from neutronclient.common.exceptions import NeutronClientException
client = self.neutron()
'description': self.properties['GroupDescription']}
})['security_group']
+ def sanitize_security_group(i):
+ # Neutron only accepts positive ints
+ if i.get('FromPort') is not None and int(i['FromPort']) < 0:
+ i['FromPort'] = None
+ if i.get('ToPort') is not None and int(i['ToPort']) < 0:
+ i['ToPort'] = None
+ if i.get('FromPort') is None and i.get('ToPort') is None:
+ i['CidrIp'] = None
+
self.resource_id_set(sec['id'])
if self.properties['SecurityGroupIngress']:
for i in self.properties['SecurityGroupIngress']:
- # Neutron only accepts positive ints
- if int(i['FromPort']) < 0:
- i['FromPort'] = None
- if int(i['ToPort']) < 0:
- i['ToPort'] = None
- if i['FromPort'] is None and i['ToPort'] is None:
- i['CidrIp'] = None
-
+ sanitize_security_group(i)
try:
rule = client.create_security_group_rule({
- 'security_group_rule': {
- 'direction': 'ingress',
- 'remote_ip_prefix': i['CidrIp'],
- 'port_range_min': i['FromPort'],
- 'ethertype': 'IPv4',
- 'port_range_max': i['ToPort'],
- 'protocol': i['IpProtocol'],
- 'security_group_id': sec['id']
- }
+ 'security_group_rule':
+ self._convert_to_neutron_rule('ingress', i)
})
except NeutronClientException as ex:
if ex.status_code == 409:
# unexpected error
raise
if self.properties['SecurityGroupEgress']:
+ # Delete the default rules which allow all egress traffic
+ for rule in sec['security_group_rules']:
+ if rule['direction'] == 'egress':
+ client.delete_security_group_rule(rule['id'])
+
for i in self.properties['SecurityGroupEgress']:
+ sanitize_security_group(i)
try:
rule = client.create_security_group_rule({
- 'security_group_rule': {
- 'direction': 'egress',
- 'remote_ip_prefix': i['CidrIp'],
- 'port_range_min': i['FromPort'],
- 'ethertype': 'IPv4',
- 'port_range_max': i['ToPort'],
- 'protocol': i['IpProtocol'],
- 'security_group_id': sec['id']
- }
+ 'security_group_rule':
+ self._convert_to_neutron_rule('egress', i)
})
except NeutronClientException as ex:
if ex.status_code == 409:
if self.properties['SecurityGroupIngress']:
rules_client = self.nova().security_group_rules
for i in self.properties['SecurityGroupIngress']:
+ source_group_id = None
+ if i.get('SourceSecurityGroupId') is not None:
+ source_group_id = i['SourceSecurityGroupId']
+ elif i.get('SourceSecurityGroupName') is not None:
+ for group in groups:
+ if group.name == i['SourceSecurityGroupName']:
+ source_group_id = group.id
+ break
try:
- rule = rules_client.create(sec.id,
- i['IpProtocol'],
- i['FromPort'],
- i['ToPort'],
- i['CidrIp'])
+ rule = rules_client.create(
+ sec.id,
+ i.get('IpProtocol'),
+ i.get('FromPort'),
+ i.get('ToPort'),
+ i.get('CidrIp'),
+ source_group_id)
except clients.novaclient.exceptions.BadRequest as ex:
if ex.message.find('already exists') >= 0:
# no worries, the rule is already there
else:
return self.physical_resource_name()
+ def validate(self):
+ res = super(SecurityGroup, self).validate()
+ if res:
+ return res
+
+ if self.properties['SecurityGroupEgress'] and not(
+ self.properties['VpcId'] and
+ clients.neutronclient is not None):
+ raise exception.EgressRuleNotAllowed()
+
def resource_mapping():
return {
import collections
from heat.engine import clients
+from heat.common import exception
from heat.common import template_format
from heat.engine import parser
from heat.engine import resource
FromPort : 80
ToPort : 80
CidrIp : 0.0.0.0/0
+ - IpProtocol: tcp
+ SourceSecurityGroupName: test
+ - IpProtocol: icmp
+ SourceSecurityGroupId: 1
+'''
+
+ test_template_nova_with_egress = '''
+HeatTemplateFormatVersion: '2012-12-12'
+Resources:
+ the_sg:
+ Type: AWS::EC2::SecurityGroup
+ Properties:
+ GroupDescription: HTTP and SSH access
+ SecurityGroupEgress:
+ - IpProtocol: tcp
+ FromPort: 22
+ ToPort: 22
+ CidrIp: 0.0.0.0/0
'''
test_template_neutron = '''
FromPort : 80
ToPort : 80
CidrIp : 0.0.0.0/0
+ - IpProtocol: tcp
+ SourceSecurityGroupId: wwww
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 10.0.1.0/24
+ - SourceSecurityGroupName: xxxx
'''
def setUp(self):
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
nova_sgr.SecurityGroupRuleManager.create(
- 2, 'tcp', 22, 22, '0.0.0.0/0').AndReturn(None)
+ 2, 'tcp', 22, 22, '0.0.0.0/0', None).AndReturn(None)
+ nova_sgr.SecurityGroupRuleManager.create(
+ 2, 'tcp', 80, 80, '0.0.0.0/0', None).AndReturn(None)
nova_sgr.SecurityGroupRuleManager.create(
- 2, 'tcp', 80, 80, '0.0.0.0/0').AndReturn(None)
+ 2, 'tcp', None, None, None, 1).AndReturn(None)
+ nova_sgr.SecurityGroupRuleManager.create(
+ 2, 'icmp', None, None, None, 1).AndReturn(None)
# delete script
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
'cidr': '0.0.0.0/0'
},
'id': 131
+ }, {
+ 'from_port': None,
+ 'group': {
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'name': 'test'
+ },
+ 'ip_protocol': 'tcp',
+ 'to_port': None,
+ 'parent_group_id': 2,
+ 'ip_range': {},
+ 'id': 132
+ }, {
+ 'from_port': None,
+ 'group': {
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'name': 'test'
+ },
+ 'ip_protocol': 'icmp',
+ 'to_port': None,
+ 'parent_group_id': 2,
+ 'ip_range': {},
+ 'id': 133
}]
))
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
nova_sgr.SecurityGroupRuleManager.delete(131).AndReturn(None)
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
+ nova_sgr.SecurityGroupRuleManager.delete(132).AndReturn(None)
+ clients.OpenStackClients.nova('compute').AndReturn(self.fc)
+ nova_sgr.SecurityGroupRuleManager.delete(133).AndReturn(None)
+ clients.OpenStackClients.nova('compute').AndReturn(self.fc)
nova_sg.SecurityGroupManager.delete(2).AndReturn(None)
self.m.ReplayAll()
#create script
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
sg_name = utils.PhysName('test_stack', 'the_sg')
- nova_sg.SecurityGroupManager.list().AndReturn([NovaSG(
- id=2,
- name=sg_name,
- description='HTTP and SSH access',
- rules=[],
- )])
+ nova_sg.SecurityGroupManager.list().AndReturn([
+ NovaSG(
+ id=2,
+ name=sg_name,
+ description='HTTP and SSH access',
+ rules=[],
+ ),
+ NovaSG(
+ id=1,
+ name='test',
+ description='FAKE_SECURITY_GROUP',
+ rules=[],
+ )
+ ])
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
nova_sgr.SecurityGroupRuleManager.create(
- 2, 'tcp', 22, 22, '0.0.0.0/0').AndRaise(
+ 2, 'tcp', 22, 22, '0.0.0.0/0', None).AndRaise(
+ clients.novaclient.exceptions.BadRequest(
+ 400, 'Rule already exists'))
+ nova_sgr.SecurityGroupRuleManager.create(
+ 2, 'tcp', 80, 80, '0.0.0.0/0', None).AndReturn(
clients.novaclient.exceptions.BadRequest(
400, 'Rule already exists'))
nova_sgr.SecurityGroupRuleManager.create(
- 2, 'tcp', 80, 80, '0.0.0.0/0').AndReturn(
+ 2, 'tcp', None, None, None, 1).AndReturn(
+ clients.novaclient.exceptions.BadRequest(
+ 400, 'Rule already exists'))
+ nova_sgr.SecurityGroupRuleManager.create(
+ 2, 'icmp', None, None, None, 1).AndReturn(
clients.novaclient.exceptions.BadRequest(
400, 'Rule already exists'))
'cidr': '0.0.0.0/0'
},
'id': 131
+ }, {
+ 'from_port': None,
+ 'group': {
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'name': 'test'
+ },
+ 'ip_protocol': 'tcp',
+ 'to_port': None,
+ 'parent_group_id': 2,
+ 'ip_range': {},
+ 'id': 132
+ }, {
+ 'from_port': None,
+ 'group': {
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'name': 'test'
+ },
+ 'ip_protocol': 'icmp',
+ 'to_port': None,
+ 'parent_group_id': 2,
+ 'ip_range': {},
+ 'id': 133
}]
))
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
nova_sgr.SecurityGroupRuleManager.delete(131).AndRaise(
clients.novaclient.exceptions.NotFound('goneburger'))
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
+ nova_sgr.SecurityGroupRuleManager.delete(132).AndRaise(
+ clients.novaclient.exceptions.NotFound('goneburger'))
+ clients.OpenStackClients.nova('compute').AndReturn(self.fc)
+ nova_sgr.SecurityGroupRuleManager.delete(133).AndRaise(
+ clients.novaclient.exceptions.NotFound('goneburger'))
+ clients.OpenStackClients.nova('compute').AndReturn(self.fc)
nova_sg.SecurityGroupManager.delete(2).AndReturn(None)
clients.OpenStackClients.nova('compute').AndReturn(self.fc)
self.m.VerifyAll()
+ def test_security_group_nova_with_egress_rules(self):
+ t = template_format.parse(self.test_template_nova_with_egress)
+ stack = self.parse_stack(t)
+
+ sg = stack['the_sg']
+ self.assertRaises(exception.EgressRuleNotAllowed, sg.validate)
+
@utils.stack_delete_after
def test_security_group_neutron(self):
#create script
'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
'name': sg_name,
'description': 'HTTP and SSH access',
- 'security_group_rules': [],
+ 'security_group_rules': [{
+ "direction": "egress",
+ "ethertype": "IPv4",
+ "id": "aaaa-1",
+ "port_range_max": None,
+ "port_range_min": None,
+ "protocol": None,
+ "remote_group_id": None,
+ "remote_ip_prefix": None,
+ "security_group_id": "aaaa",
+ "tenant_id": "f18ca530cc05425e8bac0a5ff92f7e88"
+ }, {
+ "direction": "egress",
+ "ethertype": "IPv6",
+ "id": "aaaa-2",
+ "port_range_max": None,
+ "port_range_min": None,
+ "protocol": None,
+ "remote_group_id": None,
+ "remote_ip_prefix": None,
+ "security_group_id": "aaaa",
+ "tenant_id": "f18ca530cc05425e8bac0a5ff92f7e88"
+ }],
'id': 'aaaa'
}
})
neutronclient.Client.create_security_group_rule({
'security_group_rule': {
'direction': 'ingress',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'port_range_min': 22,
'ethertype': 'IPv4',
}).AndReturn({
'security_group_rule': {
'direction': 'ingress',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'port_range_min': 22,
'ethertype': 'IPv4',
neutronclient.Client.create_security_group_rule({
'security_group_rule': {
'direction': 'ingress',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'port_range_min': 80,
'ethertype': 'IPv4',
}).AndReturn({
'security_group_rule': {
'direction': 'ingress',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'port_range_min': 80,
'ethertype': 'IPv4',
'id': 'cccc'
}
})
+ neutronclient.Client.create_security_group_rule({
+ 'security_group_rule': {
+ 'direction': 'ingress',
+ 'remote_group_id': 'wwww',
+ 'remote_ip_prefix': None,
+ 'port_range_min': None,
+ 'ethertype': 'IPv4',
+ 'port_range_max': None,
+ 'protocol': 'tcp',
+ 'security_group_id': 'aaaa'
+ }
+ }).AndReturn({
+ 'security_group_rule': {
+ 'direction': 'ingress',
+ 'remote_group_id': 'wwww',
+ 'remote_ip_prefix': None,
+ 'port_range_min': None,
+ 'ethertype': 'IPv4',
+ 'port_range_max': None,
+ 'protocol': 'tcp',
+ 'security_group_id': 'aaaa',
+ 'id': 'dddd'
+ }
+ })
+ neutronclient.Client.delete_security_group_rule('aaaa-1').AndReturn(
+ None)
+ neutronclient.Client.delete_security_group_rule('aaaa-2').AndReturn(
+ None)
neutronclient.Client.create_security_group_rule({
'security_group_rule': {
'direction': 'egress',
+ 'remote_group_id': None,
'remote_ip_prefix': '10.0.1.0/24',
'port_range_min': 22,
'ethertype': 'IPv4',
}).AndReturn({
'security_group_rule': {
'direction': 'egress',
+ 'remote_group_id': None,
'remote_ip_prefix': '10.0.1.0/24',
'port_range_min': 22,
'ethertype': 'IPv4',
'port_range_max': 22,
'protocol': 'tcp',
'security_group_id': 'aaaa',
- 'id': 'dddd'
+ 'id': 'eeee'
+ }
+ })
+ neutronclient.Client.create_security_group_rule({
+ 'security_group_rule': {
+ 'direction': 'egress',
+ 'remote_group_id': 'xxxx',
+ 'remote_ip_prefix': None,
+ 'port_range_min': None,
+ 'ethertype': 'IPv4',
+ 'port_range_max': None,
+ 'protocol': None,
+ 'security_group_id': 'aaaa'
+ }
+ }).AndReturn({
+ 'security_group_rule': {
+ 'direction': 'egress',
+ 'remote_group_id': 'xxxx',
+ 'remote_ip_prefix': None,
+ 'port_range_min': None,
+ 'ethertype': 'IPv4',
+ 'port_range_max': None,
+ 'protocol': None,
+ 'security_group_id': 'aaaa',
+ 'id': 'ffff'
}
})
'id': 'bbbb',
'ethertype': 'IPv4',
'security_group_id': 'aaaa',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
'port_range_min': 22
'id': 'cccc',
'ethertype': 'IPv4',
'security_group_id': 'aaaa',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
'port_range_min': 80
+ }, {
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_max': None,
+ 'id': 'dddd',
+ 'ethertype': 'IPv4',
+ 'security_group_id': 'aaaa',
+ 'remote_group_id': 'wwww',
+ 'remote_ip_prefix': None,
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'port_range_min': None
}, {
'direction': 'egress',
'protocol': 'tcp',
'port_range_max': 22,
- 'id': 'dddd',
+ 'id': 'eeee',
'ethertype': 'IPv4',
'security_group_id': 'aaaa',
+ 'remote_group_id': None,
'remote_ip_prefix': '10.0.1.0/24',
'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
'port_range_min': 22
+ }, {
+ 'direction': 'egress',
+ 'protocol': None,
+ 'port_range_max': None,
+ 'id': 'ffff',
+ 'ethertype': 'IPv4',
+ 'security_group_id': 'aaaa',
+ 'remote_group_id': None,
+ 'remote_ip_prefix': None,
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'port_range_min': None
}],
'id': 'aaaa'}})
neutronclient.Client.delete_security_group_rule('bbbb').AndReturn(None)
neutronclient.Client.delete_security_group_rule('cccc').AndReturn(None)
neutronclient.Client.delete_security_group_rule('dddd').AndReturn(None)
+ neutronclient.Client.delete_security_group_rule('eeee').AndReturn(None)
+ neutronclient.Client.delete_security_group_rule('ffff').AndReturn(None)
neutronclient.Client.delete_security_group('aaaa').AndReturn(None)
self.m.ReplayAll()
neutronclient.Client.create_security_group_rule({
'security_group_rule': {
'direction': 'ingress',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'port_range_min': 22,
'ethertype': 'IPv4',
neutronclient.Client.create_security_group_rule({
'security_group_rule': {
'direction': 'ingress',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'port_range_min': 80,
'ethertype': 'IPv4',
}
}).AndRaise(
NeutronClientException(status_code=409))
+ neutronclient.Client.create_security_group_rule({
+ 'security_group_rule': {
+ 'direction': 'ingress',
+ 'remote_group_id': 'wwww',
+ 'remote_ip_prefix': None,
+ 'port_range_min': None,
+ 'ethertype': 'IPv4',
+ 'port_range_max': None,
+ 'protocol': 'tcp',
+ 'security_group_id': 'aaaa'
+ }
+ }).AndRaise(
+ NeutronClientException(status_code=409))
neutronclient.Client.create_security_group_rule({
'security_group_rule': {
'direction': 'egress',
+ 'remote_group_id': None,
'remote_ip_prefix': '10.0.1.0/24',
'port_range_min': 22,
'ethertype': 'IPv4',
}
}).AndRaise(
NeutronClientException(status_code=409))
+ neutronclient.Client.create_security_group_rule({
+ 'security_group_rule': {
+ 'direction': 'egress',
+ 'remote_group_id': 'xxxx',
+ 'remote_ip_prefix': None,
+ 'port_range_min': None,
+ 'ethertype': 'IPv4',
+ 'port_range_max': None,
+ 'protocol': None,
+ 'security_group_id': 'aaaa'
+ }
+ }).AndRaise(
+ NeutronClientException(status_code=409))
# delete script
neutronclient.Client.show_security_group('aaaa').AndReturn({
'id': 'bbbb',
'ethertype': 'IPv4',
'security_group_id': 'aaaa',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
'port_range_min': 22
'id': 'cccc',
'ethertype': 'IPv4',
'security_group_id': 'aaaa',
+ 'remote_group_id': None,
'remote_ip_prefix': '0.0.0.0/0',
'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
'port_range_min': 80
+ }, {
+ 'direction': 'ingress',
+ 'protocol': 'tcp',
+ 'port_range_max': None,
+ 'id': 'dddd',
+ 'ethertype': 'IPv4',
+ 'security_group_id': 'aaaa',
+ 'remote_group_id': 'wwww',
+ 'remote_ip_prefix': None,
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'port_range_min': None
}, {
'direction': 'egress',
'protocol': 'tcp',
'port_range_max': 22,
- 'id': 'dddd',
+ 'id': 'eeee',
'ethertype': 'IPv4',
'security_group_id': 'aaaa',
+ 'remote_group_id': None,
'remote_ip_prefix': '10.0.1.0/24',
'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
'port_range_min': 22
+ }, {
+ 'direction': 'egress',
+ 'protocol': None,
+ 'port_range_max': None,
+ 'id': 'ffff',
+ 'ethertype': 'IPv4',
+ 'security_group_id': 'aaaa',
+ 'remote_group_id': None,
+ 'remote_ip_prefix': None,
+ 'tenant_id': 'f18ca530cc05425e8bac0a5ff92f7e88',
+ 'port_range_min': None
}],
'id': 'aaaa'}})
neutronclient.Client.delete_security_group_rule('bbbb').AndRaise(
NeutronClientException(status_code=404))
neutronclient.Client.delete_security_group_rule('dddd').AndRaise(
NeutronClientException(status_code=404))
+ neutronclient.Client.delete_security_group_rule('eeee').AndRaise(
+ NeutronClientException(status_code=404))
+ neutronclient.Client.delete_security_group_rule('ffff').AndRaise(
+ NeutronClientException(status_code=404))
neutronclient.Client.delete_security_group('aaaa').AndRaise(
NeutronClientException(status_code=404))