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
This commit is contained in:
Hunt Xu 2018-03-20 17:52:55 +08:00
parent 0184b7d630
commit b6c8ea8a3c
8 changed files with 262 additions and 71 deletions

View File

@ -4,6 +4,7 @@
- neutron-vpnaas-dsvm-functional-sswan - neutron-vpnaas-dsvm-functional-sswan
- neutron-vpnaas-tempest - neutron-vpnaas-tempest
- openstack-tox-lower-constraints - openstack-tox-lower-constraints
- neutron-vpnaas-tempest-libreswan-centos
gate: gate:
jobs: jobs:
- neutron-vpnaas-dsvm-functional-sswan - neutron-vpnaas-dsvm-functional-sswan
@ -35,6 +36,22 @@
- ^neutron_vpnaas/tests/unit/.*$ - ^neutron_vpnaas/tests/unit/.*$
- ^releasenotes/.*$ - ^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: - job:
name: neutron-vpnaas-dsvm-functional-sswan name: neutron-vpnaas-dsvm-functional-sswan
parent: legacy-dsvm-base parent: legacy-dsvm-base

View File

@ -17,4 +17,4 @@ rm_file: RegExpFilter, rm, root, rm, -f, .*/ipsec.secrets
strongswan: CommandFilter, strongswan, root strongswan: CommandFilter, strongswan, root
neutron_netns_wrapper: CommandFilter, neutron-vpn-netns-wrapper, root neutron_netns_wrapper: CommandFilter, neutron-vpn-netns-wrapper, root
neutron_netns_wrapper_local: CommandFilter, /usr/local/bin/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)

View File

@ -602,7 +602,7 @@ class OpenSwanProcess(BaseSwanProcess):
return routes.split(' ')[2] return routes.split(' ')[2]
return address return address
def _virtual_privates(self): def _virtual_privates(self, vpnservice):
"""Returns line of virtual_privates. """Returns line of virtual_privates.
virtual_private contains the networks virtual_private contains the networks
@ -610,14 +610,77 @@ class OpenSwanProcess(BaseSwanProcess):
""" """
virtual_privates = [] virtual_privates = []
nets = [] 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['local_cidrs']
nets += ipsec_site_conn['peer_cidrs'] nets += ipsec_site_conn['peer_cidrs']
for net in nets: for net in nets:
version = netaddr.IPNetwork(net).version version = netaddr.IPNetwork(net).version
virtual_privates.append('%%v%s:%s' % (version, net)) virtual_privates.append('%%v%s:%s' % (version, net))
virtual_privates.sort()
return ','.join(virtual_privates) 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): def start(self):
"""Start the process. """Start the process.
@ -642,21 +705,9 @@ class OpenSwanProcess(BaseSwanProcess):
if not self._process_running(): if not self._process_running():
self._cleanup_control_files() self._cleanup_control_files()
virtual_private = self._virtual_privates()
#start pluto IKE keying daemon #start pluto IKE keying daemon
cmd = [self.binary, self.start_pluto()
'pluto',
'--ctlbase', self.pid_path,
'--ipsecdir', self.etc_dir,
'--use-netkey',
'--uniqueids',
'--nat_traversal',
'--secretsfile', self.secrets_file,
'--virtual_private', virtual_private]
if self.conf.ipsec.enable_detailed_logging:
cmd += ['--perpeerlog', '--perpeerlogbase', self.log_dir]
self._execute(cmd)
#add connections #add connections
for ipsec_site_conn in self.vpnservice['ipsec_site_connections']: for ipsec_site_conn in self.vpnservice['ipsec_site_connections']:
# Don't add a connection if its admin state is down # Don't add a connection if its admin state is down
@ -664,34 +715,17 @@ class OpenSwanProcess(BaseSwanProcess):
continue continue
nexthop = self._get_nexthop(ipsec_site_conn['peer_address'], nexthop = self._get_nexthop(ipsec_site_conn['peer_address'],
ipsec_site_conn['id']) ipsec_site_conn['id'])
self._execute([self.binary, self.add_ipsec_connection(nexthop, ipsec_site_conn['id'])
'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
#start whack ipsec keying daemon #start whack ipsec keying daemon
self._execute([self.binary, self.start_whack_listening()
'whack',
'--ctlbase', self.pid_path,
'--listen',
], check_exit_code=False)
for ipsec_site_conn in self.vpnservice['ipsec_site_connections']: for ipsec_site_conn in self.vpnservice['ipsec_site_connections']:
if (not ipsec_site_conn['initiator'] == 'start' or if (not ipsec_site_conn['initiator'] == 'start' or
not ipsec_site_conn['admin_state_up']): not ipsec_site_conn['admin_state_up']):
continue continue
#initiate ipsec connection #initiate ipsec connection
self._execute([self.binary, self.initiate_connection(ipsec_site_conn['id'])
'whack',
'--ctlbase', self.pid_path,
'--name', ipsec_site_conn['id'],
'--asynchronous',
'--initiate'
])
self._copy_configs() self._copy_configs()
def get_established_connections(self): def get_established_connections(self):
@ -720,22 +754,13 @@ class OpenSwanProcess(BaseSwanProcess):
connections = self.get_established_connections() connections = self.get_established_connections()
for conn_name in connections: for conn_name in connections:
self._execute([self.binary, self.terminate_connection(conn_name)
'whack',
'--ctlbase', self.pid_path,
'--name', '%s' % conn_name,
'--terminate'
])
def stop(self): def stop(self):
#Stop process using whack #Stop process using whack
#Note this will also stop pluto #Note this will also stop pluto
self.disconnect() self.disconnect()
self._execute([self.binary, self.shutdown_whack()
'whack',
'--ctlbase', self.pid_path,
'--shutdown',
])
self.connection_status = {} self.connection_status = {}

View File

@ -15,8 +15,12 @@
import os import os
import os.path import os.path
from neutron.agent.linux import ip_lib
from neutron_vpnaas.services.vpn.device_drivers import ipsec from neutron_vpnaas.services.vpn.device_drivers import ipsec
NS_WRAPPER = 'neutron-vpn-netns-wrapper'
class LibreSwanProcess(ipsec.OpenSwanProcess): class LibreSwanProcess(ipsec.OpenSwanProcess):
"""Libreswan Process manager class. """Libreswan Process manager class.
@ -27,6 +31,33 @@ class LibreSwanProcess(ipsec.OpenSwanProcess):
super(LibreSwanProcess, self).__init__(conf, process_id, super(LibreSwanProcess, self).__init__(conf, process_id,
vpnservice, namespace) 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): def ensure_configs(self):
"""Generate config files which are needed for Libreswan. """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', self._execute(['chown', '--from=%s' % os.getuid(), 'root:root',
secrets_file]) 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 # 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 # checknss creates nssdb only if it is missing
# It is added in Libreswan version v3.10 # It is added in Libreswan version v3.10
# For prior versions use initnss # For prior versions use initnss
try: try:
self._execute([self.binary, 'checknss', self.etc_dir]) self._ipsec_execute(['checknss'])
except RuntimeError: 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): class LibreSwanDriver(ipsec.IPsecDriver):

View File

@ -1,6 +1,7 @@
# Configuration for {{vpnservice.id}} # Configuration for {{vpnservice.id}}
config setup config setup
nat_traversal=yes nat_traversal=yes
virtual_private={{virtual_privates}}
conn %default conn %default
keylife=60m keylife=60m
keyingtries=%forever keyingtries=%forever
@ -69,7 +70,7 @@ conn {{ipsec_site_connection.id}}
# IPsecPolicys params # IPsecPolicys params
########################## ##########################
# [transform_protocol] # [transform_protocol]
auth={{ipsec_site_connection.ipsecpolicy.transform_protocol}} phase2={{ipsec_site_connection.ipsecpolicy.transform_protocol}}
{% if ipsec_site_connection.ipsecpolicy.transform_protocol == "ah" -%} {% if ipsec_site_connection.ipsecpolicy.transform_protocol == "ah" -%}
# AH protocol does not support encryption # AH protocol does not support encryption
# [auth_algorithm]-[pfs] # [auth_algorithm]-[pfs]

View File

@ -14,6 +14,7 @@
# under the License. # under the License.
import netaddr import netaddr
from oslo_config import cfg
import testtools import testtools
from tempest.common import utils from tempest.common import utils
@ -30,6 +31,23 @@ from neutron_vpnaas.tests.tempest.scenario import base
CONF = config.CONF 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): class Vpnaas(base.BaseTempestTestCase):
"""Test the following topology """Test the following topology
@ -247,6 +265,9 @@ class Vpnaas4in6(Vpnaas):
@decorators.idempotent_id('2d5f18dc-6186-4deb-842b-051325bd0466') @decorators.idempotent_id('2d5f18dc-6186-4deb-842b-051325bd0466')
@testtools.skipUnless(CONF.network_feature_enabled.ipv6, @testtools.skipUnless(CONF.network_feature_enabled.ipv6,
'IPv6 tests are disabled.') '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): def test_vpnaas_4in6(self):
self._test_vpnaas() self._test_vpnaas()
@ -257,6 +278,9 @@ class Vpnaas6in4(Vpnaas):
@decorators.idempotent_id('10febf33-c5b7-48af-aa13-94b4fb585a55') @decorators.idempotent_id('10febf33-c5b7-48af-aa13-94b4fb585a55')
@testtools.skipUnless(CONF.network_feature_enabled.ipv6, @testtools.skipUnless(CONF.network_feature_enabled.ipv6,
'IPv6 tests are disabled.') '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): def test_vpnaas_6in4(self):
self._test_vpnaas() self._test_vpnaas()

View File

@ -19,6 +19,7 @@ import os
import socket import socket
import mock import mock
import netaddr
from neutron.agent.l3 import dvr_edge_router from neutron.agent.l3 import dvr_edge_router
from neutron.agent.l3 import dvr_snat_ns from neutron.agent.l3 import dvr_snat_ns
from neutron.agent.l3 import legacy_router from neutron.agent.l3 import legacy_router
@ -138,7 +139,7 @@ OPENSWAN_CONNECTION_DETAILS = '''# rightsubnet=networkA/netmaskA, networkB/netma
# IPsecPolicys params # IPsecPolicys params
########################## ##########################
# [transform_protocol] # [transform_protocol]
auth=%(auth_mode)s phase2=%(auth_mode)s
# [encapsulation_mode] # [encapsulation_mode]
type=%(encapsulation_mode)s type=%(encapsulation_mode)s
# [lifetime_value] # [lifetime_value]
@ -162,6 +163,7 @@ EXPECTED_OPENSWAN_CONF = """
# Configuration for %(vpnservice_id)s # Configuration for %(vpnservice_id)s
config setup config setup
nat_traversal=yes nat_traversal=yes
virtual_private=%(virtual_privates)s
conn %%default conn %%default
keylife=60m keylife=60m
keyingtries=%%forever keyingtries=%%forever
@ -959,6 +961,7 @@ class TestOpenSwanConfigGeneration(BaseIPsecDeviceDriver):
'ike_lifetime': 3600, 'ike_lifetime': 3600,
'life_time': 3600, 'life_time': 3600,
'encapsulation_mode': 'tunnel'} 'encapsulation_mode': 'tunnel'}
virtual_privates = []
# Convert local CIDRs into assignment strings. IF more than one, # Convert local CIDRs into assignment strings. IF more than one,
# pluralize the attribute name and enclose in brackets. # pluralize the attribute name and enclose in brackets.
cidrs = info.get('local_cidrs', [['10.0.0.0/24'], ['11.0.0.0/24']]) 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)) local_cidrs.append("s={ %s }" % ' '.join(cidr))
else: else:
local_cidrs.append("=%s" % cidr[0]) 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 # Convert peer CIDRs into space separated strings
cidrs = info.get('peer_cidrs', [['20.0.0.0/24', '30.0.0.0/24'], 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']]) ['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] peer_cidrs = [' '.join(cidr) for cidr in cidrs]
local_ip = info.get('local', '60.0.0.4') local_ip = info.get('local', '60.0.0.4')
version = info.get('local_ip_vers', 4) version = info.get('local_ip_vers', 4)
next_hop = IPV4_NEXT_HOP if version == 4 else IPV6_NEXT_HOP % local_ip 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']) peer_ips = info.get('peers', ['60.0.0.5', '60.0.0.6'])
virtual_privates.sort()
return EXPECTED_OPENSWAN_CONF % { return EXPECTED_OPENSWAN_CONF % {
'vpnservice_id': FAKE_VPNSERVICE_ID, 'vpnservice_id': FAKE_VPNSERVICE_ID,
'virtual_privates': ','.join(virtual_privates),
'next_hop': next_hop, 'next_hop': next_hop,
'local_cidrs1': local_cidrs[0], 'local_cidrs2': local_cidrs[1], 'local_cidrs1': local_cidrs[0], 'local_cidrs2': local_cidrs[1],
'local_ver': version, 'local_ver': version,
@ -1412,8 +1424,15 @@ class TestLibreSwanProcess(base.BaseTestCase):
@mock.patch('os.path.exists', return_value=True) @mock.patch('os.path.exists', return_value=True)
def test_ensure_configs_on_restart(self, exists_mock): def test_ensure_configs_on_restart(self, exists_mock):
openswan_ipsec.OpenSwanProcess.ensure_configs = mock.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() self.ipsec_process.ensure_configs()
expected = [mock.call(['rm', '-f', expected = [mock.call(['rm', '-f',
self.ipsec_process._get_config_filename( self.ipsec_process._get_config_filename(
'ipsec.secrets')]), 'ipsec.secrets')]),
@ -1421,45 +1440,76 @@ class TestLibreSwanProcess(base.BaseTestCase):
'root:root', 'root:root',
self.ipsec_process._get_config_filename( self.ipsec_process._get_config_filename(
'ipsec.secrets')]), 'ipsec.secrets')]),
mock.call(['ipsec', '_stackmanager', 'start']), mock.call(['chown', '--from=%s' % os.getuid(),
mock.call(['ipsec', 'checknss', 'root:root', self.ipsec_process.log_dir])]
self.ipsec_process.etc_dir])]
fake_execute.assert_has_calls(expected) 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) self.assertTrue(exists_mock.called)
@mock.patch('os.path.exists', return_value=False) @mock.patch('os.path.exists', return_value=False)
def test_ensure_configs(self, exists_mock): def test_ensure_configs(self, exists_mock):
openswan_ipsec.OpenSwanProcess.ensure_configs = mock.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() self.ipsec_process.ensure_configs()
expected = [mock.call(['chown', '--from=%s' % os.getuid(), expected = [mock.call(['chown', '--from=%s' % os.getuid(),
'root:root', 'root:root',
self.ipsec_process._get_config_filename( self.ipsec_process._get_config_filename(
'ipsec.secrets')]), 'ipsec.secrets')]),
mock.call(['ipsec', '_stackmanager', 'start']), mock.call(['chown', '--from=%s' % os.getuid(),
mock.call(['ipsec', 'checknss', 'root:root', self.ipsec_process.log_dir])]
self.ipsec_process.etc_dir])]
fake_execute.assert_has_calls(expected) 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) self.assertTrue(exists_mock.called)
exists_mock.reset_mock() exists_mock.reset_mock()
with mock.patch.object(self.ipsec_process, '_execute') as fake_execute: with mock.patch.object(
fake_execute.side_effect = [None, None, RuntimeError, None] 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() self.ipsec_process.ensure_configs()
expected = [mock.call(['chown', '--from=%s' % os.getuid(), expected = [mock.call(['chown', '--from=%s' % os.getuid(),
'root:root', 'root:root',
self.ipsec_process._get_config_filename( self.ipsec_process._get_config_filename(
'ipsec.secrets')]), 'ipsec.secrets')]),
mock.call(['ipsec', '_stackmanager', 'start']), mock.call(['chown', '--from=%s' % os.getuid(),
mock.call(['ipsec', 'checknss', 'root:root', self.ipsec_process.log_dir])]
self.ipsec_process.etc_dir]),
mock.call(['ipsec', 'initnss',
self.ipsec_process.etc_dir])]
fake_execute.assert_has_calls(expected) 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) self.assertTrue(exists_mock.called)

View File

@ -0,0 +1,4 @@
---
fixes:
- The libreswan driver of neutron-vpnaas can now also work with Libreswan
3.19+ (bug `#1711456 <https://launchpad.net/bugs/1711456>`_).