From b6c8ea8a3cca39bb4138bc7063569352faeb612f Mon Sep 17 00:00:00 2001 From: Hunt Xu Date: Tue, 20 Mar 2018 17:52:55 +0800 Subject: [PATCH] Make libreswan driver work with recent versions LibreSwan 3.19 introduces a new commandline argument '--nssdir' for pluto which defaults to '/etc/ipsec.d'. As older versions don't understand such an option, we cannot just add it to the commandline. The commandline arguments of LibreSwan are not stable enough to rely on. For example, in 3.19, 'ipsec initnss' has the new argument '--nssdir', and in 3.20, 'ipsec pluto' also gets this new argument '--nssdir', then in 3.22, the argument '--ctlbase' is phased out. In this commit, instead of trying new options and then fallback to old ones for older versions, the bind-mount method used in StrongSwan driver is adopted. With /etc and /var/run bind mounted, all the commandline arguments related to configuration file places can be removed. This ensures that changes of such arguments between different versions won't bother as the default places are always used. This commit also replaces 'auth=' by 'phase2=' in the configuration template as the former is for a long time an alias of the latter and removed in LibreSwan 3.19. The virtual-private argument of 'ipsec pluto' has been put into the configuration file to avoid commas(,) in the commandline so that the netns_wrapper can work well. A new tempest job for running LibreSwan as the device driver on CentOS 7 is also added to avoid regression. This commit has been simply tested on CentOS 7.4 with the following versions of LibreSwan provided by the CentOS repo: - libreswan-3.12-5.el7.x86_64.rpm - libreswan-3.12-10.1.el7_1.x86_64.rpm - libreswan-3.15-5.el7_1.x86_64.rpm - libreswan-3.15-8.el7.x86_64.rpm - libreswan-3.20-3.el7.x86_64.rpm - libreswan-3.20-5.el7_4.x86_64.rpm and different versions of LibreSwan provided by libreswan.org[1]: [1] https://download.libreswan.org/binaries/rhel/7/x86_64/ Change-Id: Iacb6f13187b49cf771f0c24662d6af9217c211b8 Closes-Bug: #1711456 --- .zuul.yaml | 17 +++ etc/neutron/rootwrap.d/vpnaas.filters | 2 +- .../services/vpn/device_drivers/ipsec.py | 119 +++++++++++------- .../vpn/device_drivers/libreswan_ipsec.py | 76 ++++++++++- .../template/openswan/ipsec.conf.template | 3 +- .../tests/tempest/scenario/test_vpnaas.py | 24 ++++ .../services/vpn/device_drivers/test_ipsec.py | 88 ++++++++++--- ...ver-works-with-3.19+-7e1fc79ac6c7efe5.yaml | 4 + 8 files changed, 262 insertions(+), 71 deletions(-) create mode 100644 releasenotes/notes/libreswan-driver-works-with-3.19+-7e1fc79ac6c7efe5.yaml diff --git a/.zuul.yaml b/.zuul.yaml index aef884c10..af0839f73 100644 --- a/.zuul.yaml +++ b/.zuul.yaml @@ -4,6 +4,7 @@ - neutron-vpnaas-dsvm-functional-sswan - neutron-vpnaas-tempest - openstack-tox-lower-constraints + - neutron-vpnaas-tempest-libreswan-centos gate: jobs: - neutron-vpnaas-dsvm-functional-sswan @@ -35,6 +36,22 @@ - ^neutron_vpnaas/tests/unit/.*$ - ^releasenotes/.*$ +- job: + name: neutron-vpnaas-tempest-libreswan-centos + parent: neutron-vpnaas-tempest + nodeset: devstack-single-node-centos-7 + vars: + devstack_localrc: + IPSEC_PACKAGE: libreswan + # VPNaaS 4in6 and 6in4 scenarios would fail after LibreSwan 3.18. + # Base node using Libreswan 3.20 on CentOS 7.4. + # Refer to https://github.com/libreswan/libreswan/issues/175. + devstack_local_conf: + test-config: + $TEMPEST_CONFIG: + neutron_vpnaas_plugin_options: + skip_4in6_6in4_tests: true + - job: name: neutron-vpnaas-dsvm-functional-sswan parent: legacy-dsvm-base diff --git a/etc/neutron/rootwrap.d/vpnaas.filters b/etc/neutron/rootwrap.d/vpnaas.filters index e8a3e0042..846ac2d1c 100644 --- a/etc/neutron/rootwrap.d/vpnaas.filters +++ b/etc/neutron/rootwrap.d/vpnaas.filters @@ -17,4 +17,4 @@ rm_file: RegExpFilter, rm, root, rm, -f, .*/ipsec.secrets strongswan: CommandFilter, strongswan, root neutron_netns_wrapper: CommandFilter, neutron-vpn-netns-wrapper, root neutron_netns_wrapper_local: CommandFilter, /usr/local/bin/neutron-vpn-netns-wrapper, root -chown: RegExpFilter, chown, root, chown, --from=.*, root.root, .*/ipsec.secrets +chown: RegExpFilter, chown, root, chown, --from=.*, root.root, .*/(ipsec.secrets|ipsec/[0-9a-z-]+/log) diff --git a/neutron_vpnaas/services/vpn/device_drivers/ipsec.py b/neutron_vpnaas/services/vpn/device_drivers/ipsec.py index 8a2b862ea..dc8cf9ae0 100644 --- a/neutron_vpnaas/services/vpn/device_drivers/ipsec.py +++ b/neutron_vpnaas/services/vpn/device_drivers/ipsec.py @@ -602,7 +602,7 @@ class OpenSwanProcess(BaseSwanProcess): return routes.split(' ')[2] return address - def _virtual_privates(self): + def _virtual_privates(self, vpnservice): """Returns line of virtual_privates. virtual_private contains the networks @@ -610,14 +610,77 @@ class OpenSwanProcess(BaseSwanProcess): """ virtual_privates = [] nets = [] - for ipsec_site_conn in self.vpnservice['ipsec_site_connections']: + for ipsec_site_conn in vpnservice['ipsec_site_connections']: nets += ipsec_site_conn['local_cidrs'] nets += ipsec_site_conn['peer_cidrs'] for net in nets: version = netaddr.IPNetwork(net).version virtual_privates.append('%%v%s:%s' % (version, net)) + virtual_privates.sort() return ','.join(virtual_privates) + def _gen_config_content(self, template_file, vpnservice): + template = _get_template(template_file) + virtual_privates = self._virtual_privates(vpnservice) + return template.render( + {'vpnservice': vpnservice, + 'virtual_privates': virtual_privates}) + + def start_pluto(self): + cmd = [self.binary, + 'pluto', + '--ctlbase', self.pid_path, + '--ipsecdir', self.etc_dir, + '--use-netkey', + '--uniqueids', + '--nat_traversal', + '--secretsfile', self.secrets_file] + + if self.conf.ipsec.enable_detailed_logging: + cmd += ['--perpeerlog', '--perpeerlogbase', self.log_dir] + self._execute(cmd) + + def add_ipsec_connection(self, nexthop, conn_id): + self._execute([self.binary, + 'addconn', + '--ctlbase', '%s.ctl' % self.pid_path, + '--defaultroutenexthop', nexthop, + '--config', self.config_file, conn_id + ]) + + def start_whack_listening(self): + #TODO(nati) fix this when openswan is fixed + #Due to openswan bug, this command always exit with 3 + self._execute([self.binary, + 'whack', + '--ctlbase', self.pid_path, + '--listen' + ], check_exit_code=False) + + def shutdown_whack(self): + self._execute([self.binary, + 'whack', + '--ctlbase', self.pid_path, + '--shutdown' + ]) + + def initiate_connection(self, conn_name): + self._execute([self.binary, + 'whack', + '--ctlbase', self.pid_path, + '--name', conn_name, + '--asynchronous', + '--initiate' + ]) + + def terminate_connection(self, conn_name): + self._execute([self.binary, + 'whack', + '--ctlbase', self.pid_path, + '--name', conn_name, + '--terminate' + ]) + def start(self): """Start the process. @@ -642,21 +705,9 @@ class OpenSwanProcess(BaseSwanProcess): if not self._process_running(): self._cleanup_control_files() - virtual_private = self._virtual_privates() #start pluto IKE keying daemon - cmd = [self.binary, - 'pluto', - '--ctlbase', self.pid_path, - '--ipsecdir', self.etc_dir, - '--use-netkey', - '--uniqueids', - '--nat_traversal', - '--secretsfile', self.secrets_file, - '--virtual_private', virtual_private] + self.start_pluto() - if self.conf.ipsec.enable_detailed_logging: - cmd += ['--perpeerlog', '--perpeerlogbase', self.log_dir] - self._execute(cmd) #add connections for ipsec_site_conn in self.vpnservice['ipsec_site_connections']: # Don't add a connection if its admin state is down @@ -664,34 +715,17 @@ class OpenSwanProcess(BaseSwanProcess): continue nexthop = self._get_nexthop(ipsec_site_conn['peer_address'], ipsec_site_conn['id']) - self._execute([self.binary, - 'addconn', - '--ctlbase', '%s.ctl' % self.pid_path, - '--defaultroutenexthop', nexthop, - '--config', self.config_file, - ipsec_site_conn['id'] - ]) - #TODO(nati) fix this when openswan is fixed - #Due to openswan bug, this command always exit with 3 + self.add_ipsec_connection(nexthop, ipsec_site_conn['id']) + #start whack ipsec keying daemon - self._execute([self.binary, - 'whack', - '--ctlbase', self.pid_path, - '--listen', - ], check_exit_code=False) + self.start_whack_listening() for ipsec_site_conn in self.vpnservice['ipsec_site_connections']: if (not ipsec_site_conn['initiator'] == 'start' or not ipsec_site_conn['admin_state_up']): continue #initiate ipsec connection - self._execute([self.binary, - 'whack', - '--ctlbase', self.pid_path, - '--name', ipsec_site_conn['id'], - '--asynchronous', - '--initiate' - ]) + self.initiate_connection(ipsec_site_conn['id']) self._copy_configs() def get_established_connections(self): @@ -720,22 +754,13 @@ class OpenSwanProcess(BaseSwanProcess): connections = self.get_established_connections() for conn_name in connections: - self._execute([self.binary, - 'whack', - '--ctlbase', self.pid_path, - '--name', '%s' % conn_name, - '--terminate' - ]) + self.terminate_connection(conn_name) def stop(self): #Stop process using whack #Note this will also stop pluto self.disconnect() - self._execute([self.binary, - 'whack', - '--ctlbase', self.pid_path, - '--shutdown', - ]) + self.shutdown_whack() self.connection_status = {} diff --git a/neutron_vpnaas/services/vpn/device_drivers/libreswan_ipsec.py b/neutron_vpnaas/services/vpn/device_drivers/libreswan_ipsec.py index 94153136c..680150c28 100644 --- a/neutron_vpnaas/services/vpn/device_drivers/libreswan_ipsec.py +++ b/neutron_vpnaas/services/vpn/device_drivers/libreswan_ipsec.py @@ -15,8 +15,12 @@ import os import os.path +from neutron.agent.linux import ip_lib + from neutron_vpnaas.services.vpn.device_drivers import ipsec +NS_WRAPPER = 'neutron-vpn-netns-wrapper' + class LibreSwanProcess(ipsec.OpenSwanProcess): """Libreswan Process manager class. @@ -27,6 +31,33 @@ class LibreSwanProcess(ipsec.OpenSwanProcess): super(LibreSwanProcess, self).__init__(conf, process_id, vpnservice, namespace) + def _ipsec_execute(self, cmd, check_exit_code=True, extra_ok_codes=None): + """Execute ipsec command on namespace. + + This execute is wrapped by namespace wrapper. + The namespace wrapper will bind /etc and /var/run + """ + ip_wrapper = ip_lib.IPWrapper(namespace=self.namespace) + mount_paths = {'/etc': '%s/etc' % self.config_dir, + '/var/run': '%s/var/run' % self.config_dir} + mount_paths_str = ','.join( + "%s:%s" % (source, target) + for source, target in mount_paths.items()) + return ip_wrapper.netns.execute( + [NS_WRAPPER, + '--mount_paths=%s' % mount_paths_str, + '--cmd=%s,%s' % (self.binary, ','.join(cmd))], + check_exit_code=check_exit_code, + extra_ok_codes=extra_ok_codes) + + def _ensure_needed_files(self): + # addconn reads from /etc/hosts and /etc/resolv.conf. As /etc would be + # bind-mounted, create these two empty files in the target directory. + with open('%s/etc/hosts' % self.config_dir, 'a'): + pass + with open('%s/etc/resolv.conf' % self.config_dir, 'a'): + pass + def ensure_configs(self): """Generate config files which are needed for Libreswan. @@ -50,15 +81,54 @@ class LibreSwanProcess(ipsec.OpenSwanProcess): self._execute(['chown', '--from=%s' % os.getuid(), 'root:root', secrets_file]) + # Libreswan needs to write logs to this directory. + self._execute(['chown', '--from=%s' % os.getuid(), 'root:root', + self.log_dir]) + + self._ensure_needed_files() + # Load the ipsec kernel module if not loaded - self._execute([self.binary, '_stackmanager', 'start']) + self._ipsec_execute(['_stackmanager', 'start']) # checknss creates nssdb only if it is missing # It is added in Libreswan version v3.10 # For prior versions use initnss try: - self._execute([self.binary, 'checknss', self.etc_dir]) + self._ipsec_execute(['checknss']) except RuntimeError: - self._execute([self.binary, 'initnss', self.etc_dir]) + self._ipsec_execute(['initnss']) + + def get_status(self): + return self._ipsec_execute(['whack', '--status'], + extra_ok_codes=[1, 3]) + + def start_pluto(self): + cmd = ['pluto', + '--use-netkey', + '--uniqueids'] + + if self.conf.ipsec.enable_detailed_logging: + cmd += ['--perpeerlog', '--perpeerlogbase', self.log_dir] + self._ipsec_execute(cmd) + + def add_ipsec_connection(self, nexthop, conn_id): + # Connections will be automatically added as auto=start/add for + # initiator=bi-directional/response-only specified in the config. + pass + + def start_whack_listening(self): + # NOTE(huntxu): This is a workaround for with a weak (len<8) secret, + # "ipsec whack --listen" will exit with 3. + self._ipsec_execute(['whack', '--listen'], extra_ok_codes=[3]) + + def shutdown_whack(self): + self._ipsec_execute(['whack', '--shutdown']) + + def initiate_connection(self, conn_name): + self._ipsec_execute( + ['whack', '--name', conn_name, '--asynchronous', '--initiate']) + + def terminate_connection(self, conn_name): + self._ipsec_execute(['whack', '--name', conn_name, '--terminate']) class LibreSwanDriver(ipsec.IPsecDriver): diff --git a/neutron_vpnaas/services/vpn/device_drivers/template/openswan/ipsec.conf.template b/neutron_vpnaas/services/vpn/device_drivers/template/openswan/ipsec.conf.template index d18cf4ffb..fa64fb175 100644 --- a/neutron_vpnaas/services/vpn/device_drivers/template/openswan/ipsec.conf.template +++ b/neutron_vpnaas/services/vpn/device_drivers/template/openswan/ipsec.conf.template @@ -1,6 +1,7 @@ # Configuration for {{vpnservice.id}} config setup nat_traversal=yes + virtual_private={{virtual_privates}} conn %default keylife=60m keyingtries=%forever @@ -69,7 +70,7 @@ conn {{ipsec_site_connection.id}} # IPsecPolicys params ########################## # [transform_protocol] - auth={{ipsec_site_connection.ipsecpolicy.transform_protocol}} + phase2={{ipsec_site_connection.ipsecpolicy.transform_protocol}} {% if ipsec_site_connection.ipsecpolicy.transform_protocol == "ah" -%} # AH protocol does not support encryption # [auth_algorithm]-[pfs] diff --git a/neutron_vpnaas/tests/tempest/scenario/test_vpnaas.py b/neutron_vpnaas/tests/tempest/scenario/test_vpnaas.py index 3cfd0aae6..2a777761e 100644 --- a/neutron_vpnaas/tests/tempest/scenario/test_vpnaas.py +++ b/neutron_vpnaas/tests/tempest/scenario/test_vpnaas.py @@ -14,6 +14,7 @@ # under the License. import netaddr +from oslo_config import cfg import testtools from tempest.common import utils @@ -30,6 +31,23 @@ from neutron_vpnaas.tests.tempest.scenario import base CONF = config.CONF +# NOTE(huntxu): This is a workaround due to a upstream bug [1]. +# VPNaaS 4in6 and 6in4 is not working properly with LibreSwan 3.19+. +# In OpenStack zuul checks the base CentOS 7 node is using Libreswan 3.20 on +# CentOS 7.4. So we need to provide a way to skip the 4in6 and 6in4 test cases +# for zuul. +# +# Once the upstream bug gets fixed and the base node uses a newer version of +# Libreswan with that fix, we can remove this. +# +# [1] https://github.com/libreswan/libreswan/issues/175 +CONF.register_opt( + cfg.BoolOpt('skip_4in6_6in4_tests', + default=False, + help='Whether to skip 4in6 and 6in4 test cases.'), + 'neutron_vpnaas_plugin_options' +) + class Vpnaas(base.BaseTempestTestCase): """Test the following topology @@ -247,6 +265,9 @@ class Vpnaas4in6(Vpnaas): @decorators.idempotent_id('2d5f18dc-6186-4deb-842b-051325bd0466') @testtools.skipUnless(CONF.network_feature_enabled.ipv6, 'IPv6 tests are disabled.') + @testtools.skipIf( + CONF.neutron_vpnaas_plugin_options.skip_4in6_6in4_tests, + 'VPNaaS 4in6 test is skipped.') def test_vpnaas_4in6(self): self._test_vpnaas() @@ -257,6 +278,9 @@ class Vpnaas6in4(Vpnaas): @decorators.idempotent_id('10febf33-c5b7-48af-aa13-94b4fb585a55') @testtools.skipUnless(CONF.network_feature_enabled.ipv6, 'IPv6 tests are disabled.') + @testtools.skipIf( + CONF.neutron_vpnaas_plugin_options.skip_4in6_6in4_tests, + 'VPNaaS 6in4 test is skipped.') def test_vpnaas_6in4(self): self._test_vpnaas() diff --git a/neutron_vpnaas/tests/unit/services/vpn/device_drivers/test_ipsec.py b/neutron_vpnaas/tests/unit/services/vpn/device_drivers/test_ipsec.py index 221011625..19f31e7d1 100644 --- a/neutron_vpnaas/tests/unit/services/vpn/device_drivers/test_ipsec.py +++ b/neutron_vpnaas/tests/unit/services/vpn/device_drivers/test_ipsec.py @@ -19,6 +19,7 @@ import os import socket import mock +import netaddr from neutron.agent.l3 import dvr_edge_router from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import legacy_router @@ -138,7 +139,7 @@ OPENSWAN_CONNECTION_DETAILS = '''# rightsubnet=networkA/netmaskA, networkB/netma # IPsecPolicys params ########################## # [transform_protocol] - auth=%(auth_mode)s + phase2=%(auth_mode)s # [encapsulation_mode] type=%(encapsulation_mode)s # [lifetime_value] @@ -162,6 +163,7 @@ EXPECTED_OPENSWAN_CONF = """ # Configuration for %(vpnservice_id)s config setup nat_traversal=yes + virtual_private=%(virtual_privates)s conn %%default keylife=60m keyingtries=%%forever @@ -959,6 +961,7 @@ class TestOpenSwanConfigGeneration(BaseIPsecDeviceDriver): 'ike_lifetime': 3600, 'life_time': 3600, 'encapsulation_mode': 'tunnel'} + virtual_privates = [] # Convert local CIDRs into assignment strings. IF more than one, # pluralize the attribute name and enclose in brackets. cidrs = info.get('local_cidrs', [['10.0.0.0/24'], ['11.0.0.0/24']]) @@ -968,16 +971,25 @@ class TestOpenSwanConfigGeneration(BaseIPsecDeviceDriver): local_cidrs.append("s={ %s }" % ' '.join(cidr)) else: local_cidrs.append("=%s" % cidr[0]) + for net in cidr: + version = netaddr.IPNetwork(net).version + virtual_privates.append('%%v%s:%s' % (version, net)) # Convert peer CIDRs into space separated strings cidrs = info.get('peer_cidrs', [['20.0.0.0/24', '30.0.0.0/24'], ['40.0.0.0/24', '50.0.0.0/24']]) + for cidr in cidrs: + for net in cidr: + version = netaddr.IPNetwork(net).version + virtual_privates.append('%%v%s:%s' % (version, net)) peer_cidrs = [' '.join(cidr) for cidr in cidrs] local_ip = info.get('local', '60.0.0.4') version = info.get('local_ip_vers', 4) next_hop = IPV4_NEXT_HOP if version == 4 else IPV6_NEXT_HOP % local_ip peer_ips = info.get('peers', ['60.0.0.5', '60.0.0.6']) + virtual_privates.sort() return EXPECTED_OPENSWAN_CONF % { 'vpnservice_id': FAKE_VPNSERVICE_ID, + 'virtual_privates': ','.join(virtual_privates), 'next_hop': next_hop, 'local_cidrs1': local_cidrs[0], 'local_cidrs2': local_cidrs[1], 'local_ver': version, @@ -1412,8 +1424,15 @@ class TestLibreSwanProcess(base.BaseTestCase): @mock.patch('os.path.exists', return_value=True) def test_ensure_configs_on_restart(self, exists_mock): openswan_ipsec.OpenSwanProcess.ensure_configs = mock.Mock() - with mock.patch.object(self.ipsec_process, '_execute') as fake_execute: + with mock.patch.object( + self.ipsec_process, '_execute' + ) as fake_execute, mock.patch.object( + self.ipsec_process, '_ipsec_execute' + ) as fake_ipsec_execute, mock.patch.object( + self.ipsec_process, '_ensure_needed_files' + ) as fake_ensure_needed_files: self.ipsec_process.ensure_configs() + expected = [mock.call(['rm', '-f', self.ipsec_process._get_config_filename( 'ipsec.secrets')]), @@ -1421,45 +1440,76 @@ class TestLibreSwanProcess(base.BaseTestCase): 'root:root', self.ipsec_process._get_config_filename( 'ipsec.secrets')]), - mock.call(['ipsec', '_stackmanager', 'start']), - mock.call(['ipsec', 'checknss', - self.ipsec_process.etc_dir])] + mock.call(['chown', '--from=%s' % os.getuid(), + 'root:root', self.ipsec_process.log_dir])] fake_execute.assert_has_calls(expected) - self.assertEqual(4, fake_execute.call_count) + self.assertEqual(3, fake_execute.call_count) + + expected = [mock.call(['_stackmanager', 'start']), + mock.call(['checknss'])] + fake_ipsec_execute.assert_has_calls(expected) + self.assertEqual(2, fake_ipsec_execute.call_count) + + self.assertTrue(fake_ensure_needed_files.called) self.assertTrue(exists_mock.called) @mock.patch('os.path.exists', return_value=False) def test_ensure_configs(self, exists_mock): openswan_ipsec.OpenSwanProcess.ensure_configs = mock.Mock() - with mock.patch.object(self.ipsec_process, '_execute') as fake_execute: + with mock.patch.object( + self.ipsec_process, '_execute' + ) as fake_execute, mock.patch.object( + self.ipsec_process, '_ipsec_execute' + ) as fake_ipsec_execute, mock.patch.object( + self.ipsec_process, '_ensure_needed_files' + ) as fake_ensure_needed_files: self.ipsec_process.ensure_configs() + expected = [mock.call(['chown', '--from=%s' % os.getuid(), 'root:root', self.ipsec_process._get_config_filename( 'ipsec.secrets')]), - mock.call(['ipsec', '_stackmanager', 'start']), - mock.call(['ipsec', 'checknss', - self.ipsec_process.etc_dir])] + mock.call(['chown', '--from=%s' % os.getuid(), + 'root:root', self.ipsec_process.log_dir])] fake_execute.assert_has_calls(expected) - self.assertEqual(3, fake_execute.call_count) + self.assertEqual(2, fake_execute.call_count) + + expected = [mock.call(['_stackmanager', 'start']), + mock.call(['checknss'])] + fake_ipsec_execute.assert_has_calls(expected) + self.assertEqual(2, fake_ipsec_execute.call_count) + + self.assertTrue(fake_ensure_needed_files.called) self.assertTrue(exists_mock.called) exists_mock.reset_mock() - with mock.patch.object(self.ipsec_process, '_execute') as fake_execute: - fake_execute.side_effect = [None, None, RuntimeError, None] + with mock.patch.object( + self.ipsec_process, '_execute' + ) as fake_execute, mock.patch.object( + self.ipsec_process, '_ipsec_execute' + ) as fake_ipsec_execute, mock.patch.object( + self.ipsec_process, '_ensure_needed_files' + ) as fake_ensure_needed_files: + fake_ipsec_execute.side_effect = [None, RuntimeError, None] self.ipsec_process.ensure_configs() + expected = [mock.call(['chown', '--from=%s' % os.getuid(), 'root:root', self.ipsec_process._get_config_filename( 'ipsec.secrets')]), - mock.call(['ipsec', '_stackmanager', 'start']), - mock.call(['ipsec', 'checknss', - self.ipsec_process.etc_dir]), - mock.call(['ipsec', 'initnss', - self.ipsec_process.etc_dir])] + mock.call(['chown', '--from=%s' % os.getuid(), + 'root:root', self.ipsec_process.log_dir])] fake_execute.assert_has_calls(expected) - self.assertEqual(4, fake_execute.call_count) + self.assertEqual(2, fake_execute.call_count) + + expected = [mock.call(['_stackmanager', 'start']), + mock.call(['checknss']), + mock.call(['initnss'])] + self.assertEqual(3, fake_ipsec_execute.call_count) + fake_ipsec_execute.assert_has_calls(expected) + + self.assertTrue(fake_ensure_needed_files.called) self.assertTrue(exists_mock.called) diff --git a/releasenotes/notes/libreswan-driver-works-with-3.19+-7e1fc79ac6c7efe5.yaml b/releasenotes/notes/libreswan-driver-works-with-3.19+-7e1fc79ac6c7efe5.yaml new file mode 100644 index 000000000..57a539641 --- /dev/null +++ b/releasenotes/notes/libreswan-driver-works-with-3.19+-7e1fc79ac6c7efe5.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - The libreswan driver of neutron-vpnaas can now also work with Libreswan + 3.19+ (bug `#1711456 `_).