From: Bartosz Górski Date: Tue, 3 Sep 2013 01:54:11 +0000 (-0700) Subject: Adding IPsec site connection to Heat resources X-Git-Tag: 2014.1~65^2 X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=452f1ec16709abf541ad6bf1a3216ba549661895;p=openstack-build%2Fheat-build.git Adding IPsec site connection to Heat resources Adds Neutron IPsec site connection component to resources supported by Heat with unit tests. Change-Id: Idf3c92b9a7ac513e7f7ab0d2501668405189ebc6 Implements: blueprint vpnaas-support --- diff --git a/heat/engine/resources/neutron/vpnservice.py b/heat/engine/resources/neutron/vpnservice.py index 313d3a2e..0e9fd004 100644 --- a/heat/engine/resources/neutron/vpnservice.py +++ b/heat/engine/resources/neutron/vpnservice.py @@ -83,6 +83,108 @@ class VPNService(neutron.NeutronResource): return scheduler.TaskRunner(self._confirm_delete)() +class IPsecSiteConnection(neutron.NeutronResource): + """ + A resource for IPsec site connection in Neutron. + """ + + dpd_schema = { + 'actions': {'Type': 'String', + 'AllowedValues': ['clear', + 'disabled', + 'hold', + 'restart', + 'restart-by-peer'], + 'Default': 'hold'}, + 'interval': {'Type': 'Integer', + 'Default': 30}, + 'timeout': {'Type': 'Integer', + 'Default': 120}, + } + + properties_schema = {'name': {'Type': 'String'}, + 'description': {'Type': 'String'}, + 'peer_address': {'Type': 'String', + 'Required': True}, + 'peer_id': {'Type': 'String', + 'Required': True}, + 'peer_cidrs': {'Type': 'List', + 'Required': True}, + 'mtu': {'Type': 'Integer', + 'Default': 1500}, + 'dpd': {'Type': 'Map', 'Schema': dpd_schema}, + 'psk': {'Type': 'String', + 'Required': True}, + 'initiator': {'Type': 'String', + 'AllowedValues': ['bi-directional', + 'response-only'], + 'Default': 'bi-directional'}, + 'admin_state_up': {'Type': 'Boolean', + 'Default': True}, + 'ikepolicy_id': {'Type': 'String', + 'Required': True}, + 'ipsecpolicy_id': {'Type': 'String', + 'Required': True}, + 'vpnservice_id': {'Type': 'String', + 'Required': True}} + + attributes_schema = { + 'admin_state_up': 'the administrative state of the ipsec site' + ' connection', + 'auth_mode': 'authentication mode used by the ipsec site connection', + 'description': 'description of the ipsec site connection', + 'dpd': 'configuration of dead peer detection protocol', + 'id': 'unique identifier for the ipsec site connection', + 'ikepolicy_id': 'unique identifier for ike policy used to create the' + ' ipsec site connection', + 'initiator': 'initiator of the ipsec site connection', + 'ipsecpolicy_id': 'unique identifier for ipsec policy used to create' + ' the ipsec site connection', + 'mtu': 'maximum transmission unit to address fragmentation', + 'name': 'name for the ipsec site connection', + 'peer_address': 'peer vpn gateway public address or FQDN', + 'peer_cidrs': 'peer private cidrs', + 'peer_id': 'peer identifier (name, string or FQDN)', + 'psk': 'pre-shared-key used to create the ipsec site connection', + 'route_mode': 'route mode used to create the ipsec site connection', + 'status': 'the status of the ipsec site connection', + 'tenant_id': 'tenant owning the ipsec site connection', + 'vpnservice_id': 'unique identifier for vpn service used to create the' + ' ipsec site connection' + } + + update_allowed_keys = ('Properties',) + + update_allowed_properties = ('name', 'description', 'admin_state_up',) + + def _show_resource(self): + return self.neutron().show_ipsec_site_connection(self.resource_id)[ + 'ipsec_site_connection'] + + def handle_create(self): + props = self.prepare_properties( + self.properties, + self.physical_resource_name()) + ipsec_site_connection = self.neutron().create_ipsec_site_connection( + {'ipsec_site_connection': props})['ipsec_site_connection'] + self.resource_id_set(ipsec_site_connection['id']) + + def handle_update(self, json_snippet, tmpl_diff, prop_diff): + if prop_diff: + self.neutron().update_ipsec_site_connection( + self.resource_id, {'ipsec_site_connection': prop_diff}) + + def handle_delete(self): + client = self.neutron() + try: + client.delete_ipsec_site_connection(self.resource_id) + except NeutronClientException as ex: + if ex.status_code != 404: + raise ex + else: + return scheduler.TaskRunner(self._confirm_delete)() + + class IKEPolicy(neutron.NeutronResource): """ A resource for IKE policy in Neutron. @@ -253,6 +355,7 @@ def resource_mapping(): return { 'OS::Neutron::VPNService': VPNService, + 'OS::Neutron::IPsecSiteConnection': IPsecSiteConnection, 'OS::Neutron::IKEPolicy': IKEPolicy, - 'OS::Neutron::IPsecPolicy': IPsecPolicy + 'OS::Neutron::IPsecPolicy': IPsecPolicy, } diff --git a/heat/tests/test_neutron_vpnservice.py b/heat/tests/test_neutron_vpnservice.py index bc98f0cc..92c2ecb9 100644 --- a/heat/tests/test_neutron_vpnservice.py +++ b/heat/tests/test_neutron_vpnservice.py @@ -49,6 +49,38 @@ vpnservice_template = ''' } ''' +ipsec_site_connection_template = ''' +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Description" : "Template to test IPsec policy resource", + "Parameters" : {}, + "Resources" : { + "IPsecSiteConnection" : { + "Type" : "OS::Neutron::IPsecSiteConnection", + "Properties" : { + "name" : "IPsecSiteConnection", + "description" : "My new VPN connection", + "peer_address" : "172.24.4.233", + "peer_id" : "172.24.4.233", + "peer_cidrs" : [ "10.2.0.0/24" ], + "mtu" : 1500, + "dpd" : { + "actions" : "hold", + "interval" : 30, + "timeout" : 120 + }, + "psk" : "secret", + "initiator" : "bi-directional", + "admin_state_up" : true, + "ikepolicy_id" : "ike123", + "ipsecpolicy_id" : "ips123", + "vpnservice_id" : "vpn123" + } + } + } +} +''' + ikepolicy_template = ''' { "AWSTemplateFormatVersion" : "2010-09-09", @@ -241,6 +273,175 @@ class VPNServiceTest(HeatTestCase): self.m.VerifyAll() +@skipIf(neutronclient is None, 'neutronclient unavailable') +class IPsecSiteConnectionTest(HeatTestCase): + + IPSEC_SITE_CONNECTION_CONF = { + 'ipsec_site_connection': { + 'name': 'IPsecSiteConnection', + 'description': 'My new VPN connection', + 'peer_address': '172.24.4.233', + 'peer_id': '172.24.4.233', + 'peer_cidrs': ['10.2.0.0/24'], + 'mtu': 1500, + 'dpd': { + 'actions': 'hold', + 'interval': 30, + 'timeout': 120 + }, + 'psk': 'secret', + 'initiator': 'bi-directional', + 'admin_state_up': True, + 'ikepolicy_id': 'ike123', + 'ipsecpolicy_id': 'ips123', + 'vpnservice_id': 'vpn123' + } + } + + def setUp(self): + super(IPsecSiteConnectionTest, self).setUp() + self.m.StubOutWithMock(neutronclient.Client, + 'create_ipsec_site_connection') + self.m.StubOutWithMock(neutronclient.Client, + 'delete_ipsec_site_connection') + self.m.StubOutWithMock(neutronclient.Client, + 'show_ipsec_site_connection') + self.m.StubOutWithMock(neutronclient.Client, + 'update_ipsec_site_connection') + self.m.StubOutWithMock(clients.OpenStackClients, 'keystone') + utils.setup_dummy_db() + + def create_ipsec_site_connection(self): + clients.OpenStackClients.keystone().AndReturn( + fakes.FakeKeystoneClient()) + neutronclient.Client.create_ipsec_site_connection( + self.IPSEC_SITE_CONNECTION_CONF).AndReturn( + {'ipsec_site_connection': {'id': 'con123'}}) + snippet = template_format.parse(ipsec_site_connection_template) + self.stack = utils.parse_stack(snippet) + return vpnservice.IPsecSiteConnection( + 'ipsec_site_connection', + snippet['Resources']['IPsecSiteConnection'], + self.stack) + + @utils.stack_delete_after + def test_create(self): + rsrc = self.create_ipsec_site_connection() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + self.assertEqual((rsrc.CREATE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + @utils.stack_delete_after + def test_create_failed(self): + clients.OpenStackClients.keystone().AndReturn( + fakes.FakeKeystoneClient()) + neutronclient.Client.create_ipsec_site_connection( + self.IPSEC_SITE_CONNECTION_CONF).AndRaise( + vpnservice.NeutronClientException()) + self.m.ReplayAll() + snippet = template_format.parse(ipsec_site_connection_template) + self.stack = utils.parse_stack(snippet) + rsrc = vpnservice.IPsecSiteConnection( + 'ipsec_site_connection', + snippet['Resources']['IPsecSiteConnection'], + self.stack) + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.create)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.CREATE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + @utils.stack_delete_after + def test_delete(self): + neutronclient.Client.delete_ipsec_site_connection('con123') + neutronclient.Client.show_ipsec_site_connection('con123').AndRaise( + vpnservice.NeutronClientException(status_code=404)) + rsrc = self.create_ipsec_site_connection() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + @utils.stack_delete_after + def test_delete_already_gone(self): + neutronclient.Client.delete_ipsec_site_connection('con123').AndRaise( + vpnservice.NeutronClientException(status_code=404)) + rsrc = self.create_ipsec_site_connection() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + scheduler.TaskRunner(rsrc.delete)() + self.assertEqual((rsrc.DELETE, rsrc.COMPLETE), rsrc.state) + self.m.VerifyAll() + + @utils.stack_delete_after + def test_delete_failed(self): + neutronclient.Client.delete_ipsec_site_connection('con123').AndRaise( + vpnservice.NeutronClientException(status_code=400)) + rsrc = self.create_ipsec_site_connection() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.ResourceFailure, + scheduler.TaskRunner(rsrc.delete)) + self.assertEqual( + 'NeutronClientException: An unknown exception occurred.', + str(error)) + self.assertEqual((rsrc.DELETE, rsrc.FAILED), rsrc.state) + self.m.VerifyAll() + + @utils.stack_delete_after + def test_attribute(self): + rsrc = self.create_ipsec_site_connection() + neutronclient.Client.show_ipsec_site_connection( + 'con123').MultipleTimes().AndReturn( + self.IPSEC_SITE_CONNECTION_CONF) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + self.assertEqual('IPsecSiteConnection', rsrc.FnGetAtt('name')) + self.assertEqual('My new VPN connection', rsrc.FnGetAtt('description')) + self.assertEqual('172.24.4.233', rsrc.FnGetAtt('peer_address')) + self.assertEqual('172.24.4.233', rsrc.FnGetAtt('peer_id')) + self.assertEqual(['10.2.0.0/24'], rsrc.FnGetAtt('peer_cidrs')) + self.assertEqual('hold', rsrc.FnGetAtt('dpd')['actions']) + self.assertEqual(30, rsrc.FnGetAtt('dpd')['interval']) + self.assertEqual(120, rsrc.FnGetAtt('dpd')['timeout']) + self.assertEqual('secret', rsrc.FnGetAtt('psk')) + self.assertEqual('bi-directional', rsrc.FnGetAtt('initiator')) + self.assertEqual(True, rsrc.FnGetAtt('admin_state_up')) + self.assertEqual('ike123', rsrc.FnGetAtt('ikepolicy_id')) + self.assertEqual('ips123', rsrc.FnGetAtt('ipsecpolicy_id')) + self.assertEqual('vpn123', rsrc.FnGetAtt('vpnservice_id')) + self.m.VerifyAll() + + @utils.stack_delete_after + def test_attribute_failed(self): + rsrc = self.create_ipsec_site_connection() + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + error = self.assertRaises(exception.InvalidTemplateAttribute, + rsrc.FnGetAtt, 'non-existent_property') + self.assertEqual( + 'The Referenced Attribute (ipsec_site_connection ' + 'non-existent_property) is incorrect.', + str(error)) + self.m.VerifyAll() + + @utils.stack_delete_after + def test_update(self): + rsrc = self.create_ipsec_site_connection() + neutronclient.Client.update_ipsec_site_connection( + 'con123', {'ipsec_site_connection': {'admin_state_up': False}}) + self.m.ReplayAll() + scheduler.TaskRunner(rsrc.create)() + update_template = copy.deepcopy(rsrc.t) + update_template['Properties']['admin_state_up'] = False + scheduler.TaskRunner(rsrc.update, update_template)() + self.m.VerifyAll() + + @skipIf(neutronclient is None, 'neutronclient unavailable') class IKEPolicyTest(HeatTestCase):