]>
Commit | Line | Data |
---|---|---|
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 | ||
66eb9ce3 | 7 | from ipaddress import IPv4Network, IPv4Address, AddressValueError, ip_address |
d486dd0d | 8 | try: |
0e936c3f | 9 | import ifupdown2.nlmanager.ipnetwork as ipnetwork |
d486dd0d | 10 | import ifupdown2.ifupdown.policymanager as policymanager |
3fb83a7a | 11 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags |
d486dd0d | 12 | |
970c72e4 | 13 | from ifupdown2.lib.addon import Vxlan |
223ba5af JF |
14 | from ifupdown2.lib.nlcache import NetlinkCacheIfnameNotFoundError |
15 | ||
d486dd0d JF |
16 | from ifupdown2.nlmanager.nlmanager import Link |
17 | ||
421e9573 | 18 | from ifupdown2.ifupdown.iface import ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus, iface |
d486dd0d | 19 | from ifupdown2.ifupdown.utils import utils |
56f34349 | 20 | from ifupdown2.ifupdown.statemanager import statemanager_api as statemanager |
d486dd0d | 21 | from ifupdown2.ifupdownaddons.cache import * |
d486dd0d | 22 | from ifupdown2.ifupdownaddons.modulebase import moduleBase |
56f34349 | 23 | |
bd441a51 | 24 | except (ImportError, ModuleNotFoundError): |
0e936c3f | 25 | import nlmanager.ipnetwork as ipnetwork |
d486dd0d | 26 | import ifupdown.policymanager as policymanager |
3fb83a7a | 27 | import ifupdown.ifupdownflags as ifupdownflags |
d486dd0d | 28 | |
db4371de | 29 | from lib.addon import Vxlan |
223ba5af JF |
30 | from lib.nlcache import NetlinkCacheIfnameNotFoundError |
31 | ||
d486dd0d JF |
32 | from nlmanager.nlmanager import Link |
33 | ||
421e9573 | 34 | from ifupdown.iface import ifaceLinkKind, ifaceLinkPrivFlags, ifaceStatus, iface |
d486dd0d | 35 | from ifupdown.utils import utils |
56f34349 | 36 | from ifupdown.statemanager import statemanager_api as statemanager |
d486dd0d JF |
37 | |
38 | from ifupdownaddons.cache import * | |
d486dd0d JF |
39 | from ifupdownaddons.modulebase import moduleBase |
40 | ||
41 | ||
db4371de | 42 | class vxlan(Vxlan, moduleBase): |
223ba5af JF |
43 | _modinfo = { |
44 | "mhelp": "vxlan module configures vxlan interfaces.", | |
45 | "attrs": { | |
46 | "vxlan-id": { | |
47 | "help": "vxlan id", | |
48 | "validrange": ["1", "16777214"], | |
49 | "required": True, | |
50 | "example": ["vxlan-id 100"] | |
51 | }, | |
52 | "vxlan-local-tunnelip": { | |
53 | "help": "vxlan local tunnel ip", | |
54 | "validvals": ["<ipv4>"], | |
55 | "example": ["vxlan-local-tunnelip 172.16.20.103"] | |
56 | }, | |
57 | "vxlan-svcnodeip": { | |
969257a4 | 58 | "help": "vxlan svc node id", |
223ba5af JF |
59 | "validvals": ["<ipv4>"], |
60 | "example": ["vxlan-svcnodeip 172.16.22.125"] | |
61 | }, | |
40658337 JF |
62 | "vxlan-svcnodeip6": { |
63 | "help": "vxlan svc node ip", | |
64 | "validvals": ["<ipv6>"], | |
65 | "example": ["vxlan-svcnodeip6 2001:DB8:8086:6502::"] | |
66 | }, | |
223ba5af JF |
67 | "vxlan-remoteip": { |
68 | "help": "vxlan remote ip", | |
69 | "validvals": ["<ipv4>"], | |
70 | "example": ["vxlan-remoteip 172.16.22.127"], | |
71 | "multiline": True | |
72 | }, | |
73 | "vxlan-learning": { | |
74 | "help": "vxlan learning yes/no", | |
75 | "validvals": ["yes", "no", "on", "off"], | |
76 | "example": ["vxlan-learning no"], | |
77 | "default": "yes" | |
78 | }, | |
79 | "vxlan-ageing": { | |
80 | "help": "vxlan aging timer", | |
81 | "validrange": ["0", "4096"], | |
82 | "example": ["vxlan-ageing 300"], | |
83 | "default": "300" | |
84 | }, | |
85 | "vxlan-purge-remotes": { | |
86 | "help": "vxlan purge existing remote entries", | |
87 | "validvals": ["yes", "no"], | |
88 | "example": ["vxlan-purge-remotes yes"], | |
89 | }, | |
90 | "vxlan-port": { | |
91 | "help": "vxlan UDP port (transmitted to vxlan driver)", | |
92 | "example": ["vxlan-port 4789"], | |
93 | "validrange": ["1", "65536"], | |
94 | "default": "4789", | |
95 | }, | |
96 | "vxlan-physdev": { | |
97 | "help": "vxlan physical device", | |
98 | "example": ["vxlan-physdev eth1"] | |
99 | }, | |
100 | "vxlan-ttl": { | |
101 | "help": "specifies the TTL value to use in outgoing packets " | |
102 | "(range 0..255), 0=auto", | |
103 | "default": "0", | |
b20f9836 SO |
104 | "validrange": ["0", "255"], |
105 | "validvals": ["<number>", "auto"], | |
223ba5af JF |
106 | "example": ['vxlan-ttl 42'], |
107 | }, | |
e521508b | 108 | "vxlan-tos": { |
a8dd54b0 | 109 | "help": "specifies the ToS value (range 0..255), 1=inherit", |
b20f9836 SO |
110 | "validrange": ["0", "255"], |
111 | "validvals": ["<number>", "inherit"], | |
e521508b SO |
112 | "example": ['vxlan-tos 42'], |
113 | }, | |
223ba5af JF |
114 | "vxlan-mcastgrp": { |
115 | "help": "vxlan multicast group", | |
116 | "validvals": ["<ip>"], | |
117 | "example": ["vxlan-mcastgrp 172.16.22.127"], | |
40658337 JF |
118 | }, |
119 | "vxlan-mcastgrp6": { | |
120 | "help": "vxlan multicast group", | |
121 | "validvals": ["<ip6>"], | |
122 | "example": ["vxlan-mcastgrp ff02::15c"], | |
ca436937 JF |
123 | }, |
124 | "vxlan-mcastgrp-map": { | |
56f34349 JF |
125 | "help": "vxlan multicast group for single-vxlan device -" |
126 | "doesn't support multiline attribute", | |
ca436937 JF |
127 | "example": ["vxlan-mcastgrp-map 1000=239.1.1.100 1001=239.1.1.200"], |
128 | }, | |
84c47c4f RP |
129 | "vxlan-vnifilter": { |
130 | "help": "vxlan vni filter for single-vxlan device", | |
131 | "validvals": ["on", "off"], | |
132 | "default": "off", | |
133 | "example": ["vxlan-vnifilter yes"], | |
134 | }, | |
66eb9ce3 JF |
135 | "vxlan-remoteip-map": { |
136 | "help": "static HREP entries for static single vxlan device", | |
137 | "example": ["vxlan-remoteip-map 1000-1002=27.0.0.10-27.0.0.12"], | |
138 | }, | |
e521508b SO |
139 | "vxlan-udp-csum": { |
140 | "help": "whether to perform checksumming or not", | |
e521508b SO |
141 | "validvals": ["yes", "no"], |
142 | "example": ["vxlan-udp-csum no"] | |
0500d5d8 JF |
143 | }, |
144 | "vxlan-vni": { | |
145 | "help": "L3 VxLAN interface (vni list and range are supported)", | |
146 | "validvals": ["<number>"], | |
147 | "example": ["vxlan-vni 42"] | |
e521508b | 148 | } |
223ba5af JF |
149 | } |
150 | } | |
151 | ||
152 | VXLAN_PHYSDEV_MCASTGRP_DEFAULT = "ipmr-lo" | |
d486dd0d JF |
153 | |
154 | def __init__(self, *args, **kargs): | |
db4371de | 155 | Vxlan.__init__(self) |
d486dd0d | 156 | moduleBase.__init__(self, *args, **kargs) |
223ba5af JF |
157 | |
158 | self._vxlan_purge_remotes = utils.get_boolean_from_string( | |
159 | policymanager.policymanager_api.get_module_globals( | |
160 | module_name=self.__class__.__name__, | |
161 | attr="vxlan-purge-remotes" | |
162 | ) | |
163 | ) | |
164 | self._vxlan_local_tunnelip = None | |
165 | self._clagd_vxlan_anycast_ip = "" | |
166 | ||
167 | # If mcastgrp is specified we need to rely on a user-configred device (via physdev) | |
168 | # or via a policy variable "vxlan-physdev_mcastgrp". If the device doesn't exist we | |
169 | # create it as a dummy device. We need to keep track of the user configuration to | |
170 | # know when to delete this dummy device (when user remove mcastgrp from it's config) | |
171 | self.vxlan_mcastgrp_ref = False | |
172 | self.vxlan_physdev_mcast = policymanager.policymanager_api.get_module_globals( | |
173 | module_name=self.__class__.__name__, | |
174 | attr="vxlan-physdev-mcastgrp" | |
175 | ) or self.VXLAN_PHYSDEV_MCASTGRP_DEFAULT | |
176 | ||
782aff35 JF |
177 | self.tvd_svd_mix_support = utils.get_boolean_from_string( |
178 | policymanager.policymanager_api.get_module_globals( | |
179 | module_name=self.__class__.__name__, | |
180 | attr="vxlan-support-mix-dev-types" | |
181 | ), | |
182 | default=True | |
183 | ) | |
184 | ||
db4371de JF |
185 | self.svd_tvd_errors = {} |
186 | ||
223ba5af JF |
187 | def reset(self): |
188 | # in daemon mode we need to reset mcastgrp_ref for every new command | |
189 | # this variable has to be set in get_dependent_ifacenames | |
190 | self.vxlan_mcastgrp_ref = False | |
d486dd0d JF |
191 | |
192 | def syntax_check(self, ifaceobj, ifaceobj_getfunc): | |
193 | if self._is_vxlan_device(ifaceobj): | |
223ba5af | 194 | if not ifaceobj.get_attr_value_first('vxlan-local-tunnelip') and not self._vxlan_local_tunnelip: |
d486dd0d JF |
195 | self.logger.warning('%s: missing vxlan-local-tunnelip' % ifaceobj.name) |
196 | return False | |
782aff35 JF |
197 | |
198 | self.check_and_raise_svd_tvd_errors(ifaceobj) | |
199 | ||
d486dd0d JF |
200 | return self.syntax_check_localip_anycastip_equal( |
201 | ifaceobj.name, | |
223ba5af JF |
202 | ifaceobj.get_attr_value_first('vxlan-local-tunnelip') or self._vxlan_local_tunnelip, |
203 | self._clagd_vxlan_anycast_ip | |
d486dd0d JF |
204 | ) |
205 | return True | |
206 | ||
207 | def syntax_check_localip_anycastip_equal(self, ifname, local_ip, anycast_ip): | |
208 | try: | |
0e936c3f | 209 | if local_ip and anycast_ip and ipnetwork.IPNetwork(local_ip) == ipnetwork.IPNetwork(anycast_ip): |
d486dd0d JF |
210 | self.logger.warning('%s: vxlan-local-tunnelip and clagd-vxlan-anycast-ip are identical (%s)' |
211 | % (ifname, local_ip)) | |
212 | return False | |
3218f49d | 213 | except Exception: |
d486dd0d JF |
214 | pass |
215 | return True | |
216 | ||
59ab29fb | 217 | def get_dependent_ifacenames(self, ifaceobj, ifaceobjs_all=None, old_ifaceobjs=False): |
e537a6e6 JF |
218 | if ifaceobj.get_attr_value_first("bridge-vlan-vni-map"): |
219 | ifaceobj.link_privflags |= ifaceLinkPrivFlags.SINGLE_VXLAN | |
220 | ||
d486dd0d JF |
221 | if self._is_vxlan_device(ifaceobj): |
222 | ifaceobj.link_kind |= ifaceLinkKind.VXLAN | |
223 | self._set_global_local_ip(ifaceobj) | |
223ba5af | 224 | |
0500d5d8 JF |
225 | self.__check_and_tag_l3vxi(ifaceobj) |
226 | ||
782aff35 | 227 | if not old_ifaceobjs and not self.tvd_svd_mix_support: |
db4371de JF |
228 | # mixing TVD and SVD is not supported - we need to warn the user |
229 | # we use a dictionary to make sure to only warn once and prevent each | |
230 | # vxlan from being configured on the system | |
231 | ||
232 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN: | |
233 | self.single_vxlan_configured.add(ifaceobj.name) | |
234 | ||
235 | if self.traditional_vxlan_configured: | |
236 | self.svd_tvd_errors[ifaceobj.name] = ( | |
237 | "%s: mixing single-vxlan-device with tradional %s is not supported (TVD: %s)" | |
238 | % (ifaceobj.name, "vxlans" if len(self.traditional_vxlan_configured) > 1 else "vxlan", ", ".join(self.traditional_vxlan_configured)) | |
239 | ) | |
0500d5d8 JF |
240 | elif ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI: |
241 | pass | |
db4371de JF |
242 | else: |
243 | self.traditional_vxlan_configured.add(ifaceobj.name) | |
244 | ||
245 | if self.single_vxlan_configured: | |
246 | self.svd_tvd_errors[ifaceobj.name] = ( | |
247 | "%s: mixing traditional vxlan with single vxlan %s is not supported (SVD: %s)" | |
248 | % (ifaceobj.name, "devices" if len(self.single_vxlan_configured) > 1 else "device", ", ".join(self.single_vxlan_configured)) | |
249 | ) | |
250 | ||
251 | ||
223ba5af JF |
252 | # if we detect a vxlan we check if mcastgrp is set (if so we set vxlan_mcastgrp_ref) |
253 | # to know when to delete this device. | |
1609696f | 254 | if not self.vxlan_mcastgrp_ref and (ifaceobj.get_attr_value("vxlan-mcastgrp") or ifaceobj.get_attr_value("vxlan-mcastgrp-map")): |
223ba5af JF |
255 | self.vxlan_mcastgrp_ref = True |
256 | ||
59ab29fb | 257 | elif ifaceobj.name == 'lo' and not old_ifaceobjs: |
d486dd0d JF |
258 | clagd_vxlan_list = ifaceobj.get_attr_value('clagd-vxlan-anycast-ip') |
259 | if clagd_vxlan_list: | |
260 | if len(clagd_vxlan_list) != 1: | |
261 | self.log_warn('%s: multiple clagd-vxlan-anycast-ip lines, using first one' | |
262 | % (ifaceobj.name,)) | |
223ba5af | 263 | self._clagd_vxlan_anycast_ip = clagd_vxlan_list[0] |
d486dd0d JF |
264 | |
265 | self._set_global_local_ip(ifaceobj) | |
a382b488 JF |
266 | |
267 | # If we should use a specific underlay device for the VXLAN | |
268 | # tunnel make sure this device is set up before the VXLAN iface. | |
269 | physdev = ifaceobj.get_attr_value_first('vxlan-physdev') | |
270 | ||
271 | if physdev: | |
272 | return [physdev] | |
273 | ||
d486dd0d JF |
274 | return None |
275 | ||
0500d5d8 JF |
276 | def __check_and_tag_l3vxi(self, ifaceobj): |
277 | if ifaceobj.get_attr_value_first("vxlan-vni"): | |
278 | # to validate the l3vxi interface we need to see the vrf attribute | |
279 | if ifaceobj.get_attr_value_first("vrf"): | |
280 | ifaceobj.link_privflags |= ifaceLinkPrivFlags.L3VXI | |
281 | else: | |
282 | self.logger.warning("%s: l3vxi misconfiguration? missing `vrf` attribute" % ifaceobj.name) | |
283 | ||
d486dd0d JF |
284 | def _set_global_local_ip(self, ifaceobj): |
285 | vxlan_local_tunnel_ip = ifaceobj.get_attr_value_first('vxlan-local-tunnelip') | |
223ba5af JF |
286 | if vxlan_local_tunnel_ip and not self._vxlan_local_tunnelip: |
287 | self._vxlan_local_tunnelip = vxlan_local_tunnel_ip | |
d486dd0d | 288 | |
223ba5af JF |
289 | @staticmethod |
290 | def _is_vxlan_device(ifaceobj): | |
e537a6e6 JF |
291 | return ifaceobj.link_kind & ifaceLinkKind.VXLAN \ |
292 | or ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN \ | |
0500d5d8 | 293 | or ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI \ |
e537a6e6 | 294 | or ifaceobj.get_attr_value_first("vxlan-id") \ |
0500d5d8 | 295 | or ifaceobj.get_attr_value_first("vxlan-vni") \ |
e537a6e6 | 296 | or ifaceobj.get_attr_value_first("bridge-vlan-vni-map") |
d486dd0d | 297 | |
223ba5af | 298 | def __get_vlxan_purge_remotes(self, ifaceobj): |
d486dd0d | 299 | if not ifaceobj: |
223ba5af | 300 | return self._vxlan_purge_remotes |
d486dd0d JF |
301 | purge_remotes = ifaceobj.get_attr_value_first('vxlan-purge-remotes') |
302 | if purge_remotes: | |
303 | purge_remotes = utils.get_boolean_from_string(purge_remotes) | |
304 | else: | |
223ba5af | 305 | purge_remotes = self._vxlan_purge_remotes |
d486dd0d JF |
306 | return purge_remotes |
307 | ||
ec25a08c JF |
308 | def get_vxlan_ttl_from_string(self, ttl_config): |
309 | ttl = 0 | |
310 | if ttl_config: | |
311 | if ttl_config.lower() == "auto": | |
312 | ttl = 0 | |
313 | else: | |
314 | ttl = int(ttl_config) | |
315 | return ttl | |
316 | ||
e521508b | 317 | def get_vxlan_tos_from_string(self, tos_config): |
e521508b SO |
318 | if tos_config: |
319 | if tos_config.lower() == "inherit": | |
a8dd54b0 | 320 | return 1 |
e521508b | 321 | else: |
a8dd54b0 JF |
322 | return int(tos_config) |
323 | return None | |
e521508b | 324 | |
223ba5af JF |
325 | def __config_vxlan_id(self, ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
326 | """ | |
327 | Get vxlan-id user config and check it's value before inserting it in our netlink dictionary | |
328 | :param ifname: | |
329 | :param ifaceobj: | |
330 | :param vxlan_id_str: | |
331 | :param user_request_vxlan_info_data: | |
332 | :param cached_vxlan_ifla_info_data: | |
333 | :return: | |
334 | """ | |
335 | try: | |
336 | vxlan_id = int(vxlan_id_str) | |
337 | cached_vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID) | |
338 | ||
339 | if cached_vxlan_id and cached_vxlan_id != vxlan_id: | |
340 | self.log_error( | |
341 | "%s: Cannot change running vxlan id (%s): Operation not supported" | |
342 | % (ifname, cached_vxlan_id), | |
343 | ifaceobj | |
344 | ) | |
345 | user_request_vxlan_info_data[Link.IFLA_VXLAN_ID] = vxlan_id | |
346 | except ValueError: | |
347 | self.log_error("%s: invalid vxlan-id '%s'" % (ifname, vxlan_id_str), ifaceobj) | |
348 | ||
349 | def __get_vxlan_ageing_int(self, ifname, ifaceobj, link_exists): | |
350 | """ | |
351 | Get vxlan-ageing user config or via policy, return integer value, None or raise on error | |
352 | :param ifname: | |
353 | :param ifaceobj: | |
354 | :param link_exists: | |
355 | :return: | |
356 | """ | |
357 | vxlan_ageing_str = ifaceobj.get_attr_value_first("vxlan-ageing") | |
358 | try: | |
359 | if vxlan_ageing_str: | |
360 | return int(vxlan_ageing_str) | |
361 | ||
362 | vxlan_ageing_str = policymanager.policymanager_api.get_attr_default( | |
363 | module_name=self.__class__.__name__, | |
364 | attr="vxlan-ageing" | |
365 | ) | |
366 | ||
367 | if not vxlan_ageing_str and link_exists: | |
368 | # if link doesn't exist we let the kernel define ageing | |
369 | vxlan_ageing_str = self.get_attr_default_value("vxlan-ageing") | |
370 | ||
371 | if vxlan_ageing_str: | |
372 | return int(vxlan_ageing_str) | |
3218f49d | 373 | except Exception: |
223ba5af | 374 | self.log_error("%s: invalid vxlan-ageing '%s'" % (ifname, vxlan_ageing_str), ifaceobj) |
d486dd0d | 375 | |
223ba5af JF |
376 | def __config_vxlan_ageing(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
377 | """ | |
378 | Check user config vxlan-ageing and insert it in our netlink dictionary if needed | |
379 | """ | |
380 | vxlan_ageing = self.__get_vxlan_ageing_int(ifname, ifaceobj, link_exists) | |
381 | ||
382 | if not vxlan_ageing or (link_exists and vxlan_ageing == cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_AGEING)): | |
383 | return | |
384 | ||
385 | self.logger.info("%s: set vxlan-ageing %s" % (ifname, vxlan_ageing)) | |
386 | user_request_vxlan_info_data[Link.IFLA_VXLAN_AGEING] = vxlan_ageing | |
387 | ||
388 | def __config_vxlan_port(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): | |
389 | """ | |
390 | Check vxlan-port user config, validate the integer value and insert it in the netlink dictionary if needed | |
391 | :param ifname: | |
392 | :param ifaceobj: | |
393 | :param link_exists: | |
394 | :param user_request_vxlan_info_data: | |
395 | :param cached_vxlan_ifla_info_data: | |
396 | :return: | |
397 | """ | |
398 | vxlan_port_str = ifaceobj.get_attr_value_first("vxlan-port") | |
399 | try: | |
400 | if not vxlan_port_str: | |
401 | vxlan_port_str = policymanager.policymanager_api.get_attr_default( | |
402 | module_name=self.__class__.__name__, | |
403 | attr="vxlan-port" | |
404 | ) | |
d486dd0d | 405 | |
ec25a08c | 406 | try: |
223ba5af JF |
407 | vxlan_port = int(vxlan_port_str) |
408 | except TypeError: | |
409 | # TypeError means vxlan_port was None | |
410 | # ie: not provided by the user or the policy | |
411 | vxlan_port = self.netlink.VXLAN_UDP_PORT | |
412 | except ValueError as e: | |
413 | self.logger.warning( | |
414 | "%s: vxlan-port: using default %s: invalid configured value %s" | |
415 | % (ifname, self.netlink.VXLAN_UDP_PORT, str(e)) | |
416 | ) | |
417 | vxlan_port = self.netlink.VXLAN_UDP_PORT | |
418 | ||
419 | cached_vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT) | |
420 | ||
421 | if link_exists: | |
422 | if vxlan_port != cached_vxlan_port: | |
423 | self.logger.warning( | |
424 | "%s: vxlan-port (%s) cannot be changed - to apply the desired change please run: ifdown %s && ifup %s" | |
425 | % (ifname, cached_vxlan_port, ifname, ifname) | |
ec25a08c | 426 | ) |
ec25a08c JF |
427 | return |
428 | ||
223ba5af JF |
429 | self.logger.info("%s: set vxlan-port %s" % (ifname, vxlan_port)) |
430 | user_request_vxlan_info_data[Link.IFLA_VXLAN_PORT] = vxlan_port | |
3218f49d | 431 | except Exception: |
223ba5af JF |
432 | self.log_error("%s: invalid vxlan-port '%s'" % (ifname, vxlan_port_str), ifaceobj) |
433 | ||
e521508b SO |
434 | def __config_vxlan_tos(self, ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
435 | """ | |
436 | Get vxlan-tos from user config or policy, validate integer value and insert in netlink dict | |
437 | :param ifname: | |
438 | :param ifaceobj: | |
439 | :param user_request_vxlan_info_data: | |
440 | :param cached_vxlan_ifla_info_data: | |
441 | :return: | |
442 | """ | |
443 | vxlan_tos_str = ifaceobj.get_attr_value_first("vxlan-tos") | |
444 | try: | |
445 | if vxlan_tos_str: | |
446 | vxlan_tos = self.get_vxlan_tos_from_string(vxlan_tos_str) | |
447 | else: | |
448 | vxlan_tos = self.get_vxlan_tos_from_string( | |
449 | policymanager.policymanager_api.get_attr_default( | |
450 | module_name=self.__class__.__name__, | |
451 | attr="vxlan-tos" | |
452 | ) | |
453 | ) | |
454 | ||
a8dd54b0 JF |
455 | if not vxlan_tos_str: |
456 | return | |
457 | ||
e521508b | 458 | cached_ifla_vxlan_tos = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_TOS) |
a8dd54b0 | 459 | |
e521508b SO |
460 | if vxlan_tos != cached_ifla_vxlan_tos: |
461 | ||
462 | if cached_ifla_vxlan_tos is not None: | |
463 | self.logger.info("%s: set vxlan-tos %s (cache %s)" % (ifname, vxlan_tos_str if vxlan_tos_str else vxlan_tos, cached_ifla_vxlan_tos)) | |
464 | else: | |
465 | self.logger.info("%s: set vxlan-tos %s" % (ifname, vxlan_tos_str if vxlan_tos_str else vxlan_tos)) | |
466 | ||
467 | user_request_vxlan_info_data[Link.IFLA_VXLAN_TOS] = vxlan_tos | |
468 | except Exception: | |
469 | self.log_error("%s: invalid vxlan-tos '%s'" % (ifname, vxlan_tos_str), ifaceobj) | |
470 | ||
223ba5af JF |
471 | def __config_vxlan_ttl(self, ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
472 | """ | |
473 | Get vxlan-ttl from user config or policy, validate integer value and insert in netlink dict | |
474 | :param ifname: | |
475 | :param ifaceobj: | |
476 | :param user_request_vxlan_info_data: | |
477 | :param cached_vxlan_ifla_info_data: | |
478 | :return: | |
479 | """ | |
480 | vxlan_ttl_str = ifaceobj.get_attr_value_first("vxlan-ttl") | |
481 | try: | |
482 | if vxlan_ttl_str: | |
483 | vxlan_ttl = self.get_vxlan_ttl_from_string(vxlan_ttl_str) | |
484 | else: | |
485 | vxlan_ttl = self.get_vxlan_ttl_from_string( | |
486 | policymanager.policymanager_api.get_attr_default( | |
487 | module_name=self.__class__.__name__, | |
488 | attr="vxlan-ttl" | |
489 | ) | |
490 | ) | |
491 | ||
492 | cached_ifla_vxlan_ttl = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_TTL) | |
493 | if vxlan_ttl != cached_ifla_vxlan_ttl: | |
494 | ||
495 | if cached_ifla_vxlan_ttl is not None: | |
496 | 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)) | |
497 | else: | |
498 | self.logger.info("%s: set vxlan-ttl %s" % (ifname, vxlan_ttl_str if vxlan_ttl_str else vxlan_ttl)) | |
499 | ||
500 | user_request_vxlan_info_data[Link.IFLA_VXLAN_TTL] = vxlan_ttl | |
9144496d | 501 | return vxlan_ttl |
3218f49d | 502 | except Exception: |
223ba5af JF |
503 | self.log_error("%s: invalid vxlan-ttl '%s'" % (ifname, vxlan_ttl_str), ifaceobj) |
504 | ||
59ab29fb JF |
505 | def is_vxlan_on_a_clag_bridge(self, ifaceobj) -> bool: |
506 | return bool(ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT and self._clagd_vxlan_anycast_ip and self.is_process_running('clagd')) | |
507 | ||
223ba5af JF |
508 | def __config_vxlan_local_tunnelip(self, ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
509 | """ | |
510 | Get vxlan-local-tunnelip user config or policy, validate ip address format and insert in netlink dict | |
511 | :param ifname: | |
512 | :param ifaceobj: | |
513 | :param link_exists: | |
514 | :param user_request_vxlan_info_data: | |
515 | :param cached_vxlan_ifla_info_data: | |
516 | :return: | |
517 | """ | |
518 | local = ifaceobj.get_attr_value_first("vxlan-local-tunnelip") | |
519 | ||
520 | if not local and self._vxlan_local_tunnelip: | |
521 | local = self._vxlan_local_tunnelip | |
522 | ||
523 | if link_exists: | |
59ab29fb JF |
524 | cached_ifla_vxlan_local = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL) |
525 | ||
223ba5af JF |
526 | # on ifreload do not overwrite anycast_ip to individual ip |
527 | # if clagd has modified | |
59ab29fb | 528 | if self._clagd_vxlan_anycast_ip and cached_ifla_vxlan_local: |
0e936c3f | 529 | anycastip = ipnetwork.IPNetwork(self._clagd_vxlan_anycast_ip) |
59ab29fb JF |
530 | |
531 | if ( | |
532 | anycastip == cached_ifla_vxlan_local | |
533 | # there's a change that the cache hasn't been updated in | |
534 | # time to reflect the new anycast ip set by clagd, extra checks: | |
535 | or self.is_vxlan_on_a_clag_bridge(ifaceobj) | |
536 | ): | |
537 | local = cached_ifla_vxlan_local = anycastip | |
538 | self.logger.info("%s: clagd-vxlan-anycast-ip (%s) inherited from loopback interface" % (ifname, local)) | |
539 | else: | |
540 | cached_ifla_vxlan_local = None | |
223ba5af JF |
541 | |
542 | if not local: | |
543 | local = policymanager.policymanager_api.get_attr_default( | |
544 | module_name=self.__class__.__name__, | |
545 | attr="vxlan-local-tunnelip" | |
546 | ) | |
547 | ||
548 | if local: | |
549 | try: | |
0e936c3f JF |
550 | local = ipnetwork.IPv4Address(local) |
551 | ||
552 | if local.initialized_with_prefixlen: | |
223ba5af | 553 | self.logger.warning("%s: vxlan-local-tunnelip %s: netmask ignored" % (ifname, local)) |
0e936c3f JF |
554 | |
555 | except Exception as e: | |
556 | raise Exception("%s: invalid vxlan-local-tunnelip %s: %s" % (ifname, local, str(e))) | |
223ba5af | 557 | |
223ba5af JF |
558 | |
559 | if local: | |
560 | if local != cached_ifla_vxlan_local: | |
561 | self.logger.info("%s: set vxlan-local-tunnelip %s" % (ifname, local)) | |
562 | user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = local | |
563 | ||
564 | # if both local-ip and anycast-ip are identical the function prints a warning | |
565 | self.syntax_check_localip_anycastip_equal(ifname, local, self._clagd_vxlan_anycast_ip) | |
566 | elif cached_ifla_vxlan_local: | |
567 | self.logger.info("%s: removing vxlan-local-tunnelip (cache %s)" % (ifname, cached_ifla_vxlan_local)) | |
568 | user_request_vxlan_info_data[Link.IFLA_VXLAN_LOCAL] = None | |
569 | ||
570 | return local | |
571 | ||
40658337 JF |
572 | def __get_vxlan_attribute(self, ifaceobj, attr_name): |
573 | vxlan_attr_value = ifaceobj.get_attr_value_first(attr_name) | |
d486dd0d | 574 | |
40658337 JF |
575 | if not vxlan_attr_value: |
576 | vxlan_attr_value = policymanager.policymanager_api.get_attr_default( | |
223ba5af | 577 | module_name=self.__class__.__name__, |
40658337 | 578 | attr=attr_name |
223ba5af | 579 | ) |
d486dd0d | 580 | |
40658337 | 581 | return vxlan_attr_value |
d486dd0d | 582 | |
6015cce2 QZ |
583 | def __syntax_check_vxlan_mcast_vni(self, ifaceobj, m, vni): |
584 | try: | |
585 | int(vni) | |
586 | except ValueError: | |
587 | self.log_error('%s: vxlan-mcastgrp-map "%s" vni format is invalid' % (ifaceobj.name, m)) | |
588 | ||
589 | def __syntax_check_vxlan_mcast_vni_range(self, ifaceobj, m, vni_range): | |
590 | if len(vni_range) != 2: | |
591 | self.log_error('%s: vxlan-mcastgrp-map "%s" vni range format is invalid' % (ifaceobj.name, m)) | |
592 | for vni in vni_range: | |
593 | self.__syntax_check_vxlan_mcast_vni(ifaceobj, m, vni) | |
594 | if int(vni_range[0]) >= int(vni_range[1]): | |
595 | self.log_error('%s: vxlan-mcastgrp-map "%s" vni range is invalid' % (ifaceobj.name, m)) | |
596 | ||
597 | def __syntax_check_vxlan_mcast_grp(self, ifaceobj, m, grp): | |
598 | try: | |
599 | ip = IPv4Address(grp) | |
600 | except AddressValueError: | |
601 | self.log_error('%s: vxlan-mcastgrp-map "%s" group format is invalid' % (ifaceobj.name, m)) | |
602 | if not ip.is_multicast: | |
603 | self.log_error('%s: vxlan-mcastgrp-map "%s" group is not multicast' % (ifaceobj.name, m)) | |
604 | ||
605 | def __syntax_check_vxlan_mcast_grp_range(self, ifaceobj, m, grp_range): | |
606 | if len(grp_range) != 2: | |
607 | self.log_error('%s: vxlan-mcastgrp-map "%s" group format is invalid' % (ifaceobj.name, m)) | |
608 | for grp in grp_range: | |
609 | self.__syntax_check_vxlan_mcast_grp(ifaceobj, m, grp) | |
610 | if int(IPv4Address(grp_range[0])) >= int(IPv4Address(grp_range[1])): | |
611 | self.log_error('%s: vxlan-mcastgrp-map "%s" group range is invalid' % (ifaceobj.name, m)) | |
612 | ||
613 | def __syntax_check_vxlan_mcast_network(self, ifaceobj, m, network, len_vni): | |
614 | try: | |
615 | ip = IPv4Network(network) | |
616 | ip[0] | |
617 | ip[len_vni - 1] | |
618 | except IndexError: | |
619 | self.log_error('%s: vxlan-mcastgrp-map "%s" network range is insufficient' % (ifaceobj.name, m)) | |
620 | except AddressValueError: | |
621 | self.log_error('%s: vxlan-mcastgrp-map "%s" network format is invalid' % (ifaceobj.name, m)) | |
622 | if not ip.is_multicast: | |
623 | self.log_error('%s: vxlan-mcastgrp-map "%s" network is not multicast' % (ifaceobj.name, m)) | |
624 | ||
625 | def __get_vxlan_mcastgrp_map(self, ifaceobj): | |
626 | maps = ifaceobj.get_attr_value('vxlan-mcastgrp-map') | |
627 | if not maps: | |
628 | maps = policymanager.policymanager_api.get_attr_default( | |
629 | module_name=self.__class__.__name__, | |
630 | attr='vxlan-mcastgrp-map' | |
631 | ) | |
632 | return maps | |
633 | ||
634 | parsed_maps = {} | |
635 | for m_line in maps: | |
636 | # Cover single-line multi-entry case | |
637 | map = m_line.split() | |
638 | for m in map: | |
639 | m_parts = m.split('=') | |
640 | if len(m_parts) != 2: | |
641 | self.log_error('%s: vxlan-mcastgrp-map %s format is invalid' % (ifaceobj.name, m)) | |
642 | vni = m_parts[0] | |
643 | grp = m_parts[1] | |
644 | _range = "-" | |
645 | _network = "/" | |
646 | ||
647 | # One to one mapping case | |
648 | if _range not in vni and _range not in grp: | |
649 | self.__syntax_check_vxlan_mcast_vni(ifaceobj, m, vni) | |
650 | self.__syntax_check_vxlan_mcast_grp(ifaceobj, m, grp) | |
651 | if int(vni) not in parsed_maps: | |
652 | parsed_maps[int(vni)] = IPv4Address(grp) | |
653 | else: | |
654 | self.log_warn('%s: vxlan-mcastgrp-map %s vni %s duplicate' % (ifaceobj.name, vni, m)) | |
655 | ||
656 | # Many VNI case | |
657 | if _range in vni: | |
658 | v_parts = vni.split(_range) | |
659 | self.__syntax_check_vxlan_mcast_vni_range(ifaceobj, m, v_parts) | |
660 | vnis = list(range(int(v_parts[0]), int(v_parts[1]) + 1)) | |
661 | ||
662 | if _range not in grp and _network not in grp: | |
663 | self.__syntax_check_vxlan_mcast_grp(ifaceobj, m, grp) | |
664 | for i in vnis: | |
665 | if i not in parsed_maps: | |
666 | parsed_maps[i] = IPv4Address(grp) | |
667 | else: | |
668 | self.log_warn('%s: vxlan-mcastgrp-map %s vni %s duplicate' % (ifaceobj.name, vni, m)) | |
669 | else: | |
670 | if _network in grp: | |
671 | self.__syntax_check_vxlan_mcast_network(ifaceobj, m, grp, len(vnis)) | |
672 | network = IPv4Network(grp) | |
673 | g_parts = [network[0], network[len(vnis) - 1]] | |
674 | else: | |
675 | g_parts = grp.split(_range) | |
676 | ||
677 | self.__syntax_check_vxlan_mcast_grp_range(ifaceobj, m, g_parts) | |
678 | grp_range = list(range(int(IPv4Address(g_parts[0])), int(IPv4Address(g_parts[1])) + 1)) | |
679 | if len(grp_range) != len(vnis): | |
680 | self.log_error('%s: vxlan-mcastgrp-map "%s" range lengths do not match.' | |
681 | % (ifaceobj.name, m)) | |
682 | ||
683 | for v, g in zip(vnis, grp_range): | |
684 | if v not in parsed_maps: | |
685 | parsed_maps[v] = IPv4Address(g) | |
686 | else: | |
687 | self.log_warn('%s: vxlan-mcastgrp-map %s vni %s duplicate' % (ifaceobj.name, v, m)) | |
688 | ||
689 | return parsed_maps | |
690 | ||
223ba5af JF |
691 | def __config_vxlan_group(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
692 | """ | |
693 | vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive | |
694 | this function validates ip format for both attribute and tries to understand | |
695 | what the user really want (remote or group option). | |
696 | ||
697 | :param ifname: | |
698 | :param ifaceobj: | |
699 | :param mcast_grp: | |
700 | :param group: | |
701 | :param physdev: | |
702 | :param user_request_vxlan_info_data: | |
703 | :param cached_vxlan_ifla_info_data: | |
704 | :return: | |
705 | """ | |
706 | if mcast_grp and group: | |
707 | self.log_error("%s: both group (vxlan-mcastgrp %s) and " | |
708 | "remote (vxlan-svcnodeip %s) cannot be specified" | |
709 | % (ifname, mcast_grp, group), ifaceobj) | |
710 | ||
711 | attribute_name = "vxlan-svcnodeip" | |
712 | multicast_group_change = False | |
713 | ||
714 | if group: | |
715 | try: | |
0e936c3f JF |
716 | group = ipnetwork.IPv4Address(group) |
717 | ||
718 | if group.initialized_with_prefixlen: | |
223ba5af | 719 | self.logger.warning("%s: vxlan-svcnodeip %s: netmask ignored" % (ifname, group)) |
223ba5af | 720 | |
0e936c3f JF |
721 | except Exception as e: |
722 | raise Exception("%s: invalid vxlan-svcnodeip %s: %s" % (ifname, group, str(e))) | |
723 | ||
724 | if group.ip.is_multicast: | |
223ba5af JF |
725 | self.logger.warning("%s: vxlan-svcnodeip %s: invalid group address, " |
726 | "for multicast IP please use attribute \"vxlan-mcastgrp\"" % (ifname, group)) | |
727 | # if svcnodeip is used instead of mcastgrp we warn the user | |
728 | # if mcast_grp is not provided by the user we can instead | |
729 | # use the svcnodeip value | |
730 | if not physdev: | |
731 | self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname)) | |
732 | ||
733 | elif mcast_grp: | |
734 | try: | |
0e936c3f JF |
735 | mcast_grp = ipnetwork.IPv4Address(mcast_grp) |
736 | ||
737 | if mcast_grp.initialized_with_prefixlen: | |
223ba5af | 738 | self.logger.warning("%s: vxlan-mcastgrp %s: netmask ignored" % (ifname, mcast_grp)) |
223ba5af | 739 | |
0e936c3f JF |
740 | except Exception as e: |
741 | raise Exception("%s: invalid vxlan-mcastgrp %s: %s" % (ifname, mcast_grp, str(e))) | |
742 | ||
743 | if not mcast_grp.ip.is_multicast: | |
223ba5af JF |
744 | self.logger.warning("%s: vxlan-mcastgrp %s: invalid group address, " |
745 | "for non-multicast IP please use attribute \"vxlan-svcnodeip\"" | |
746 | % (ifname, mcast_grp)) | |
747 | # if mcastgrp is specified with a non-multicast address | |
748 | # we warn the user. If the svcnodeip wasn't specified by | |
749 | # the user we can use the mcastgrp value as svcnodeip | |
750 | if not group: | |
751 | group = mcast_grp | |
752 | mcast_grp = None | |
d486dd0d | 753 | else: |
223ba5af JF |
754 | attribute_name = "vxlan-mcastgrp" |
755 | ||
756 | if mcast_grp: | |
757 | group = mcast_grp | |
758 | ||
759 | if not physdev: | |
760 | self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp) requires 'vxlan-physdev' to be specified" % (ifname)) | |
761 | ||
762 | cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP) | |
763 | ||
764 | if group != cached_ifla_vxlan_group: | |
765 | ||
766 | if not group: | |
0e936c3f | 767 | group = ipnetwork.IPNetwork("0.0.0.0") |
223ba5af JF |
768 | attribute_name = "vxlan-svcnodeip/vxlan-mcastgrp" |
769 | ||
770 | self.logger.info("%s: set %s %s" % (ifname, attribute_name, group)) | |
771 | user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP] = group | |
772 | ||
773 | # if the mcastgrp address is changed we need to signal this to the upper function | |
774 | # in this case vxlan needs to be down before applying changes then up'd | |
775 | multicast_group_change = True | |
d486dd0d JF |
776 | |
777 | if link_exists: | |
223ba5af JF |
778 | if cached_ifla_vxlan_group: |
779 | self.logger.info( | |
780 | "%s: vxlan-mcastgrp configuration changed (cache %s): flapping vxlan device required" | |
781 | % (ifname, cached_ifla_vxlan_group) | |
782 | ) | |
b067bba9 | 783 | else: |
223ba5af JF |
784 | self.logger.info( |
785 | "%s: vxlan-mcastgrp configuration changed: flapping vxlan device required" % ifname | |
786 | ) | |
b067bba9 | 787 | |
223ba5af | 788 | return group, multicast_group_change |
d486dd0d | 789 | |
40658337 JF |
790 | def __config_vxlan_group6(self, ifname, ifaceobj, link_exists, mcast_grp, group, physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
791 | """ | |
792 | vxlan-mcastgrp and vxlan-svcnodeip are mutually exclusive | |
793 | this function validates ip format for both attribute and tries to understand | |
794 | what the user really want (remote or group option). | |
795 | ||
796 | :param ifname: | |
797 | :param ifaceobj: | |
798 | :param mcast_grp: | |
799 | :param group: | |
800 | :param physdev: | |
801 | :param user_request_vxlan_info_data: | |
802 | :param cached_vxlan_ifla_info_data: | |
803 | :return: | |
804 | """ | |
805 | if mcast_grp and group: | |
806 | self.log_error("%s: both group (vxlan-mcastgrp6 %s) and " | |
807 | "remote (vxlan-svcnodeip6 %s) cannot be specified" | |
808 | % (ifname, mcast_grp, group), ifaceobj) | |
809 | ||
810 | attribute_name = "vxlan-svcnodeip6" | |
811 | multicast_group_change = False | |
812 | ||
813 | if group: | |
814 | try: | |
815 | group = ipnetwork.IPv6Address(group) | |
816 | except Exception: | |
817 | try: | |
818 | group_ip = ipnetwork.IPv6Network(group).ip | |
819 | self.logger.warning("%s: vxlan-svcnodeip6 %s: netmask ignored" % (ifname, group)) | |
820 | group = group_ip | |
1b7f1f34 | 821 | except Exception: |
40658337 JF |
822 | raise Exception("%s: invalid vxlan-svcnodeip6 %s: must be in ipv4 format" % (ifname, group)) |
823 | ||
824 | if group.is_multicast: | |
825 | self.logger.warning("%s: vxlan-svcnodeip6 %s: invalid group address, " | |
826 | "for multicast IP please use attribute \"vxlan-mcastgrp6\"" % (ifname, group)) | |
827 | # if svcnodeip is used instead of mcastgrp we warn the user | |
828 | # if mcast_grp is not provided by the user we can instead | |
829 | # use the svcnodeip value | |
830 | if not physdev: | |
831 | self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp6) requires 'vxlan-physdev' to be specified" % (ifname)) | |
832 | ||
833 | elif mcast_grp: | |
834 | try: | |
835 | mcast_grp = ipnetwork.IPv6Address(mcast_grp) | |
836 | except Exception: | |
837 | try: | |
838 | group_ip = ipnetwork.IPv6Network(mcast_grp).ip | |
839 | self.logger.warning("%s: vxlan-mcastgrp6 %s: netmask ignored" % (ifname, mcast_grp)) | |
840 | mcast_grp = group_ip | |
1b7f1f34 | 841 | except Exception: |
40658337 JF |
842 | raise Exception("%s: invalid vxlan-mcastgrp6 %s: must be in ipv4 format" % (ifname, mcast_grp)) |
843 | ||
844 | if not mcast_grp.is_multicast: | |
845 | self.logger.warning("%s: vxlan-mcastgrp6 %s: invalid group address, " | |
846 | "for non-multicast IP please use attribute \"vxlan-svcnodeip6\"" | |
847 | % (ifname, mcast_grp)) | |
848 | # if mcastgrp is specified with a non-multicast address | |
849 | # we warn the user. If the svcnodeip wasn't specified by | |
850 | # the user we can use the mcastgrp value as svcnodeip | |
851 | if not group: | |
852 | group = mcast_grp | |
853 | mcast_grp = None | |
854 | else: | |
855 | attribute_name = "vxlan-mcastgrp6" | |
856 | ||
857 | if mcast_grp: | |
858 | group = mcast_grp | |
859 | ||
860 | if not physdev: | |
861 | self.log_error("%s: vxlan: 'group' (vxlan-mcastgrp6) requires 'vxlan-physdev' to be specified" % (ifname)) | |
862 | ||
863 | cached_ifla_vxlan_group = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP6) | |
864 | ||
865 | if group != cached_ifla_vxlan_group: | |
866 | ||
867 | if not group: | |
868 | group = ipnetwork.IPNetwork("::0", family=6) | |
869 | attribute_name = "vxlan-svcnodeip6/vxlan-mcastgrp6" | |
870 | ||
871 | self.logger.info("%s: set %s %s" % (ifname, attribute_name, group)) | |
872 | user_request_vxlan_info_data[Link.IFLA_VXLAN_GROUP6] = group | |
873 | ||
874 | # if the mcastgrp address is changed we need to signal this to the upper function | |
875 | # in this case vxlan needs to be down before applying changes then up'd | |
876 | multicast_group_change = True | |
877 | ||
878 | if link_exists: | |
879 | if cached_ifla_vxlan_group: | |
880 | self.logger.info( | |
881 | "%s: vxlan-mcastgrp6 configuration changed (cache %s): flapping vxlan device required" | |
882 | % (ifname, cached_ifla_vxlan_group) | |
883 | ) | |
884 | else: | |
885 | self.logger.info( | |
886 | "%s: vxlan-mcastgrp6 configuration changed: flapping vxlan device required" % ifname | |
887 | ) | |
888 | ||
889 | return group, multicast_group_change | |
890 | ||
223ba5af JF |
891 | def __config_vxlan_learning(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
892 | if not link_exists or not ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT: | |
893 | vxlan_learning = ifaceobj.get_attr_value_first('vxlan-learning') | |
894 | if not vxlan_learning: | |
895 | vxlan_learning = self.get_attr_default_value('vxlan-learning') | |
896 | vxlan_learning = utils.get_boolean_from_string(vxlan_learning) | |
897 | else: | |
898 | vxlan_learning = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING) | |
d486dd0d | 899 | |
223ba5af JF |
900 | if vxlan_learning != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LEARNING): |
901 | self.logger.info("%s: set vxlan-learning %s" % (ifaceobj.name, "on" if vxlan_learning else "off")) | |
902 | user_request_vxlan_info_data[Link.IFLA_VXLAN_LEARNING] = vxlan_learning | |
903 | ||
a8dd54b0 | 904 | def __config_vxlan_udp_csum(self, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): |
e521508b | 905 | vxlan_udp_csum = ifaceobj.get_attr_value_first('vxlan-udp-csum') |
a8dd54b0 JF |
906 | |
907 | if not vxlan_udp_csum: | |
908 | vxlan_udp_csum = policymanager.policymanager_api.get_attr_default( | |
909 | module_name=self.__class__.__name__, | |
910 | attr="vxlan-udp-csum" | |
911 | ) | |
912 | ||
913 | if not vxlan_udp_csum and not link_exists: | |
914 | return | |
915 | ||
e521508b SO |
916 | if not vxlan_udp_csum: |
917 | vxlan_udp_csum = self.get_attr_default_value('vxlan-udp-csum') | |
e521508b | 918 | |
a8dd54b0 JF |
919 | if vxlan_udp_csum: |
920 | vxlan_udp_csum = utils.get_boolean_from_string(vxlan_udp_csum) | |
921 | else: | |
922 | return | |
e521508b SO |
923 | |
924 | if vxlan_udp_csum != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_UDP_CSUM): | |
925 | self.logger.info("%s: set vxlan-udp-csum %s" % (ifaceobj.name, "on" if vxlan_udp_csum else "off")) | |
926 | user_request_vxlan_info_data[Link.IFLA_VXLAN_UDP_CSUM] = vxlan_udp_csum | |
927 | ||
1609696f | 928 | def __get_vxlan_physdev(self, ifaceobj, mcastgrp, mcastgrp_map): |
223ba5af JF |
929 | """ |
930 | vxlan-physdev wrapper, special handling is required for mcastgrp is provided | |
931 | the vxlan needs to use a dummy or real device for tunnel endpoint communication | |
932 | This wrapper will get the physdev from user config or policy. IF the device | |
933 | doesnt exists we create a dummy device. | |
934 | ||
935 | :param ifaceobj: | |
936 | :param mcastgrp: | |
937 | :return physdev: | |
938 | """ | |
939 | physdev = ifaceobj.get_attr_value_first("vxlan-physdev") | |
d486dd0d | 940 | |
223ba5af JF |
941 | # if the user provided a physdev we need to honor his config |
942 | # or if mcastgrp wasn't specified we don't need to go further | |
1609696f | 943 | if physdev or (not mcastgrp and not mcastgrp_map): |
223ba5af JF |
944 | return physdev |
945 | ||
946 | physdev = self.vxlan_physdev_mcast | |
947 | ||
948 | if not self.cache.link_exists(physdev): | |
1609696f JF |
949 | if mcastgrp_map: |
950 | self.logger.info("%s: needs a dummy device (%s) to use for " | |
951 | "multicast termination (vxlan-mcastgrp-map %s)" | |
952 | % (ifaceobj.name, physdev, mcastgrp)) | |
953 | else: | |
954 | self.logger.info("%s: needs a dummy device (%s) to use for " | |
955 | "multicast termination (vxlan-mcastgrp %s)" | |
956 | % (ifaceobj.name, physdev, mcastgrp)) | |
223ba5af JF |
957 | self.netlink.link_add_with_attributes(ifname=physdev, kind="dummy", ifla={Link.IFLA_MTU: 16000, Link.IFLA_LINKMODE: 1}) |
958 | self.netlink.link_up(physdev) | |
959 | ||
960 | return physdev | |
961 | ||
962 | def __config_vxlan_physdev(self, link_exists, ifaceobj, vxlan_physdev, user_request_vxlan_info_data, cached_vxlan_ifla_info_data): | |
963 | if vxlan_physdev: | |
964 | try: | |
965 | vxlan_physdev_ifindex = self.cache.get_ifindex(vxlan_physdev) | |
966 | except NetlinkCacheIfnameNotFoundError: | |
d486dd0d | 967 | try: |
223ba5af | 968 | vxlan_physdev_ifindex = int(self.sysfs.read_file_oneline("/sys/class/net/%s/ifindex" % vxlan_physdev)) |
3218f49d | 969 | except Exception: |
223ba5af JF |
970 | self.logger.error("%s: physdev %s doesn't exists" % (ifaceobj.name, vxlan_physdev)) |
971 | return | |
d486dd0d | 972 | |
223ba5af JF |
973 | if vxlan_physdev_ifindex != cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LINK): |
974 | self.logger.info("%s: set vxlan-physdev %s" % (ifaceobj.name, vxlan_physdev)) | |
975 | user_request_vxlan_info_data[Link.IFLA_VXLAN_LINK] = vxlan_physdev_ifindex | |
d486dd0d | 976 | |
223ba5af JF |
977 | # if the vxlan exists we need to return True, meaning that the vxlan |
978 | # needs to be flapped because we detected a vxlan-physdev change | |
979 | if link_exists: | |
980 | self.logger.info("%s: vxlan-physdev configuration changed: flapping vxlan device required" % ifaceobj.name) | |
981 | return True | |
982 | ||
983 | return False | |
984 | ||
66eb9ce3 JF |
985 | def __get_vxlan_remote_ip_map(self, ifaceobj): |
986 | attr_name = "vxlan-remoteip-map" | |
987 | ||
988 | maps = ifaceobj.get_attr_value(attr_name) | |
989 | if not maps: | |
990 | maps = policymanager.policymanager_api.get_attr_default( | |
991 | module_name=self.__class__.__name__, | |
992 | attr=attr_name | |
993 | ) | |
238e0485 JF |
994 | if not maps: |
995 | return {} | |
66eb9ce3 JF |
996 | |
997 | parsed_maps = {} | |
998 | for m_line in maps: | |
999 | # Cover single-line multi-entry case | |
1000 | map = m_line.split() | |
1001 | for m in map: | |
1002 | m_parts = m.split('=') | |
1003 | if len(m_parts) != 2: | |
1004 | self.log_error('%s: %s %s format is invalid' % (ifaceobj.name, attr_name, m)) | |
1005 | ||
1006 | vnis = m_parts[0] | |
1007 | _range = "-" | |
1008 | remote_ips = [] | |
1009 | ||
1010 | for config_remote_ip in m_parts[1].split(","): | |
1011 | if _range in config_remote_ip: | |
1012 | ip_range = config_remote_ip.split("-") | |
1013 | try: | |
1014 | start = ip_address(ip_range[0]) | |
1015 | end = ip_address(ip_range[1]) | |
1016 | except Exception as e: | |
1017 | self.log_error("%s: %s: invalid ip range '%s': %s" % (ifaceobj.name, attr_name, config_remote_ip, e), ifaceobj) | |
1018 | return | |
1019 | remote_ips.extend([ipnetwork.ip_address(i) for i in range(int(start), int(end) + 1)]) | |
1020 | else: | |
1021 | remote_ips.append(ipnetwork.ip_address(config_remote_ip)) | |
1022 | ||
1023 | # vxlan-remoteip-map 42,84,1000-1005=10.0.0.1,10.0.0.42-45,222.0.0.1-5 | |
1024 | # higher priority is the comma | |
1025 | for vni in utils.ranges_to_ints(vnis.split(",")) or []: | |
1026 | parsed_maps.setdefault(vni, []).extend(remote_ips) | |
1027 | ||
1028 | return parsed_maps | |
1029 | ||
20aabf55 | 1030 | def single_vxlan_device_vni_filter(self, ifaceobj, vxlan_mcast_grp): |
af8d5db2 | 1031 | vnisd = {} |
84c47c4f | 1032 | for vlan_vni_map in ifaceobj.get_attr_value("bridge-vlan-vni-map"): |
7f0310a7 RP |
1033 | try: |
1034 | (vls, vis) = utils.get_vlan_vnis_in_map(vlan_vni_map) | |
af8d5db2 RP |
1035 | for v in utils.ranges_to_ints(vis): |
1036 | vnisd[v] = None | |
1037 | except Exception as e: | |
1038 | self.logger.error("%s: %s (%s)" %(ifaceobj.name, vlan_vni_map, str(e))) | |
1039 | return | |
20aabf55 | 1040 | if vxlan_mcast_grp: |
af8d5db2 | 1041 | try: |
20aabf55 | 1042 | for v, g in vxlan_mcast_grp.items(): |
af8d5db2 | 1043 | if v not in vnisd.keys(): |
b3a93dfc JF |
1044 | self.logger.error("%s: group %s configured for a vni (%s) not specified in vlan vni map" |
1045 | %(ifaceobj.name, g, v)) | |
af8d5db2 | 1046 | return |
20aabf55 | 1047 | vnisd[v] = str(g) |
7f0310a7 RP |
1048 | except Exception as e: |
1049 | self.logger.error("%s: %s (%s)" %(ifaceobj.name, vlan_vni_map, str(e))) | |
1050 | return | |
af8d5db2 RP |
1051 | |
1052 | vnis_int = utils.ranges_to_ints(vnis) | |
1053 | self.iproute2.bridge_link_update_vni_filter(ifaceobj.name, vnisd) | |
84c47c4f | 1054 | |
db4371de JF |
1055 | def check_and_raise_svd_tvd_errors(self, ifaceobj): |
1056 | err = self.svd_tvd_errors.get(ifaceobj.name) | |
1057 | ||
1058 | if err: | |
1059 | self.log_error(err, ifaceobj) | |
1060 | ||
0500d5d8 JF |
1061 | def __get_vxlan_vni_list(self, ifaceobj, string=True): |
1062 | vxlan_vni_str = self.__get_vxlan_attribute(ifaceobj, "vxlan-vni") | |
1063 | ||
1064 | if vxlan_vni_str: | |
1065 | # validate range but return string to be used in bridge vni add cmd | |
1066 | vxlan_vni_range = utils.ranges_to_ints(vxlan_vni_str.split()) | |
1067 | return vxlan_vni_str if string else vxlan_vni_range | |
1068 | ||
1069 | return None | |
db4371de | 1070 | |
223ba5af | 1071 | def _up(self, ifaceobj): |
db4371de JF |
1072 | self.check_and_raise_svd_tvd_errors(ifaceobj) |
1073 | ||
223ba5af JF |
1074 | vxlan_id_str = ifaceobj.get_attr_value_first("vxlan-id") |
1075 | ||
0500d5d8 | 1076 | if not ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN and not ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI and not vxlan_id_str: |
e537a6e6 | 1077 | self.logger.warning("%s: missing vxlan-id attribute on vxlan device" % ifaceobj.name) |
223ba5af JF |
1078 | return |
1079 | ||
1080 | ifname = ifaceobj.name | |
1081 | link_exists = self.cache.link_exists(ifname) | |
1082 | ||
1083 | if link_exists: | |
1084 | # if link already exists make sure this is a vxlan | |
1085 | device_link_kind = self.cache.get_link_kind(ifname) | |
1086 | ||
1087 | if device_link_kind != "vxlan": | |
1088 | self.logger.error( | |
1089 | "%s: device already exists and is not a vxlan (type %s)" | |
1090 | % (ifname, device_link_kind) | |
d486dd0d | 1091 | ) |
223ba5af JF |
1092 | ifaceobj.set_status(ifaceStatus.ERROR) |
1093 | return | |
d486dd0d | 1094 | |
223ba5af JF |
1095 | # get vxlan running attributes |
1096 | cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname) | |
1097 | else: | |
1098 | cached_vxlan_ifla_info_data = {} | |
d486dd0d | 1099 | |
223ba5af | 1100 | user_request_vxlan_info_data = {} |
d486dd0d | 1101 | |
e537a6e6 JF |
1102 | if vxlan_id_str: |
1103 | # for single vxlan device we don't have a vxlan-id | |
1104 | self.__config_vxlan_id(ifname, ifaceobj, vxlan_id_str, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) | |
1105 | ||
223ba5af JF |
1106 | self.__config_vxlan_learning(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) |
1107 | self.__config_vxlan_ageing(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) | |
1108 | self.__config_vxlan_port(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) | |
9144496d | 1109 | vxlan_ttl = self.__config_vxlan_ttl(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) |
e521508b | 1110 | self.__config_vxlan_tos(ifname, ifaceobj, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) |
a8dd54b0 | 1111 | self.__config_vxlan_udp_csum(ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) |
223ba5af JF |
1112 | local = self.__config_vxlan_local_tunnelip(ifname, ifaceobj, link_exists, user_request_vxlan_info_data, cached_vxlan_ifla_info_data) |
1113 | ||
0500d5d8 JF |
1114 | vxlan_vni = self.__get_vxlan_vni_list(ifaceobj) |
1115 | ||
40658337 JF |
1116 | vxlan_mcast_grp = self.__get_vxlan_attribute(ifaceobj, "vxlan-mcastgrp") |
1117 | vxlan_svcnodeip = self.__get_vxlan_attribute(ifaceobj, "vxlan-svcnodeip") | |
1118 | ||
1119 | vxlan_mcast_grp6 = self.__get_vxlan_attribute(ifaceobj, "vxlan-mcastgrp6") | |
1120 | vxlan_svcnodeip6 = self.__get_vxlan_attribute(ifaceobj, "vxlan-svcnodeip6") | |
1121 | ||
6015cce2 | 1122 | vxlan_mcast_grp_map = self.__get_vxlan_mcastgrp_map(ifaceobj) |
1609696f JF |
1123 | |
1124 | vxlan_physdev = self.__get_vxlan_physdev(ifaceobj, vxlan_mcast_grp, vxlan_mcast_grp_map) | |
223ba5af | 1125 | |
84c47c4f RP |
1126 | vxlan_vnifilter = self.__get_vxlan_attribute(ifaceobj, "vxlan-vnifilter") |
1127 | ||
223ba5af JF |
1128 | vxlan_physdev_changed = self.__config_vxlan_physdev( |
1129 | link_exists, | |
1130 | ifaceobj, | |
1131 | vxlan_physdev, | |
1132 | user_request_vxlan_info_data, | |
1133 | cached_vxlan_ifla_info_data | |
1134 | ) | |
1135 | ||
1136 | group, multicast_group_changed = self.__config_vxlan_group( | |
1137 | ifname, | |
1138 | ifaceobj, | |
1139 | link_exists, | |
1140 | vxlan_mcast_grp, | |
1141 | vxlan_svcnodeip, | |
1142 | vxlan_physdev, | |
1143 | user_request_vxlan_info_data, | |
1144 | cached_vxlan_ifla_info_data | |
1145 | ) | |
1146 | ||
40658337 JF |
1147 | group6, multicast_group_changed6 = self.__config_vxlan_group6( |
1148 | ifname, | |
1149 | ifaceobj, | |
1150 | link_exists, | |
1151 | vxlan_mcast_grp6, | |
1152 | vxlan_svcnodeip6, | |
1153 | vxlan_physdev, | |
1154 | user_request_vxlan_info_data, | |
1155 | cached_vxlan_ifla_info_data | |
1156 | ) | |
1157 | ||
1158 | flap_vxlan_device = link_exists and (multicast_group_changed or multicast_group_changed6 or vxlan_physdev_changed) | |
223ba5af JF |
1159 | |
1160 | if user_request_vxlan_info_data: | |
1161 | ||
2b867068 | 1162 | if link_exists and len(user_request_vxlan_info_data) == 1 and Link.IFLA_VXLAN_ID in user_request_vxlan_info_data: |
223ba5af JF |
1163 | # if the vxlan already exists it's already cached |
1164 | # user_request_vxlan_info_data always contains at least one | |
1165 | # element: vxlan-id | |
1166 | self.logger.info('%s: vxlan already exists - no change detected' % ifname) | |
1167 | else: | |
e537a6e6 | 1168 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN: |
2b867068 | 1169 | self.iproute2.link_add_single_vxlan( |
e7ecab23 | 1170 | link_exists, |
2b867068 JF |
1171 | ifname, |
1172 | local.ip if local else None, | |
1173 | group.ip if group else None, | |
1174 | vxlan_physdev, | |
1175 | user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT), | |
1176 | vxlan_vnifilter, | |
1177 | vxlan_ttl | |
1178 | ) | |
0500d5d8 JF |
1179 | elif ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI: |
1180 | self.iproute2.link_add_l3vxi( | |
1181 | link_exists, | |
1182 | ifname, | |
1183 | local.ip if local else None, | |
1184 | group.ip if group else None, | |
1185 | vxlan_physdev, | |
1186 | user_request_vxlan_info_data.get(Link.IFLA_VXLAN_PORT), | |
1187 | vxlan_ttl | |
1188 | ) | |
e537a6e6 JF |
1189 | else: |
1190 | try: | |
1191 | if flap_vxlan_device: | |
1192 | self.netlink.link_down_force(ifname) | |
1193 | ||
1194 | self.netlink.link_add_vxlan_with_info_data(ifname, user_request_vxlan_info_data) | |
1195 | ||
1196 | if flap_vxlan_device: | |
1197 | self.netlink.link_up_force(ifname) | |
1198 | except Exception as e: | |
1199 | if link_exists: | |
1200 | self.log_error("%s: applying vxlan change failed: %s" % (ifname, str(e)), ifaceobj) | |
1201 | else: | |
1202 | self.log_error("%s: vxlan creation failed: %s" % (ifname, str(e)), ifaceobj) | |
1203 | return | |
223ba5af | 1204 | |
2fbbfa72 JF |
1205 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI: |
1206 | add_vni = True | |
1207 | if link_exists: | |
1208 | running_vxlan_vni_set = set() | |
1209 | ||
1210 | for obj in json.loads(utils.exec_command("bridge -j -p vni show dev %s" % ifname) or "[]"): | |
1211 | for vni_obj in obj.get("vnis", []): | |
1212 | start = vni_obj.get("vni") | |
1213 | end = vni_obj.get("vniEnd") | |
1214 | ||
1215 | for vni in utils.ranges_to_ints(["%s-%s" % (start, end if end else start)]): | |
1216 | running_vxlan_vni_set.add(vni) | |
1217 | ||
1218 | if running_vxlan_vni_set != set(utils.ranges_to_ints([vxlan_vni])): | |
1219 | self.iproute2.bridge_vni_int_set_del(ifname, running_vxlan_vni_set) | |
1220 | else: | |
1221 | add_vni = False | |
1222 | ||
1223 | if add_vni: | |
1224 | try: | |
1225 | self.iproute2.bridge_vni_add(ifname, vxlan_vni) | |
1226 | except Exception as e: | |
1227 | self.logger.warning("%s: l3 vxlan vni failure: %s" % (ifname, e)) | |
1228 | ||
84c47c4f RP |
1229 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.SINGLE_VXLAN: |
1230 | if vxlan_vnifilter and utils.get_boolean_from_string(vxlan_vnifilter): | |
20aabf55 | 1231 | self.single_vxlan_device_vni_filter(ifaceobj, vxlan_mcast_grp_map) |
84c47c4f | 1232 | |
223ba5af JF |
1233 | vxlan_purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj) |
1234 | ||
1235 | remoteips = ifaceobj.get_attr_value('vxlan-remoteip') | |
1236 | if remoteips: | |
1237 | try: | |
1238 | for remoteip in remoteips: | |
0e936c3f | 1239 | ipnetwork.IPv4Address(remoteip) |
223ba5af JF |
1240 | except Exception as e: |
1241 | self.log_error('%s: vxlan-remoteip: %s' % (ifaceobj.name, str(e))) | |
1242 | ||
35a4278f JF |
1243 | # get old remote ips to compare with new user config value and |
1244 | # purge any removed remote ip | |
1245 | old_remoteips = self.get_old_remote_ips(ifaceobj.name) | |
1246 | ||
1247 | if vxlan_purge_remotes or remoteips or (remoteips != old_remoteips): | |
223ba5af JF |
1248 | # figure out the diff for remotes and do the bridge fdb updates |
1249 | # only if provisioned by user and not by an vxlan external | |
1250 | # controller. | |
0e936c3f JF |
1251 | local_str = str(local) |
1252 | ||
1253 | if local_str and remoteips and local_str in remoteips: | |
1254 | remoteips.remove(local_str) | |
1255 | ||
1256 | peers = self.iproute2.get_vxlan_peers(ifaceobj.name, str(group.ip) if group else None) | |
1257 | ||
223ba5af JF |
1258 | cur_peers = set(peers) |
1259 | if remoteips: | |
1260 | new_peers = set(remoteips) | |
1261 | del_list = cur_peers.difference(new_peers) | |
1262 | add_list = new_peers.difference(cur_peers) | |
1263 | else: | |
1264 | del_list = cur_peers | |
1265 | add_list = [] | |
d486dd0d | 1266 | |
223ba5af | 1267 | for addr in del_list: |
d486dd0d | 1268 | try: |
223ba5af JF |
1269 | self.iproute2.bridge_fdb_del( |
1270 | ifaceobj.name, | |
1271 | "00:00:00:00:00:00", | |
1272 | None, True, addr | |
1273 | ) | |
3218f49d | 1274 | except Exception: |
d486dd0d | 1275 | pass |
d486dd0d | 1276 | |
223ba5af | 1277 | for addr in add_list: |
d486dd0d | 1278 | try: |
223ba5af JF |
1279 | self.iproute2.bridge_fdb_append( |
1280 | ifaceobj.name, | |
1281 | "00:00:00:00:00:00", | |
1282 | None, True, addr | |
1283 | ) | |
3218f49d | 1284 | except Exception: |
223ba5af | 1285 | pass |
d486dd0d | 1286 | |
66eb9ce3 JF |
1287 | self.vxlan_remote_ip_map(ifaceobj, vxlan_mcast_grp_map) |
1288 | ||
35a4278f JF |
1289 | @staticmethod |
1290 | def get_old_remote_ips(ifname): | |
1291 | old_remoteips = [] | |
1292 | for old_ifaceobj in statemanager.get_ifaceobjs(ifname) or []: | |
1293 | for remote in old_ifaceobj.get_attr_value("vxlan-remoteip") or []: | |
1294 | old_remoteips.append(remote) | |
1295 | return old_remoteips | |
1296 | ||
66eb9ce3 JF |
1297 | def vxlan_remote_ip_map(self, ifaceobj, vxlan_mcast_grp_map): |
1298 | # get user configured remote ip map | |
1299 | vxlan_remote_ip_map = self.__get_vxlan_remote_ip_map(ifaceobj) or {} | |
1300 | ||
238e0485 JF |
1301 | # if we have an older config we need to see what needs to be removed |
1302 | # and not check the running state as FRR or other component can add fdb entries | |
1303 | old_vxlan_remote_ip_map = {} | |
1304 | ||
1305 | for old_ifaceobj in statemanager.get_ifaceobjs(ifaceobj.name) or []: | |
1306 | old_vxlan_remote_ip_map = {**old_vxlan_remote_ip_map, **self.__get_vxlan_remote_ip_map(old_ifaceobj)} | |
1307 | ||
3376c233 | 1308 | # go through the user config and add new entries while removing existing entries from 'old_vxlan_remote_ip_map' |
66eb9ce3 JF |
1309 | for vni, ips in vxlan_remote_ip_map.items(): |
1310 | for ip in ips: | |
238e0485 | 1311 | if ip not in old_vxlan_remote_ip_map.get(vni, []): |
66eb9ce3 JF |
1312 | self.iproute2.bridge_fdb_append(ifaceobj.name, "00:00:00:00:00:00", remote=ip, src_vni=vni) |
1313 | else: | |
238e0485 | 1314 | old_vxlan_remote_ip_map.get(vni, []).remove(ip) |
66eb9ce3 | 1315 | |
238e0485 JF |
1316 | # in old_vxlan_remote_ip_map we have the delta between user config and running config. We should delete those |
1317 | # extra fdb entries. First we need to make sure that those are not added by vxlan-mcastgrp-map | |
1318 | if old_vxlan_remote_ip_map: | |
66eb9ce3 JF |
1319 | for vni, ip in (vxlan_mcast_grp_map or {}).items(): |
1320 | try: | |
238e0485 | 1321 | old_vxlan_remote_ip_map[vni].remove(ip) |
7c8627f8 | 1322 | except Exception: |
66eb9ce3 JF |
1323 | pass |
1324 | ||
238e0485 JF |
1325 | for vni, ips in old_vxlan_remote_ip_map.items(): |
1326 | for ip in ips: | |
1327 | try: | |
1328 | self.iproute2.bridge_fdb_del_raw(ifaceobj.name, "00:00:00:00:00:00 dst %s src_vni %s" % (ip, vni)) | |
7c8627f8 | 1329 | except Exception: |
238e0485 | 1330 | pass |
66eb9ce3 | 1331 | |
ca436937 JF |
1332 | @staticmethod |
1333 | def get_vxlan_fdb_src_vni(vxlan_mcast_grp_map): | |
1334 | fdbs = [] | |
56f34349 | 1335 | if vxlan_mcast_grp_map: |
6015cce2 | 1336 | for src_vni, dst_ip in vxlan_mcast_grp_map.items(): |
ca436937 JF |
1337 | fdbs.append(("00:00:00:00:00:00", src_vni, dst_ip)) |
1338 | return fdbs | |
1339 | ||
56f34349 JF |
1340 | @staticmethod |
1341 | def get_svd_running_fdb(ifname): | |
1342 | vxlan_fdb_data = utils.exec_command("bridge fdb show dev %s" % ifname) | |
1343 | current_fdb = [] | |
1344 | ||
1345 | if vxlan_fdb_data: | |
1346 | # each entry should look like the following: | |
1347 | # 00:00:00:00:00:00 dst 239.1.1.100 src_vni 1000 self permanent | |
1348 | for entry in [line for line in vxlan_fdb_data.strip().split("\n") if "src_vni" in line and "00:00:00:00:00:00" in line]: | |
1349 | mac, _, dst, _, src_vni = entry.split()[0:5] | |
1350 | current_fdb.append((mac, src_vni, dst)) | |
1351 | ||
1352 | return current_fdb | |
1353 | ||
1354 | def single_vxlan_device_mcast_grp_map_fdb(self, ifaceobj, ifname, vxlan_mcast_grp_map): | |
1355 | # in this piece of code we won't be checking the running state of the fdb table | |
1356 | # dumping all fdb entries would cause scalability issues in certain cases. | |
1357 | ||
1358 | # pulling old mcastgrp-map configuration | |
1359 | old_user_config_fdb = [] | |
1360 | ||
1361 | for old_ifaceobj in statemanager.get_ifaceobjs(ifname) or []: | |
6015cce2 | 1362 | old_user_config_fdb += self.get_vxlan_fdb_src_vni(self.__get_vxlan_mcastgrp_map(old_ifaceobj)) |
56f34349 JF |
1363 | |
1364 | # new user configuration | |
1365 | user_config_fdb = self.get_vxlan_fdb_src_vni(vxlan_mcast_grp_map) | |
1366 | ||
1367 | # compare old and new config to know if we should remove any stale fdb entries. | |
1368 | fdb_entries_to_remove = set(old_user_config_fdb) - set(user_config_fdb) | |
1369 | ||
1370 | if fdb_entries_to_remove: | |
1371 | for mac, src_vni, dst_ip in fdb_entries_to_remove: | |
1372 | try: | |
1373 | self.iproute2.bridge_fdb_del_src_vni(ifname, mac, src_vni) | |
1374 | except Exception as e: | |
1375 | if "no such file or directory" not in str(e).lower(): | |
1376 | self.logger.warning("%s: removing stale fdb entries failed: %s" % (ifname, str(e))) | |
af8d5db2 RP |
1377 | |
1378 | if not user_config_fdb: | |
1379 | # if vxlan-mcastgrp-map wasn't configure return | |
1380 | return | |
1381 | ||
1382 | for mac, src_vni, dst_ip in user_config_fdb: | |
1383 | try: | |
1384 | self.iproute2.bridge_fdb_add_src_vni(ifname, src_vni, dst_ip) | |
1385 | except Exception as e: | |
1386 | if "file exists" not in str(e).lower(): | |
1387 | ifaceobj.set_status(ifaceStatus.ERROR) | |
1388 | self.log_error( | |
1389 | "%s: vxlan-mcastgrp-map: %s=%s: %s" | |
1390 | % (ifname, src_vni, dst_ip, str(e)), raise_error=False | |
1391 | ) | |
1392 | ||
1393 | def single_vxlan_device_mcast_grp_map_vnifilter(self, ifaceobj, ifname, vxlan_mcast_grp_map): | |
1394 | # in this piece of code we won't be checking the running state of the fdb table | |
1395 | # dumping all fdb entries would cause scalability issues in certain cases. | |
1396 | ||
1397 | # pulling old mcastgrp-map configuration | |
1398 | old_user_config_fdb = [] | |
1399 | ||
1400 | for old_ifaceobj in statemanager.get_ifaceobjs(ifname) or []: | |
1401 | old_user_config_fdb += self.get_vxlan_fdb_src_vni(self.__get_vxlan_mcastgrp_map(old_ifaceobj)) | |
1402 | ||
1403 | # new user configuration | |
1404 | user_config_fdb = self.get_vxlan_fdb_src_vni(vxlan_mcast_grp_map) | |
1405 | ||
1406 | # compare old and new config to know if we should remove any stale fdb entries. | |
1407 | fdb_entries_to_remove = set(old_user_config_fdb) - set(user_config_fdb) | |
1408 | ||
1409 | self.logger.info(old_user_config_fdb) | |
1410 | self.logger.info(user_config_fdb) | |
1411 | self.logger.info(fdb_entries_to_remove) | |
1412 | ||
1413 | if fdb_entries_to_remove: | |
1414 | for mac, src_vni, dst_ip in fdb_entries_to_remove: | |
1415 | try: | |
1416 | self.iproute2.bridge_fdb_del_src_vni(ifname, mac, src_vni) | |
1417 | except Exception as e: | |
1418 | if "no such file or directory" not in str(e).lower(): | |
1419 | self.logger.warning("%s: removing stale fdb entries failed: %s" % (ifname, str(e))) | |
56f34349 JF |
1420 | |
1421 | if not user_config_fdb: | |
1422 | # if vxlan-mcastgrp-map wasn't configure return | |
1423 | return | |
1424 | ||
1425 | for mac, src_vni, dst_ip in user_config_fdb: | |
1426 | try: | |
1427 | self.iproute2.bridge_fdb_add_src_vni(ifname, src_vni, dst_ip) | |
1428 | except Exception as e: | |
1429 | if "file exists" not in str(e).lower(): | |
1430 | ifaceobj.set_status(ifaceStatus.ERROR) | |
1431 | self.log_error( | |
1432 | "%s: vxlan-mcastgrp-map: %s=%s: %s" | |
1433 | % (ifname, src_vni, dst_ip, str(e)), raise_error=False | |
1434 | ) | |
1435 | ||
d486dd0d JF |
1436 | def _down(self, ifaceobj): |
1437 | try: | |
223ba5af | 1438 | self.netlink.link_del(ifaceobj.name) |
3b01ed76 | 1439 | except Exception as e: |
d486dd0d JF |
1440 | self.log_warn(str(e)) |
1441 | ||
223ba5af JF |
1442 | @staticmethod |
1443 | def _query_check_n_update(ifaceobj, ifaceobjcurr, attrname, attrval, running_attrval): | |
d486dd0d JF |
1444 | if not ifaceobj.get_attr_value_first(attrname): |
1445 | return | |
1446 | if running_attrval and attrval == running_attrval: | |
223ba5af | 1447 | ifaceobjcurr.update_config_with_status(attrname, attrval, 0) |
d486dd0d | 1448 | else: |
223ba5af | 1449 | ifaceobjcurr.update_config_with_status(attrname, running_attrval, 1) |
d486dd0d | 1450 | |
223ba5af JF |
1451 | @staticmethod |
1452 | def _query_check_n_update_addresses(ifaceobjcurr, attrname, addresses, running_addresses): | |
d486dd0d JF |
1453 | if addresses: |
1454 | for a in addresses: | |
1455 | if a in running_addresses: | |
1456 | ifaceobjcurr.update_config_with_status(attrname, a, 0) | |
1457 | else: | |
1458 | ifaceobjcurr.update_config_with_status(attrname, a, 1) | |
3b01ed76 JF |
1459 | running_addresses = set(running_addresses).difference( |
1460 | set(addresses)) | |
223ba5af | 1461 | [ifaceobjcurr.update_config_with_status(attrname, a, 1) for a in running_addresses] |
d486dd0d JF |
1462 | |
1463 | def _query_check(self, ifaceobj, ifaceobjcurr): | |
223ba5af JF |
1464 | ifname = ifaceobj.name |
1465 | ||
1466 | if not self.cache.link_exists(ifname): | |
d486dd0d | 1467 | return |
d486dd0d | 1468 | |
223ba5af JF |
1469 | cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname) |
1470 | ||
1471 | if not cached_vxlan_ifla_info_data: | |
1472 | ifaceobjcurr.check_n_update_config_with_status_many(ifaceobj, self.get_mod_attrs(), -1) | |
1473 | return | |
1474 | ||
1475 | for vxlan_attr_str, vxlan_attr_nl, callable_type in ( | |
1476 | ('vxlan-id', Link.IFLA_VXLAN_ID, int), | |
1477 | ('vxlan-ttl', Link.IFLA_VXLAN_TTL, int), | |
e521508b | 1478 | ('vxlan-tos', Link.IFLA_VXLAN_TOS, int), |
223ba5af JF |
1479 | ('vxlan-port', Link.IFLA_VXLAN_PORT, int), |
1480 | ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, int), | |
0e936c3f | 1481 | ('vxlan-mcastgrp', Link.IFLA_VXLAN_GROUP, ipnetwork.IPv4Address), |
40658337 | 1482 | ('vxlan-mcastgrp6', Link.IFLA_VXLAN_GROUP6, ipnetwork.IPv6Address), |
0e936c3f | 1483 | ('vxlan-svcnodeip', Link.IFLA_VXLAN_GROUP, ipnetwork.IPv4Address), |
40658337 | 1484 | ('vxlan-svcnodeip6', Link.IFLA_VXLAN_GROUP6, ipnetwork.IPv6Address), |
223ba5af JF |
1485 | ('vxlan-physdev', Link.IFLA_VXLAN_LINK, lambda x: self.cache.get_ifindex(x)), |
1486 | ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda boolean_str: utils.get_boolean_from_string(boolean_str)), | |
e521508b | 1487 | ('vxlan-udp-csum', Link.IFLA_VXLAN_UDP_CSUM, lambda boolean_str: utils.get_boolean_from_string(boolean_str)), |
223ba5af JF |
1488 | ): |
1489 | vxlan_attr_value = ifaceobj.get_attr_value_first(vxlan_attr_str) | |
d486dd0d | 1490 | |
223ba5af JF |
1491 | if not vxlan_attr_value: |
1492 | continue | |
1493 | ||
1494 | cached_vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl) | |
1495 | ||
1496 | try: | |
1497 | vxlan_attr_value_nl = callable_type(vxlan_attr_value) | |
1498 | except Exception as e: | |
1499 | self.logger.warning('%s: %s: %s' % (ifname, vxlan_attr_str, str(e))) | |
1500 | ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1) | |
1501 | continue | |
1502 | ||
1503 | if vxlan_attr_value_nl == cached_vxlan_attr_value: | |
1504 | ifaceobjcurr.update_config_with_status(vxlan_attr_str, vxlan_attr_value, 0) | |
1505 | else: | |
1506 | ifaceobjcurr.update_config_with_status(vxlan_attr_str, cached_vxlan_attr_value or 'None', 1) | |
1507 | ||
1508 | # | |
1509 | # vxlan-local-tunnelip | |
1510 | # | |
1511 | running_attrval = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_LOCAL) | |
d486dd0d JF |
1512 | attrval = ifaceobj.get_attr_value_first('vxlan-local-tunnelip') |
1513 | if not attrval: | |
223ba5af | 1514 | attrval = self._vxlan_local_tunnelip |
0e936c3f | 1515 | # TODO: vxlan._vxlan_local_tunnelip should be a ipnetwork.IPNetwork obj |
d486dd0d JF |
1516 | ifaceobj.update_config('vxlan-local-tunnelip', attrval) |
1517 | ||
223ba5af | 1518 | if str(running_attrval) == self._clagd_vxlan_anycast_ip: |
d486dd0d JF |
1519 | # if local ip is anycast_ip, then let query_check to go through |
1520 | attrval = self._clagd_vxlan_anycast_ip | |
d486dd0d | 1521 | |
223ba5af JF |
1522 | self._query_check_n_update( |
1523 | ifaceobj, | |
1524 | ifaceobjcurr, | |
1525 | 'vxlan-local-tunnelip', | |
1526 | str(attrval), | |
0e936c3f | 1527 | str(running_attrval.ip) if running_attrval else None |
223ba5af | 1528 | ) |
d486dd0d | 1529 | |
223ba5af JF |
1530 | # |
1531 | # vxlan-remoteip | |
1532 | # | |
1533 | purge_remotes = self.__get_vlxan_purge_remotes(ifaceobj) | |
d486dd0d JF |
1534 | if purge_remotes or ifaceobj.get_attr_value('vxlan-remoteip'): |
1535 | # If purge remotes or if vxlan-remoteip's are set | |
1536 | # in the config file, we are owners of the installed | |
1537 | # remote-ip's, lets check and report any remote ips we don't | |
1538 | # understand | |
223ba5af | 1539 | cached_svcnode = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP) |
d486dd0d | 1540 | |
223ba5af JF |
1541 | self._query_check_n_update_addresses( |
1542 | ifaceobjcurr, | |
1543 | 'vxlan-remoteip', | |
1544 | ifaceobj.get_attr_value('vxlan-remoteip'), | |
0e936c3f | 1545 | self.iproute2.get_vxlan_peers(ifaceobj.name, str(cached_svcnode.ip) if cached_svcnode else None) |
223ba5af | 1546 | ) |
a382b488 | 1547 | |
0500d5d8 JF |
1548 | # not ideal but will work for now, l3vxi dev: |
1549 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.L3VXI: | |
1550 | user_config_vni_list = set(self.__get_vxlan_vni_list(ifaceobj, string=False)) | |
1551 | vxlan_vni_list = set() | |
1552 | ||
1553 | for obj in json.loads(utils.exec_command("bridge -j -p vni show dev %s" % ifname) or "[]"): | |
1554 | for vni_obj in obj.get("vnis", []): | |
1555 | start = vni_obj.get("vni") | |
1556 | end = vni_obj.get("vniEnd") | |
1557 | ||
1558 | for vni in utils.ranges_to_ints(["%s-%s" % (start, end if end else start)]): | |
1559 | vxlan_vni_list.add(vni) | |
1560 | ||
1561 | ifaceobjcurr.update_config_with_status( | |
1562 | "vxlan-vni", | |
1563 | " ".join(utils.compress_into_ranges(vxlan_vni_list)), | |
1564 | vxlan_vni_list != user_config_vni_list | |
1565 | ) | |
1566 | ||
e6edcd21 | 1567 | # |
66eb9ce3 | 1568 | # vxlan-mcastgrp-map & vxlan-remoteip-map |
d4403f1e JF |
1569 | # fdb entries can be added by FRR, so we won't be checking the running |
1570 | # state if there's no record of a user configuration in /e/n/i | |
1571 | user_mcastgrp_map = self.__get_vxlan_mcastgrp_map(ifaceobj) | |
1572 | user_remote_ip_map = self.__get_vxlan_remote_ip_map(ifaceobj) | |
1573 | ||
9ca87c5e | 1574 | if not user_mcastgrp_map and not user_remote_ip_map: |
d4403f1e JF |
1575 | return |
1576 | ||
66eb9ce3 JF |
1577 | fdb_mcast = {} |
1578 | fdb_remote = {} | |
e6edcd21 | 1579 | |
9ca87c5e JF |
1580 | if user_remote_ip_map: |
1581 | for _, src_vni, dst in self.get_svd_running_fdb(ifname): | |
1582 | ip = ipnetwork.IPv4Address(dst) | |
e6edcd21 | 1583 | |
9ca87c5e JF |
1584 | if not ip.is_multicast: |
1585 | fdb_remote.setdefault(int(src_vni), []).append(ip) | |
1586 | ||
1587 | if user_mcastgrp_map: | |
1588 | for obj in json.loads(utils.exec_command("bridge -j -p vni show dev %s" % ifname) or "[]"): | |
1589 | for vni in obj.get("vnis", []): | |
1590 | group = vni.get("group") | |
1591 | ||
1592 | if not group: | |
1593 | continue | |
1594 | ||
1595 | # we need to reconvert back to ipaddress.IPv4Address because | |
1596 | # the existing code uses this type of obj (namely: __get_vxlan_mcastgrp_map) | |
1597 | fdb_mcast[vni.get("vni")] = IPv4Address(group) | |
e6edcd21 | 1598 | |
66eb9ce3 JF |
1599 | # |
1600 | # vxlan-mcastgrp-map | |
1601 | # | |
66eb9ce3 JF |
1602 | if not user_mcastgrp_map and fdb_mcast: |
1603 | ifaceobjcurr.update_config_with_status( | |
1604 | "vxlan-mcastgrp-map", | |
1605 | " ".join(["%s=%s" % (vni, ip) for vni, ip in fdb_mcast.items()]), | |
1606 | 1 | |
1607 | ) | |
1608 | elif user_mcastgrp_map and not fdb_mcast: | |
1609 | ifaceobjcurr.update_config_with_status("vxlan-mcastgrp-map", "", 1) | |
1610 | elif user_mcastgrp_map or fdb_mcast: | |
1611 | ifaceobjcurr.update_config_with_status( | |
1612 | "vxlan-mcastgrp-map", | |
1613 | " ".join(["%s=%s" % (vni, ip) for vni, ip in fdb_mcast.items()]), | |
1614 | user_mcastgrp_map != fdb_mcast | |
1615 | ) | |
e6edcd21 | 1616 | |
66eb9ce3 JF |
1617 | # |
1618 | # vxlan-remoteip-map | |
1619 | # | |
66eb9ce3 | 1620 | if not user_remote_ip_map and fdb_remote: |
e6edcd21 | 1621 | ifaceobjcurr.update_config_with_status( |
66eb9ce3 JF |
1622 | "vxlan-remoteip-map", |
1623 | " ".join(["%s=%s" % (vni, ",".join(map(str, ips))) for vni, ips in fdb_remote.items()]), | |
1624 | 1 | |
e6edcd21 | 1625 | ) |
66eb9ce3 JF |
1626 | elif user_remote_ip_map and not fdb_remote: |
1627 | ifaceobjcurr.update_config_with_status("vxlan-remoteip-map", "", 1) | |
1628 | elif user_remote_ip_map or fdb_remote: | |
1629 | ||
1630 | if user_remote_ip_map == fdb_remote: | |
1631 | # display the user config with "pass" | |
1632 | for config in ifaceobj.get_attr_value("vxlan-remoteip-map"): | |
1633 | ifaceobjcurr.update_config_with_status( | |
1634 | "vxlan-remoteip-map", | |
1635 | config, | |
1636 | 0 | |
1637 | ) | |
1638 | else: | |
1639 | # display current running state with ip ranges (but no vni ranges yet) | |
1640 | ifaceobjcurr.update_config_with_status( | |
1641 | "vxlan-remoteip-map", | |
1642 | " ".join(["%s=%s" % (vni, ",".join(utils.compress_into_ip_ranges(ips))) for vni, ips in fdb_remote.items()]), | |
1643 | 1 | |
1644 | ) | |
e6edcd21 | 1645 | |
223ba5af JF |
1646 | def _query_running(self, ifaceobjrunning): |
1647 | ifname = ifaceobjrunning.name | |
a382b488 | 1648 | |
223ba5af JF |
1649 | if not self.cache.link_exists(ifname): |
1650 | return | |
a382b488 | 1651 | |
e8b9d3ab | 1652 | if self.cache.get_link_kind(ifname) != 'vxlan': |
d486dd0d | 1653 | return |
223ba5af JF |
1654 | |
1655 | cached_vxlan_ifla_info_data = self.cache.get_link_info_data(ifname) | |
1656 | ||
1657 | if not cached_vxlan_ifla_info_data: | |
d486dd0d JF |
1658 | return |
1659 | ||
223ba5af JF |
1660 | # |
1661 | # vxlan-id | |
1662 | # | |
1663 | vxlan_id = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_ID) | |
1664 | ||
1665 | if not vxlan_id: | |
1666 | # no vxlan id, meaning this not a vxlan | |
1667 | return | |
1668 | ||
1669 | ifaceobjrunning.update_config('vxlan-id', str(vxlan_id)) | |
1670 | ||
1671 | # | |
1672 | # vxlan-port | |
1673 | # | |
1674 | vxlan_port = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_PORT) | |
1675 | ||
1676 | if vxlan_port: | |
1677 | ifaceobjrunning.update_config('vxlan-port', vxlan_port) | |
1678 | ||
1679 | # | |
1680 | # vxlan-svcnode | |
1681 | # | |
1682 | vxlan_svcnode_value = cached_vxlan_ifla_info_data.get(Link.IFLA_VXLAN_GROUP) | |
1683 | ||
1684 | if vxlan_svcnode_value: | |
1685 | vxlan_svcnode_value = str(vxlan_svcnode_value) | |
1686 | ifaceobjrunning.update_config('vxlan-svcnode', vxlan_svcnode_value) | |
d486dd0d | 1687 | |
223ba5af JF |
1688 | # |
1689 | # vxlan-remoteip | |
1690 | # | |
1691 | purge_remotes = self.__get_vlxan_purge_remotes(None) | |
d486dd0d JF |
1692 | if purge_remotes: |
1693 | # if purge_remotes is on, it means we own the | |
1694 | # remote ips. Query them and add it to the running config | |
223ba5af | 1695 | attrval = self.iproute2.get_vxlan_peers(ifname, vxlan_svcnode_value) |
d486dd0d | 1696 | if attrval: |
223ba5af JF |
1697 | [ifaceobjrunning.update_config('vxlan-remoteip', a) for a in attrval] |
1698 | ||
1699 | # | |
1700 | # vxlan-link | |
1701 | # vxlan-ageing | |
1702 | # vxlan-learning | |
1703 | # vxlan-local-tunnelip | |
1704 | # | |
1705 | for vxlan_attr_name, vxlan_attr_nl, callable_netlink_value_to_string in ( | |
1706 | ('vxlan-physdev', Link.IFLA_VXLAN_LINK, self._get_ifname_for_ifindex), | |
1707 | ('vxlan-ageing', Link.IFLA_VXLAN_AGEING, str), | |
1708 | ('vxlan-learning', Link.IFLA_VXLAN_LEARNING, lambda value: 'on' if value else 'off'), | |
e521508b | 1709 | ('vxlan-udp-csum', Link.IFLA_VXLAN_UDP_CSUM, lambda value: 'on' if value else 'off'), |
223ba5af JF |
1710 | ('vxlan-local-tunnelip', Link.IFLA_VXLAN_LOCAL, str), |
1711 | ): | |
1712 | vxlan_attr_value = cached_vxlan_ifla_info_data.get(vxlan_attr_nl) | |
1713 | ||
1714 | if vxlan_attr_value is not None: | |
dc74ceda | 1715 | vxlan_attr_value_str = callable_netlink_value_to_string(vxlan_attr_value) |
223ba5af JF |
1716 | |
1717 | if vxlan_attr_value: | |
1718 | ifaceobjrunning.update_config(vxlan_attr_name, vxlan_attr_value_str) | |
1719 | ||
1720 | def _get_ifname_for_ifindex(self, ifindex): | |
1721 | """ | |
1722 | we need this middle-man function to query the cache | |
1723 | cache.get_ifname can raise KeyError, we need to catch | |
1724 | it and return None | |
1725 | """ | |
1726 | try: | |
1727 | return self.cache.get_ifname(ifindex) | |
1728 | except KeyError: | |
1729 | return None | |
a382b488 | 1730 | |
223ba5af JF |
1731 | _run_ops = { |
1732 | "pre-up": _up, | |
1733 | "post-down": _down, | |
1734 | "query-running": _query_running, | |
1735 | "query-checkcurr": _query_check | |
1736 | } | |
d486dd0d JF |
1737 | |
1738 | def get_ops(self): | |
3b01ed76 | 1739 | return list(self._run_ops.keys()) |
d486dd0d | 1740 | |
d486dd0d JF |
1741 | def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args): |
1742 | op_handler = self._run_ops.get(operation) | |
1743 | if not op_handler: | |
1744 | return | |
223ba5af | 1745 | |
66eb9ce3 JF |
1746 | if not self._is_vxlan_device(ifaceobj): |
1747 | return | |
223ba5af | 1748 | |
66eb9ce3 | 1749 | if "query" not in operation: |
223ba5af JF |
1750 | if not self.vxlan_mcastgrp_ref \ |
1751 | and self.vxlan_physdev_mcast \ | |
1752 | and self.cache.link_exists(self.vxlan_physdev_mcast): | |
1753 | self.netlink.link_del(self.vxlan_physdev_mcast) | |
1754 | self.reset() | |
1755 | ||
d486dd0d JF |
1756 | if operation == 'query-checkcurr': |
1757 | op_handler(self, ifaceobj, query_ifaceobj) | |
1758 | else: | |
1759 | op_handler(self, ifaceobj) |