]>
Commit | Line | Data |
---|---|---|
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 |
7 | import os |
8 | import glob | |
007cae35 | 9 | import socket |
15ef32ea | 10 | |
d486dd0d JF |
11 | from ipaddr import IPNetwork, IPv6Network |
12 | ||
13 | try: | |
14 | from ifupdown2.ifupdown.iface import * | |
7b711dc5 | 15 | from ifupdown2.ifupdown.utils import utils |
d486dd0d JF |
16 | from ifupdown2.ifupdown.netlink import netlink |
17 | ||
18 | from ifupdown2.ifupdownaddons.LinkUtils import LinkUtils | |
19 | from ifupdown2.ifupdownaddons.modulebase import moduleBase | |
20 | ||
21 | import ifupdown2.ifupdown.statemanager as statemanager | |
23e8546d | 22 | import ifupdown2.ifupdown.policymanager as policymanager |
d486dd0d JF |
23 | import ifupdown2.ifupdown.ifupdownflags as ifupdownflags |
24 | import ifupdown2.ifupdown.ifupdownconfig as ifupdownconfig | |
25 | except ImportError: | |
26 | from ifupdown.iface import * | |
7b711dc5 | 27 | from ifupdown.utils import utils |
d486dd0d JF |
28 | from ifupdown.netlink import netlink |
29 | ||
30 | from ifupdownaddons.LinkUtils import LinkUtils | |
31 | from ifupdownaddons.modulebase import moduleBase | |
32 | ||
33 | import ifupdown.statemanager as statemanager | |
23e8546d | 34 | import ifupdown.policymanager as policymanager |
d486dd0d JF |
35 | import ifupdown.ifupdownflags as ifupdownflags |
36 | import ifupdown.ifupdownconfig as ifupdownconfig | |
37 | ||
38 | ||
15ef32ea RP |
39 | class addressvirtual(moduleBase): |
40 | """ ifupdown2 addon module to configure virtual addresses """ | |
41 | ||
42 | _modinfo = {'mhelp' : 'address module configures virtual addresses for ' + | |
43 | 'interfaces. It creates a macvlan interface for ' + | |
44 | 'every mac ip address-virtual line', | |
45 | 'attrs' : { | |
46 | 'address-virtual' : | |
482b2fab | 47 | { 'help' : 'bridge router virtual mac and ips', |
100ab3ea | 48 | 'multivalue' : True, |
2c592263 | 49 | 'validvals' : ['<mac-ip/prefixlen-list>',], |
7b711dc5 JF |
50 | 'example': ['address-virtual 00:11:22:33:44:01 11.0.1.1/24 11.0.1.2/24'] |
51 | }, | |
52 | 'address-virtual-ipv6-addrgen': { | |
53 | 'help': 'enable disable ipv6 link addrgenmode', | |
54 | 'validvals': ['on', 'off'], | |
55 | 'default': 'on', | |
56 | 'example': [ | |
b478792b JF |
57 | 'address-virtual-ipv6-addrgen on', |
58 | 'address-virtual-ipv6-addrgen off' | |
7b711dc5 JF |
59 | ] |
60 | } | |
61 | }} | |
15ef32ea | 62 | |
8e113d63 | 63 | |
15ef32ea RP |
64 | def __init__(self, *args, **kargs): |
65 | moduleBase.__init__(self, *args, **kargs) | |
66 | self.ipcmd = None | |
8e113d63 | 67 | self._bridge_fdb_query_cache = {} |
23e8546d JF |
68 | self.addressvirtual_with_route_metric = utils.get_boolean_from_string( |
69 | policymanager.policymanager_api.get_module_globals( | |
70 | module_name=self.__class__.__name__, | |
71 | attr='addressvirtual_with_route_metric' | |
72 | ), | |
73 | default=True | |
74 | ) | |
15ef32ea | 75 | |
17da0561 JF |
76 | self.address_virtual_ipv6_addrgen_value_dict = {'on': 0, 'yes': 0, '0': 0, 'off': 1, 'no': 1, '1': 1} |
77 | ||
42e85fc8 RP |
78 | def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None): |
79 | if ifaceobj.get_attr_value('address-virtual'): | |
80 | ifaceobj.link_privflags |= ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE | |
15ef32ea | 81 | |
aaef0a79 RP |
82 | def _get_macvlan_prefix(self, ifaceobj): |
83 | return '%s-v' %ifaceobj.name[0:13].replace('.', '-') | |
84 | ||
e1601369 | 85 | def _add_addresses_to_bridge(self, ifaceobj, hwaddress): |
8e113d63 | 86 | # XXX: batch the addresses |
d486dd0d JF |
87 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: |
88 | bridgename = ifaceobj.lowerifaces[0] | |
89 | vlan = self._get_vlan_id(ifaceobj) | |
e1601369 RP |
90 | if self.ipcmd.bridge_is_vlan_aware(bridgename): |
91 | [self.ipcmd.bridge_fdb_add(bridgename, addr, | |
92 | vlan) for addr in hwaddress] | |
8e113d63 RP |
93 | elif self.ipcmd.is_bridge(ifaceobj.name): |
94 | [self.ipcmd.bridge_fdb_add(ifaceobj.name, addr) | |
95 | for addr in hwaddress] | |
e1601369 RP |
96 | |
97 | def _remove_addresses_from_bridge(self, ifaceobj, hwaddress): | |
8e113d63 | 98 | # XXX: batch the addresses |
d486dd0d JF |
99 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: |
100 | bridgename = ifaceobj.lowerifaces[0] | |
101 | vlan = self._get_vlan_id(ifaceobj) | |
e1601369 | 102 | if self.ipcmd.bridge_is_vlan_aware(bridgename): |
55072bd1 ST |
103 | for addr in hwaddress: |
104 | try: | |
105 | self.ipcmd.bridge_fdb_del(bridgename, addr, vlan) | |
106 | except Exception, e: | |
107 | self.logger.debug("%s: %s" %(ifaceobj.name, str(e))) | |
108 | pass | |
8e113d63 | 109 | elif self.ipcmd.is_bridge(ifaceobj.name): |
55072bd1 ST |
110 | for addr in hwaddress: |
111 | try: | |
112 | self.ipcmd.bridge_fdb_del(ifaceobj.name, addr) | |
113 | except Exception, e: | |
114 | self.logger.debug("%s: %s" %(ifaceobj.name, str(e))) | |
115 | pass | |
8e113d63 RP |
116 | |
117 | def _get_bridge_fdbs(self, bridgename, vlan): | |
118 | fdbs = self._bridge_fdb_query_cache.get(bridgename) | |
119 | if not fdbs: | |
120 | fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename) | |
121 | if not fdbs: | |
122 | return | |
123 | self._bridge_fdb_query_cache[bridgename] = fdbs | |
124 | return fdbs.get(vlan) | |
125 | ||
126 | def _check_addresses_in_bridge(self, ifaceobj, hwaddress): | |
127 | """ If the device is a bridge, make sure the addresses | |
128 | are in the bridge """ | |
d486dd0d JF |
129 | if ifaceobj.link_kind & ifaceLinkKind.VLAN: |
130 | bridgename = ifaceobj.lowerifaces[0] | |
131 | vlan = self._get_vlan_id(ifaceobj) | |
8e113d63 | 132 | if self.ipcmd.bridge_is_vlan_aware(bridgename): |
d486dd0d | 133 | fdb_addrs = self._get_bridge_fdbs(bridgename, str(vlan)) |
8e113d63 RP |
134 | if not fdb_addrs or hwaddress not in fdb_addrs: |
135 | return False | |
136 | return True | |
e1601369 | 137 | |
00f6105d RP |
138 | def _fix_connected_route(self, ifaceobj, vifacename, addr): |
139 | # | |
d486dd0d | 140 | # XXX: Hack to make sure the primary address |
00f6105d RP |
141 | # is the first in the routing table. |
142 | # | |
143 | # We use `ip route get` on the vrr network to see which | |
144 | # device the kernel returns. if it is the mac vlan device, | |
145 | # flap the macvlan device to adjust the routing table entry. | |
d486dd0d | 146 | # |
00f6105d RP |
147 | # flapping the macvlan device makes sure the macvlan |
148 | # connected route goes through delete + add, hence adjusting | |
149 | # the order in the routing table. | |
150 | # | |
151 | try: | |
152 | self.logger.info('%s: checking route entry ...' %ifaceobj.name) | |
153 | ip = IPNetwork(addr) | |
d486dd0d JF |
154 | |
155 | # we don't support ip6 route fix yet | |
156 | if type(ip) == IPv6Network: | |
157 | return | |
158 | ||
00f6105d RP |
159 | route_prefix = '%s/%d' %(ip.network, ip.prefixlen) |
160 | ||
aa895ecd JF |
161 | if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE: |
162 | vrf_master = self.ipcmd.link_get_master(ifaceobj.name) | |
163 | else: | |
164 | vrf_master = None | |
165 | ||
166 | dev = self.ipcmd.ip_route_get_dev(route_prefix, vrf_master=vrf_master) | |
167 | ||
d486dd0d | 168 | if dev and dev != ifaceobj.name: |
00f6105d RP |
169 | self.logger.info('%s: preferred routing entry ' %ifaceobj.name + |
170 | 'seems to be of the macvlan dev %s' | |
171 | %vifacename + | |
172 | ' .. flapping macvlan dev to fix entry.') | |
173 | self.ipcmd.link_down(vifacename) | |
174 | self.ipcmd.link_up(vifacename) | |
175 | except Exception, e: | |
176 | self.logger.debug('%s: fixing route entry failed (%s)' | |
d486dd0d | 177 | % (ifaceobj.name, str(e))) |
00f6105d RP |
178 | pass |
179 | ||
1b284018 RP |
180 | def _handle_vrf_slaves(self, macvlan_ifacename, ifaceobj): |
181 | vrfname = self.ipcmd.link_get_master(ifaceobj.name) | |
182 | if vrfname: | |
d486dd0d | 183 | self.ipcmd.link_set(macvlan_ifacename, 'master', vrfname) |
1b284018 | 184 | |
55072bd1 ST |
185 | def _get_macs_from_old_config(self, ifaceobj=None): |
186 | """ This method returns a list of the mac addresses | |
187 | in the address-virtual attribute for the bridge. """ | |
188 | maclist = [] | |
189 | saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) | |
190 | if not saved_ifaceobjs: | |
191 | return maclist | |
192 | # we need the old saved configs from the statemanager | |
193 | for oldifaceobj in saved_ifaceobjs: | |
194 | if not oldifaceobj.get_attr_value('address-virtual'): | |
195 | continue | |
196 | for av in oldifaceobj.get_attr_value('address-virtual'): | |
197 | macip = av.split() | |
198 | if len(macip) < 2: | |
199 | self.logger.debug("%s: incorrect old address-virtual attrs '%s'" | |
200 | %(oldifaceobj.name, av)) | |
201 | continue | |
202 | maclist.append(macip[0]) | |
203 | return maclist | |
204 | ||
7b711dc5 JF |
205 | def get_addressvirtual_ipv6_addrgen_user_conf(self, ifaceobj): |
206 | ipv6_addrgen = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen') | |
207 | ||
208 | if ipv6_addrgen: | |
007cae35 JF |
209 | # IFLA_INET6_ADDR_GEN_MODE values: |
210 | # 0 = eui64 | |
211 | # 1 = none | |
17da0561 | 212 | ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get(ipv6_addrgen.lower(), None) |
007cae35 JF |
213 | |
214 | if ipv6_addrgen_nl is None: | |
007cae35 JF |
215 | self.logger.warning('%s: invalid value "%s" for attribute address-virtual-ipv6-addrgen' % (ifaceobj.name, ipv6_addrgen)) |
216 | else: | |
217 | return True, ipv6_addrgen_nl | |
7b711dc5 | 218 | |
17da0561 JF |
219 | else: |
220 | # if user didn't configure ipv6-addrgen, should we reset to default? | |
221 | ipv6_addrgen_nl = self.address_virtual_ipv6_addrgen_value_dict.get( | |
222 | self.get_attr_default_value('address-virtual-ipv6-addrgen'), | |
223 | None | |
224 | ) | |
225 | if ipv6_addrgen_nl is not None: | |
226 | return True, ipv6_addrgen_nl | |
227 | ||
7b711dc5 JF |
228 | return False, None |
229 | ||
e1601369 | 230 | def _apply_address_config(self, ifaceobj, address_virtual_list): |
fc5e1735 | 231 | purge_existing = False if ifupdownflags.flags.PERFMODE else True |
15ef32ea | 232 | |
9e0be374 | 233 | lower_iface_mtu = update_mtu = None |
d486dd0d | 234 | if ifupdownconfig.config.get('adjust_logical_dev_mtu', '1') != '0': |
9e0be374 JF |
235 | if ifaceobj.lowerifaces and address_virtual_list: |
236 | update_mtu = True | |
237 | ||
007cae35 | 238 | user_configured_ipv6_addrgenmode, ipv6_addrgen_user_value = self.get_addressvirtual_ipv6_addrgen_user_conf(ifaceobj) |
7b711dc5 | 239 | |
e1601369 | 240 | hwaddress = [] |
15ef32ea RP |
241 | self.ipcmd.batch_start() |
242 | av_idx = 0 | |
aaef0a79 | 243 | macvlan_prefix = self._get_macvlan_prefix(ifaceobj) |
15ef32ea RP |
244 | for av in address_virtual_list: |
245 | av_attrs = av.split() | |
246 | if len(av_attrs) < 2: | |
bf3eda91 RP |
247 | self.log_error("%s: incorrect address-virtual attrs '%s'" |
248 | %(ifaceobj.name, av), ifaceobj, | |
249 | raise_error=False) | |
15ef32ea RP |
250 | av_idx += 1 |
251 | continue | |
252 | ||
4d3dc0f7 N |
253 | mac = av_attrs[0] |
254 | if not self.check_mac_address(ifaceobj, mac): | |
255 | continue | |
15ef32ea RP |
256 | # Create a macvlan device on this device and set the virtual |
257 | # router mac and ip on it | |
00f6105d | 258 | link_created = False |
cb46a208 | 259 | macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx) |
e74d01e1 | 260 | if not self.ipcmd.link_exists(macvlan_ifacename): |
d486dd0d JF |
261 | try: |
262 | netlink.link_add_macvlan(ifaceobj.name, macvlan_ifacename) | |
263 | except: | |
264 | self.ipcmd.link_add_macvlan(ifaceobj.name, macvlan_ifacename) | |
00f6105d | 265 | link_created = True |
d486dd0d JF |
266 | |
267 | # first thing we need to handle vrf enslavement | |
268 | if (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE): | |
269 | self._handle_vrf_slaves(macvlan_ifacename, ifaceobj) | |
270 | ||
007cae35 JF |
271 | if user_configured_ipv6_addrgenmode: |
272 | self.ipcmd.ipv6_addrgen(macvlan_ifacename, ipv6_addrgen_user_value, link_created) | |
7b711dc5 | 273 | |
5df79763 ST |
274 | ips = av_attrs[1:] |
275 | if mac != 'None': | |
276 | mac = mac.lower() | |
277 | # customer could have used UPPERCASE for MAC | |
278 | self.ipcmd.link_set_hwaddress(macvlan_ifacename, mac) | |
279 | hwaddress.append(mac) | |
23e8546d JF |
280 | |
281 | if self.addressvirtual_with_route_metric and self.ipcmd.addr_metric_support(): | |
282 | metric = self.ipcmd.get_default_ip_metric() | |
283 | else: | |
284 | metric = None | |
285 | ||
dfaa8a2d JF |
286 | self.ipcmd.addr_add_multiple( |
287 | ifaceobj, | |
288 | macvlan_ifacename, | |
289 | ips, | |
290 | purge_existing, | |
23e8546d | 291 | metric=metric |
dfaa8a2d | 292 | ) |
9e0be374 | 293 | |
00f6105d RP |
294 | # If link existed before, flap the link |
295 | if not link_created: | |
20e547e7 | 296 | |
23e8546d | 297 | if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support(): |
20e547e7 JF |
298 | # if the system doesn't support ip addr set METRIC |
299 | # we need to do manually check the ordering of the ip4 routes | |
300 | self._fix_connected_route(ifaceobj, macvlan_ifacename, ips[0]) | |
301 | ||
9e0be374 | 302 | if update_mtu: |
0493bac6 | 303 | lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.name, refresh=True) |
9e0be374 JF |
304 | update_mtu = False |
305 | ||
d486dd0d | 306 | if lower_iface_mtu and lower_iface_mtu != self.ipcmd.link_get_mtu(macvlan_ifacename, refresh=True): |
0493bac6 JF |
307 | try: |
308 | self.ipcmd.link_set_mtu(macvlan_ifacename, | |
309 | lower_iface_mtu) | |
310 | except Exception as e: | |
311 | self.logger.info('%s: failed to set mtu %s: %s' % | |
312 | (macvlan_ifacename, lower_iface_mtu, e)) | |
9e0be374 | 313 | |
dc3f4c45 RP |
314 | # set macvlan device to up in anycase. |
315 | # since we auto create them here..we are responsible | |
316 | # to bring them up here in the case they were brought down | |
317 | # by some other entity in the system. | |
318 | netlink.link_set_updown(macvlan_ifacename, "up") | |
d486dd0d JF |
319 | else: |
320 | try: | |
23e8546d | 321 | if not self.addressvirtual_with_route_metric or not self.ipcmd.addr_metric_support(): |
dfaa8a2d JF |
322 | # if the system doesn't support ip addr set METRIC |
323 | # we need to do manually check the ordering of the ip6 routes | |
324 | self.ipcmd.fix_ipv6_route_metric(ifaceobj, macvlan_ifacename, ips) | |
d486dd0d JF |
325 | except Exception as e: |
326 | self.logger.debug('fix_vrf_slave_ipv6_route_metric: failed: %s' % e) | |
1b284018 | 327 | |
f8ad40ce SE |
328 | # Disable IPv6 duplicate address detection on VRR interfaces |
329 | for key, sysval in { 'accept_dad' : '0', 'dad_transmits' : '0' }.iteritems(): | |
330 | syskey = 'net.ipv6.conf.%s.%s' % (macvlan_ifacename, key) | |
331 | if self.sysctl_get(syskey) != sysval: | |
332 | self.sysctl_set(syskey, sysval) | |
333 | ||
15ef32ea RP |
334 | av_idx += 1 |
335 | self.ipcmd.batch_commit() | |
336 | ||
55072bd1 ST |
337 | # check the statemanager for old configs. |
338 | # We need to remove only the previously configured FDB entries | |
339 | oldmacs = self._get_macs_from_old_config(ifaceobj) | |
340 | # get a list of fdbs in old that are not in new config meaning they should | |
341 | # be removed since they are gone from the config | |
5df79763 | 342 | removed_macs = [mac for mac in oldmacs if mac.lower() not in hwaddress] |
55072bd1 | 343 | self._remove_addresses_from_bridge(ifaceobj, removed_macs) |
e1601369 RP |
344 | # if ifaceobj is a bridge and bridge is a vlan aware bridge |
345 | # add the vid to the bridge | |
346 | self._add_addresses_to_bridge(ifaceobj, hwaddress) | |
347 | ||
348 | def _remove_running_address_config(self, ifaceobj): | |
349 | if not self.ipcmd.link_exists(ifaceobj.name): | |
15ef32ea | 350 | return |
e1601369 | 351 | hwaddress = [] |
15ef32ea | 352 | self.ipcmd.batch_start() |
aaef0a79 | 353 | macvlan_prefix = self._get_macvlan_prefix(ifaceobj) |
55072bd1 | 354 | for macvlan_ifacename in glob.glob("/sys/class/net/%s*" %macvlan_prefix): |
e1601369 RP |
355 | macvlan_ifacename = os.path.basename(macvlan_ifacename) |
356 | if not self.ipcmd.link_exists(macvlan_ifacename): | |
357 | continue | |
358 | hwaddress.append(self.ipcmd.link_get_hwaddress(macvlan_ifacename)) | |
15ef32ea | 359 | self.ipcmd.link_delete(os.path.basename(macvlan_ifacename)) |
e1601369 RP |
360 | # XXX: Also delete any fdb addresses. This requires, checking mac address |
361 | # on individual macvlan interfaces and deleting the vlan from that. | |
15ef32ea | 362 | self.ipcmd.batch_commit() |
e1601369 RP |
363 | if any(hwaddress): |
364 | self._remove_addresses_from_bridge(ifaceobj, hwaddress) | |
15ef32ea | 365 | |
e1601369 RP |
366 | def _remove_address_config(self, ifaceobj, address_virtual_list=None): |
367 | if not address_virtual_list: | |
368 | self._remove_running_address_config(ifaceobj) | |
369 | return | |
370 | ||
371 | if not self.ipcmd.link_exists(ifaceobj.name): | |
372 | return | |
373 | hwaddress = [] | |
374 | self.ipcmd.batch_start() | |
375 | av_idx = 0 | |
aaef0a79 | 376 | macvlan_prefix = self._get_macvlan_prefix(ifaceobj) |
e1601369 RP |
377 | for av in address_virtual_list: |
378 | av_attrs = av.split() | |
379 | if len(av_attrs) < 2: | |
bf3eda91 RP |
380 | self.log_error("%s: incorrect address-virtual attrs '%s'" |
381 | %(ifaceobj.name, av), ifaceobj, | |
382 | raise_error=False) | |
e1601369 RP |
383 | av_idx += 1 |
384 | continue | |
385 | ||
386 | # Delete the macvlan device on this device | |
cb46a208 | 387 | macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx) |
e1601369 RP |
388 | self.ipcmd.link_delete(os.path.basename(macvlan_ifacename)) |
389 | if av_attrs[0] != 'None': | |
390 | hwaddress.append(av_attrs[0]) | |
391 | av_idx += 1 | |
392 | self.ipcmd.batch_commit() | |
393 | self._remove_addresses_from_bridge(ifaceobj, hwaddress) | |
15ef32ea | 394 | |
4d3dc0f7 N |
395 | def check_mac_address(self, ifaceobj, mac): |
396 | if mac == 'None': | |
d486dd0d | 397 | return True |
4d3dc0f7 N |
398 | mac = mac.lower() |
399 | try: | |
586535e8 | 400 | if int(mac.split(":")[0], 16) & 1 : |
5b1fffaf JF |
401 | self.log_error("%s: Multicast bit is set in the virtual mac address '%s'" |
402 | % (ifaceobj.name, mac), ifaceobj=ifaceobj) | |
4d3dc0f7 N |
403 | return False |
404 | return True | |
5b1fffaf | 405 | except ValueError: |
4d3dc0f7 N |
406 | return False |
407 | ||
42e85fc8 RP |
408 | def _fixup_vrf_enslavements(self, ifaceobj, ifaceobj_getfunc=None): |
409 | """ This function fixes up address virtual interfaces | |
410 | (macvlans) on vrf slaves. Since this fixup is an overhead, | |
411 | this must be called only in cases when ifupdown2 is | |
412 | called on the vrf device or its slave and not when | |
413 | ifupdown2 is called for all devices. When all | |
414 | interfaces are brought up, the expectation is that | |
415 | the normal path will fix up a vrf device or its slaves""" | |
416 | ||
417 | if not ifaceobj_getfunc: | |
418 | return | |
419 | if ((ifaceobj.link_kind & ifaceLinkKind.VRF) and | |
420 | self.ipcmd.link_exists(ifaceobj.name)): | |
421 | # if I am a vrf device and I have slaves | |
422 | # that have address virtual config, | |
423 | # enslave the slaves 'address virtual | |
424 | # interfaces (macvlans)' to myself: | |
425 | running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name) | |
426 | if running_slaves: | |
427 | # pick up any existing slaves of a vrf device and | |
428 | # look for their upperdevices and enslave them to the | |
429 | # vrf device: | |
430 | for s in running_slaves: | |
431 | sobjs = ifaceobj_getfunc(s) | |
432 | if (sobjs and | |
433 | (sobjs[0].link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE)): | |
434 | # enslave all its upper devices to | |
435 | # the vrf device | |
436 | upperdevs = self.ipcmd.link_get_uppers(sobjs[0].name) | |
437 | if not upperdevs: | |
438 | continue | |
439 | for u in upperdevs: | |
440 | # skip vrf device which | |
441 | # will also show up in the | |
442 | # upper device list | |
443 | if u == ifaceobj.name: | |
444 | continue | |
d486dd0d JF |
445 | self.ipcmd.link_set(u, 'master', ifaceobj.name, |
446 | state='up') | |
42e85fc8 RP |
447 | elif ((ifaceobj.link_privflags & ifaceLinkPrivFlags.ADDRESS_VIRTUAL_SLAVE) and |
448 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) and | |
449 | self.ipcmd.link_exists(ifaceobj.name)): | |
450 | # If I am a vrf slave and I have 'address virtual' | |
451 | # config, make sure my addrress virtual interfaces | |
452 | # (macvlans) are also enslaved to the vrf device | |
453 | vrfname = ifaceobj.get_attr_value_first('vrf') | |
454 | if not vrfname or not self.ipcmd.link_exists(vrfname): | |
455 | return | |
456 | running_uppers = self.ipcmd.link_get_uppers(ifaceobj.name) | |
457 | if not running_uppers: | |
458 | return | |
459 | macvlan_prefix = self._get_macvlan_prefix(ifaceobj) | |
460 | if not macvlan_prefix: | |
461 | return | |
462 | for u in running_uppers: | |
463 | if u == vrfname: | |
464 | continue | |
465 | if u.startswith(macvlan_prefix): | |
d486dd0d JF |
466 | self.ipcmd.link_set(u, 'master', vrfname, |
467 | state='up') | |
42e85fc8 RP |
468 | |
469 | def _up(self, ifaceobj, ifaceobj_getfunc=None): | |
470 | if not ifupdownflags.flags.ALL: | |
471 | self._fixup_vrf_enslavements(ifaceobj, ifaceobj_getfunc) | |
15ef32ea RP |
472 | address_virtual_list = ifaceobj.get_attr_value('address-virtual') |
473 | if not address_virtual_list: | |
474 | # XXX: address virtual is not present. In which case, | |
00f6105d | 475 | # delete stale macvlan devices. |
e1601369 | 476 | self._remove_address_config(ifaceobj, address_virtual_list) |
15ef32ea RP |
477 | return |
478 | ||
1b284018 RP |
479 | if (ifaceobj.upperifaces and |
480 | not ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE): | |
f466af7a JF |
481 | self.log_error('%s: invalid placement of address-virtual lines (must be configured under an interface with no upper interfaces or parent interfaces)' |
482 | % (ifaceobj.name), ifaceobj) | |
483 | return | |
484 | ||
e1601369 | 485 | if not self.ipcmd.link_exists(ifaceobj.name): |
15ef32ea | 486 | return |
e1601369 | 487 | self._apply_address_config(ifaceobj, address_virtual_list) |
15ef32ea | 488 | |
42e85fc8 | 489 | def _down(self, ifaceobj, ifaceobj_getfunc=None): |
15ef32ea | 490 | try: |
cb46a208 RP |
491 | self._remove_address_config(ifaceobj, |
492 | ifaceobj.get_attr_value('address-virtual')) | |
15ef32ea RP |
493 | except Exception, e: |
494 | self.log_warn(str(e)) | |
495 | ||
496 | def _query_check(self, ifaceobj, ifaceobjcurr): | |
497 | address_virtual_list = ifaceobj.get_attr_value('address-virtual') | |
498 | if not address_virtual_list: | |
499 | return | |
cb46a208 RP |
500 | if not self.ipcmd.link_exists(ifaceobj.name): |
501 | return | |
007cae35 JF |
502 | |
503 | user_config_address_virtual_ipv6_addr = ifaceobj.get_attr_value_first('address-virtual-ipv6-addrgen') | |
504 | if user_config_address_virtual_ipv6_addr and user_config_address_virtual_ipv6_addr not in utils._string_values: | |
505 | ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1) | |
506 | user_config_address_virtual_ipv6_addr = None | |
507 | macvlans_running_ipv6_addr = [] | |
508 | ||
15ef32ea | 509 | av_idx = 0 |
aaef0a79 | 510 | macvlan_prefix = self._get_macvlan_prefix(ifaceobj) |
15ef32ea RP |
511 | for address_virtual in address_virtual_list: |
512 | av_attrs = address_virtual.split() | |
513 | if len(av_attrs) < 2: | |
514 | self.logger.warn("%s: incorrect address-virtual attrs '%s'" | |
515 | %(ifaceobj.name, address_virtual)) | |
516 | av_idx += 1 | |
517 | continue | |
518 | ||
519 | # Check if the macvlan device on this interface | |
cb46a208 RP |
520 | macvlan_ifacename = '%s%d' %(macvlan_prefix, av_idx) |
521 | if not self.ipcmd.link_exists(macvlan_ifacename): | |
522 | ifaceobjcurr.update_config_with_status('address-virtual', | |
523 | '', 1) | |
524 | av_idx += 1 | |
525 | continue | |
007cae35 JF |
526 | |
527 | if user_config_address_virtual_ipv6_addr: | |
528 | macvlans_running_ipv6_addr.append(self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename)) | |
529 | ||
cb46a208 RP |
530 | # Check mac and ip address |
531 | rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename) | |
d486dd0d JF |
532 | raddrs = self.ipcmd.get_running_addrs( |
533 | ifname=macvlan_ifacename, | |
534 | details=False, | |
535 | addr_virtual_ifaceobj=ifaceobj | |
536 | ) | |
cb46a208 RP |
537 | if not raddrs or not rhwaddress: |
538 | ifaceobjcurr.update_config_with_status('address-virtual', '', 1) | |
539 | av_idx += 1 | |
540 | continue | |
b6688146 JF |
541 | try: |
542 | av_attrs[0] = ':'.join([i if len(i) == 2 else '0%s' % i | |
543 | for i in av_attrs[0].split(':')]) | |
544 | except: | |
545 | self.logger.info('%s: %s: invalid value for address-virtual (%s)' | |
546 | % (ifaceobj.name, | |
547 | macvlan_ifacename, | |
548 | ' '.join(av_attrs))) | |
90649d37 | 549 | try: |
d486dd0d JF |
550 | if (rhwaddress == av_attrs[0].lower() and |
551 | self.ipcmd.compare_user_config_vs_running_state(raddrs, av_attrs[1:]) and | |
552 | self._check_addresses_in_bridge(ifaceobj, av_attrs[0].lower())): | |
90649d37 JF |
553 | ifaceobjcurr.update_config_with_status('address-virtual', |
554 | address_virtual, 0) | |
555 | else: | |
556 | raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs)) | |
557 | ifaceobjcurr.update_config_with_status('address-virtual', | |
558 | raddress_virtual, 1) | |
559 | except: | |
560 | raddress_virtual = '%s %s' % (rhwaddress, ' '.join(raddrs)) | |
561 | ifaceobjcurr.update_config_with_status('address-virtual', | |
562 | raddress_virtual, 1) | |
15ef32ea | 563 | av_idx += 1 |
007cae35 JF |
564 | |
565 | if user_config_address_virtual_ipv6_addr: | |
566 | bool_user_ipv6_addrgen = utils.get_boolean_from_string(user_config_address_virtual_ipv6_addr) | |
567 | for running_ipv6_addrgen in macvlans_running_ipv6_addr: | |
568 | if (not bool_user_ipv6_addrgen) != running_ipv6_addrgen: | |
569 | ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 1) | |
570 | return | |
571 | ifaceobjcurr.update_config_with_status('address-virtual-ipv6-addrgen', user_config_address_virtual_ipv6_addr, 0) | |
15ef32ea | 572 | |
42e85fc8 | 573 | def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): |
aaef0a79 | 574 | macvlan_prefix = self._get_macvlan_prefix(ifaceobjrunning) |
8e113d63 | 575 | address_virtuals = glob.glob("/sys/class/net/%s*" %macvlan_prefix) |
007cae35 | 576 | macvlans_ipv6_addrgen_list = [] |
8e113d63 RP |
577 | for av in address_virtuals: |
578 | macvlan_ifacename = os.path.basename(av) | |
579 | rhwaddress = self.ipcmd.link_get_hwaddress(macvlan_ifacename) | |
c3175b31 JF |
580 | |
581 | raddress = [] | |
582 | for obj in ifaceobj_getfunc(ifaceobjrunning.name) or []: | |
583 | raddress.extend(self.ipcmd.get_running_addrs(None, macvlan_ifacename, addr_virtual_ifaceobj=obj) or []) | |
584 | ||
585 | raddress = list(set(raddress)) | |
586 | ||
8e113d63 RP |
587 | if not raddress: |
588 | self.logger.warn('%s: no running addresses' | |
589 | %ifaceobjrunning.name) | |
590 | raddress = [] | |
591 | ifaceobjrunning.update_config('address-virtual', | |
c3175b31 | 592 | '%s %s' %(rhwaddress, ' '.join(raddress))) |
007cae35 JF |
593 | |
594 | macvlans_ipv6_addrgen_list.append((macvlan_ifacename, self.ipcmd.get_ipv6_addrgen_mode(macvlan_ifacename))) | |
595 | ||
596 | macvlan_count = len(address_virtuals) | |
597 | if not macvlan_count: | |
598 | return | |
599 | ipv6_addrgen = macvlans_ipv6_addrgen_list[0][1] | |
600 | ||
601 | for macvlan_ifname, macvlan_ipv6_addrgen in macvlans_ipv6_addrgen_list: | |
602 | if macvlan_ipv6_addrgen != ipv6_addrgen: | |
603 | # one macvlan has a different ipv6-addrgen configuration | |
604 | # we simply return, ifquery-running will print the macvlan | |
605 | # stanzas with the ipv6-addrgen on/off attribute | |
606 | return | |
607 | ifaceobjrunning.update_config('address-virtual-ipv6-addrgen', 'off' if ipv6_addrgen else 'on') | |
608 | ||
15ef32ea RP |
609 | |
610 | _run_ops = {'up' : _up, | |
611 | 'down' : _down, | |
612 | 'query-checkcurr' : _query_check, | |
613 | 'query-running' : _query_running} | |
614 | ||
615 | def get_ops(self): | |
616 | """ returns list of ops supported by this module """ | |
617 | return self._run_ops.keys() | |
618 | ||
619 | def _init_command_handlers(self): | |
620 | if not self.ipcmd: | |
d486dd0d | 621 | self.ipcmd = LinkUtils() |
15ef32ea | 622 | |
42e85fc8 RP |
623 | def run(self, ifaceobj, operation, query_ifaceobj=None, |
624 | ifaceobj_getfunc=None, **extra_args): | |
15ef32ea RP |
625 | """ run vlan configuration on the interface object passed as argument |
626 | ||
627 | Args: | |
628 | **ifaceobj** (object): iface object | |
629 | ||
630 | **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr', | |
631 | 'query-running' | |
632 | Kwargs: | |
633 | **query_ifaceobj** (object): query check ifaceobject. This is only | |
634 | valid when op is 'query-checkcurr'. It is an object same as | |
635 | ifaceobj, but contains running attribute values and its config | |
636 | status. The modules can use it to return queried running state | |
637 | of interfaces. status is success if the running state is same | |
638 | as user required state in ifaceobj. error otherwise. | |
639 | """ | |
84ca006f RP |
640 | if ifaceobj.type == ifaceType.BRIDGE_VLAN: |
641 | return | |
15ef32ea RP |
642 | op_handler = self._run_ops.get(operation) |
643 | if not op_handler: | |
644 | return | |
645 | self._init_command_handlers() | |
646 | if operation == 'query-checkcurr': | |
647 | op_handler(self, ifaceobj, query_ifaceobj) | |
648 | else: | |
42e85fc8 | 649 | op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc) |