]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/addons/addressvirtual.py
addons: addressvirtual: make sure vrr macvlans names are 15 chars long max
[mirror_ifupdown2.git] / ifupdown2 / addons / addressvirtual.py
CommitLineData
15ef32ea
RP
1#!/usr/bin/python
2#
d486dd0d 3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
15ef32ea
RP
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
15ef32ea
RP
7import os
8import glob
9
5bc963f0
JF
10from string import maketrans
11from collections import deque
d486dd0d
JF
12from ipaddr import IPNetwork, IPv6Network
13
14try:
15 from ifupdown2.ifupdown.iface import *
7b711dc5 16 from ifupdown2.ifupdown.utils import utils
d486dd0d
JF
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
23e8546d 23 import ifupdown2.ifupdown.policymanager as policymanager
d486dd0d
JF
24 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
25 import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig
26except ImportError:
27 from ifupdown.iface import *
7b711dc5 28 from ifupdown.utils import utils
d486dd0d
JF
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
23e8546d 35 import ifupdown.policymanager as policymanager
d486dd0d
JF
36 import ifupdown.ifupdownflags as ifupdownflags
37 import ifupdown.ifupdownconfig as ifupdownconfig
38
39
15ef32ea
RP
40class 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' :
482b2fab 48 { 'help' : 'bridge router virtual mac and ips',
100ab3ea 49 'multivalue' : True,
2c592263 50 'validvals' : ['<mac-ip/prefixlen-list>',],
7b711dc5
JF
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': [
b478792b
JF
58 'address-virtual-ipv6-addrgen on',
59 'address-virtual-ipv6-addrgen off'
7b711dc5 60 ]
5bc963f0
JF
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 ]
7b711dc5
JF
69 }
70 }}
15ef32ea 71
8e113d63 72
15ef32ea
RP
73 def __init__(self, *args, **kargs):
74 moduleBase.__init__(self, *args, **kargs)
75 self.ipcmd = None
8e113d63 76 self._bridge_fdb_query_cache = {}
23e8546d
JF
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 )
15ef32ea 84
17da0561 85 self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1}
5bc963f0 86 self.mac_translate_tab = maketrans(":.-,", " ")
17da0561 87
42e85fc8 88 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
5bc963f0 89 if ifaceobj.get_attr_value('address-virtual') or ifaceobj.get_attr_value("vrrp"):
42e85fc8 90 ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE
15ef32ea 91
aaef0a79
RP
92 def _get_macvlan_prefix(self, ifaceobj):
93 return '%s-v' %ifaceobj.name[0:13].replace('.', '-')
94
8fb6dd67
JF
95 @staticmethod
96 def get_vrr_prefix(ifname, family):
97 return "%s-%sv" % (ifname[0:10].replace(".", "-"), family)
98
e1601369 99 def _add_addresses_to_bridge(self, ifaceobj, hwaddress):
8e113d63 100 # XXX: batch the addresses
d486dd0d
JF
101 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
102 bridgename = ifaceobj.lowerifaces[0]
103 vlan = self._get_vlan_id(ifaceobj)
e1601369
RP
104 if self.ipcmd.bridge_is_vlan_aware(bridgename):
105 [self.ipcmd.bridge_fdb_add(bridgename, addr,
106 vlan) for addr in hwaddress]
8e113d63
RP
107 elif self.ipcmd.is_bridge(ifaceobj.name):
108 [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr)
109 for addr in hwaddress]
e1601369
RP
110
111 def _remove_addresses_from_bridge(self, ifaceobj, hwaddress):
8e113d63 112 # XXX: batch the addresses
d486dd0d
JF
113 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
114 bridgename = ifaceobj.lowerifaces[0]
115 vlan = self._get_vlan_id(ifaceobj)
e1601369 116 if self.ipcmd.bridge_is_vlan_aware(bridgename):
55072bd1
ST
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
8e113d63 123 elif self.ipcmd.is_bridge(ifaceobj.name):
55072bd1
ST
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
8e113d63
RP
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 """
d486dd0d
JF
143 if ifaceobj.link_kind & ifaceLinkKind.VLAN:
144 bridgename = ifaceobj.lowerifaces[0]
145 vlan = self._get_vlan_id(ifaceobj)
8e113d63 146 if self.ipcmd.bridge_is_vlan_aware(bridgename):
d486dd0d 147 fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan))
5bc963f0 148 if not fdb_addrs:
8e113d63 149 return False
5bc963f0
JF
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
8e113d63 155 return True
e1601369 156
00f6105d
RP
157 def _fix_connected_route(self, ifaceobj, vifacename, addr):
158 #
d486dd0d 159 # XXX: Hack to make sure the primary address
00f6105d
RP
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.
d486dd0d 165 #
00f6105d
RP
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)
d486dd0d
JF
173
174 # we don't support ip6 route fix yet
175 if type(ip) == IPv6Network:
176 return
177
00f6105d
RP
178 route_prefix = '%s/%d' %(ip.network, ip.prefixlen)
179
aa895ecd
JF
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
d486dd0d 187 if dev and dev != ifaceobj.name:
00f6105d
RP
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)'
d486dd0d 196 % (ifaceobj.name, str(e)))
00f6105d
RP
197 pass
198
1b284018
RP
199 def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj):
200 vrfname = self.ipcmd.link_get_master(ifaceobj.name)
201 if vrfname:
d486dd0d 202 self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname)
1b284018 203
55072bd1
ST
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
7b711dc5
JF
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:
007cae35
JF
228 # IFLA_INET6_ADDR_GEN_MODE values:
229 # 0 = eui64
230 # 1 = none
17da0561 231 ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get(ipv6_addrgen.lower(), None)
007cae35
JF
232
233 if ipv6_addrgen_nl is None:
007cae35
JF
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
7b711dc5 237
17da0561
JF
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
7b711dc5
JF
247 return False, None
248
e1601369
RP
249 def _remove_running_address_config(self, ifaceobj):
250 if not self.ipcmd.link_exists(ifaceobj.name):
15ef32ea 251 return
e1601369 252 hwaddress = []
15ef32ea 253 self.ipcmd.batch_start()
709f7942
JF
254 for macvlan_prefix in [
255 self._get_macvlan_prefix(ifaceobj),
256 self.get_vrr_prefix(ifaceobj.name, "4"),
257 self.get_vrr_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.
15ef32ea 267 self.ipcmd.batch_commit()
e1601369
RP
268 if any(hwaddress):
269 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
15ef32ea 270
e1601369
RP
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
aaef0a79 281 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
e1601369
RP
282 for av in address_virtual_list:
283 av_attrs = av.split()
284 if len(av_attrs) < 2:
bf3eda91
RP
285 self.log_error("%s: incorrect address-virtual attrs '%s'"
286 %(ifaceobj.name, av), ifaceobj,
287 raise_error=False)
e1601369
RP
288 av_idx += 1
289 continue
290
291 # Delete the macvlan device on this device
cb46a208 292 macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx)
e1601369
RP
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)
15ef32ea 299
4d3dc0f7 300 def check_mac_address(self, ifaceobj, mac):
5bc963f0 301 if mac == 'none':
d486dd0d 302 return True
4d3dc0f7 303 try:
586535e8 304 if int(mac.split(":")[0], 16) & 1 :
5b1fffaf
JF
305 self.log_error("%s: Multicast bit is set in the virtual mac address '%s'"
306 % (ifaceobj.name, mac), ifaceobj=ifaceobj)
4d3dc0f7
N
307 return False
308 return True
5b1fffaf 309 except ValueError:
4d3dc0f7
N
310 return False
311
42e85fc8
RP
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
d486dd0d
JF
349 self.ipcmd.link_set(u, 'master', ifaceobj.name,
350 state='up')
42e85fc8
RP
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):
d486dd0d
JF
370 self.ipcmd.link_set(u, 'master', vrfname,
371 state='up')
42e85fc8 372
5bc963f0
JF
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 ips = intf_config_dict.get("ips")
406
407 if not self.ipcmd.link_exists(macvlan_ifname):
408 self.ipcmd.link_add_macvlan(ifname, macvlan_ifname)
409 link_created = True
410
411 # first thing we need to handle vrf enslavement
412 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
413 self._handle_vrf_slaves(macvlan_ifname, ifaceobj)
414
415 if user_configured_ipv6_addrgenmode:
416 self.ipcmd.ipv6_addrgen(macvlan_ifname, ipv6_addrgen_user_value, link_created)
417
418 if macvlan_hwaddr:
419 self.ipcmd.link_set_hwaddress(macvlan_ifname, macvlan_hwaddr)
420 hw_address_list.append(macvlan_hwaddr)
421
422 if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support():
423 metric = self.ipcmd.get_default_ip_metric()
424 else:
425 metric = None
426
427 self.ipcmd.addr_add_multiple(
428 ifaceobj,
429 macvlan_ifname,
430 ips,
431 purge_existing,
432 metric=metric
433 )
434
435 # If link existed before, flap the link
436 if not link_created:
437
438 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
439 # if the system doesn't support ip addr set METRIC
440 # we need to do manually check the ordering of the ip4 routes
441 self._fix_connected_route(ifaceobj, macvlan_ifname, ips[0])
442
443 if update_mtu:
444 lower_iface_mtu = self.ipcmd.link_get_mtu(ifname, refresh=True)
445 update_mtu = False
446
447 if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifname, refresh=True):
448 try:
449 self.ipcmd.link_set_mtu(macvlan_ifname,
450 lower_iface_mtu)
451 except Exception as e:
452 self.logger.info('%s: failed to set mtu %s: %s' %
453 (macvlan_ifname, lower_iface_mtu, e))
454
455 # set macvlan device to up in anycase.
456 # since we auto create them here..we are responsible
457 # to bring them up here in the case they were brought down
458 # by some other entity in the system.
459 netlink.link_set_updown(macvlan_ifname, "up")
460 else:
461 try:
462 if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support():
463 # if the system doesn't support ip addr set METRIC
464 # we need to do manually check the ordering of the ip6 routes
465 self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifname, ips)
466 except Exception as e:
467 self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e)
468
469 # Disable IPv6 duplicate address detection on VRR interfaces
470 for key, sysval in {
471 "accept_dad": "0",
472 "dad_transmits": "0"
473 }.iteritems():
474 syskey = "net.ipv6.conf.%s.%s" % (macvlan_ifname, key)
475 if self.sysctl_get(syskey) != sysval:
476 self.sysctl_set(syskey, sysval)
477
478 self.ipcmd.batch_commit()
479 return hw_address_list
480
42e85fc8
RP
481 def _up(self, ifaceobj, ifaceobj_getfunc=None):
482 if not ifupdownflags.flags.ALL:
483 self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc)
5bc963f0 484
15ef32ea 485 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
5bc963f0
JF
486 vrr_config_list = ifaceobj.get_attr_value("vrrp")
487
488 if not address_virtual_list and not vrr_config_list:
15ef32ea 489 # XXX: address virtual is not present. In which case,
00f6105d 490 # delete stale macvlan devices.
5bc963f0 491 self._remove_running_address_config(ifaceobj)
15ef32ea
RP
492 return
493
5bc963f0
JF
494 if ifaceobj.upperifaces and not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
495 self.log_error("%s: invalid placement of address-virtual/vrrp lines "
496 "(must be configured under an interface "
497 "with no upper interfaces or parent interfaces)"
498 % ifaceobj.name, ifaceobj)
f466af7a 499
e1601369 500 if not self.ipcmd.link_exists(ifaceobj.name):
15ef32ea 501 return
5bc963f0
JF
502
503 addr_virtual_macs = self.create_macvlan_and_apply_config(
504 ifaceobj,
505 self.translate_addrvirtual_user_config_to_list(
506 ifaceobj,
507 address_virtual_list
508 )
509 )
510
511 vrr_macs = self.create_macvlan_and_apply_config(
512 ifaceobj,
513 self.translate_vrr_user_config_to_list(
514 ifaceobj,
515 vrr_config_list
516 )
517 )
518
519 hw_address_list = addr_virtual_macs + vrr_macs
520
521 # check the statemanager for old configs.
522 # We need to remove only the previously configured FDB entries
523 oldmacs = self._get_macs_from_old_config(ifaceobj)
524 # get a list of fdbs in old that are not in new config meaning they should
525 # be removed since they are gone from the config
526 removed_macs = [mac for mac in oldmacs if mac.lower() not in hw_address_list]
527 self._remove_addresses_from_bridge(ifaceobj, removed_macs)
528 # if ifaceobj is a bridge and bridge is a vlan aware bridge
529 # add the vid to the bridge
530 self._add_addresses_to_bridge(ifaceobj, hw_address_list)
531
5bc963f0
JF
532 def translate_vrr_user_config_to_list(self, ifaceobj, vrr_config_list, ifquery=False):
533 """
534 If (IPv4 addresses provided):
535 00:00:5e:00:01:<V>
536 else if (IPv6 addresses provided):
537 00:00:5e:00:02:<V>
538
539 vrrp 1 10.0.0.15/24
540 vrrp 1 2001:0db8::0370:7334/64
541
542 # Translate:
543 # vrrp 255 10.0.0.15/24 10.0.0.2/1
544 # To:
545 # [
546 # {
547 # "ifname": "macvlan_ifname",
548 # "hwaddress": "macvlan_hwaddress",
549 # "ips": [str(IPNetwork), ]
550 # },
551 # ]
552 """
553 ifname = ifaceobj.name
554 user_config_list = []
555
8fb6dd67 556 for index, config in enumerate(vrr_config_list or []):
5bc963f0
JF
557 vrrp_id, ip_addrs = config.split(" ", 1)
558 hex_id = '%02x' % int(vrrp_id)
559 ip4 = []
560 ip6 = []
561
562 for ip_addr in ip_addrs.split():
563 ip_network_obj = IPNetwork(ip_addr)
564 is_ip6 = isinstance(ip_network_obj, IPv6Network)
565
566 if is_ip6:
567 ip6.append(ip_addr)
568 else:
569 ip4.append(ip_addr)
570
8fb6dd67
JF
571 macvlan_ip4_ifname = "%s%s" % (self.get_vrr_prefix(ifname, "4"), index)
572 macvlan_ip6_ifname = "%s%s" % (self.get_vrr_prefix(ifname, "6"), index)
5bc963f0
JF
573
574 merged_with_existing_obj = False
575 # if the vrr config is defined in different lines for the same ID
576 # we need to save the ip4 and ip6 in the objects we previously
577 # created, example:
578 # vrrp 255 10.0.0.15/24 10.0.0.2/15
579 # vrrp 255 fe80::a00:27ff:fe04:42/64
580 for obj in user_config_list:
581 if obj.get("ifname") == macvlan_ip4_ifname:
582 obj["ips"] += ip4
583 merged_with_existing_obj = True
584 elif obj.get("ifname") == macvlan_ip6_ifname:
585 obj["ips"] += ip6
586 merged_with_existing_obj = True
587
588 if merged_with_existing_obj:
589 continue
590
591 if ip4 or ifquery:
592 # config_ip4
593 macvlan_ip4_mac = "00:00:5e:00:01:%s" % hex_id
594 user_config_list.append({
595 "ifname": macvlan_ip4_ifname,
596 "hwaddress": macvlan_ip4_mac,
597 "hwaddress_int": self.mac_str_to_int(macvlan_ip4_mac),
598 "ips": ip4,
599 "id": vrrp_id
600 })
601
602 if ip6 or ifquery:
603 # config_ip6
604 macvlan_ip6_mac = "00:00:5e:00:02:%s" % hex_id
605 user_config_list.append({
606 "ifname": macvlan_ip6_ifname,
607 "hwaddress": macvlan_ip6_mac,
608 "hwaddress_int": self.mac_str_to_int(macvlan_ip6_mac),
609 "ips": ip6,
610 "id": vrrp_id
611 })
612
613 return user_config_list
614
615 def translate_addrvirtual_user_config_to_list(self, ifaceobj, address_virtual_list):
616 """
617 # Translate:
618 # address-virtual 00:11:22:33:44:01 2001:0db8::0370:7334/64 11.0.1.1/24 11.0.1.2/24
619 # To:
620 # [
621 # {
622 # "ifname": "macvlan_ifname",
623 # "hwaddress": "macvlan_hwaddress",
624 # "ips": [str(IPNetwork), ]
625 # },
626 # ]
627 """
628 user_config_list = []
629
630 if not address_virtual_list:
631 return user_config_list
632
633 macvlan_prefix = self._get_macvlan_prefix(ifaceobj)
634
635 for index, addr_virtual in enumerate(address_virtual_list):
636 av_attrs = addr_virtual.split()
637
638 if len(av_attrs) < 2:
639 self.log_error("%s: incorrect address-virtual attrs '%s'"
640 % (ifaceobj.name, addr_virtual), ifaceobj,
641 raise_error=False)
642 continue
643
644 mac = av_attrs[0]
645 if mac:
646 mac = mac.lower()
647
648 if not self.check_mac_address(ifaceobj, mac):
649 continue
650
651 config = {"ifname": "%s%d" % (macvlan_prefix, index)}
652
653 if mac != "none":
654 config["hwaddress"] = mac
655 config["hwaddress_int"] = self.mac_str_to_int(mac)
656
657 ip_network_obj_list = []
658 for ip in av_attrs[1:]:
659 ip_network_obj_list.append(str(IPNetwork(ip)))
660
661 config["ips"] = ip_network_obj_list
662 user_config_list.append(config)
663
664 return user_config_list
665
666 def process_macvlans_config(self, ifaceobj, attr_name, virtual_addr_list_raw, macvlan_config_list):
667 return self.create_macvlan_and_apply_config(ifaceobj, macvlan_config_list)
15ef32ea 668
42e85fc8 669 def _down(self, ifaceobj, ifaceobj_getfunc=None):
15ef32ea 670 try:
cb46a208
RP
671 self._remove_address_config(ifaceobj,
672 ifaceobj.get_attr_value('address-virtual'))
5bc963f0
JF
673
674 #### VRR
675 hwaddress = []
676 self.ipcmd.batch_start()
677 for vrr_prefix in [self.get_vrr_prefix(ifaceobj.name, "4"), self.get_vrr_prefix(ifaceobj.name, "6")]:
678 for macvlan_ifacename in glob.glob("/sys/class/net/%s*" % vrr_prefix):
679 macvlan_ifacename = os.path.basename(macvlan_ifacename)
680 if not self.ipcmd.link_exists(macvlan_ifacename):
681 continue
682 hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename))
683 self.ipcmd.link_delete(os.path.basename(macvlan_ifacename))
684 # XXX: Also delete any fdb addresses. This requires, checking mac address
685 # on individual macvlan interfaces and deleting the vlan from that.
686 self.ipcmd.batch_commit()
687 if any(hwaddress):
688 self._remove_addresses_from_bridge(ifaceobj, hwaddress)
15ef32ea 689 except Exception, e:
5bc963f0
JF
690 import traceback
691 traceback.print_exc()
15ef32ea
RP
692 self.log_warn(str(e))
693
694 def _query_check(self, ifaceobj, ifaceobjcurr):
5bc963f0 695
cb46a208
RP
696 if not self.ipcmd.link_exists(ifaceobj.name):
697 return
007cae35
JF
698
699 user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen')
700 if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values:
701 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
702 user_config_address_virtual_ipv6_addr = None
5bc963f0
JF
703
704 address_virtual_list = ifaceobj.get_attr_value('address-virtual')
705
706 macvlans_running_ipv6_addr_virtual = self.query_check_macvlan_config(
707 ifaceobj,
708 ifaceobjcurr,
709 "address-virtual",
710 user_config_address_virtual_ipv6_addr,
711 virtual_addr_list_raw=address_virtual_list,
712 macvlan_config_list=self.translate_addrvirtual_user_config_to_list(
713 ifaceobj,
714 address_virtual_list
715 )
716 )
717
718 vrr_config_list = ifaceobj.get_attr_value("vrrp")
719
720 macvlans_running_ipv6_addr_vrr = self.query_check_macvlan_config(
721 ifaceobj,
722 ifaceobjcurr,
723 "vrrp",
724 user_config_address_virtual_ipv6_addr,
725 virtual_addr_list_raw=vrr_config_list,
726 macvlan_config_list=self.translate_vrr_user_config_to_list(
727 ifaceobj,
728 vrr_config_list,
729 ifquery=True
730 )
731 )
732
733 macvlans_running_ipv6_addr = macvlans_running_ipv6_addr_virtual + macvlans_running_ipv6_addr_vrr
734 if user_config_address_virtual_ipv6_addr:
735 bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr)
736 for running_ipv6_addrgen in macvlans_running_ipv6_addr:
737 if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen:
738 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1)
739 return
740 ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0)
741
742 def query_check_macvlan_config(self, ifaceobj, ifaceobjcurr, attr_name, user_config_address_virtual_ipv6_addr, virtual_addr_list_raw, macvlan_config_list):
743 """
744 macvlan_config_list = [
745 {
746 "ifname": "macvlan_ifname",
747 "hwaddress": "macvlan_hwaddress",
748 "ips": [str(IPNetwork), ]
749 },
750 ]
751 """
752 is_vrr = attr_name == "vrrp"
007cae35
JF
753 macvlans_running_ipv6_addr = []
754
5bc963f0
JF
755 if not virtual_addr_list_raw:
756 return macvlans_running_ipv6_addr
757
758 macvlan_config_queue = deque(macvlan_config_list)
759
760 while macvlan_config_queue:
761
762 ip4_config = None
763 ip6_config = None
764
765 config = macvlan_config_queue.popleft()
766
767 if is_vrr:
768 ip4_config = config
769 ip6_config = macvlan_config_queue.popleft()
770
771 macvlan_ifacename = config.get("ifname")
15ef32ea 772
cb46a208 773 if not self.ipcmd.link_exists(macvlan_ifacename):
5bc963f0 774 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
cb46a208 775 continue
007cae35 776
5bc963f0
JF
777 macvlan_hwaddress = config.get("hwaddress")
778 macvlan_hwaddress_int = config.get("hwaddress_int")
779
007cae35
JF
780 if user_config_address_virtual_ipv6_addr:
781 macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))
782
cb46a208 783 # Check mac and ip address
5bc963f0
JF
784 rhwaddress = ip4_macvlan_hwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
785 raddrs = ip4_running_addrs = self.ipcmd.get_running_addrs(
d486dd0d
JF
786 ifname=macvlan_ifacename,
787 details=False,
788 addr_virtual_ifaceobj=ifaceobj
789 )
007cae35 790
5bc963f0
JF
791 if not is_vrr:
792 ips = config.get("ips")
793
794 if not raddrs or not rhwaddress:
795 ifaceobjcurr.update_config_with_status(attr_name, "", 1)
796 continue
797
798 try:
799 if self.mac_str_to_int(rhwaddress) == macvlan_hwaddress_int \
800 and self.ipcmd.compare_user_config_vs_running_state(raddrs, ips) \
801 and self._check_addresses_in_bridge(ifaceobj, macvlan_hwaddress):
802 ifaceobjcurr.update_config_with_status(
803 attr_name,
804 " ".join(virtual_addr_list_raw),
805 0
806 )
807 else:
808 ifaceobjcurr.update_config_with_status(
809 attr_name,
810 '%s %s' % (rhwaddress, ' '.join(raddrs)),
811 1
812 )
813 except:
814 ifaceobjcurr.update_config_with_status(
815 attr_name,
816 '%s %s' % (rhwaddress, ' '.join(raddrs)),
817 1
818 )
819 else:
820 # VRRP
821
822 ok = False
823 # check macvlan ip4 hwaddress (only if ip4 were provided by the user)
824 if not ip4_config.get("ips") or ip4_macvlan_hwaddress == ip4_config.get("hwaddress"):
825 ip6_macvlan_ifname = ip6_config.get("ifname")
826 ip6_macvlan_hwaddress = ip6_config.get("hwaddress")
827
828 # check macvlan ip6 hwaddress (only if ip6 were provided by the user)
829 if not ip6_config.get("ips") or self.ipcmd.link_get_hwaddress(ip6_macvlan_ifname) == ip6_macvlan_hwaddress:
830
831 # check all ip4
832 if self.ipcmd.compare_user_config_vs_running_state(
833 ip4_running_addrs,
834 ip4_config.get("ips")
835 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
836 ip6_running_addrs = self.ipcmd.get_running_addrs(
837 ifname=ip6_macvlan_ifname,
838 details=False,
839 addr_virtual_ifaceobj=ifaceobj
840 )
841
842 # check all ip6
843 if self.ipcmd.compare_user_config_vs_running_state(
844 ip6_running_addrs,
845 ip6_config.get("ips")
846 ) and self._check_addresses_in_bridge(ifaceobj, ip4_macvlan_hwaddress):
847 ifaceobjcurr.update_config_with_status(
848 attr_name,
849 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
850 0
851 )
852 ok = True
853
854 if not ok:
855 ifaceobjcurr.update_config_with_status(
856 attr_name,
857 "%s %s" % (ip4_config.get("id"), " ".join(ip4_config.get("ips") + ip6_config.get("ips"))),
858 1
859 )
860
861 return macvlans_running_ipv6_addr
15ef32ea 862
42e85fc8 863 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
aaef0a79 864 macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning)
8e113d63 865 address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix)
007cae35 866 macvlans_ipv6_addrgen_list = []
8e113d63
RP
867 for av in address_virtuals:
868 macvlan_ifacename = os.path.basename(av)
869 rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename)
c3175b31
JF
870
871 raddress = []
872 for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []:
873 raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or [])
874
875 raddress = list(set(raddress))
876
8e113d63
RP
877 if not raddress:
878 self.logger.warn('%s: no running addresses'
879 %ifaceobjrunning.name)
880 raddress = []
881 ifaceobjrunning.update_config('address-virtual',
c3175b31 882 '%s %s' %(rhwaddress, ' '.join(raddress)))
007cae35
JF
883
884 macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)))
885
886 macvlan_count = len(address_virtuals)
887 if not macvlan_count:
888 return
889 ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1]
890
891 for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list:
892 if macvlan_ipv6_addrgen != ipv6_addrgen:
893 # one macvlan has a different ipv6-addrgen configuration
894 # we simply return, ifquery-running will print the macvlan
895 # stanzas with the ipv6-addrgen on/off attribute
896 return
897 ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on')
898
15ef32ea
RP
899
900 _run_ops = {'up' : _up,
901 'down' : _down,
902 'query-checkcurr' : _query_check,
903 'query-running' : _query_running}
904
905 def get_ops(self):
906 """ returns list of ops supported by this module """
907 return self._run_ops.keys()
908
909 def _init_command_handlers(self):
910 if not self.ipcmd:
d486dd0d 911 self.ipcmd = LinkUtils()
15ef32ea 912
42e85fc8
RP
913 def run(self, ifaceobj, operation, query_ifaceobj=None,
914 ifaceobj_getfunc=None, **extra_args):
15ef32ea
RP
915 """ run vlan configuration on the interface object passed as argument
916
917 Args:
918 **ifaceobj** (object): iface object
919
920 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
921 'query-running'
922 Kwargs:
923 **query_ifaceobj** (object): query check ifaceobject. This is only
924 valid when op is 'query-checkcurr'. It is an object same as
925 ifaceobj, but contains running attribute values and its config
926 status. The modules can use it to return queried running state
927 of interfaces. status is success if the running state is same
928 as user required state in ifaceobj. error otherwise.
929 """
84ca006f
RP
930 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
931 return
15ef32ea
RP
932 op_handler = self._run_ops.get(operation)
933 if not op_handler:
934 return
935 self._init_command_handlers()
936 if operation == 'query-checkcurr':
937 op_handler(self, ifaceobj, query_ifaceobj)
938 else:
42e85fc8 939 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)