]> review.fuel-infra Code Review - openstack-build/heat-build.git/commitdiff
Adding IPsec site connection to Heat resources
authorBartosz Górski <bartosz.gorski@ntti3.com>
Tue, 3 Sep 2013 01:54:11 +0000 (18:54 -0700)
committerSteve Baker <sbaker@redhat.com>
Wed, 4 Sep 2013 22:57:16 +0000 (10:57 +1200)
Adds Neutron IPsec site connection component to resources
supported by Heat with unit tests.

Change-Id: Idf3c92b9a7ac513e7f7ab0d2501668405189ebc6
Implements: blueprint vpnaas-support

heat/engine/resources/neutron/vpnservice.py
heat/tests/test_neutron_vpnservice.py

index 313d3a2eee03d156c5eadc46b67a10f7152b2b29..0e9fd004cb222003ae96595bbb89ff0ed082a078 100644 (file)
@@ -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,
     }
index bc98f0cc4ab0979dd25343d276a7f7607557e81e..92c2ecb994c0e8d1f8477239fb40ecee54911290 100644 (file)
@@ -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):