]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
VPNaaS support for VPN service admin state change and reporting
authorPaul Michali <pcm@cisco.com>
Fri, 14 Mar 2014 15:21:03 +0000 (15:21 +0000)
committerPaul Michali <pcm@cisco.com>
Fri, 21 Mar 2014 13:43:12 +0000 (13:43 +0000)
For VPN service admin state changes, hook up the API call to the service
driver so that the configuration changes can be applied.

Modify the status reporting, so that the VPN service and IPSec
connection status' match the actual status when admin state down
actions are performed (on both the service and connection).

Change-Id: Ife7176675d20bb3ea529a4d79fa92a61f9550a6a
Closes-Bug: 1291609

neutron/services/vpn/device_drivers/ipsec.py
neutron/services/vpn/device_drivers/template/openswan/ipsec.conf.template
neutron/services/vpn/plugin.py
neutron/tests/unit/services/vpn/device_drivers/test_ipsec.py
neutron/tests/unit/services/vpn/test_vpnaas_driver_plugin.py

index cdf9c58710bb3162c99212c5d3312560d9cf74a0..90e95c96db740a354150f623057b34c8012f8250 100644 (file)
@@ -77,6 +77,8 @@ STATUS_MAP = {
     'unrouted': constants.DOWN
 }
 
+IPSEC_CONNS = 'ipsec_site_connections'
+
 
 def _get_template(template_file):
     global JINJA_ENV
@@ -622,14 +624,32 @@ class IPsecDriver(device_drivers.DeviceDriver):
             'ipsec_site_connections': copy.deepcopy(process.connection_status)
         }
 
+    def update_downed_connections(self, process_id, new_status):
+        """Update info to be reported, if connections just went down.
+
+        If there is no longer any information for a connection (because it
+        has been removed (e.g. due to an admin down of VPN service or IPSec
+        connection), but there was previous status information for the
+        connection, mark the connection as down for reporting purposes.
+        """
+        if process_id in self.process_status_cache:
+            for conn in self.process_status_cache[process_id][IPSEC_CONNS]:
+                if conn not in new_status[IPSEC_CONNS]:
+                    new_status[IPSEC_CONNS][conn] = {
+                        'status': constants.DOWN,
+                        'updated_pending_status': True
+                    }
+
     def report_status(self, context):
         status_changed_vpn_services = []
         for process in self.processes.values():
             previous_status = self.get_process_status_cache(process)
             if self.is_status_updated(process, previous_status):
                 new_status = self.copy_process_status(process)
-                self.process_status_cache[process.id] = new_status
+                self.update_downed_connections(process.id, new_status)
                 status_changed_vpn_services.append(new_status)
+                self.process_status_cache[process.id] = (
+                    self.copy_process_status(process))
                 # We need unset updated_pending status after it
                 # is reported to the server side
                 self.unset_updated_pending_status(process)
index 180b4a19e5fbdcc77d2481d6a5767463d880efa5..546e27ec6d606b3ee5fbfc153d04eca5a60a9ea9 100644 (file)
@@ -6,7 +6,7 @@ conn %default
     ikelifetime=480m
     keylife=60m
     keyingtries=%forever
-{% for ipsec_site_connection in vpnservice.ipsec_site_connections
+{% for ipsec_site_connection in vpnservice.ipsec_site_connections if ipsec_site_connection.admin_state_up
 %}conn {{ipsec_site_connection.id}}
     # NOTE: a default route is required for %defaultroute to work...
     left={{vpnservice.external_ip}}
index 1a128dafc373f9328a9f98a80bf547f0d81c6cdb..771188d65edabb222e6a5b32880c54d6ec1776c3 100644 (file)
@@ -91,6 +91,15 @@ class VPNDriverPlugin(VPNPlugin, vpn_db.VPNPluginRpcDbMixin):
             context, old_ipsec_site_connection, ipsec_site_connection)
         return ipsec_site_connection
 
+    def update_vpnservice(self, context, vpnservice_id, vpnservice):
+        old_vpn_service = self.get_vpnservice(context, vpnservice_id)
+        new_vpn_service = super(
+            VPNDriverPlugin, self).update_vpnservice(context, vpnservice_id,
+                                                     vpnservice)
+        driver = self._get_driver_for_vpnservice(old_vpn_service)
+        driver.update_vpnservice(context, old_vpn_service, new_vpn_service)
+        return new_vpn_service
+
     def delete_vpnservice(self, context, vpnservice_id):
         vpnservice = self._get_vpnservice(context, vpnservice_id)
         super(VPNDriverPlugin, self).delete_vpnservice(context, vpnservice_id)
index bbd65928abb2f5526730bfcac96d48d3410fec75..c7bc4d7198a42a2876b8934ae2619ef8e4599c8d 100644 (file)
@@ -190,3 +190,69 @@ class TestIPsecDeviceDriver(base.BaseTestCase):
         process_id = _uuid()
         self.driver.sync(context, [{'id': process_id}])
         self.assertNotIn(process_id, self.driver.processes)
+
+    def test_status_updated_on_connection_admin_down(self):
+        self.driver.process_status_cache = {
+            '1': {
+                'status': constants.ACTIVE,
+                'id': 123,
+                'updated_pending_status': False,
+                'ipsec_site_connections': {
+                    '10': {
+                        'status': constants.ACTIVE,
+                        'updated_pending_status': False,
+                    },
+                    '20': {
+                        'status': constants.ACTIVE,
+                        'updated_pending_status': False,
+                    }
+                }
+            }
+        }
+        # Simulate that there is no longer status for connection '20'
+        # e.g. connection admin down
+        new_status = {
+            'ipsec_site_connections': {
+                '10': {
+                    'status': constants.ACTIVE,
+                    'updated_pending_status': False
+                }
+            }
+        }
+        self.driver.update_downed_connections('1', new_status)
+        existing_conn = new_status['ipsec_site_connections'].get('10')
+        self.assertIsNotNone(existing_conn)
+        self.assertEqual(constants.ACTIVE, existing_conn['status'])
+        missing_conn = new_status['ipsec_site_connections'].get('20')
+        self.assertIsNotNone(missing_conn)
+        self.assertEqual(constants.DOWN, missing_conn['status'])
+
+    def test_status_updated_on_service_admin_down(self):
+        self.driver.process_status_cache = {
+            '1': {
+                'status': constants.ACTIVE,
+                'id': 123,
+                'updated_pending_status': False,
+                'ipsec_site_connections': {
+                    '10': {
+                        'status': constants.ACTIVE,
+                        'updated_pending_status': False,
+                    },
+                    '20': {
+                        'status': constants.ACTIVE,
+                        'updated_pending_status': False,
+                    }
+                }
+            }
+        }
+        # Simulate that there are no connections now
+        new_status = {
+            'ipsec_site_connections': {}
+        }
+        self.driver.update_downed_connections('1', new_status)
+        missing_conn = new_status['ipsec_site_connections'].get('10')
+        self.assertIsNotNone(missing_conn)
+        self.assertEqual(constants.DOWN, missing_conn['status'])
+        missing_conn = new_status['ipsec_site_connections'].get('20')
+        self.assertIsNotNone(missing_conn)
+        self.assertEqual(constants.DOWN, missing_conn['status'])
index f5ed5bc01b53e4eafe572845e1dc16d8a78eb21f..45f932ec149ea349bd3847d1e4ba63a59062d7d3 100644 (file)
@@ -58,6 +58,11 @@ class TestVPNDriverPlugin(test_db_vpnaas.TestVpnaas,
         self.driver.delete_vpnservice.assert_called_once_with(
             mock.ANY, mock.ANY)
 
+    def test_update_vpnservice(self, **extras):
+        super(TestVPNDriverPlugin, self).test_update_vpnservice()
+        self.driver.update_vpnservice.assert_called_once_with(
+            mock.ANY, mock.ANY, mock.ANY)
+
     @contextlib.contextmanager
     def vpnservice_set(self):
         """Test case to create a ipsec_site_connection."""