]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/addressvirtual.py
addons: addressvirtual: vrrp: fix multiline-merge attribute detection
[mirror_ifupdown2.git] / ifupdown2 / addons / addressvirtual.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import os
8 import glob
9
10 from string import maketrans
11 from collections import deque
12 from ipaddr import IPNetwork, IPv6Network
13
14 try:
15 from ifupdown2.ifupdown.iface import *
16 from ifupdown2.ifupdown.utils import utils
17 from ifupdown2.ifupdown.netlink import netlink
18
19 from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils
20 from ifupdown2.ifupdownaddons.modulebase import moduleBase
21
22 import ifupdown2.ifupdown.statemanager as statemanager
23 import ifupdown2.ifupdown.policymanager as policymanager
24 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
25 import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
26 except ImportError:
27 from ifupdown.iface import *
28 from ifupdown.utils import utils
29 from ifupdown.netlink import netlink
30
31 from ifupdownaddons.LinkUtils import LinkUtils
32 from ifupdownaddons.modulebase import moduleBase
33
34 import ifupdown.statemanager as statemanager
35 import ifupdown.policymanager as policymanager
36 import ifupdown.ifupdownflags as ifupdownflags
37 import ifupdown.ifupdownconfig as ifupdownconfig
38
39
40 class addressvirtual(moduleBase):
41 """ ifupdown2 addon module to configure virtual addresses """
42
43 _modinfo = {'mhelp' : 'address module configures virtual addresses for ' +
44 'interfaces. It creates a macvlan interface for ' +
45 'every mac ip address-virtual line',
46 'attrs' : {
47 'address-virtual' :
48 { 'help' : 'bridge router virtual mac and ips',
49 'multivalue' : True,
50 'validvals' : ['<mac-ip/prefixlen-list>',],
51 'example': ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24']
52 },
53 'address-virtual-ipv6-addrgen': {
54 'help': 'enable disable ipv6 link addrgenmode',
55 'validvals': ['on', 'off'],
56 'default': 'on',
57 'example': [
58 'address-virtual-ipv6-addrgen on',
59 'address-virtual-ipv6-addrgen off'
60 ]
61 },
62 "vrrp": {
63 "help": "VRRP support",
64 "multivalue": True,
65 "example": [
66 "vrrp 1 10.0.0.15/24 2001:0db8::0370:7334/64",
67 "vrrp 42 10.0.0.42/24"
68 ]
69 }
70 }}
71
72
73 def __init__(self, *args, **kargs):
74 moduleBase.__init__(self, *args, **kargs)
75 self.ipcmd = None
76 self._bridge_fdb_query_cache = {}
77 self.addressvirtual_with_route_metric = utils.get_boolean_from_string(
78 policymanager.policymanager_api.get_module_globals(
79 module_name=self.__class__.__name__,
80 attr='addressvirtual_with_route_metric'
81 ),
82 default=True
83 )
84
85 self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
86 self.mac_translate_tab = maketrans(":.-,", " ")
87
88 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
89 if ifaceobj.get_attr_value('address-virtual') or ifaceobj.get_attr_value("vrrp"):
90 ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
91
92 def _get_macvlan_prefix(self, ifaceobj):
93 return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
94
95 @staticmethod
96 def get_vrrp_prefix(ifname, family):
97 return "vrrp%s-%s-" % (family, netlink.get_iface_index(ifname))
98
99 def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
100 # XXX: batch the addresses
101 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
102 bridgename = ifaceobj.lowerifaces[0]
103 vlan = self._get_vlan_id(ifaceobj)
104 if self.ipcmd.bridge_is_vlan_aware(bridgename):
105 [self.ipcmd.bridge_fdb_add(bridgename, addr,
106 vlan) for addr in hwaddress]
107 elif self.ipcmd.is_bridge(ifaceobj.name):
108 [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
109 for addr in hwaddress]
110
111 def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
112 # XXX: batch the addresses
113 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
114 bridgename = ifaceobj.lowerifaces[0]
115 vlan = self._get_vlan_id(ifaceobj)
116 if self.ipcmd.bridge_is_vlan_aware(bridgename):
117 for addr in hwaddress:
118 try:
119 self.ipcmd.bridge_fdb_del(bridgename, addr, vlan)
120 except Exception, e:
121 self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
122 pass
123 elif self.ipcmd.is_bridge(ifaceobj.name):
124 for addr in hwaddress:
125 try:
126 self.ipcmd.bridge_fdb_del(ifaceobj.name, addr)
127 except Exception, e:
128 self.logger.debug("%s: %s" %(ifaceobj.name, str(e)))
129 pass
130
131 def _get_bridge_fdbs(self, bridgename, vlan):
132 fdbs = self._bridge_fdb_query_cache.get(bridgename)
133 if not fdbs:
134 fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
135 if not fdbs:
136 return
137 self._bridge_fdb_query_cache[bridgename] = fdbs
138 return fdbs.get(vlan)
139
140 def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
141 """ If the device is a bridge, make sure the addresses
142 are in the bridge """
143 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
144 bridgename = ifaceobj.lowerifaces[0]
145 vlan = self._get_vlan_id(ifaceobj)
146 if self.ipcmd.bridge_is_vlan_aware(bridgename):
147 fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
148 if not fdb_addrs:
149 return False
150 hwaddress_int = self.mac_str_to_int(hwaddress)
151 for mac in fdb_addrs:
152 if self.mac_str_to_int(mac) == hwaddress_int:
153 return True
154 return False
155 return True
156
157 def _fix_connected_route(self, ifaceobj, vifacename, addr):
158 #
159 # XXX: Hack to make sure the primary address
160 # is the first in the routing table.
161 #
162 # We use `ip route get` on the vrr network to see which
163 # device the kernel returns. if it is the mac vlan device,
164 # flap the macvlan device to adjust the routing table entry.
165 #
166 # flapping the macvlan device makes sure the macvlan
167 # connected route goes through delete + add, hence adjusting
168 # the order in the routing table.
169 #
170 try:
171 self.logger.info('%s: checking route entry ...' %ifaceobj.name)
172 ip = IPNetwork(addr)
173
174 # we don't support ip6 route fix yet
175 if type(ip) == IPv6Network:
176 return
177
178 route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
179
180 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
181 vrf_master = self.ipcmd.link_get_master(ifaceobj.name)
182 else:
183 vrf_master = None
184
185 dev = self.ipcmd.ip_route_get_dev(route_prefix, vrf_master=vrf_master)
186
187 if dev and dev != ifaceobj.name:
188 self.logger.info('%s: preferred routing entry ' %ifaceobj.name +
189 'seems to be of the macvlan dev %s'
190 %vifacename +
191 ' .. flapping macvlan dev to fix entry.')
192 self.ipcmd.link_down(vifacename)
193 self.ipcmd.link_up(vifacename)
194 except Exception, e:
195 self.logger.debug('%s: fixing route entry failed (%s)'
196 % (ifaceobj.name, str(e)))
197 pass
198
199 def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
200 vrfname = self.ipcmd.link_get_master(ifaceobj.name)
201 if vrfname:
202 self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
203
204 def _get_macs_from_old_config(self, ifaceobj=None):
205 """ This method returns a list of the mac addresses
206 in the address-virtual attribute for the bridge. """
207 maclist = []
208 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
209 if not saved_ifaceobjs:
210 return maclist
211 # we need the old saved configs from the statemanager
212 for oldifaceobj in saved_ifaceobjs:
213 if not oldifaceobj.get_attr_value('address-virtual'):
214 continue
215 for av in oldifaceobj.get_attr_value('address-virtual'):
216 macip = av.split()
217 if len(macip) < 2:
218 self.logger.debug("%s: incorrect old address-virtual attrs '%s'"
219 %(oldifaceobj.name, av))
220 continue
221 maclist.append(macip[0])
222 return maclist
223
224 def get_addressvirtual_ipv6_addrgen_user_conf(self, ifaceobj):
225 ipv6_addrgen = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
226
227 if ipv6_addrgen:
228 # IFLA_INET6_ADDR_GEN_MODE values:
229 # 0 = eui64
230 # 1 = none
231 ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get(ipv6_addrgen.lower(), None)
232
233 if ipv6_addrgen_nl is None:
234 self.logger.warning('%s: invalid value "%s" for attribute address-virtual-ipv6-addrgen' % (ifaceobj.name, ipv6_addrgen))
235 else:
236 return True, ipv6_addrgen_nl
237
238 else:
239 # if user didn't configure ipv6-addrgen, should we reset to default?
240 ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get(
241 self.get_attr_default_value('address-virtual-ipv6-addrgen'),
242 None
243 )
244 if ipv6_addrgen_nl is not None:
245 return True, ipv6_addrgen_nl
246
247 return False, None
248
249 def _remove_running_address_config(self, ifaceobj):
250 if not self.ipcmd.link_exists(ifaceobj.name):
251 return
252 hwaddress = []
253 self.ipcmd.batch_start()
254 for macvlan_prefix in [
255 self._get_macvlan_prefix(ifaceobj),
256 self.get_vrrp_prefix(ifaceobj.name, "4"),
257 self.get_vrrp_prefix(ifaceobj.name, "6")
258 ]:
259 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % macvlan_prefix):
260 macvlan_ifacename = os.path.basename(macvlan_ifacename)
261 if not self.ipcmd.link_exists(macvlan_ifacename):
262 continue
263 hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
264 self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
265 # XXX: Also delete any fdb addresses. This requires, checking mac address
266 # on individual macvlan interfaces and deleting the vlan from that.
267 self.ipcmd.batch_commit()
268 if any(hwaddress):
269 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
270
271 def _remove_address_config(self, ifaceobj, address_virtual_list=None):
272 if not address_virtual_list:
273 self._remove_running_address_config(ifaceobj)
274 return
275
276 if not self.ipcmd.link_exists(ifaceobj.name):
277 return
278 hwaddress = []
279 self.ipcmd.batch_start()
280 av_idx = 0
281 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
282 for av in address_virtual_list:
283 av_attrs = av.split()
284 if len(av_attrs) < 2:
285 self.log_error("%s: incorrect address-virtual attrs '%s'"
286 %(ifaceobj.name, av), ifaceobj,
287 raise_error=False)
288 av_idx += 1
289 continue
290
291 # Delete the macvlan device on this device
292 macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
293 self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
294 if av_attrs[0] != 'None':
295 hwaddress.append(av_attrs[0])
296 av_idx += 1
297 self.ipcmd.batch_commit()
298 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
299
300 def check_mac_address(self, ifaceobj, mac):
301 if mac == 'none':
302 return True
303 try:
304 if int(mac.split(":")[0], 16) & 1 :
305 self.log_error("%s: Multicast bit is set in the virtual mac address '%s'"
306 % (ifaceobj.name, mac), ifaceobj=ifaceobj)
307 return False
308 return True
309 except ValueError:
310 return False
311
312 def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None):
313 """ This function fixes up address virtual interfaces
314 (macvlans) on vrf slaves. Since this fixup is an overhead,
315 this must be called only in cases when ifupdown2 is
316 called on the vrf device or its slave and not when
317 ifupdown2 is called for all devices. When all
318 interfaces are brought up, the expectation is that
319 the normal path will fix up a vrf device or its slaves"""
320
321 if not ifaceobj_getfunc:
322 return
323 if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and
324 self.ipcmd.link_exists(ifaceobj.name)):
325 # if I am a vrf device and I have slaves
326 # that have address virtual config,
327 # enslave the slaves 'address virtual
328 # interfaces (macvlans)' to myself:
329 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
330 if running_slaves:
331 # pick up any existing slaves of a vrf device and
332 # look for their upperdevices and enslave them to the
333 # vrf device:
334 for s in running_slaves:
335 sobjs = ifaceobj_getfunc(s)
336 if (sobjs and
337 (sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)):
338 # enslave all its upper devices to
339 # the vrf device
340 upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name)
341 if not upperdevs:
342 continue
343 for u in upperdevs:
344 # skip vrf device which
345 # will also show up in the
346 # upper device list
347 if u == ifaceobj.name:
348 continue
349 self.ipcmd.link_set(u, 'master', ifaceobj.name,
350 state='up')
351 elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and
352 (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and
353 self.ipcmd.link_exists(ifaceobj.name)):
354 # If I am a vrf slave and I have 'address virtual'
355 # config, make sure my addrress virtual interfaces
356 # (macvlans) are also enslaved to the vrf device
357 vrfname = ifaceobj.get_attr_value_first('vrf')
358 if not vrfname or not self.ipcmd.link_exists(vrfname):
359 return
360 running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name)
361 if not running_uppers:
362 return
363 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
364 if not macvlan_prefix:
365 return
366 for u in running_uppers:
367 if u == vrfname:
368 continue
369 if u.startswith(macvlan_prefix):
370 self.ipcmd.link_set(u, 'master', vrfname,
371 state='up')
372
373 def mac_str_to_int(self, mac):
374 mac_int = 0
375 for n in mac.translate(self.mac_translate_tab).split():
376 mac_int += int(n, 16)
377 return mac_int
378
379 def create_macvlan_and_apply_config(self, ifaceobj, intf_config_list):
380 """
381 intf_config_list = [
382 {
383 "ifname": "macvlan_ifname",
384 "hwaddress": "macvlan_hwaddress",
385 "ips": [str(IPNetwork), ]
386 },
387 ]
388 """
389 user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj)
390 purge_existing = False if ifupdownflags.flags.PERFMODE else True
391 hw_address_list = []
392 ifname = ifaceobj.name
393
394 lower_iface_mtu = update_mtu = None
395 if ifupdownconfig.config.get("adjust_logical_dev_mtu", "1") != "0":
396 if ifaceobj.lowerifaces and intf_config_list:
397 update_mtu = True
398
399 self.ipcmd.batch_start()
400
401 for intf_config_dict in intf_config_list:
402 link_created = False
403 macvlan_ifname = intf_config_dict.get("ifname")
404 macvlan_hwaddr = intf_config_dict.get("hwaddress")
405 macvlan_mode = intf_config_dict.get("mode")
406 ips = intf_config_dict.get("ips")
407
408 if not self.ipcmd.link_exists(macvlan_ifname):
409 self.ipcmd.link_add_macvlan(ifname, macvlan_ifname, macvlan_mode)
410 link_created = True
411
412 # first thing we need to handle vrf enslavement
413 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
414 self._handle_vrf_slaves(macvlan_ifname, ifaceobj)
415
416 if user_configured_ipv6_addrgenmode:
417 self.ipcmd.ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
418
419 if macvlan_hwaddr:
420 self.ipcmd.link_set_hwaddress(macvlan_ifname, macvlan_hwaddr)
421 hw_address_list.append(macvlan_hwaddr)
422
423 if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support():
424 metric = self.ipcmd.get_default_ip_metric()
425 else:
426 metric = None
427
428 self.ipcmd.addr_add_multiple(
429 ifaceobj,
430 macvlan_ifname,
431 ips,
432 purge_existing,
433 metric=metric
434 )
435
436 # If link existed before, flap the link
437 if not link_created:
438
439 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
440 # if the system doesn't support ip addr set METRIC
441 # we need to do manually check the ordering of the ip4 routes
442 self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
443
444 if update_mtu:
445 lower_iface_mtu = self.ipcmd.link_get_mtu(ifname, refresh=True)
446 update_mtu = False
447
448 if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifname, refresh=True):
449 try:
450 self.ipcmd.link_set_mtu(macvlan_ifname,
451 lower_iface_mtu)
452 except Exception as e:
453 self.logger.info('%s: failed to set mtu %s: %s' %
454 (macvlan_ifname, lower_iface_mtu, e))
455
456 # set macvlan device to up in anycase.
457 # since we auto create them here..we are responsible
458 # to bring them up here in the case they were brought down
459 # by some other entity in the system.
460 netlink.link_set_updown(macvlan_ifname, "up")
461 else:
462 try:
463 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
464 # if the system doesn't support ip addr set METRIC
465 # we need to do manually check the ordering of the ip6 routes
466 self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
467 except Exception as e:
468 self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
469
470 # Disable IPv6 duplicate address detection on VRR interfaces
471 for key, sysval in {
472 "accept_dad": "0",
473 "dad_transmits": "0"
474 }.iteritems():
475 syskey = "net.ipv6.conf.%s.%s" % (macvlan_ifname, key)
476 if self.sysctl_get(syskey) != sysval:
477 self.sysctl_set(syskey, sysval)
478
479 self.ipcmd.batch_commit()
480 return hw_address_list
481
482 def _up(self, ifaceobj, ifaceobj_getfunc=None):
483 if not ifupdownflags.flags.ALL:
484 self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
485
486 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
487 vrr_config_list = ifaceobj.get_attr_value("vrrp")
488
489 if not address_virtual_list and not vrr_config_list:
490 # XXX: address virtual is not present. In which case,
491 # delete stale macvlan devices.
492 self._remove_running_address_config(ifaceobj)
493 return
494
495 if ifaceobj.upperifaces and not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
496 self.log_error("%s: invalid placement of address-virtual/vrrp lines "
497 "(must be configured under an interface "
498 "with no upper interfaces or parent interfaces)"
499 % ifaceobj.name, ifaceobj)
500
501 if not self.ipcmd.link_exists(ifaceobj.name):
502 return
503
504 addr_virtual_macs = self.create_macvlan_and_apply_config(
505 ifaceobj,
506 self.translate_addrvirtual_user_config_to_list(
507 ifaceobj,
508 address_virtual_list
509 )
510 )
511
512 vrr_macs = self.create_macvlan_and_apply_config(
513 ifaceobj,
514 self.translate_vrr_user_config_to_list(
515 ifaceobj,
516 vrr_config_list
517 )
518 )
519
520 hw_address_list = addr_virtual_macs + vrr_macs
521
522 # check the statemanager for old configs.
523 # We need to remove only the previously configured FDB entries
524 oldmacs = self._get_macs_from_old_config(ifaceobj)
525 # get a list of fdbs in old that are not in new config meaning they should
526 # be removed since they are gone from the config
527 removed_macs = [mac for mac in oldmacs if mac.lower() not in hw_address_list]
528 self._remove_addresses_from_bridge(ifaceobj, removed_macs)
529 # if ifaceobj is a bridge and bridge is a vlan aware bridge
530 # add the vid to the bridge
531 self._add_addresses_to_bridge(ifaceobj, hw_address_list)
532
533 def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=False):
534 """
535 If (IPv4 addresses provided):
536 00:00:5e:00:01:<V>
537 else if (IPv6 addresses provided):
538 00:00:5e:00:02:<V>
539
540 vrrp 1 10.0.0.15/24
541 vrrp 1 2001:0db8::0370:7334/64
542
543 # Translate:
544 # vrrp 255 10.0.0.15/24 10.0.0.2/1
545 # To:
546 # [
547 # {
548 # "ifname": "macvlan_ifname",
549 # "hwaddress": "macvlan_hwaddress",
550 # "mode": "macvlan_mode",
551 # "ips": [str(IPNetwork), ]
552 # },
553 # ]
554 """
555 ifname = ifaceobj.name
556 user_config_list = []
557
558 for index, config in enumerate(vrr_config_list or []):
559 vrrp_id, ip_addrs = config.split(" ", 1)
560 hex_id = '%02x' % int(vrrp_id)
561 ip4 = []
562 ip6 = []
563
564 for ip_addr in ip_addrs.split():
565 ip_network_obj = IPNetwork(ip_addr)
566 is_ip6 = isinstance(ip_network_obj, IPv6Network)
567
568 if is_ip6:
569 ip6.append(ip_addr)
570 else:
571 ip4.append(ip_addr)
572
573 macvlan_ip4_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "4"), vrrp_id)
574 macvlan_ip6_ifname = "%s%s" % (self.get_vrrp_prefix(ifname, "6"), vrrp_id)
575
576 if ip4 or ifquery:
577 merged_with_existing_obj = False
578 macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
579 macvlan_ip4_mac_int = self.mac_str_to_int(macvlan_ip4_mac)
580 # if the vrr config is defined in different lines for the same ID
581 # we need to save the ip4 and ip6 in the objects we previously
582 # created, example:
583 # vrrp 255 10.0.0.15/24 10.0.0.2/15
584 # vrrp 255 fe80::a00:27ff:fe04:42/64
585 for obj in user_config_list:
586 if obj.get("hwaddress_int") == macvlan_ip4_mac_int:
587 obj["ips"] += ip4
588 merged_with_existing_obj = True
589
590 if not merged_with_existing_obj:
591 # if ip4 config wasn't merge with an existing object
592 # we need to insert it in our list
593 user_config_list.append({
594 "ifname": macvlan_ip4_ifname,
595 "hwaddress": macvlan_ip4_mac,
596 "hwaddress_int": macvlan_ip4_mac_int,
597 "mode": "bridge",
598 "ips": ip4,
599 "id": vrrp_id
600 })
601 elif not ip4 and not ifquery:
602 # special check to see if all ipv4 were removed from the vrrp
603 # configuration, if so we need to remove the associated macvlan
604 if self.ipcmd.link_exists(macvlan_ip4_ifname):
605 netlink.link_del(macvlan_ip4_ifname)
606
607 if ip6 or ifquery:
608 merged_with_existing_obj = False
609 macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
610 macvlan_ip6_mac_int = self.mac_str_to_int(macvlan_ip6_mac)
611 # if the vrr config is defined in different lines for the same ID
612 # we need to save the ip4 and ip6 in the objects we previously
613 # created, example:
614 # vrrp 255 10.0.0.15/24 10.0.0.2/15
615 # vrrp 255 fe80::a00:27ff:fe04:42/64
616
617 for obj in user_config_list:
618 if obj.get("hwaddress_int") == macvlan_ip6_mac_int:
619 obj["ips"] += ip6
620 merged_with_existing_obj = True
621
622 if not merged_with_existing_obj:
623 # if ip6 config wasn't merge with an existing object
624 # we need to insert it in our list
625 user_config_list.append({
626 "ifname": macvlan_ip6_ifname,
627 "hwaddress": macvlan_ip6_mac,
628 "hwaddress_int": macvlan_ip6_mac_int,
629 "mode": "bridge",
630 "ips": ip6,
631 "id": vrrp_id
632 })
633 elif not ip6 and not ifquery:
634 # special check to see if all ipv6 were removed from the vrrp
635 # configuration, if so we need to remove the associated macvlan
636 if self.ipcmd.link_exists(macvlan_ip6_ifname):
637 netlink.link_del(macvlan_ip6_ifname)
638
639 return user_config_list
640
641 def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_list):
642 """
643 # Translate:
644 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
645 # To:
646 # [
647 # {
648 # "ifname": "macvlan_ifname",
649 # "hwaddress": "macvlan_hwaddress",
650 # "ips": [str(IPNetwork), ]
651 # },
652 # ]
653 """
654 user_config_list = []
655
656 if not address_virtual_list:
657 return user_config_list
658
659 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
660
661 for index, addr_virtual in enumerate(address_virtual_list):
662 av_attrs = addr_virtual.split()
663
664 if len(av_attrs) < 2:
665 self.log_error("%s: incorrect address-virtual attrs '%s'"
666 % (ifaceobj.name, addr_virtual), ifaceobj,
667 raise_error=False)
668 continue
669
670 mac = av_attrs[0]
671 if mac:
672 mac = mac.lower()
673
674 if not self.check_mac_address(ifaceobj, mac):
675 continue
676
677 config = {
678 "ifname": "%s%d" % (macvlan_prefix, index),
679 "mode": "private"
680 }
681
682 if mac != "none":
683 config["hwaddress"] = mac
684 config["hwaddress_int"] = self.mac_str_to_int(mac)
685
686 ip_network_obj_list = []
687 for ip in av_attrs[1:]:
688 ip_network_obj_list.append(str(IPNetwork(ip)))
689
690 config["ips"] = ip_network_obj_list
691 user_config_list.append(config)
692
693 return user_config_list
694
695 def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
696 return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
697
698 def _down(self, ifaceobj, ifaceobj_getfunc=None):
699 try:
700 self._remove_address_config(ifaceobj,
701 ifaceobj.get_attr_value('address-virtual'))
702
703 #### VRR
704 hwaddress = []
705 self.ipcmd.batch_start()
706 for vrr_prefix in [self.get_vrrp_prefix(ifaceobj.name, "4"), self.get_vrrp_prefix(ifaceobj.name, "6")]:
707 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
708 macvlan_ifacename = os.path.basename(macvlan_ifacename)
709 if not self.ipcmd.link_exists(macvlan_ifacename):
710 continue
711 hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
712 self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
713 # XXX: Also delete any fdb addresses. This requires, checking mac address
714 # on individual macvlan interfaces and deleting the vlan from that.
715 self.ipcmd.batch_commit()
716 if any(hwaddress):
717 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
718 except Exception, e:
719 import traceback
720 traceback.print_exc()
721 self.log_warn(str(e))
722
723 def _query_check(self, ifaceobj, ifaceobjcurr):
724
725 if not self.ipcmd.link_exists(ifaceobj.name):
726 return
727
728 user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
729 if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values:
730 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
731 user_config_address_virtual_ipv6_addr = None
732
733 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
734
735 macvlans_running_ipv6_addr_virtual = self.query_check_macvlan_config(
736 ifaceobj,
737 ifaceobjcurr,
738 "address-virtual",
739 user_config_address_virtual_ipv6_addr,
740 virtual_addr_list_raw=address_virtual_list,
741 macvlan_config_list=self.translate_addrvirtual_user_config_to_list(
742 ifaceobj,
743 address_virtual_list
744 )
745 )
746
747 vrr_config_list = ifaceobj.get_attr_value("vrrp")
748
749 macvlans_running_ipv6_addr_vrr = self.query_check_macvlan_config(
750 ifaceobj,
751 ifaceobjcurr,
752 "vrrp",
753 user_config_address_virtual_ipv6_addr,
754 virtual_addr_list_raw=vrr_config_list,
755 macvlan_config_list=self.translate_vrr_user_config_to_list(
756 ifaceobj,
757 vrr_config_list,
758 ifquery=True
759 )
760 )
761
762 macvlans_running_ipv6_addr = macvlans_running_ipv6_addr_virtual + macvlans_running_ipv6_addr_vrr
763 if user_config_address_virtual_ipv6_addr:
764 bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr)
765 for running_ipv6_addrgen in macvlans_running_ipv6_addr:
766 if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen:
767 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
768 return
769 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
770
771 def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
772 """
773 macvlan_config_list = [
774 {
775 "ifname": "macvlan_ifname",
776 "hwaddress": "macvlan_hwaddress",
777 "ips": [str(IPNetwork), ]
778 },
779 ]
780 """
781 is_vrr = attr_name == "vrrp"
782 macvlans_running_ipv6_addr = []
783
784 if not virtual_addr_list_raw:
785 return macvlans_running_ipv6_addr
786
787 macvlan_config_queue = deque(macvlan_config_list)
788
789 while macvlan_config_queue:
790
791 ip4_config = None
792 ip6_config = None
793
794 config = macvlan_config_queue.popleft()
795
796 if is_vrr:
797 ip4_config = config
798 ip6_config = macvlan_config_queue.popleft()
799
800 macvlan_ifacename = config.get("ifname")
801
802 if not self.ipcmd.link_exists(macvlan_ifacename):
803 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
804 continue
805
806 macvlan_hwaddress = config.get("hwaddress")
807 macvlan_hwaddress_int = config.get("hwaddress_int")
808
809 if user_config_address_virtual_ipv6_addr:
810 macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
811
812 # Check mac and ip address
813 rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
814 raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
815 ifname=macvlan_ifacename,
816 details=False,
817 addr_virtual_ifaceobj=ifaceobj
818 )
819
820 if not is_vrr:
821 ips = config.get("ips")
822
823 if not raddrs or not rhwaddress:
824 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
825 continue
826
827 try:
828 if self.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
829 and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
830 and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
831 ifaceobjcurr.update_config_with_status(
832 attr_name,
833 " ".join(virtual_addr_list_raw),
834 0
835 )
836 else:
837 ifaceobjcurr.update_config_with_status(
838 attr_name,
839 '%s %s' % (rhwaddress, ' '.join(raddrs)),
840 1
841 )
842 except:
843 ifaceobjcurr.update_config_with_status(
844 attr_name,
845 '%s %s' % (rhwaddress, ' '.join(raddrs)),
846 1
847 )
848 else:
849 # VRRP
850
851 ok = False
852 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
853 if not ip4_config.get("ips") or ip4_macvlan_hwaddress == ip4_config.get("hwaddress"):
854 ip6_macvlan_ifname = ip6_config.get("ifname")
855 ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
856
857 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
858 if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
859
860 # check all ip4
861 if self.ipcmd.compare_user_config_vs_running_state(
862 ip4_running_addrs,
863 ip4_config.get("ips")
864 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
865 ip6_running_addrs = self.ipcmd.get_running_addrs(
866 ifname=ip6_macvlan_ifname,
867 details=False,
868 addr_virtual_ifaceobj=ifaceobj
869 )
870
871 # check all ip6
872 if self.ipcmd.compare_user_config_vs_running_state(
873 ip6_running_addrs,
874 ip6_config.get("ips")
875 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
876 ifaceobjcurr.update_config_with_status(
877 attr_name,
878 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
879 0
880 )
881 ok = True
882
883 if not ok:
884 ifaceobjcurr.update_config_with_status(
885 attr_name,
886 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
887 1
888 )
889
890 return macvlans_running_ipv6_addr
891
892 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
893 macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
894 address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
895 macvlans_ipv6_addrgen_list = []
896 for av in address_virtuals:
897 macvlan_ifacename = os.path.basename(av)
898 rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
899
900 raddress = []
901 for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
902 raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
903
904 raddress = list(set(raddress))
905
906 if not raddress:
907 self.logger.warn('%s: no running addresses'
908 %ifaceobjrunning.name)
909 raddress = []
910 ifaceobjrunning.update_config('address-virtual',
911 '%s %s' %(rhwaddress, ' '.join(raddress)))
912
913 macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
914
915 macvlan_count = len(address_virtuals)
916 if not macvlan_count:
917 return
918 ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1]
919
920 for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list:
921 if macvlan_ipv6_addrgen != ipv6_addrgen:
922 # one macvlan has a different ipv6-addrgen configuration
923 # we simply return, ifquery-running will print the macvlan
924 # stanzas with the ipv6-addrgen on/off attribute
925 return
926 ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
927
928
929 _run_ops = {'up' : _up,
930 'down' : _down,
931 'query-checkcurr' : _query_check,
932 'query-running' : _query_running}
933
934 def get_ops(self):
935 """ returns list of ops supported by this module """
936 return self._run_ops.keys()
937
938 def _init_command_handlers(self):
939 if not self.ipcmd:
940 self.ipcmd = LinkUtils()
941
942 def run(self, ifaceobj, operation, query_ifaceobj=None,
943 ifaceobj_getfunc=None, **extra_args):
944 """ run vlan configuration on the interface object passed as argument
945
946 Args:
947 **ifaceobj** (object): iface object
948
949 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
950 'query-running'
951 Kwargs:
952 **query_ifaceobj** (object): query check ifaceobject. This is only
953 valid when op is 'query-checkcurr'. It is an object same as
954 ifaceobj, but contains running attribute values and its config
955 status. The modules can use it to return queried running state
956 of interfaces. status is success if the running state is same
957 as user required state in ifaceobj. error otherwise.
958 """
959 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
960 return
961 op_handler = self._run_ops.get(operation)
962 if not op_handler:
963 return
964 self._init_command_handlers()
965 if operation == 'query-checkcurr':
966 op_handler(self, ifaceobj, query_ifaceobj)
967 else:
968 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)