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