]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/vxlan.py
addons: vxlan: fix AttributeError: 'NoneType' object has no attribute 'ip'
[mirror_ifupdown2.git] / ifupdown2 / addons / vxlan.py
CommitLineData
35681c06 1#!/usr/bin/env python3
d486dd0d
JF
2#
3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
d486dd0d 7try:
0e936c3f 8 import ifupdown2.nlmanager.ipnetwork as ipnetwork
d486dd0d 9 import ifupdown2.ifupdown.policymanager as policymanager
3fb83a7a 10 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
d486dd0d 11
223ba5af
JF
12 from ifupdown2.lib.addon import Addon
13 from ifupdown2.lib.nlcache import NetlinkCacheIfnameNotFoundError
14
d486dd0d
JF
15 from ifupdown2.nlmanager.nlmanager import Link
16
17 from ifupdown2.ifupdown.iface import *
18 from ifupdown2.ifupdown.utils import utils
d486dd0d 19 from ifupdown2.ifupdownaddons.cache import *
d486dd0d 20 from ifupdown2.ifupdownaddons.modulebase import moduleBase
bd441a51 21except (ImportError, ModuleNotFoundError):
0e936c3f 22 import nlmanager.ipnetwork as ipnetwork
d486dd0d 23 import ifupdown.policymanager as policymanager
3fb83a7a 24 import ifupdown.ifupdownflags as ifupdownflags
d486dd0d 25
223ba5af
JF
26 from lib.addon import Addon
27 from lib.nlcache import NetlinkCacheIfnameNotFoundError
28
d486dd0d
JF
29 from nlmanager.nlmanager import Link
30
31 from ifupdown.iface import *
32 from ifupdown.utils import utils
d486dd0d
JF
33
34 from ifupdownaddons.cache import *
d486dd0d
JF
35 from ifupdownaddons.modulebase import moduleBase
36
37
223ba5af
JF
38class vxlan(Addon, moduleBase):
39 _modinfo = {
40 "mhelp": "vxlan module configures vxlan interfaces.",
41 "attrs": {
42 "vxlan-id": {
43 "help": "vxlan id",
44 "validrange": ["1", "16777214"],
45 "required": True,
46 "example": ["vxlan-id 100"]
47 },
48 "vxlan-local-tunnelip": {
49 "help": "vxlan local tunnel ip",
50 "validvals": ["<ipv4>"],
51 "example": ["vxlan-local-tunnelip 172.16.20.103"]
52 },
53 "vxlan-svcnodeip": {
54 "help": "vxlan id",
55 "validvals": ["<ipv4>"],
56 "example": ["vxlan-svcnodeip 172.16.22.125"]
57 },
40658337
JF
58 "vxlan-svcnodeip6": {
59 "help": "vxlan svc node ip",
60 "validvals": ["<ipv6>"],
61 "example": ["vxlan-svcnodeip6 2001:DB8:8086:6502::"]
62 },
223ba5af
JF
63 "vxlan-remoteip": {
64 "help": "vxlan remote ip",
65 "validvals": ["<ipv4>"],
66 "example": ["vxlan-remoteip 172.16.22.127"],
67 "multiline": True
68 },
69 "vxlan-learning": {
70 "help": "vxlan learning yes/no",
71 "validvals": ["yes", "no", "on", "off"],
72 "example": ["vxlan-learning no"],
73 "default": "yes"
74 },
75 "vxlan-ageing": {
76 "help": "vxlan aging timer",
77 "validrange": ["0", "4096"],
78 "example": ["vxlan-ageing 300"],
79 "default": "300"
80 },
81 "vxlan-purge-remotes": {
82 "help": "vxlan purge existing remote entries",
83 "validvals": ["yes", "no"],
84 "example": ["vxlan-purge-remotes yes"],
85 },
86 "vxlan-port": {
87 "help": "vxlan UDP port (transmitted to vxlan driver)",
88 "example": ["vxlan-port 4789"],
89 "validrange": ["1", "65536"],
90 "default": "4789",
91 },
92 "vxlan-physdev": {
93 "help": "vxlan physical device",
94 "example": ["vxlan-physdev eth1"]
95 },
96 "vxlan-ttl": {
97 "help": "specifies the TTL value to use in outgoing packets "
98 "(range 0..255), 0=auto",
99 "default": "0",
100 "validvals": ["0", "255"],
101 "example": ['vxlan-ttl 42'],
102 },
103 "vxlan-mcastgrp": {
104 "help": "vxlan multicast group",
105 "validvals": ["<ip>"],
106 "example": ["vxlan-mcastgrp 172.16.22.127"],
40658337
JF
107 },
108 "vxlan-mcastgrp6": {
109 "help": "vxlan multicast group",
110 "validvals": ["<ip6>"],
111 "example": ["vxlan-mcastgrp ff02::15c"],
223ba5af
JF
112 }
113 }
114 }
115
116 VXLAN_PHYSDEV_MCASTGRP_DEFAULT = "ipmr-lo"
d486dd0d
JF
117
118 def __init__(self, *args, **kargs):
223ba5af 119 Addon.__init__(self)
d486dd0d 120 moduleBase.__init__(self, *args, **kargs)
223ba5af
JF
121
122 self._vxlan_purge_remotes = utils.get_boolean_from_string(
123 policymanager.policymanager_api.get_module_globals(
124 module_name=self.__class__.__name__,
125 attr="vxlan-purge-remotes"
126 )
127 )
128 self._vxlan_local_tunnelip = None
129 self._clagd_vxlan_anycast_ip = ""
130
131 # If mcastgrp is specified we need to rely on a user-configred device (via physdev)
132 # or via a policy variable "vxlan-physdev_mcastgrp". If the device doesn't exist we
133 # create it as a dummy device. We need to keep track of the user configuration to
134 # know when to delete this dummy device (when user remove mcastgrp from it's config)
135 self.vxlan_mcastgrp_ref = False
136 self.vxlan_physdev_mcast = policymanager.policymanager_api.get_module_globals(
137 module_name=self.__class__.__name__,
138 attr="vxlan-physdev-mcastgrp"
139 ) or self.VXLAN_PHYSDEV_MCASTGRP_DEFAULT
140
141 def reset(self):
142 # in daemon mode we need to reset mcastgrp_ref for every new command
143 # this variable has to be set in get_dependent_ifacenames
144 self.vxlan_mcastgrp_ref = False
d486dd0d
JF
145
146 def syntax_check(self, ifaceobj, ifaceobj_getfunc):
147 if self._is_vxlan_device(ifaceobj):
223ba5af 148 if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not self._vxlan_local_tunnelip:
d486dd0d
JF
149 self.logger.warning('%s: missing vxlan-local-tunnelip' % ifaceobj.name)
150 return False
151 return self.syntax_check_localip_anycastip_equal(
152 ifaceobj.name,
223ba5af
JF
153 ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or self._vxlan_local_tunnelip,
154 self._clagd_vxlan_anycast_ip
d486dd0d
JF
155 )
156 return True
157
158 def syntax_check_localip_anycastip_equal(self, ifname, local_ip, anycast_ip):
159 try:
0e936c3f 160 if local_ip and anycast_ip and ipnetwork.IPNetwork(local_ip) == ipnetwork.IPNetwork(anycast_ip):
d486dd0d
JF
161 self.logger.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)'
162 % (ifname, local_ip))
163 return False
3218f49d 164 except Exception:
d486dd0d
JF
165 pass
166 return True
167
168 def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None):
e537a6e6
JF
169 if ifaceobj.get_attr_value_first("bridge-vlan-vni-map"):
170 ifaceobj.link_privflags |= ifaceLinkPrivFlags.SINGLE_VXLAN
171
d486dd0d
JF
172 if self._is_vxlan_device(ifaceobj):
173 ifaceobj.link_kind |= ifaceLinkKind.VXLAN
174 self._set_global_local_ip(ifaceobj)
223ba5af
JF
175
176 # if we detect a vxlan we check if mcastgrp is set (if so we set vxlan_mcastgrp_ref)
177 # to know when to delete this device.
178 if not self.vxlan_mcastgrp_ref and ifaceobj.get_attr_value("vxlan-mcastgrp"):
179 self.vxlan_mcastgrp_ref = True
180
d486dd0d
JF
181 elif ifaceobj.name == 'lo':
182 clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip')
183 if clagd_vxlan_list:
184 if len(clagd_vxlan_list) != 1:
185 self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one'
186 % (ifaceobj.name,))
223ba5af 187 self._clagd_vxlan_anycast_ip = clagd_vxlan_list[0]
d486dd0d
JF
188
189 self._set_global_local_ip(ifaceobj)
a382b488
JF
190
191 # If we should use a specific underlay device for the VXLAN
192 # tunnel make sure this device is set up before the VXLAN iface.
193 physdev = ifaceobj.get_attr_value_first('vxlan-physdev')
194
195 if physdev:
196 return [physdev]
197
d486dd0d
JF
198 return None
199
200 def _set_global_local_ip(self, ifaceobj):
201 vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
223ba5af
JF
202 if vxlan_local_tunnel_ip and not self._vxlan_local_tunnelip:
203 self._vxlan_local_tunnelip = vxlan_local_tunnel_ip
d486dd0d 204
223ba5af
JF
205 @staticmethod
206 def _is_vxlan_device(ifaceobj):
e537a6e6
JF
207 return ifaceobj.link_kind & ifaceLinkKind.VXLAN \
208 or ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN \
209 or ifaceobj.get_attr_value_first("vxlan-id") \
210 or ifaceobj.get_attr_value_first("bridge-vlan-vni-map")
d486dd0d 211
223ba5af 212 def __get_vlxan_purge_remotes(self, ifaceobj):
d486dd0d 213 if not ifaceobj:
223ba5af 214 return self._vxlan_purge_remotes
d486dd0d
JF
215 purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes')
216 if purge_remotes:
217 purge_remotes = utils.get_boolean_from_string(purge_remotes)
218 else:
223ba5af 219 purge_remotes = self._vxlan_purge_remotes
d486dd0d
JF
220 return purge_remotes
221
ec25a08c
JF
222 def get_vxlan_ttl_from_string(self, ttl_config):
223 ttl = 0
224 if ttl_config:
225 if ttl_config.lower() == "auto":
226 ttl = 0
227 else:
228 ttl = int(ttl_config)
229 return ttl
230
223ba5af
JF
231 def __config_vxlan_id(self, ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
232 """
233 Get vxlan-id user config and check it's value before inserting it in our netlink dictionary
234 :param ifname:
235 :param ifaceobj:
236 :param vxlan_id_str:
237 :param user_request_vxlan_info_data:
238 :param cached_vxlan_ifla_info_data:
239 :return:
240 """
241 try:
242 vxlan_id = int(vxlan_id_str)
243 cached_vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID)
244
245 if cached_vxlan_id and cached_vxlan_id != vxlan_id:
246 self.log_error(
247 "%s: Cannot change running vxlan id (%s): Operation not supported"
248 % (ifname, cached_vxlan_id),
249 ifaceobj
250 )
251 user_request_vxlan_info_data[Link.IFLA_VXLAN_ID] = vxlan_id
252 except ValueError:
253 self.log_error("%s: invalid vxlan-id '%s'" % (ifname, vxlan_id_str), ifaceobj)
254
255 def __get_vxlan_ageing_int(self, ifname, ifaceobj, link_exists):
256 """
257 Get vxlan-ageing user config or via policy, return integer value, None or raise on error
258 :param ifname:
259 :param ifaceobj:
260 :param link_exists:
261 :return:
262 """
263 vxlan_ageing_str = ifaceobj.get_attr_value_first("vxlan-ageing")
264 try:
265 if vxlan_ageing_str:
266 return int(vxlan_ageing_str)
267
268 vxlan_ageing_str = policymanager.policymanager_api.get_attr_default(
269 module_name=self.__class__.__name__,
270 attr="vxlan-ageing"
271 )
272
273 if not vxlan_ageing_str and link_exists:
274 # if link doesn't exist we let the kernel define ageing
275 vxlan_ageing_str = self.get_attr_default_value("vxlan-ageing")
276
277 if vxlan_ageing_str:
278 return int(vxlan_ageing_str)
3218f49d 279 except Exception:
223ba5af 280 self.log_error("%s: invalid vxlan-ageing '%s'" % (ifname, vxlan_ageing_str), ifaceobj)
d486dd0d 281
223ba5af
JF
282 def __config_vxlan_ageing(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
283 """
284 Check user config vxlan-ageing and insert it in our netlink dictionary if needed
285 """
286 vxlan_ageing = self.__get_vxlan_ageing_int(ifname, ifaceobj, link_exists)
287
288 if not vxlan_ageing or (link_exists and vxlan_ageing == cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_AGEING)):
289 return
290
291 self.logger.info("%s: set vxlan-ageing %s" % (ifname, vxlan_ageing))
292 user_request_vxlan_info_data[Link.IFLA_VXLAN_AGEING] = vxlan_ageing
293
294 def __config_vxlan_port(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
295 """
296 Check vxlan-port user config, validate the integer value and insert it in the netlink dictionary if needed
297 :param ifname:
298 :param ifaceobj:
299 :param link_exists:
300 :param user_request_vxlan_info_data:
301 :param cached_vxlan_ifla_info_data:
302 :return:
303 """
304 vxlan_port_str = ifaceobj.get_attr_value_first("vxlan-port")
305 try:
306 if not vxlan_port_str:
307 vxlan_port_str = policymanager.policymanager_api.get_attr_default(
308 module_name=self.__class__.__name__,
309 attr="vxlan-port"
310 )
d486dd0d 311
ec25a08c 312 try:
223ba5af
JF
313 vxlan_port = int(vxlan_port_str)
314 except TypeError:
315 # TypeError means vxlan_port was None
316 # ie: not provided by the user or the policy
317 vxlan_port = self.netlink.VXLAN_UDP_PORT
318 except ValueError as e:
319 self.logger.warning(
320 "%s: vxlan-port: using default %s: invalid configured value %s"
321 % (ifname, self.netlink.VXLAN_UDP_PORT, str(e))
322 )
323 vxlan_port = self.netlink.VXLAN_UDP_PORT
324
325 cached_vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT)
326
327 if link_exists:
328 if vxlan_port != cached_vxlan_port:
329 self.logger.warning(
330 "%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s"
331 % (ifname, cached_vxlan_port, ifname, ifname)
ec25a08c 332 )
ec25a08c
JF
333 return
334
223ba5af
JF
335 self.logger.info("%s: set vxlan-port %s" % (ifname, vxlan_port))
336 user_request_vxlan_info_data[Link.IFLA_VXLAN_PORT] = vxlan_port
3218f49d 337 except Exception:
223ba5af
JF
338 self.log_error("%s: invalid vxlan-port '%s'" % (ifname, vxlan_port_str), ifaceobj)
339
340 def __config_vxlan_ttl(self, ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
341 """
342 Get vxlan-ttl from user config or policy, validate integer value and insert in netlink dict
343 :param ifname:
344 :param ifaceobj:
345 :param user_request_vxlan_info_data:
346 :param cached_vxlan_ifla_info_data:
347 :return:
348 """
349 vxlan_ttl_str = ifaceobj.get_attr_value_first("vxlan-ttl")
350 try:
351 if vxlan_ttl_str:
352 vxlan_ttl = self.get_vxlan_ttl_from_string(vxlan_ttl_str)
353 else:
354 vxlan_ttl = self.get_vxlan_ttl_from_string(
355 policymanager.policymanager_api.get_attr_default(
356 module_name=self.__class__.__name__,
357 attr="vxlan-ttl"
358 )
359 )
360
361 cached_ifla_vxlan_ttl = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_TTL)
362 if vxlan_ttl != cached_ifla_vxlan_ttl:
363
364 if cached_ifla_vxlan_ttl is not None:
365 self.logger.info("%s: set vxlan-ttl %s (cache %s)" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl, cached_ifla_vxlan_ttl))
366 else:
367 self.logger.info("%s: set vxlan-ttl %s" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl))
368
369 user_request_vxlan_info_data[Link.IFLA_VXLAN_TTL] = vxlan_ttl
3218f49d 370 except Exception:
223ba5af
JF
371 self.log_error("%s: invalid vxlan-ttl '%s'" % (ifname, vxlan_ttl_str), ifaceobj)
372
373 def __config_vxlan_local_tunnelip(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
374 """
375 Get vxlan-local-tunnelip user config or policy, validate ip address format and insert in netlink dict
376 :param ifname:
377 :param ifaceobj:
378 :param link_exists:
379 :param user_request_vxlan_info_data:
380 :param cached_vxlan_ifla_info_data:
381 :return:
382 """
383 local = ifaceobj.get_attr_value_first("vxlan-local-tunnelip")
384
385 if not local and self._vxlan_local_tunnelip:
386 local = self._vxlan_local_tunnelip
387
388 if link_exists:
389 # on ifreload do not overwrite anycast_ip to individual ip
390 # if clagd has modified
391 running_localtunnelip = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
392
393 if self._clagd_vxlan_anycast_ip and running_localtunnelip:
0e936c3f 394 anycastip = ipnetwork.IPNetwork(self._clagd_vxlan_anycast_ip)
223ba5af
JF
395 if anycastip == running_localtunnelip:
396 local = running_localtunnelip
397
398 if not local:
399 local = policymanager.policymanager_api.get_attr_default(
400 module_name=self.__class__.__name__,
401 attr="vxlan-local-tunnelip"
402 )
403
404 if local:
405 try:
0e936c3f
JF
406 local = ipnetwork.IPv4Address(local)
407
408 if local.initialized_with_prefixlen:
223ba5af 409 self.logger.warning("%s: vxlan-local-tunnelip %s: netmask ignored" % (ifname, local))
0e936c3f
JF
410
411 except Exception as e:
412 raise Exception("%s: invalid vxlan-local-tunnelip %s: %s" % (ifname, local, str(e)))
223ba5af
JF
413
414 cached_ifla_vxlan_local = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
415
416 if local:
417 if local != cached_ifla_vxlan_local:
418 self.logger.info("%s: set vxlan-local-tunnelip %s" % (ifname, local))
419 user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = local
420
421 # if both local-ip and anycast-ip are identical the function prints a warning
422 self.syntax_check_localip_anycastip_equal(ifname, local, self._clagd_vxlan_anycast_ip)
423 elif cached_ifla_vxlan_local:
424 self.logger.info("%s: removing vxlan-local-tunnelip (cache %s)" % (ifname, cached_ifla_vxlan_local))
425 user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = None
426
427 return local
428
40658337
JF
429 def __get_vxlan_attribute(self, ifaceobj, attr_name):
430 vxlan_attr_value = ifaceobj.get_attr_value_first(attr_name)
d486dd0d 431
40658337
JF
432 if not vxlan_attr_value:
433 vxlan_attr_value = policymanager.policymanager_api.get_attr_default(
223ba5af 434 module_name=self.__class__.__name__,
40658337 435 attr=attr_name
223ba5af 436 )
d486dd0d 437
40658337 438 return vxlan_attr_value
d486dd0d 439
223ba5af
JF
440 def __config_vxlan_group(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
441 """
442 vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive
443 this function validates ip format for both attribute and tries to understand
444 what the user really want (remote or group option).
445
446 :param ifname:
447 :param ifaceobj:
448 :param mcast_grp:
449 :param group:
450 :param physdev:
451 :param user_request_vxlan_info_data:
452 :param cached_vxlan_ifla_info_data:
453 :return:
454 """
455 if mcast_grp and group:
456 self.log_error("%s: both group (vxlan-mcastgrp %s) and "
457 "remote (vxlan-svcnodeip %s) cannot be specified"
458 % (ifname, mcast_grp, group), ifaceobj)
459
460 attribute_name = "vxlan-svcnodeip"
461 multicast_group_change = False
462
463 if group:
464 try:
0e936c3f
JF
465 group = ipnetwork.IPv4Address(group)
466
467 if group.initialized_with_prefixlen:
223ba5af 468 self.logger.warning("%s: vxlan-svcnodeip %s: netmask ignored" % (ifname, group))
223ba5af 469
0e936c3f
JF
470 except Exception as e:
471 raise Exception("%s: invalid vxlan-svcnodeip %s: %s" % (ifname, group, str(e)))
472
473 if group.ip.is_multicast:
223ba5af
JF
474 self.logger.warning("%s: vxlan-svcnodeip %s: invalid group address, "
475 "for multicast IP please use attribute \"vxlan-mcastgrp\"" % (ifname, group))
476 # if svcnodeip is used instead of mcastgrp we warn the user
477 # if mcast_grp is not provided by the user we can instead
478 # use the svcnodeip value
479 if not physdev:
480 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname))
481
482 elif mcast_grp:
483 try:
0e936c3f
JF
484 mcast_grp = ipnetwork.IPv4Address(mcast_grp)
485
486 if mcast_grp.initialized_with_prefixlen:
223ba5af 487 self.logger.warning("%s: vxlan-mcastgrp %s: netmask ignored" % (ifname, mcast_grp))
223ba5af 488
0e936c3f
JF
489 except Exception as e:
490 raise Exception("%s: invalid vxlan-mcastgrp %s: %s" % (ifname, mcast_grp, str(e)))
491
492 if not mcast_grp.ip.is_multicast:
223ba5af
JF
493 self.logger.warning("%s: vxlan-mcastgrp %s: invalid group address, "
494 "for non-multicast IP please use attribute \"vxlan-svcnodeip\""
495 % (ifname, mcast_grp))
496 # if mcastgrp is specified with a non-multicast address
497 # we warn the user. If the svcnodeip wasn't specified by
498 # the user we can use the mcastgrp value as svcnodeip
499 if not group:
500 group = mcast_grp
501 mcast_grp = None
d486dd0d 502 else:
223ba5af
JF
503 attribute_name = "vxlan-mcastgrp"
504
505 if mcast_grp:
506 group = mcast_grp
507
508 if not physdev:
509 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname))
510
511 cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
512
513 if group != cached_ifla_vxlan_group:
514
515 if not group:
0e936c3f 516 group = ipnetwork.IPNetwork("0.0.0.0")
223ba5af
JF
517 attribute_name = "vxlan-svcnodeip/vxlan-mcastgrp"
518
519 self.logger.info("%s: set %s %s" % (ifname, attribute_name, group))
520 user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP] = group
521
522 # if the mcastgrp address is changed we need to signal this to the upper function
523 # in this case vxlan needs to be down before applying changes then up'd
524 multicast_group_change = True
d486dd0d
JF
525
526 if link_exists:
223ba5af
JF
527 if cached_ifla_vxlan_group:
528 self.logger.info(
529 "%s: vxlan-mcastgrp configuration changed (cache %s): flapping vxlan device required"
530 % (ifname, cached_ifla_vxlan_group)
531 )
b067bba9 532 else:
223ba5af
JF
533 self.logger.info(
534 "%s: vxlan-mcastgrp configuration changed: flapping vxlan device required" % ifname
535 )
b067bba9 536
223ba5af 537 return group, multicast_group_change
d486dd0d 538
40658337
JF
539 def __config_vxlan_group6(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
540 """
541 vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive
542 this function validates ip format for both attribute and tries to understand
543 what the user really want (remote or group option).
544
545 :param ifname:
546 :param ifaceobj:
547 :param mcast_grp:
548 :param group:
549 :param physdev:
550 :param user_request_vxlan_info_data:
551 :param cached_vxlan_ifla_info_data:
552 :return:
553 """
554 if mcast_grp and group:
555 self.log_error("%s: both group (vxlan-mcastgrp6 %s) and "
556 "remote (vxlan-svcnodeip6 %s) cannot be specified"
557 % (ifname, mcast_grp, group), ifaceobj)
558
559 attribute_name = "vxlan-svcnodeip6"
560 multicast_group_change = False
561
562 if group:
563 try:
564 group = ipnetwork.IPv6Address(group)
565 except Exception:
566 try:
567 group_ip = ipnetwork.IPv6Network(group).ip
568 self.logger.warning("%s: vxlan-svcnodeip6 %s: netmask ignored" % (ifname, group))
569 group = group_ip
570 except:
571 raise Exception("%s: invalid vxlan-svcnodeip6 %s: must be in ipv4 format" % (ifname, group))
572
573 if group.is_multicast:
574 self.logger.warning("%s: vxlan-svcnodeip6 %s: invalid group address, "
575 "for multicast IP please use attribute \"vxlan-mcastgrp6\"" % (ifname, group))
576 # if svcnodeip is used instead of mcastgrp we warn the user
577 # if mcast_grp is not provided by the user we can instead
578 # use the svcnodeip value
579 if not physdev:
580 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp6) requires 'vxlan-physdev' to be specified" % (ifname))
581
582 elif mcast_grp:
583 try:
584 mcast_grp = ipnetwork.IPv6Address(mcast_grp)
585 except Exception:
586 try:
587 group_ip = ipnetwork.IPv6Network(mcast_grp).ip
588 self.logger.warning("%s: vxlan-mcastgrp6 %s: netmask ignored" % (ifname, mcast_grp))
589 mcast_grp = group_ip
590 except:
591 raise Exception("%s: invalid vxlan-mcastgrp6 %s: must be in ipv4 format" % (ifname, mcast_grp))
592
593 if not mcast_grp.is_multicast:
594 self.logger.warning("%s: vxlan-mcastgrp6 %s: invalid group address, "
595 "for non-multicast IP please use attribute \"vxlan-svcnodeip6\""
596 % (ifname, mcast_grp))
597 # if mcastgrp is specified with a non-multicast address
598 # we warn the user. If the svcnodeip wasn't specified by
599 # the user we can use the mcastgrp value as svcnodeip
600 if not group:
601 group = mcast_grp
602 mcast_grp = None
603 else:
604 attribute_name = "vxlan-mcastgrp6"
605
606 if mcast_grp:
607 group = mcast_grp
608
609 if not physdev:
610 self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp6) requires 'vxlan-physdev' to be specified" % (ifname))
611
612 cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP6)
613
614 if group != cached_ifla_vxlan_group:
615
616 if not group:
617 group = ipnetwork.IPNetwork("::0", family=6)
618 attribute_name = "vxlan-svcnodeip6/vxlan-mcastgrp6"
619
620 self.logger.info("%s: set %s %s" % (ifname, attribute_name, group))
621 user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP6] = group
622
623 # if the mcastgrp address is changed we need to signal this to the upper function
624 # in this case vxlan needs to be down before applying changes then up'd
625 multicast_group_change = True
626
627 if link_exists:
628 if cached_ifla_vxlan_group:
629 self.logger.info(
630 "%s: vxlan-mcastgrp6 configuration changed (cache %s): flapping vxlan device required"
631 % (ifname, cached_ifla_vxlan_group)
632 )
633 else:
634 self.logger.info(
635 "%s: vxlan-mcastgrp6 configuration changed: flapping vxlan device required" % ifname
636 )
637
638 return group, multicast_group_change
639
223ba5af
JF
640 def __config_vxlan_learning(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
641 if not link_exists or not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT:
642 vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning')
643 if not vxlan_learning:
644 vxlan_learning = self.get_attr_default_value('vxlan-learning')
645 vxlan_learning = utils.get_boolean_from_string(vxlan_learning)
646 else:
647 vxlan_learning = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING)
d486dd0d 648
223ba5af
JF
649 if vxlan_learning != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING):
650 self.logger.info("%s: set vxlan-learning %s" % (ifaceobj.name, "on" if vxlan_learning else "off"))
651 user_request_vxlan_info_data[Link.IFLA_VXLAN_LEARNING] = vxlan_learning
652
653 def __get_vxlan_physdev(self, ifaceobj, mcastgrp):
654 """
655 vxlan-physdev wrapper, special handling is required for mcastgrp is provided
656 the vxlan needs to use a dummy or real device for tunnel endpoint communication
657 This wrapper will get the physdev from user config or policy. IF the device
658 doesnt exists we create a dummy device.
659
660 :param ifaceobj:
661 :param mcastgrp:
662 :return physdev:
663 """
664 physdev = ifaceobj.get_attr_value_first("vxlan-physdev")
d486dd0d 665
223ba5af
JF
666 # if the user provided a physdev we need to honor his config
667 # or if mcastgrp wasn't specified we don't need to go further
668 if physdev or not mcastgrp:
669 return physdev
670
671 physdev = self.vxlan_physdev_mcast
672
673 if not self.cache.link_exists(physdev):
674 self.logger.info("%s: needs a dummy device (%s) to use for "
675 "multicast termination (vxlan-mcastgrp %s)"
676 % (ifaceobj.name, physdev, mcastgrp))
677 self.netlink.link_add_with_attributes(ifname=physdev, kind="dummy", ifla={Link.IFLA_MTU: 16000, Link.IFLA_LINKMODE: 1})
678 self.netlink.link_up(physdev)
679
680 return physdev
681
682 def __config_vxlan_physdev(self, link_exists, ifaceobj, vxlan_physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data):
683 if vxlan_physdev:
684 try:
685 vxlan_physdev_ifindex = self.cache.get_ifindex(vxlan_physdev)
686 except NetlinkCacheIfnameNotFoundError:
d486dd0d 687 try:
223ba5af 688 vxlan_physdev_ifindex = int(self.sysfs.read_file_oneline("/sys/class/net/%s/ifindex" % vxlan_physdev))
3218f49d 689 except Exception:
223ba5af
JF
690 self.logger.error("%s: physdev %s doesn't exists" % (ifaceobj.name, vxlan_physdev))
691 return
d486dd0d 692
223ba5af
JF
693 if vxlan_physdev_ifindex != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LINK):
694 self.logger.info("%s: set vxlan-physdev %s" % (ifaceobj.name, vxlan_physdev))
695 user_request_vxlan_info_data[Link.IFLA_VXLAN_LINK] = vxlan_physdev_ifindex
d486dd0d 696
223ba5af
JF
697 # if the vxlan exists we need to return True, meaning that the vxlan
698 # needs to be flapped because we detected a vxlan-physdev change
699 if link_exists:
700 self.logger.info("%s: vxlan-physdev configuration changed: flapping vxlan device required" % ifaceobj.name)
701 return True
702
703 return False
704
705 def _up(self, ifaceobj):
706 vxlan_id_str = ifaceobj.get_attr_value_first("vxlan-id")
707
e537a6e6
JF
708 if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and not vxlan_id_str:
709 self.logger.warning("%s: missing vxlan-id attribute on vxlan device" % ifaceobj.name)
223ba5af
JF
710 return
711
712 ifname = ifaceobj.name
713 link_exists = self.cache.link_exists(ifname)
714
715 if link_exists:
716 # if link already exists make sure this is a vxlan
717 device_link_kind = self.cache.get_link_kind(ifname)
718
719 if device_link_kind != "vxlan":
720 self.logger.error(
721 "%s: device already exists and is not a vxlan (type %s)"
722 % (ifname, device_link_kind)
d486dd0d 723 )
223ba5af
JF
724 ifaceobj.set_status(ifaceStatus.ERROR)
725 return
d486dd0d 726
223ba5af
JF
727 # get vxlan running attributes
728 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
729 else:
730 cached_vxlan_ifla_info_data = {}
d486dd0d 731
223ba5af 732 user_request_vxlan_info_data = {}
d486dd0d 733
e537a6e6
JF
734 if vxlan_id_str:
735 # for single vxlan device we don't have a vxlan-id
736 self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
737
223ba5af
JF
738 self.__config_vxlan_learning(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
739 self.__config_vxlan_ageing(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
740 self.__config_vxlan_port(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
741 self.__config_vxlan_ttl(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
742 local = self.__config_vxlan_local_tunnelip(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data)
743
40658337
JF
744 vxlan_mcast_grp = self.__get_vxlan_attribute(ifaceobj, "vxlan-mcastgrp")
745 vxlan_svcnodeip = self.__get_vxlan_attribute(ifaceobj, "vxlan-svcnodeip")
746
747 vxlan_mcast_grp6 = self.__get_vxlan_attribute(ifaceobj, "vxlan-mcastgrp6")
748 vxlan_svcnodeip6 = self.__get_vxlan_attribute(ifaceobj, "vxlan-svcnodeip6")
749
223ba5af
JF
750 vxlan_physdev = self.__get_vxlan_physdev(ifaceobj, vxlan_mcast_grp)
751
752 vxlan_physdev_changed = self.__config_vxlan_physdev(
753 link_exists,
754 ifaceobj,
755 vxlan_physdev,
756 user_request_vxlan_info_data,
757 cached_vxlan_ifla_info_data
758 )
759
760 group, multicast_group_changed = self.__config_vxlan_group(
761 ifname,
762 ifaceobj,
763 link_exists,
764 vxlan_mcast_grp,
765 vxlan_svcnodeip,
766 vxlan_physdev,
767 user_request_vxlan_info_data,
768 cached_vxlan_ifla_info_data
769 )
770
40658337
JF
771 group6, multicast_group_changed6 = self.__config_vxlan_group6(
772 ifname,
773 ifaceobj,
774 link_exists,
775 vxlan_mcast_grp6,
776 vxlan_svcnodeip6,
777 vxlan_physdev,
778 user_request_vxlan_info_data,
779 cached_vxlan_ifla_info_data
780 )
781
782 flap_vxlan_device = link_exists and (multicast_group_changed or multicast_group_changed6 or vxlan_physdev_changed)
223ba5af
JF
783
784 if user_request_vxlan_info_data:
785
786 if link_exists and not len(user_request_vxlan_info_data) > 1:
787 # if the vxlan already exists it's already cached
788 # user_request_vxlan_info_data always contains at least one
789 # element: vxlan-id
790 self.logger.info('%s: vxlan already exists - no change detected' % ifname)
791 else:
e537a6e6 792 if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN:
223ba5af 793 if link_exists:
e537a6e6 794 self.logger.warning("%s: updating existing single vxlan device is not support yet" % ifname)
223ba5af 795 else:
e537a6e6
JF
796 self.iproute2.link_add_single_vxlan(
797 ifname,
a7e5fb25 798 local.ip if local else None,
e537a6e6
JF
799 user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT)
800 )
801 else:
802 try:
803 if flap_vxlan_device:
804 self.netlink.link_down_force(ifname)
805
806 self.netlink.link_add_vxlan_with_info_data(ifname, user_request_vxlan_info_data)
807
808 if flap_vxlan_device:
809 self.netlink.link_up_force(ifname)
810 except Exception as e:
811 if link_exists:
812 self.log_error("%s: applying vxlan change failed: %s" % (ifname, str(e)), ifaceobj)
813 else:
814 self.log_error("%s: vxlan creation failed: %s" % (ifname, str(e)), ifaceobj)
815 return
223ba5af
JF
816
817 vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj)
818
819 remoteips = ifaceobj.get_attr_value('vxlan-remoteip')
820 if remoteips:
821 try:
822 for remoteip in remoteips:
0e936c3f 823 ipnetwork.IPv4Address(remoteip)
223ba5af
JF
824 except Exception as e:
825 self.log_error('%s: vxlan-remoteip: %s' % (ifaceobj.name, str(e)))
826
827 if vxlan_purge_remotes or remoteips:
828 # figure out the diff for remotes and do the bridge fdb updates
829 # only if provisioned by user and not by an vxlan external
830 # controller.
0e936c3f
JF
831 local_str = str(local)
832
833 if local_str and remoteips and local_str in remoteips:
834 remoteips.remove(local_str)
835
836 peers = self.iproute2.get_vxlan_peers(ifaceobj.name, str(group.ip) if group else None)
837
223ba5af
JF
838 cur_peers = set(peers)
839 if remoteips:
840 new_peers = set(remoteips)
841 del_list = cur_peers.difference(new_peers)
842 add_list = new_peers.difference(cur_peers)
843 else:
844 del_list = cur_peers
845 add_list = []
d486dd0d 846
223ba5af 847 for addr in del_list:
d486dd0d 848 try:
223ba5af
JF
849 self.iproute2.bridge_fdb_del(
850 ifaceobj.name,
851 "00:00:00:00:00:00",
852 None, True, addr
853 )
3218f49d 854 except Exception:
d486dd0d 855 pass
d486dd0d 856
223ba5af 857 for addr in add_list:
d486dd0d 858 try:
223ba5af
JF
859 self.iproute2.bridge_fdb_append(
860 ifaceobj.name,
861 "00:00:00:00:00:00",
862 None, True, addr
863 )
3218f49d 864 except Exception:
223ba5af 865 pass
d486dd0d
JF
866
867 def _down(self, ifaceobj):
868 try:
223ba5af 869 self.netlink.link_del(ifaceobj.name)
3b01ed76 870 except Exception as e:
d486dd0d
JF
871 self.log_warn(str(e))
872
223ba5af
JF
873 @staticmethod
874 def _query_check_n_update(ifaceobj, ifaceobjcurr, attrname, attrval, running_attrval):
d486dd0d
JF
875 if not ifaceobj.get_attr_value_first(attrname):
876 return
877 if running_attrval and attrval == running_attrval:
223ba5af 878 ifaceobjcurr.update_config_with_status(attrname, attrval, 0)
d486dd0d 879 else:
223ba5af 880 ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1)
d486dd0d 881
223ba5af
JF
882 @staticmethod
883 def _query_check_n_update_addresses(ifaceobjcurr, attrname, addresses, running_addresses):
d486dd0d
JF
884 if addresses:
885 for a in addresses:
886 if a in running_addresses:
887 ifaceobjcurr.update_config_with_status(attrname, a, 0)
888 else:
889 ifaceobjcurr.update_config_with_status(attrname, a, 1)
3b01ed76
JF
890 running_addresses = set(running_addresses).difference(
891 set(addresses))
223ba5af 892 [ifaceobjcurr.update_config_with_status(attrname, a, 1) for a in running_addresses]
d486dd0d
JF
893
894 def _query_check(self, ifaceobj, ifaceobjcurr):
223ba5af
JF
895 ifname = ifaceobj.name
896
897 if not self.cache.link_exists(ifname):
d486dd0d 898 return
d486dd0d 899
223ba5af
JF
900 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
901
902 if not cached_vxlan_ifla_info_data:
903 ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1)
904 return
905
906 for vxlan_attr_str, vxlan_attr_nl, callable_type in (
907 ('vxlan-id', Link.IFLA_VXLAN_ID, int),
908 ('vxlan-ttl', Link.IFLA_VXLAN_TTL, int),
909 ('vxlan-port', Link.IFLA_VXLAN_PORT, int),
910 ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, int),
0e936c3f 911 ('vxlan-mcastgrp', Link.IFLA_VXLAN_GROUP, ipnetwork.IPv4Address),
40658337 912 ('vxlan-mcastgrp6', Link.IFLA_VXLAN_GROUP6, ipnetwork.IPv6Address),
0e936c3f 913 ('vxlan-svcnodeip', Link.IFLA_VXLAN_GROUP, ipnetwork.IPv4Address),
40658337 914 ('vxlan-svcnodeip6', Link.IFLA_VXLAN_GROUP6, ipnetwork.IPv6Address),
223ba5af
JF
915 ('vxlan-physdev', Link.IFLA_VXLAN_LINK, lambda x: self.cache.get_ifindex(x)),
916 ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda boolean_str: utils.get_boolean_from_string(boolean_str)),
917 ):
918 vxlan_attr_value = ifaceobj.get_attr_value_first(vxlan_attr_str)
d486dd0d 919
223ba5af
JF
920 if not vxlan_attr_value:
921 continue
922
923 cached_vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl)
924
925 try:
926 vxlan_attr_value_nl = callable_type(vxlan_attr_value)
927 except Exception as e:
928 self.logger.warning('%s: %s: %s' % (ifname, vxlan_attr_str, str(e)))
929 ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1)
930 continue
931
932 if vxlan_attr_value_nl == cached_vxlan_attr_value:
933 ifaceobjcurr.update_config_with_status(vxlan_attr_str, vxlan_attr_value, 0)
934 else:
935 ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1)
936
937 #
938 # vxlan-local-tunnelip
939 #
940 running_attrval = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL)
d486dd0d
JF
941 attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip')
942 if not attrval:
223ba5af 943 attrval = self._vxlan_local_tunnelip
0e936c3f 944 # TODO: vxlan._vxlan_local_tunnelip should be a ipnetwork.IPNetwork obj
d486dd0d
JF
945 ifaceobj.update_config('vxlan-local-tunnelip', attrval)
946
223ba5af 947 if str(running_attrval) == self._clagd_vxlan_anycast_ip:
d486dd0d
JF
948 # if local ip is anycast_ip, then let query_check to go through
949 attrval = self._clagd_vxlan_anycast_ip
d486dd0d 950
223ba5af
JF
951 self._query_check_n_update(
952 ifaceobj,
953 ifaceobjcurr,
954 'vxlan-local-tunnelip',
955 str(attrval),
0e936c3f 956 str(running_attrval.ip) if running_attrval else None
223ba5af 957 )
d486dd0d 958
223ba5af
JF
959 #
960 # vxlan-remoteip
961 #
962 purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj)
d486dd0d
JF
963 if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'):
964 # If purge remotes or if vxlan-remoteip's are set
965 # in the config file, we are owners of the installed
966 # remote-ip's, lets check and report any remote ips we don't
967 # understand
223ba5af 968 cached_svcnode = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
d486dd0d 969
223ba5af
JF
970 self._query_check_n_update_addresses(
971 ifaceobjcurr,
972 'vxlan-remoteip',
973 ifaceobj.get_attr_value('vxlan-remoteip'),
0e936c3f 974 self.iproute2.get_vxlan_peers(ifaceobj.name, str(cached_svcnode.ip) if cached_svcnode else None)
223ba5af 975 )
a382b488 976
223ba5af
JF
977 def _query_running(self, ifaceobjrunning):
978 ifname = ifaceobjrunning.name
a382b488 979
223ba5af
JF
980 if not self.cache.link_exists(ifname):
981 return
a382b488 982
223ba5af 983 if not self.cache.get_link_kind(ifname) == 'vxlan':
d486dd0d 984 return
223ba5af
JF
985
986 cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname)
987
988 if not cached_vxlan_ifla_info_data:
d486dd0d
JF
989 return
990
223ba5af
JF
991 #
992 # vxlan-id
993 #
994 vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID)
995
996 if not vxlan_id:
997 # no vxlan id, meaning this not a vxlan
998 return
999
1000 ifaceobjrunning.update_config('vxlan-id', str(vxlan_id))
1001
1002 #
1003 # vxlan-port
1004 #
1005 vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT)
1006
1007 if vxlan_port:
1008 ifaceobjrunning.update_config('vxlan-port', vxlan_port)
1009
1010 #
1011 # vxlan-svcnode
1012 #
1013 vxlan_svcnode_value = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP)
1014
1015 if vxlan_svcnode_value:
1016 vxlan_svcnode_value = str(vxlan_svcnode_value)
1017 ifaceobjrunning.update_config('vxlan-svcnode', vxlan_svcnode_value)
d486dd0d 1018
223ba5af
JF
1019 #
1020 # vxlan-remoteip
1021 #
1022 purge_remotes = self.__get_vlxan_purge_remotes(None)
d486dd0d
JF
1023 if purge_remotes:
1024 # if purge_remotes is on, it means we own the
1025 # remote ips. Query them and add it to the running config
223ba5af 1026 attrval = self.iproute2.get_vxlan_peers(ifname, vxlan_svcnode_value)
d486dd0d 1027 if attrval:
223ba5af
JF
1028 [ifaceobjrunning.update_config('vxlan-remoteip', a) for a in attrval]
1029
1030 #
1031 # vxlan-link
1032 # vxlan-ageing
1033 # vxlan-learning
1034 # vxlan-local-tunnelip
1035 #
1036 for vxlan_attr_name, vxlan_attr_nl, callable_netlink_value_to_string in (
1037 ('vxlan-physdev', Link.IFLA_VXLAN_LINK, self._get_ifname_for_ifindex),
1038 ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, str),
1039 ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda value: 'on' if value else 'off'),
1040 ('vxlan-local-tunnelip', Link.IFLA_VXLAN_LOCAL, str),
1041 ):
1042 vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl)
1043
1044 if vxlan_attr_value is not None:
dc74ceda 1045 vxlan_attr_value_str = callable_netlink_value_to_string(vxlan_attr_value)
223ba5af
JF
1046
1047 if vxlan_attr_value:
1048 ifaceobjrunning.update_config(vxlan_attr_name, vxlan_attr_value_str)
1049
1050 def _get_ifname_for_ifindex(self, ifindex):
1051 """
1052 we need this middle-man function to query the cache
1053 cache.get_ifname can raise KeyError, we need to catch
1054 it and return None
1055 """
1056 try:
1057 return self.cache.get_ifname(ifindex)
1058 except KeyError:
1059 return None
a382b488 1060
223ba5af
JF
1061 _run_ops = {
1062 "pre-up": _up,
1063 "post-down": _down,
1064 "query-running": _query_running,
1065 "query-checkcurr": _query_check
1066 }
d486dd0d
JF
1067
1068 def get_ops(self):
3b01ed76 1069 return list(self._run_ops.keys())
d486dd0d 1070
d486dd0d
JF
1071 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
1072 op_handler = self._run_ops.get(operation)
1073 if not op_handler:
1074 return
223ba5af
JF
1075
1076 if operation != 'query-running':
1077 if not self._is_vxlan_device(ifaceobj):
1078 return
1079
1080 if not self.vxlan_mcastgrp_ref \
1081 and self.vxlan_physdev_mcast \
1082 and self.cache.link_exists(self.vxlan_physdev_mcast):
1083 self.netlink.link_del(self.vxlan_physdev_mcast)
1084 self.reset()
1085
d486dd0d
JF
1086 if operation == 'query-checkcurr':
1087 op_handler(self, ifaceobj, query_ifaceobj)
1088 else:
1089 op_handler(self, ifaceobj)