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