]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/address.py
Merge pull request #80 from BarbarossaTM/tunnel-fixes-master
[mirror_ifupdown2.git] / addons / address.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import os
8
9 try:
10 from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPv4Address, IPv6Address
11 from sets import Set
12 from ifupdown.iface import *
13 from ifupdown.utils import utils
14 from ifupdownaddons.modulebase import moduleBase
15 from ifupdownaddons.iproute2 import iproute2
16 from ifupdownaddons.dhclient import dhclient
17 import ifupdown.policymanager as policymanager
18 from ifupdown.netlink import netlink
19 import ifupdown.ifupdownconfig as ifupdownConfig
20 import ifupdown.ifupdownflags as ifupdownflags
21 import ifupdown.statemanager as statemanager
22 except ImportError, e:
23 raise ImportError (str(e) + "- required module not found")
24
25 class address(moduleBase):
26 """ ifupdown2 addon module to configure address, mtu, hwaddress, alias
27 (description) on an interface """
28
29 _modinfo = {'mhelp' : 'address configuration module for interfaces',
30 'attrs': {
31 'address' :
32 {'help' : 'ipv4 or ipv6 addresses',
33 'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
34 'multiline' : True,
35 'example' : ['address 10.0.12.3/24',
36 'address 2000:1000:1000:1000:3::5/128']},
37 'netmask' :
38 {'help': 'netmask',
39 'example' : ['netmask 255.255.255.0'],
40 'compat' : True},
41 'broadcast' :
42 {'help': 'broadcast address',
43 'validvals' : ['<ipv4>', ],
44 'example' : ['broadcast 10.0.1.255']},
45 'scope' :
46 {'help': 'scope',
47 'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'],
48 'example' : ['scope host']},
49 'preferred-lifetime' :
50 {'help': 'preferred lifetime',
51 'validrange' : ['0', '65535'],
52 'example' : ['preferred-lifetime forever',
53 'preferred-lifetime 10']},
54 'gateway' :
55 {'help': 'default gateway',
56 'validvals' : ['<ipv4>', '<ipv6>'],
57 'multiline' : True,
58 'example' : ['gateway 255.255.255.0']},
59 'mtu' :
60 { 'help': 'interface mtu',
61 'validrange' : ['552', '9216'],
62 'example' : ['mtu 1600'],
63 'default' : '1500'},
64 'hwaddress' :
65 {'help' : 'hw address',
66 'validvals' : ['<mac>',],
67 'example': ['hwaddress 44:38:39:00:27:b8']},
68 'alias' :
69 { 'help': 'description/alias',
70 'example' : ['alias testnetwork']},
71 'address-purge' :
72 { 'help': 'purge existing addresses. By default ' +
73 'any existing ip addresses on an interface are ' +
74 'purged to match persistant addresses in the ' +
75 'interfaces file. Set this attribute to \'no\'' +
76 'if you want to preserve existing addresses',
77 'validvals' : ['yes', 'no'],
78 'default' : 'yes',
79 'example' : ['address-purge yes/no']},
80 'clagd-vxlan-anycast-ip' :
81 { 'help' : 'Anycast local IP address for ' +
82 'dual connected VxLANs',
83 'validvals' : ['<ipv4>', ],
84 'example' : ['clagd-vxlan-anycast-ip 36.0.0.11']}}}
85
86 def __init__(self, *args, **kargs):
87 moduleBase.__init__(self, *args, **kargs)
88 self.ipcmd = None
89 self._bridge_fdb_query_cache = {}
90 self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu')
91 self.max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='max_mtu')
92
93 if not self.default_mtu:
94 self.default_mtu = '1500'
95
96 self.logger.info('address: using default mtu %s' %self.default_mtu)
97
98 if self.max_mtu:
99 self.logger.info('address: using max mtu %s' %self.max_mtu)
100
101 def syntax_check(self, ifaceobj, ifaceobj_getfunc=None):
102 return (self.syntax_check_multiple_gateway(ifaceobj)
103 and self.syntax_check_addr_allowed_on(ifaceobj, True)
104 and self.syntax_check_mtu(ifaceobj, ifaceobj_getfunc))
105
106 def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc):
107 mtu = ifaceobj.get_attr_value_first('mtu')
108 if mtu:
109 return self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc,
110 syntaxcheck=True)
111 return True
112
113 def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False):
114 if ifaceobj.get_attr_value('address'):
115 return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=syntax_check)
116 return True
117
118 def _syntax_check_multiple_gateway(self, family, found, addr, type_obj):
119 if type(IPNetwork(addr)) == type_obj:
120 if found:
121 raise Exception('%s: multiple gateways for %s family'
122 % (addr, family))
123 return True
124 return False
125
126 def syntax_check_multiple_gateway(self, ifaceobj):
127 result = True
128 inet = False
129 inet6 = False
130 gateways = ifaceobj.get_attr_value('gateway')
131 for addr in gateways if gateways else []:
132 try:
133 if self._syntax_check_multiple_gateway('inet', inet, addr,
134 IPv4Network):
135 inet = True
136 if self._syntax_check_multiple_gateway('inet6', inet6, addr,
137 IPv6Network):
138 inet6 = True
139 except Exception as e:
140 self.logger.warning('%s: address: %s' % (ifaceobj.name, str(e)))
141 result = False
142 return result
143
144 def _address_valid(self, addrs):
145 if not addrs:
146 return False
147 if any(map(lambda a: True if a[:7] != '0.0.0.0'
148 else False, addrs)):
149 return True
150 return False
151
152 def _get_hwaddress(self, ifaceobj):
153 hwaddress = ifaceobj.get_attr_value_first('hwaddress')
154 if hwaddress and hwaddress.startswith("ether"):
155 hwaddress = hwaddress[5:].strip()
156 return hwaddress
157
158 def _process_bridge(self, ifaceobj, up):
159 hwaddress = self._get_hwaddress(ifaceobj)
160 addrs = ifaceobj.get_attr_value_first('address')
161 is_vlan_dev_on_vlan_aware_bridge = False
162 is_bridge = self.ipcmd.is_bridge(ifaceobj.name)
163 if not is_bridge:
164 if '.' in ifaceobj.name:
165 (bridgename, vlan) = ifaceobj.name.split('.')
166 is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename)
167 if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
168 or is_vlan_dev_on_vlan_aware_bridge):
169 if self._address_valid(addrs):
170 if up:
171 self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
172 '/arp_accept', '1')
173 else:
174 self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
175 '/arp_accept', '0')
176 if hwaddress and is_vlan_dev_on_vlan_aware_bridge:
177 if up:
178 self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
179 else:
180 self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
181
182 def _get_anycast_addr(self, ifaceobjlist):
183 for ifaceobj in ifaceobjlist:
184 anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')
185 if anycast_addr:
186 anycast_addr = anycast_addr+'/32'
187 return anycast_addr
188 return None
189
190 def _inet_address_convert_to_cidr(self, ifaceobjlist):
191 newaddrs = []
192 newaddr_attrs = {}
193
194 for ifaceobj in ifaceobjlist:
195 addrs = ifaceobj.get_attr_value('address')
196 if not addrs:
197 continue
198
199 if not self.syntax_check_addr_allowed_on(ifaceobj,
200 syntax_check=False):
201 return (False, newaddrs, newaddr_attrs)
202 # If user address is not in CIDR notation, convert them to CIDR
203 for addr_index in range(0, len(addrs)):
204 addr = addrs[addr_index]
205 newaddr = addr
206 if '/' in addr:
207 newaddrs.append(addr)
208 else:
209 netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
210 if netmask:
211 prefixlen = IPNetwork('%s' %addr +
212 '/%s' %netmask).prefixlen
213 newaddr = addr + '/%s' %prefixlen
214 else:
215 # we are here because there is no slash (/xx) and no netmask
216 # just let IPNetwork handle the ipv4 or ipv6 address mask
217 prefixlen = IPNetwork(addr).prefixlen
218 newaddr = addr + '/%s' %prefixlen
219 newaddrs.append(newaddr)
220
221 attrs = {}
222 for a in ['broadcast', 'pointopoint', 'scope',
223 'preferred-lifetime']:
224 aval = ifaceobj.get_attr_value_n(a, addr_index)
225 if aval:
226 attrs[a] = aval
227
228 if attrs:
229 newaddr_attrs[newaddr]= attrs
230 return (True, newaddrs, newaddr_attrs)
231
232 def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs):
233 for addr_index in range(0, len(newaddrs)):
234 try:
235 if newaddr_attrs:
236 self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
237 newaddr_attrs.get(newaddrs[addr_index],
238 {}).get('broadcast'),
239 newaddr_attrs.get(newaddrs[addr_index],
240 {}).get('pointopoint'),
241 newaddr_attrs.get(newaddrs[addr_index],
242 {}).get('scope'),
243 newaddr_attrs.get(newaddrs[addr_index],
244 {}).get('preferred-lifetime'))
245 else:
246 self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index])
247 except Exception, e:
248 self.log_error(str(e), ifaceobj)
249
250 def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None,
251 force_reapply=False):
252 squash_addr_config = (True if \
253 ifupdownConfig.config.get('addr_config_squash', \
254 '0') == '1' else False)
255
256 if (squash_addr_config and
257 not (ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING)):
258 return
259
260 purge_addresses = ifaceobj.get_attr_value_first('address-purge')
261 if not purge_addresses:
262 purge_addresses = 'yes'
263
264 if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS:
265 ifaceobjlist = ifaceobj_getfunc(ifaceobj.name)
266 else:
267 ifaceobjlist = [ifaceobj]
268
269 (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist)
270 newaddrs = utils.get_normalized_ip_addr(ifaceobj.name, newaddrs)
271 if not addr_supported:
272 return
273 if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)):
274 # if youngest sibling and squash addr is not set
275 # print a warning that addresses will not be purged
276 if (ifaceobj.flags & iface.YOUNGEST_SIBLING):
277 self.logger.warn('%s: interface has multiple ' %ifaceobj.name +
278 'iface stanzas, skip purging existing addresses')
279 purge_addresses = 'no'
280
281 if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes':
282 # if perfmode is not set and purge addresses is not set to 'no'
283 # lets purge addresses not in the config
284 runningaddrs = utils.get_normalized_ip_addr(ifaceobj.name, self.ipcmd.addr_get(ifaceobj.name, details=False))
285
286 # if anycast address is configured on 'lo' and is in running config
287 # add it to newaddrs so that ifreload doesn't wipe it out
288 anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist))
289
290 if runningaddrs and anycast_addr and anycast_addr in runningaddrs:
291 newaddrs.append(anycast_addr)
292 if newaddrs == runningaddrs:
293 if force_reapply:
294 self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
295 return
296 try:
297 # if primary address is not same, there is no need to keep any.
298 # reset all addresses
299 if (newaddrs and runningaddrs and
300 (newaddrs[0] != runningaddrs[0])):
301 self.ipcmd.del_addr_all(ifaceobj.name)
302 else:
303 self.ipcmd.del_addr_all(ifaceobj.name, newaddrs)
304 except Exception, e:
305 self.log_warn(str(e))
306 if not newaddrs:
307 return
308 self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
309
310 def _add_delete_gateway(self, ifaceobj, gateways=[], prev_gw=[]):
311 vrf = ifaceobj.get_attr_value_first('vrf')
312 metric = ifaceobj.get_attr_value_first('metric')
313 for del_gw in list(set(prev_gw) - set(gateways)):
314 try:
315 self.ipcmd.route_del_gateway(ifaceobj.name, del_gw, vrf, metric)
316 except Exception as e:
317 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
318 for add_gw in gateways:
319 try:
320 self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf)
321 except Exception as e:
322 self.log_error('%s: %s' % (ifaceobj.name, str(e)))
323
324 def _get_prev_gateway(self, ifaceobj, gateways):
325 ipv = []
326 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name)
327 if not saved_ifaceobjs:
328 return ipv
329 prev_gateways = saved_ifaceobjs[0].get_attr_value('gateway')
330 if not prev_gateways:
331 return ipv
332 return prev_gateways
333
334 def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False):
335 retval = True
336 if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE):
337 if syntaxcheck:
338 self.logger.warn('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name)
339 retval = False
340 else:
341 self.logger.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name)
342 elif ifaceobj_getfunc:
343 if ((ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
344 ifaceobj.upperifaces):
345 masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0])
346 if masterobj:
347 master_mtu = masterobj[0].get_attr_value_first('mtu')
348 if master_mtu and master_mtu != mtu:
349 if syntaxcheck:
350 self.logger.warn('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
351 retval = False
352 else:
353 self.logger.info('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu))
354 elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and
355 ifaceobj.lowerifaces):
356 lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0])
357 if lowerobj:
358 if syntaxcheck:
359 lowerdev_mtu = lowerobj[0].get_attr_value_first('mtu')
360 else:
361 lowerdev_mtu = self.ipcmd.link_get_mtu(lowerobj[0].name)
362 if lowerdev_mtu and int(mtu) > int(lowerdev_mtu):
363 self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
364 %(ifaceobj.name, mtu, lowerobj[0].name, lowerdev_mtu))
365 retval = False
366 elif (not lowerobj[0].link_kind and
367 not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and
368 self.default_mtu and (int(mtu) > int(self.default_mtu))):
369 # only check default mtu on lower device which is a physical interface
370 self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s'
371 %(ifaceobj.name, mtu, lowerobj[0].name, self.default_mtu))
372 retval = False
373 if self.max_mtu and mtu > self.max_mtu:
374 self.logger.warn('%s: specified mtu %s is greater than max mtu %s'
375 %(ifaceobj.name, mtu, self.max_mtu))
376 retval = False
377 return retval
378
379 def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc):
380 if (not ifaceobj.upperifaces or
381 (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or
382 (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or
383 (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)):
384 return
385 for u in ifaceobj.upperifaces:
386 upperobjs = ifaceobj_getfunc(u)
387 if (not upperobjs or
388 not (upperobjs[0].link_kind & ifaceLinkKind.VLAN)):
389 continue
390 # only adjust mtu for vlan devices on ifaceobj
391 umtu = upperobjs[0].get_attr_value_first('mtu')
392 if not umtu:
393 running_mtu = self.ipcmd.link_get_mtu(upperobjs[0].name)
394 if not running_mtu or (running_mtu != mtu):
395 self.ipcmd.link_set(u, 'mtu', mtu)
396
397 def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc):
398 mtu = ifaceobj.get_attr_value_first('mtu')
399 if mtu:
400 if not self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc):
401 return
402 running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
403 if not running_mtu or (running_mtu and running_mtu != mtu):
404 self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu)
405 if (not ifupdownflags.flags.ALL and
406 not ifaceobj.link_kind and
407 ifupdownConfig.config.get('adjust_logical_dev_mtu', '1') != '0'):
408 # This is additional cost to us, so do it only when
409 # ifupdown2 is called on a particular interface and
410 # it is a physical interface
411 self._propagate_mtu_to_upper_devs(ifaceobj, mtu, ifaceobj_getfunc)
412 return
413
414 if ifaceobj.link_kind:
415 # bonds and vxlan devices need an explicit set of mtu.
416 # bridges don't need mtu set
417 if (ifaceobj.link_kind & ifaceLinkKind.BOND or
418 ifaceobj.link_kind & ifaceLinkKind.VXLAN):
419 running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
420 if (self.default_mtu and running_mtu != self.default_mtu):
421 self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
422 return
423 if (ifupdownConfig.config.get('adjust_logical_dev_mtu', '1') != '0'
424 and ifaceobj.lowerifaces):
425 # set vlan interface mtu to lower device mtu
426 if (ifaceobj.link_kind & ifaceLinkKind.VLAN):
427 lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.lowerifaces[0], refresh=True)
428 if not lower_iface_mtu == self.ipcmd.link_get_mtu(ifaceobj.name):
429 self.ipcmd.link_set_mtu(ifaceobj.name, lower_iface_mtu)
430
431 elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and
432 not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
433 self.default_mtu):
434 # logical devices like bridges and vlan devices rely on mtu
435 # from their lower devices. ie mtu travels from
436 # lower devices to upper devices. For bonds mtu travels from
437 # upper to lower devices. running mtu depends on upper and
438 # lower device mtu. With all this implicit mtu
439 # config by the kernel in play, we try to be cautious here
440 # on which devices we want to reset mtu to default.
441 # essentially only physical interfaces which are not bond slaves
442 running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name)
443 if running_mtu != self.default_mtu:
444 self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
445
446 def _up(self, ifaceobj, ifaceobj_getfunc=None):
447 if not self.ipcmd.link_exists(ifaceobj.name):
448 return
449
450 alias = ifaceobj.get_attr_value_first('alias')
451 current_alias = self.ipcmd.link_get_alias(ifaceobj.name)
452 if alias and alias != current_alias:
453 self.ipcmd.link_set_alias(ifaceobj.name, alias)
454 elif not alias and current_alias:
455 self.ipcmd.link_set_alias(ifaceobj.name, '')
456
457 addr_method = ifaceobj.addr_method
458 force_reapply = False
459 try:
460 # release any stale dhcp addresses if present
461 if (addr_method not in ["dhcp", "ppp"] and not ifupdownflags.flags.PERFMODE and
462 not (ifaceobj.flags & iface.HAS_SIBLINGS)):
463 # if not running in perf mode and ifaceobj does not have
464 # any sibling iface objects, kill any stale dhclient
465 # processes
466 dhclientcmd = dhclient()
467 if dhclientcmd.is_running(ifaceobj.name):
468 # release any dhcp leases
469 dhclientcmd.release(ifaceobj.name)
470 force_reapply = True
471 elif dhclientcmd.is_running6(ifaceobj.name):
472 dhclientcmd.release6(ifaceobj.name)
473 force_reapply = True
474 except:
475 pass
476
477 self.ipcmd.batch_start()
478 if addr_method not in ["dhcp", "ppp"]:
479 self._inet_address_config(ifaceobj, ifaceobj_getfunc,
480 force_reapply)
481 self._process_mtu_config(ifaceobj, ifaceobj_getfunc)
482
483 try:
484 self.ipcmd.batch_commit()
485 except Exception as e:
486 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False)
487
488 try:
489 hwaddress = self._get_hwaddress(ifaceobj)
490 if hwaddress:
491 running_hwaddress = None
492 if not ifupdownflags.flags.PERFMODE: # system is clean
493 running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
494 if hwaddress != running_hwaddress:
495 slave_down = False
496 netlink.link_set_updown(ifaceobj.name, "down")
497 if ifaceobj.link_kind & ifaceLinkKind.BOND:
498 # if bond, down all the slaves
499 if ifaceobj.lowerifaces:
500 for l in ifaceobj.lowerifaces:
501 netlink.link_set_updown(l, "down")
502 slave_down = True
503 try:
504 self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
505 finally:
506 netlink.link_set_updown(ifaceobj.name, "up")
507 if slave_down:
508 for l in ifaceobj.lowerifaces:
509 netlink.link_set_updown(l, "up")
510
511 # Handle special things on a bridge
512 self._process_bridge(ifaceobj, True)
513 except Exception, e:
514 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
515
516 if addr_method not in ["dhcp", "ppp"]:
517 gateways = ifaceobj.get_attr_value('gateway')
518 if not gateways:
519 gateways = []
520 prev_gw = self._get_prev_gateway(ifaceobj, gateways)
521 self._add_delete_gateway(ifaceobj, gateways, prev_gw)
522 return
523
524 def _down(self, ifaceobj, ifaceobj_getfunc=None):
525 try:
526 if not self.ipcmd.link_exists(ifaceobj.name):
527 return
528 addr_method = ifaceobj.addr_method
529 if addr_method not in ["dhcp", "ppp"]:
530 if ifaceobj.get_attr_value_first('address-purge')=='no':
531 addrlist = ifaceobj.get_attr_value('address')
532 for addr in addrlist:
533 self.ipcmd.addr_del(ifaceobj.name, addr)
534 #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0])
535 else:
536 self.ipcmd.del_addr_all(ifaceobj.name)
537 mtu = ifaceobj.get_attr_value_first('mtu')
538 if (not ifaceobj.link_kind and mtu and
539 self.default_mtu and (mtu != self.default_mtu)):
540 self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
541 alias = ifaceobj.get_attr_value_first('alias')
542 if alias:
543 filename = '/sys/class/net/%s/ifalias' %ifaceobj.name
544 self.logger.info('executing echo "" > %s' %filename)
545 os.system('echo "" > %s' %filename)
546 # XXX hwaddress reset cannot happen because we dont know last
547 # address.
548
549 # Handle special things on a bridge
550 self._process_bridge(ifaceobj, False)
551 except Exception, e:
552 self.logger.debug('%s : %s' %(ifaceobj.name, str(e)))
553 pass
554
555 def _get_iface_addresses(self, ifaceobj):
556 addrlist = ifaceobj.get_attr_value('address')
557 outaddrlist = []
558
559 if not addrlist: return None
560 for addrindex in range(0, len(addrlist)):
561 addr = addrlist[addrindex]
562 netmask = ifaceobj.get_attr_value_n('netmask', addrindex)
563 if netmask:
564 prefixlen = IPNetwork('%s' %addr +
565 '/%s' %netmask).prefixlen
566 addr = addr + '/%s' %prefixlen
567 outaddrlist.append(addr)
568 return outaddrlist
569
570 def _get_bridge_fdbs(self, bridgename, vlan):
571 fdbs = self._bridge_fdb_query_cache.get(bridgename)
572 if not fdbs:
573 fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
574 if not fdbs:
575 return
576 self._bridge_fdb_query_cache[bridgename] = fdbs
577 return fdbs.get(vlan)
578
579 def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
580 """ If the device is a bridge, make sure the addresses
581 are in the bridge """
582 if '.' in ifaceobj.name:
583 (bridgename, vlan) = ifaceobj.name.split('.')
584 if self.ipcmd.bridge_is_vlan_aware(bridgename):
585 fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
586 if not fdb_addrs or hwaddress not in fdb_addrs:
587 return False
588 return True
589
590 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
591 runningaddrsdict = None
592 if not self.ipcmd.link_exists(ifaceobj.name):
593 self.logger.debug('iface %s not found' %ifaceobj.name)
594 return
595 addr_method = ifaceobj.addr_method
596 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
597 'mtu', self.ipcmd.link_get_mtu)
598 hwaddress = self._get_hwaddress(ifaceobj)
599 if hwaddress:
600 rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
601 if not rhwaddress or rhwaddress != hwaddress:
602 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
603 1)
604 elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
605 # XXX: hw address is not in bridge
606 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
607 1)
608 ifaceobjcurr.status_str = 'bridge fdb error'
609 else:
610 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
611 0)
612 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
613 'alias', self.ipcmd.link_get_alias)
614 # compare addresses
615 if addr_method in ["dhcp", "ppp"]:
616 return
617 addrs = utils.get_normalized_ip_addr(ifaceobj.name,
618 self._get_iface_addresses(ifaceobj))
619 runningaddrsdict = self.ipcmd.addr_get(ifaceobj.name)
620 # if anycast address is configured on 'lo' and is in running config
621 # add it to addrs so that query_check doesn't fail
622 anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip'))
623 if anycast_addr:
624 anycast_addr = anycast_addr+'/32'
625 if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr):
626 addrs.append(anycast_addr)
627
628 # Set ifaceobjcurr method and family
629 ifaceobjcurr.addr_method = ifaceobj.addr_method
630 ifaceobjcurr.addr_family = ifaceobj.addr_family
631 if not runningaddrsdict and not addrs:
632 return
633 runningaddrs = runningaddrsdict.keys() if runningaddrsdict else []
634 # Add /32 netmask to configured address without netmask.
635 # This may happen on interfaces where pointopoint is used.
636 runningaddrs = [ addr if '/' in addr else addr + '/32' for addr in runningaddrs]
637 if runningaddrs != addrs:
638 runningaddrsset = set(runningaddrs) if runningaddrs else set([])
639 addrsset = set(addrs) if addrs else set([])
640 if (ifaceobj.flags & iface.HAS_SIBLINGS):
641 if not addrsset:
642 return
643 # only check for addresses present in running config
644 addrsdiff = addrsset.difference(runningaddrsset)
645 for addr in addrs:
646 if addr in addrsdiff:
647 ifaceobjcurr.update_config_with_status('address',
648 addr, 1)
649 else:
650 ifaceobjcurr.update_config_with_status('address',
651 addr, 0)
652 else:
653 addrsdiff = addrsset.symmetric_difference(runningaddrsset)
654 for addr in addrsset.union(runningaddrsset):
655 if addr in addrsdiff:
656 ifaceobjcurr.update_config_with_status('address',
657 addr, 1)
658 else:
659 ifaceobjcurr.update_config_with_status('address',
660 addr, 0)
661 elif addrs:
662 [ifaceobjcurr.update_config_with_status('address',
663 addr, 0) for addr in addrs]
664 #XXXX Check broadcast address, scope, etc
665 return
666
667 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
668 if not self.ipcmd.link_exists(ifaceobjrunning.name):
669 self.logger.debug('iface %s not found' %ifaceobjrunning.name)
670 return
671 dhclientcmd = dhclient()
672 if (dhclientcmd.is_running(ifaceobjrunning.name) or
673 dhclientcmd.is_running6(ifaceobjrunning.name)):
674 # If dhcp is configured on the interface, we skip it
675 return
676 isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
677 if isloopback:
678 default_addrs = ['127.0.0.1/8', '::1/128']
679 ifaceobjrunning.addr_family.append('inet')
680 ifaceobjrunning.addr_method = 'loopback'
681 else:
682 default_addrs = []
683 runningaddrsdict = self.ipcmd.addr_get(ifaceobjrunning.name)
684 if runningaddrsdict:
685 [ifaceobjrunning.update_config('address', addr)
686 for addr, addrattrs in runningaddrsdict.items()
687 if addr not in default_addrs]
688 mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
689 if (mtu and
690 (ifaceobjrunning.name == 'lo' and mtu != '16436') or
691 (ifaceobjrunning.name != 'lo' and
692 mtu != self.get_mod_subattr('mtu', 'default'))):
693 ifaceobjrunning.update_config('mtu', mtu)
694 alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
695 if alias:
696 ifaceobjrunning.update_config('alias', alias)
697
698
699 _run_ops = {'up' : _up,
700 'down' : _down,
701 'query-checkcurr' : _query_check,
702 'query-running' : _query_running }
703
704 def get_ops(self):
705 """ returns list of ops supported by this module """
706 return self._run_ops.keys()
707
708 def _init_command_handlers(self):
709 if not self.ipcmd:
710 self.ipcmd = iproute2()
711
712 def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
713 """ run address configuration on the interface object passed as argument
714
715 Args:
716 **ifaceobj** (object): iface object
717
718 **operation** (str): any of 'up', 'down', 'query-checkcurr',
719 'query-running'
720 Kwargs:
721 query_ifaceobj (object): query check ifaceobject. This is only
722 valid when op is 'query-checkcurr'. It is an object same as
723 ifaceobj, but contains running attribute values and its config
724 status. The modules can use it to return queried running state
725 of interfaces. status is success if the running state is same
726 as user required state in ifaceobj. error otherwise.
727 """
728 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
729 return
730 op_handler = self._run_ops.get(operation)
731 if not op_handler:
732 return
733 self._init_command_handlers()
734 if operation == 'query-checkcurr':
735 op_handler(self, ifaceobj, query_ifaceobj,
736 ifaceobj_getfunc=ifaceobj_getfunc)
737 else:
738 op_handler(self, ifaceobj,
739 ifaceobj_getfunc=ifaceobj_getfunc)