addons: tunnel: support non-disruptive config change

Current design destroys existing tunnel when a config
change is detected. This behaviour causes traffic loss.

Signed-off-by: Julien Fortin <jfortin@nvidia.com>
This commit is contained in:
Julien Fortin 2022-05-27 14:54:41 +02:00
parent 46be384512
commit 70263054b3
2 changed files with 23 additions and 14 deletions

View File

@ -155,6 +155,7 @@ class tunnel(Addon, moduleBase):
}.get(link_kind, lambda x: {})(self.cache.get_link_info_data(ifname)) }.get(link_kind, lambda x: {})(self.cache.get_link_info_data(ifname))
def _up(self, ifaceobj): def _up(self, ifaceobj):
ifname = ifaceobj.name
attr_map = { attr_map = {
# attr_name -> ip route param name # attr_name -> ip route param name
'tunnel-local': 'local', 'tunnel-local': 'local',
@ -181,26 +182,32 @@ class tunnel(Addon, moduleBase):
if tos and tos != 'inherit': if tos and tos != 'inherit':
attrs_mapped['tos'] = "{:x}".format(int(tos)) attrs_mapped['tos'] = "{:x}".format(int(tos))
link_exists = self.cache.link_exists(ifname)
# Create the tunnel if it doesn't exist yet... # Create the tunnel if it doesn't exist yet...
if not self.cache.link_exists(ifaceobj.name): if not link_exists:
self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped) self.iproute2.tunnel_create(ifname, mode, attrs_mapped)
return return
# If it's present, check if there were changes # If it's present, check if there were changes
current_mode = self.cache.get_link_kind(ifaceobj.name) current_mode = self.cache.get_link_kind(ifname)
current_attrs = self.get_linkinfo_attrs(ifaceobj.name, current_mode) current_attrs = self.get_linkinfo_attrs(ifname, current_mode)
self.convert_user_config_to_ipnetwork(attrs, "tunnel-local") self.convert_user_config_to_ipnetwork(attrs, "tunnel-local")
self.convert_user_config_to_ipnetwork(attrs, "tunnel-endpoint") self.convert_user_config_to_ipnetwork(attrs, "tunnel-endpoint")
try: try:
if current_attrs and current_mode != mode or self._has_config_changed(current_attrs, attrs): if current_attrs and current_mode != mode or self._has_config_changed(current_attrs, attrs):
if link_exists and current_mode != mode:
# Mode and some other changes are not possible without recreating the interface, # Mode and some other changes are not possible without recreating the interface,
# so just recreate it IFF there have been changes. # so just recreate it IFF there have been changes.
self.netlink.link_del(ifaceobj.name) self.netlink.link_del(ifaceobj.name)
self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped) link_exists = False
self.iproute2.tunnel_create(ifaceobj.name, mode, attrs_mapped, link_exists=link_exists)
except Exception as e: except Exception as e:
self.log_warn(str(e)) self.log_error(str(e), ifaceobj)
def _down(self, ifaceobj): def _down(self, ifaceobj):
if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name): if not ifupdownflags.flags.PERFMODE and not self.cache.link_exists(ifaceobj.name):

View File

@ -443,18 +443,20 @@ class IPRoute2(Cache, Requirements):
# TUNNEL # TUNNEL
############################################################################ ############################################################################
def tunnel_create(self, tunnelname, mode, attrs=None): def tunnel_create(self, tunnelname, mode, attrs=None, link_exists=False):
if self.cache.link_exists(tunnelname): if link_exists:
return op = "change"
else:
op = "add"
cmd = [] cmd = []
if "6" in mode: if "6" in mode:
cmd.append("-6") cmd.append("-6")
if mode in ["gretap"]: if mode in ["gretap"]:
cmd.append("link add %s type %s" % (tunnelname, mode)) cmd.append("link %s %s type %s" % (op, tunnelname, mode))
else: else:
cmd.append("tunnel add %s mode %s" % (tunnelname, mode)) cmd.append("tunnel %s %s mode %s" % (op, tunnelname, mode))
if attrs: if attrs:
for k, v in attrs.items(): for k, v in attrs.items():