Page MenuHomeVyOS Platform

No OneTemporary

Size
26 KB
Referenced Files
None
Subscribers
None
diff --git a/python/vyos/ifconfig/tunnel.py b/python/vyos/ifconfig/tunnel.py
index 00dc36420..1af4f8e72 100644
--- a/python/vyos/ifconfig/tunnel.py
+++ b/python/vyos/ifconfig/tunnel.py
@@ -1,246 +1,251 @@
# Copyright 2019-2020 VyOS maintainers and contributors <maintainers@vyos.io>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.
# https://developers.redhat.com/blog/2019/05/17/an-introduction-to-linux-virtual-interfaces-tunnels/
# https://community.hetzner.com/tutorials/linux-setup-gre-tunnel
from copy import deepcopy
from netaddr import EUI
from netaddr import mac_unix_expanded
from random import getrandbits
from vyos.ifconfig.interface import Interface
from vyos.validate import assert_list
def enable_to_on(value):
if value == 'enable':
return 'on'
if value == 'disable':
return 'off'
raise ValueError(f'expect enable or disable but got "{value}"')
@Interface.register
class _Tunnel(Interface):
"""
_Tunnel: private base class for tunnels
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/tunnel.c
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/ip6tunnel.c
"""
definition = {
**Interface.definition,
**{
'section': 'tunnel',
'prefixes': ['tun',],
},
}
# TODO: This is surely used for more than tunnels
# TODO: could be refactored elsewhere
_command_set = {**Interface._command_set, **{
'multicast': {
'validate': lambda v: assert_list(v, ['enable', 'disable']),
'convert': enable_to_on,
'shellcmd': 'ip link set dev {ifname} multicast {value}',
},
'allmulticast': {
'validate': lambda v: assert_list(v, ['enable', 'disable']),
'convert': enable_to_on,
'shellcmd': 'ip link set dev {ifname} allmulticast {value}',
},
}}
+ _create_cmd = 'ip tunnel add {ifname} mode {type}'
+
def __init__(self, ifname, **config):
self.config = deepcopy(config) if config else {}
super().__init__(ifname, **config)
def _create(self):
- create = 'ip tunnel add {ifname} mode {type}'
-
# add " option-name option-name-value ..." for all options set
options = " ".join(["{} {}".format(k, self.config[k])
for k in self.options if k in self.config and self.config[k]])
- self._cmd('{} {}'.format(create.format(**self.config), options))
+ self._cmd('{} {}'.format(self._create_cmd.format(**self.config), options))
self.set_admin_state('down')
def change_options(self):
- change = 'ip tunnel cha {ifname} mode {type}'
+ change = 'ip tunnel change {ifname} mode {type}'
# add " option-name option-name-value ..." for all options set
options = " ".join(["{} {}".format(k, self.config[k])
for k in self.options if k in self.config and self.config[k]])
self._cmd('{} {}'.format(change.format(**self.config), options))
@classmethod
def get_config(cls):
return dict(zip(cls.options, ['']*len(cls.options)))
def get_mac(self):
"""
Get current interface MAC (Media Access Contrl) address used.
NOTE: Tunnel interfaces have no "MAC" address by default. The content
of the 'address' file in /sys/class/net/device contains the
local-ip thus we generate a random MAC address instead
Example:
>>> from vyos.ifconfig import Interface
>>> Interface('eth0').get_mac()
'00:50:ab:cd:ef:00'
"""
# we choose 40 random bytes for the MAC address, this gives
# us e.g. EUI('00-EA-EE-D6-A3-C8') or EUI('00-41-B9-0D-F2-2A')
tmp = EUI(getrandbits(48)).value
# set locally administered bit in MAC address
tmp |= 0xf20000000000
# convert integer to "real" MAC address representation
mac = EUI(hex(tmp).split('x')[-1])
# change dialect to use : as delimiter instead of -
mac.dialect = mac_unix_expanded
return str(mac)
def update(self, config):
""" General helper function which works on a dictionary retrived by
get_config_dict(). It's main intention is to consolidate the scattered
interface setup code and provide a single point of entry when workin
on any interface. """
# call base class first
super().update(config)
# Enable/Disable of an interface must always be done at the end of the
# derived class to make use of the ref-counting set_admin_state()
# function. We will only enable the interface if 'up' was called as
# often as 'down'. This is required by some interface implementations
# as certain parameters can only be changed when the interface is
# in admin-down state. This ensures the link does not flap during
# reconfiguration.
state = 'down' if 'disable' in config else 'up'
self.set_admin_state(state)
class GREIf(_Tunnel):
"""
GRE: Generic Routing Encapsulation
For more information please refer to:
RFC1701, RFC1702, RFC2784
https://tools.ietf.org/html/rfc2784
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre.c
"""
default = {'type': 'gre'}
options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
# GreTap also called GRE Bridge
class GRETapIf(_Tunnel):
"""
GRETapIF: GreIF using TAP instead of TUN
https://en.wikipedia.org/wiki/TUN/TAP
"""
# no multicast, ttl or tos for gretap
definition = {
**_Tunnel.definition,
**{
'bridgeable': True,
},
}
default = {'type': 'gretap'}
options = ['local', 'remote', 'ttl',]
+ _create_cmd = 'ip link add name {ifname} type {type}'
+
+ def change_options(self):
+ pass
+
class IP6GREIf(_Tunnel):
"""
IP6Gre: IPv6 Support for Generic Routing Encapsulation (GRE)
For more information please refer to:
https://tools.ietf.org/html/rfc7676
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_gre6.c
"""
default = {'type': 'ip6gre'}
options = ['local', 'remote', 'dev', 'encaplimit',
'hoplimit', 'tclass', 'flowlabel']
class IPIPIf(_Tunnel):
"""
IPIP: IP Encapsulation within IP
For more information please refer to:
https://tools.ietf.org/html/rfc2003
"""
# IPIP does not allow to pass multicast, unlike GRE
# but the interface itself can be set with multicast
default = {'type': 'ipip'}
options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
class IPIP6If(_Tunnel):
"""
IPIP6: IPv4 over IPv6 tunnel
For more information please refer to:
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_ip6tnl.c
"""
default = {'type': 'ipip6'}
options = ['local', 'remote', 'dev', 'encaplimit',
'hoplimit', 'tclass', 'flowlabel']
class IP6IP6If(IPIP6If):
"""
IP6IP6: IPv6 over IPv6 tunnel
For more information please refer to:
https://tools.ietf.org/html/rfc2473
"""
default = {'type': 'ip6ip6'}
class SitIf(_Tunnel):
"""
Sit: Simple Internet Transition
For more information please refer to:
https://git.kernel.org/pub/scm/network/iproute2/iproute2.git/tree/ip/link_iptnl.c
"""
default = {'type': 'sit'}
options = ['local', 'remote', 'dev', 'ttl', 'tos', 'key']
class Sit6RDIf(SitIf):
"""
Sit6RDIf: Simple Internet Transition with 6RD
https://en.wikipedia.org/wiki/IPv6_rapid_deployment
"""
# TODO: check if key can really be used with 6RD
options = ['remote', 'ttl', 'tos', 'key', '6rd-prefix', '6rd-relay-prefix']
def _create(self):
# do not call _Tunnel.create, building fully here
create = 'ip tunnel add {ifname} mode {type} remote {remote}'
self._cmd(create.format(**self.config))
self.set_interface('state','down')
set6rd = 'ip tunnel 6rd dev {ifname} 6rd-prefix {6rd-prefix}'
if '6rd-relay-prefix' in self.config:
set6rd += ' 6rd-relay-prefix {6rd-relay-prefix}'
self._cmd(set6rd.format(**self.config))
diff --git a/smoketest/scripts/cli/test_interfaces_tunnel.py b/smoketest/scripts/cli/test_interfaces_tunnel.py
index ca68cb8ba..6115f0a07 100755
--- a/smoketest/scripts/cli/test_interfaces_tunnel.py
+++ b/smoketest/scripts/cli/test_interfaces_tunnel.py
@@ -1,379 +1,206 @@
#!/usr/bin/env python3
#
# Copyright (C) 2020 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import unittest
import json
from vyos.configsession import ConfigSession
from vyos.configsession import ConfigSessionError
from vyos.util import cmd
from base_interfaces_test import BasicInterfaceTest
remote_ip4 = '192.0.2.100'
remote_ip6 = '2001:db8::ffff'
source_if = 'dum2222'
mtu = 1476
def tunnel_conf(interface):
tmp = cmd(f'ip -d -j link show {interface}')
# {'address': '2.2.2.2',
# 'broadcast': '192.0.2.10',
# 'flags': ['POINTOPOINT', 'NOARP', 'UP', 'LOWER_UP'],
# 'group': 'default',
# 'gso_max_segs': 65535,
# 'gso_max_size': 65536,
# 'ifindex': 10,
# 'ifname': 'tun10',
# 'inet6_addr_gen_mode': 'none',
# 'link': None,
# 'link_pointtopoint': True,
# 'link_type': 'gre',
# 'linkinfo': {'info_data': {'local': '2.2.2.2',
# 'pmtudisc': True,
# 'remote': '192.0.2.10',
# 'tos': '0x1',
# 'ttl': 255},
# 'info_kind': 'gre'},
# 'linkmode': 'DEFAULT',
# 'max_mtu': 65511,
# 'min_mtu': 68,
# 'mtu': 1476,
# 'num_rx_queues': 1,
# 'num_tx_queues': 1,
# 'operstate': 'UNKNOWN',
# 'promiscuity': 0,
# 'qdisc': 'noqueue',
# 'txqlen': 1000}
return json.loads(tmp)[0]
class TunnelInterfaceTest(BasicInterfaceTest.BaseTest):
def setUp(self):
+ self._test_ip = True
self._test_mtu = True
self._base_path = ['interfaces', 'tunnel']
self.local_v4 = '192.0.2.1'
self.local_v6 = '2001:db8::1'
self._options = {
'tun10': ['encapsulation ipip', 'remote-ip 192.0.2.10', 'local-ip ' + self.local_v4],
'tun20': ['encapsulation gre', 'remote-ip 192.0.2.20', 'local-ip ' + self.local_v4],
}
self._interfaces = list(self._options)
super().setUp()
self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v4 + '/32'])
self.session.set(['interfaces', 'dummy', source_if, 'address', self.local_v6 + '/128'])
def tearDown(self):
self.session.delete(['interfaces', 'dummy', source_if])
super().tearDown()
- def test_ipip(self):
- interface = 'tun100'
- encapsulation = 'ipip'
- local_if_addr = '10.10.10.1/24'
- self.session.set(self._base_path + [interface, 'address', local_if_addr])
-
- # Must provide an "encapsulation" for tunnel tun10
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
-
- # Must configure either local-ip or dhcp-interface for tunnel ipip tun100
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
-
- # missing required option remote for ipip
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
-
- # Configure Tunnel Source interface
- self.session.set(self._base_path + [interface, 'source-interface', source_if])
-
- self.session.commit()
-
- conf = tunnel_conf(interface)
- self.assertEqual(interface, conf['ifname'])
- self.assertEqual(encapsulation, conf['link_type'])
- self.assertEqual(mtu, conf['mtu'])
- self.assertEqual(source_if, conf['link'])
-
- self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
- self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
-
- def test_ipip6(self):
- interface = 'tun110'
- encapsulation = 'ipip6'
- local_if_addr = '10.10.10.1/24'
-
- self.session.set(self._base_path + [interface, 'address', local_if_addr])
-
- # Must provide an "encapsulation" for tunnel tun10
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
-
- # Must configure either local-ip or dhcp-interface for tunnel ipip tun100
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
-
- # missing required option remote for ipip
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
-
- # Configure Tunnel Source interface
- self.session.set(self._base_path + [interface, 'source-interface', source_if])
-
- self.session.commit()
-
- conf = tunnel_conf(interface)
- self.assertEqual(interface, conf['ifname'])
- self.assertEqual('tunnel6', conf['link_type'])
- self.assertEqual(mtu, conf['mtu'])
- self.assertEqual(source_if, conf['link'])
-
- self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
- self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
-
- def test_tunnel_verify_ipv4_local_remote_addr(self):
+ def test_ipv4_encapsulations(self):
# When running tests ensure that for certain encapsulation types the
# local and remote IP address is actually an IPv4 address
interface = f'tun1000'
local_if_addr = f'10.10.200.1/24'
- for encapsulation in ['ipip', 'sit', 'gre']:
+ for encapsulation in ['ipip', 'sit', 'gre', 'gre-bridge']:
self.session.set(self._base_path + [interface, 'address', local_if_addr])
self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
# Encapsulation mode requires IPv4 local-ip
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
# Encapsulation mode requires IPv4 local-ip
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
+
+ # Source interface can not be used with sit and gre-bridge
+ if encapsulation in ['sit', 'gre-bridge']:
+ with self.assertRaises(ConfigSessionError):
+ self.session.commit()
+ self.session.delete(self._base_path + [interface, 'source-interface'])
+
# Check if commit is ok
self.session.commit()
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual(mtu, conf['mtu'])
+
+ if encapsulation not in ['sit', 'gre-bridge']:
+ self.assertEqual(source_if, conf['link'])
+ self.assertEqual(encapsulation, conf['link_type'])
+ elif encapsulation in ['gre-bridge']:
+ self.assertEqual('ether', conf['link_type'])
+
+ self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
+
# cleanup this instance
self.session.delete(self._base_path + [interface])
self.session.commit()
- def test_tunnel_verify_ipv6_local_remote_addr(self):
+ def test_ipv6_encapsulations(self):
# When running tests ensure that for certain encapsulation types the
# local and remote IP address is actually an IPv6 address
interface = f'tun1010'
local_if_addr = f'10.10.200.1/24'
for encapsulation in ['ipip6', 'ip6ip6', 'ip6gre']:
self.session.set(self._base_path + [interface, 'address', local_if_addr])
self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
# Encapsulation mode requires IPv6 local-ip
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
# Encapsulation mode requires IPv6 local-ip
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
+ # Configure Tunnel Source interface
+ self.session.set(self._base_path + [interface, 'source-interface', source_if])
+
# Check if commit is ok
self.session.commit()
+ conf = tunnel_conf(interface)
+ self.assertEqual(interface, conf['ifname'])
+ self.assertEqual(mtu, conf['mtu'])
+ self.assertEqual(source_if, conf['link'])
+
+ # remap encapsulation protocol(s)
+ if encapsulation in ['ipip6', 'ip6ip6']:
+ encapsulation = 'tunnel6'
+ elif encapsulation in ['ip6gre']:
+ encapsulation = 'gre6'
+
+ self.assertEqual(encapsulation, conf['link_type'])
+
+ self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
+ self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
+
# cleanup this instance
self.session.delete(self._base_path + [interface])
self.session.commit()
def test_tunnel_verify_local_dhcp(self):
# We can not use local-ip and dhcp-interface at the same time
interface = f'tun1020'
local_if_addr = f'10.0.0.1/24'
self.session.set(self._base_path + [interface, 'address', local_if_addr])
self.session.set(self._base_path + [interface, 'encapsulation', 'gre'])
self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
self.session.set(self._base_path + [interface, 'dhcp-interface', 'eth0'])
# local-ip and dhcp-interface can not be used at the same time
with self.assertRaises(ConfigSessionError):
self.session.commit()
self.session.delete(self._base_path + [interface, 'dhcp-interface'])
# Check if commit is ok
self.session.commit()
- def test_tunnel_ip6ip6(self):
- interface = 'tun120'
- encapsulation = 'ip6ip6'
- local_if_addr = '2001:db8:f00::1/24'
-
- self.session.set(self._base_path + [interface, 'address', local_if_addr])
-
- # Must provide an "encapsulation" for tunnel tun10
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
-
- # Must configure either local-ip or dhcp-interface for tunnel ipip tun100
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
-
- # missing required option remote for ipip
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
-
- # Configure Tunnel Source interface
- self.session.set(self._base_path + [interface, 'source-interface', source_if])
-
- self.session.commit()
-
- conf = tunnel_conf(interface)
- self.assertEqual(interface, conf['ifname'])
- self.assertEqual('tunnel6', conf['link_type'])
- self.assertEqual(mtu, conf['mtu'])
- self.assertEqual(source_if, conf['link'])
-
- self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
- self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
-
- def test_tunnel_gre_ipv4(self):
- interface = 'tun200'
- encapsulation = 'gre'
- local_if_addr = '172.16.1.1/24'
-
- self.session.set(self._base_path + [interface, 'address', local_if_addr])
-
- # Must provide an "encapsulation" for tunnel tun10
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
-
- # Must configure either local-ip or dhcp-interface
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
-
- # No assertion is raised for GRE remote-ip when missing
- self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
-
- # Configure Tunnel Source interface
- self.session.set(self._base_path + [interface, 'source-interface', source_if])
-
- self.session.commit()
-
- conf = tunnel_conf(interface)
- self.assertEqual(interface, conf['ifname'])
- self.assertEqual(encapsulation, conf['link_type'])
- self.assertEqual(mtu, conf['mtu'])
- self.assertEqual(source_if, conf['link'])
-
- self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
- self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
-
-
- def test_gre_ipv6(self):
- interface = 'tun210'
- encapsulation = 'ip6gre'
- local_if_addr = '2001:db8:f01::1/24'
-
- self.session.set(self._base_path + [interface, 'address', local_if_addr])
-
- # Must provide an "encapsulation" for tunnel tun10
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
-
- # Must configure either local-ip or dhcp-interface
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'local-ip', self.local_v6])
-
- # No assertion is raised for GRE remote-ip when missing
- self.session.set(self._base_path + [interface, 'remote-ip', remote_ip6])
-
- # Configure Tunnel Source interface
- self.session.set(self._base_path + [interface, 'source-interface', source_if])
-
- self.session.commit()
-
- conf = tunnel_conf(interface)
- self.assertEqual(interface, conf['ifname'])
- self.assertEqual(encapsulation, conf['link_type'])
- self.assertEqual(mtu, conf['mtu'])
- self.assertEqual(source_if, conf['link'])
-
- self.assertEqual(self.local_v6, conf['linkinfo']['info_data']['local'])
- self.assertEqual(remote_ip6, conf['linkinfo']['info_data']['remote'])
-
-
- def test_tunnel_sit(self):
- interface = 'tun300'
- encapsulation = 'sit'
- local_if_addr = '172.16.2.1/24'
-
- self.session.set(self._base_path + [interface, 'address', local_if_addr])
-
- # Must provide an "encapsulation" for tunnel tun10
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'encapsulation', encapsulation])
-
- # Must configure either local-ip or dhcp-interface
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.set(self._base_path + [interface, 'local-ip', self.local_v4])
-
- # No assertion is raised for GRE remote-ip when missing
- self.session.set(self._base_path + [interface, 'remote-ip', remote_ip4])
-
- # Source interface can not be used with sit
- self.session.set(self._base_path + [interface, 'source-interface', source_if])
- with self.assertRaises(ConfigSessionError):
- self.session.commit()
- self.session.delete(self._base_path + [interface, 'source-interface'])
-
- self.session.commit()
-
- conf = tunnel_conf(interface)
- self.assertEqual(interface, conf['ifname'])
- self.assertEqual(encapsulation, conf['link_type'])
- self.assertEqual(mtu, conf['mtu'])
-
- self.assertEqual(self.local_v4, conf['linkinfo']['info_data']['local'])
- self.assertEqual(remote_ip4, conf['linkinfo']['info_data']['remote'])
-
-
if __name__ == '__main__':
- unittest.main(verbosity=2)
+ unittest.main(verbosity=2, failfast=True)

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 6, 6:54 AM (7 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4652905
Default Alt Text
(26 KB)

Event Timeline