]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/address.py
'address-virtual' needs to be multivalue
[mirror_ifupdown2.git] / addons / address.py
CommitLineData
15ef32ea
RP
1#!/usr/bin/python
2#
3# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5#
6
9087e727
JF
7import os
8
15ef32ea 9try:
c6370b56 10 from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPv4Address, IPv6Address
15ef32ea
RP
11 from sets import Set
12 from ifupdown.iface import *
82908a2d 13 from ifupdown.utils import utils
15ef32ea
RP
14 from ifupdownaddons.modulebase import moduleBase
15 from ifupdownaddons.iproute2 import iproute2
16 from ifupdownaddons.dhclient import dhclient
84f33af6 17 import ifupdown.policymanager as policymanager
2864d6f3 18 from ifupdown.netlink import netlink
0582f185 19 import ifupdown.ifupdownconfig as ifupdownConfig
fc5e1735 20 import ifupdown.ifupdownflags as ifupdownflags
0232d1bb 21 import ifupdown.statemanager as statemanager
15ef32ea
RP
22except ImportError, e:
23 raise ImportError (str(e) + "- required module not found")
24
25class 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',
482b2fab 33 'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'],
c6370b56 34 'multiline' : True,
15ef32ea
RP
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',
482b2fab 43 'validvals' : ['<ipv4>', ],
15ef32ea
RP
44 'example' : ['broadcast 10.0.1.255']},
45 'scope' :
46 {'help': 'scope',
c6370b56 47 'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'],
15ef32ea
RP
48 'example' : ['scope host']},
49 'preferred-lifetime' :
50 {'help': 'preferred lifetime',
c6370b56 51 'validrange' : ['0', '65535'],
15ef32ea
RP
52 'example' : ['preferred-lifetime forever',
53 'preferred-lifetime 10']},
54 'gateway' :
55 {'help': 'default gateway',
482b2fab 56 'validvals' : ['<ipv4>', '<ipv6>'],
2ed2adeb 57 'multiline' : True,
15ef32ea
RP
58 'example' : ['gateway 255.255.255.0']},
59 'mtu' :
60 { 'help': 'interface mtu',
c6370b56 61 'validrange' : ['552', '9216'],
15ef32ea
RP
62 'example' : ['mtu 1600'],
63 'default' : '1500'},
64 'hwaddress' :
65 {'help' : 'hw address',
c6370b56 66 'validvals' : ['<mac>',],
15ef32ea
RP
67 'example': ['hwaddress 44:38:39:00:27:b8']},
68 'alias' :
69 { 'help': 'description/alias',
394e68b5
RP
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',
c6370b56 77 'validvals' : ['yes', 'no'],
394e68b5 78 'default' : 'yes',
a794fb31
BR
79 'example' : ['address-purge yes/no']},
80 'clagd-vxlan-anycast-ip' :
81 { 'help' : 'Anycast local IP address for ' +
82 'dual connected VxLANs',
482b2fab 83 'validvals' : ['<ipv4>', ],
a794fb31 84 'example' : ['clagd-vxlan-anycast-ip 36.0.0.11']}}}
15ef32ea
RP
85
86 def __init__(self, *args, **kargs):
87 moduleBase.__init__(self, *args, **kargs)
88 self.ipcmd = None
8e113d63 89 self._bridge_fdb_query_cache = {}
84f33af6 90 self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu')
9f30b2cc
RP
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)
15ef32ea 100
09096420 101 def syntax_check(self, ifaceobj, ifaceobj_getfunc=None):
22b49c28 102 return (self.syntax_check_multiple_gateway(ifaceobj)
09096420
RP
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
22b49c28
JF
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
38365d4a
JF
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
22b49c28 126 def syntax_check_multiple_gateway(self, ifaceobj):
38365d4a
JF
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
75afe2a7
RP
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
428206bf 152 def _get_hwaddress(self, ifaceobj):
2876ca35 153 hwaddress = ifaceobj.get_attr_value_first('hwaddress')
428206bf
JF
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)
75afe2a7
RP
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)
8c2c9f26
RP
167 if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
168 or is_vlan_dev_on_vlan_aware_bridge):
75afe2a7
RP
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)
cb46a208 181
0582f185
RP
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):
15ef32ea 191 newaddrs = []
0582f185
RP
192 newaddr_attrs = {}
193
194 for ifaceobj in ifaceobjlist:
195 addrs = ifaceobj.get_attr_value('address')
196 if not addrs:
197 continue
198
22b49c28
JF
199 if not self.syntax_check_addr_allowed_on(ifaceobj,
200 syntax_check=False):
0582f185 201 return (False, newaddrs, newaddr_attrs)
15ef32ea
RP
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 if '/' in addr:
206 newaddrs.append(addr)
207 continue
494d31d2 208 newaddr = addr
15ef32ea
RP
209 netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
210 if netmask:
211 prefixlen = IPNetwork('%s' %addr +
212 '/%s' %netmask).prefixlen
0582f185 213 newaddr = addr + '/%s' %prefixlen
42ae7838
ST
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
0582f185 219 newaddrs.append(newaddr)
15ef32ea 220
0582f185
RP
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:
72c964c2 226 attrs[a] = aval
0582f185
RP
227
228 if attrs:
229 newaddr_attrs[newaddr]= attrs
230 return (True, newaddrs, newaddr_attrs)
231
77021aa1
RP
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:
bf3eda91 248 self.log_error(str(e), ifaceobj)
77021aa1
RP
249
250 def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None,
251 force_reapply=False):
0582f185
RP
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)
82908a2d 270 newaddrs = utils.get_normalized_ip_addr(ifaceobj.name, newaddrs)
0582f185
RP
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
fc5e1735 281 if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes':
0582f185
RP
282 # if perfmode is not set and purge addresses is not set to 'no'
283 # lets purge addresses not in the config
82908a2d 284 runningaddrs = utils.get_normalized_ip_addr(ifaceobj.name, self.ipcmd.addr_get(ifaceobj.name, details=False))
0582f185 285
a794fb31
BR
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
82908a2d 288 anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist))
0582f185 289
a794fb31
BR
290 if runningaddrs and anycast_addr and anycast_addr in runningaddrs:
291 newaddrs.append(anycast_addr)
15ef32ea 292 if newaddrs == runningaddrs:
77021aa1
RP
293 if force_reapply:
294 self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
15ef32ea
RP
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
77021aa1 308 self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs)
15ef32ea 309
0232d1bb
N
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:
317 pass
318 for add_gw in list(set(gateways) - set(prev_gw)):
319 try:
320 self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf)
321 except:
322 pass
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
8d1e346f
RP
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:
9f30b2cc 341 self.logger.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name)
8d1e346f
RP
342 elif ifaceobj_getfunc:
343 if ((ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and
344 ifaceobj.upperifaces):
9f30b2cc
RP
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:
8d1e346f
RP
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
9f30b2cc
RP
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))
8d1e346f
RP
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)
9f30b2cc
RP
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
0582f185 446 def _up(self, ifaceobj, ifaceobj_getfunc=None):
15ef32ea
RP
447 if not self.ipcmd.link_exists(ifaceobj.name):
448 return
68d9fee0 449 addr_method = ifaceobj.addr_method
77021aa1 450 force_reapply = False
15ef32ea
RP
451 try:
452 # release any stale dhcp addresses if present
fc5e1735 453 if (addr_method != "dhcp" and not ifupdownflags.flags.PERFMODE and
15ef32ea
RP
454 not (ifaceobj.flags & iface.HAS_SIBLINGS)):
455 # if not running in perf mode and ifaceobj does not have
456 # any sibling iface objects, kill any stale dhclient
457 # processes
75afe2a7 458 dhclientcmd = dhclient()
7c1135ea 459 if dhclientcmd.is_running(ifaceobj.name):
15ef32ea
RP
460 # release any dhcp leases
461 dhclientcmd.release(ifaceobj.name)
77021aa1 462 force_reapply = True
7c1135ea 463 elif dhclientcmd.is_running6(ifaceobj.name):
15ef32ea 464 dhclientcmd.release6(ifaceobj.name)
77021aa1 465 force_reapply = True
15ef32ea
RP
466 except:
467 pass
8e113d63 468
15ef32ea 469 self.ipcmd.batch_start()
68d9fee0 470 if addr_method != "dhcp":
77021aa1
RP
471 self._inet_address_config(ifaceobj, ifaceobj_getfunc,
472 force_reapply)
9f30b2cc 473 self._process_mtu_config(ifaceobj, ifaceobj_getfunc)
13e22530 474
15ef32ea
RP
475 alias = ifaceobj.get_attr_value_first('alias')
476 if alias:
8e113d63 477 self.ipcmd.link_set_alias(ifaceobj.name, alias)
093ffa00
JF
478 try:
479 self.ipcmd.batch_commit()
480 except Exception as e:
03092649 481 self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False)
264dcaa0 482
68d9fee0 483 try:
707aeb73
JF
484 hwaddress = self._get_hwaddress(ifaceobj)
485 if hwaddress:
486 running_hwaddress = None
487 if not ifupdownflags.flags.PERFMODE: # system is clean
488 running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
489 if hwaddress != running_hwaddress:
490 slave_down = False
491 netlink.link_set_updown(ifaceobj.name, "down")
492 if ifaceobj.link_kind & ifaceLinkKind.BOND:
493 # if bond, down all the slaves
494 if ifaceobj.lowerifaces:
495 for l in ifaceobj.lowerifaces:
496 netlink.link_set_updown(l, "down")
497 slave_down = True
498 try:
499 self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
500 finally:
501 netlink.link_set_updown(ifaceobj.name, "up")
502 if slave_down:
503 for l in ifaceobj.lowerifaces:
504 netlink.link_set_updown(l, "up")
505
68d9fee0
RP
506 # Handle special things on a bridge
507 self._process_bridge(ifaceobj, True)
508 except Exception, e:
bf3eda91 509 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
cb46a208 510
68d9fee0 511 if addr_method != "dhcp":
0232d1bb
N
512 gateways = ifaceobj.get_attr_value('gateway')
513 if not gateways:
514 gateways = []
515 prev_gw = self._get_prev_gateway(ifaceobj, gateways)
516 self._add_delete_gateway(ifaceobj, gateways, prev_gw)
517 return
15ef32ea 518
0582f185 519 def _down(self, ifaceobj, ifaceobj_getfunc=None):
15ef32ea
RP
520 try:
521 if not self.ipcmd.link_exists(ifaceobj.name):
522 return
68d9fee0
RP
523 addr_method = ifaceobj.addr_method
524 if addr_method != "dhcp":
aa052170
N
525 if ifaceobj.get_attr_value_first('address-purge')=='no':
526 addrlist = ifaceobj.get_attr_value('address')
527 for addr in addrlist:
528 self.ipcmd.addr_del(ifaceobj.name, addr)
529 #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0])
530 else:
531 self.ipcmd.del_addr_all(ifaceobj.name)
84f33af6 532 mtu = ifaceobj.get_attr_value_first('mtu')
00c12960
RP
533 if (not ifaceobj.link_kind and mtu and
534 self.default_mtu and (mtu != self.default_mtu)):
84f33af6 535 self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu)
15ef32ea
RP
536 alias = ifaceobj.get_attr_value_first('alias')
537 if alias:
9087e727 538 filename = '/sys/class/net/%s/ifalias' %ifaceobj.name
a4a53f4b 539 self.logger.info('executing echo "" > %s' %filename)
9087e727 540 os.system('echo "" > %s' %filename)
75afe2a7
RP
541 # XXX hwaddress reset cannot happen because we dont know last
542 # address.
543
544 # Handle special things on a bridge
545 self._process_bridge(ifaceobj, False)
15ef32ea 546 except Exception, e:
bcf11b14
RP
547 self.logger.debug('%s : %s' %(ifaceobj.name, str(e)))
548 pass
15ef32ea
RP
549
550 def _get_iface_addresses(self, ifaceobj):
551 addrlist = ifaceobj.get_attr_value('address')
552 outaddrlist = []
553
554 if not addrlist: return None
555 for addrindex in range(0, len(addrlist)):
556 addr = addrlist[addrindex]
557 netmask = ifaceobj.get_attr_value_n('netmask', addrindex)
558 if netmask:
559 prefixlen = IPNetwork('%s' %addr +
560 '/%s' %netmask).prefixlen
561 addr = addr + '/%s' %prefixlen
562 outaddrlist.append(addr)
563 return outaddrlist
564
8e113d63
RP
565 def _get_bridge_fdbs(self, bridgename, vlan):
566 fdbs = self._bridge_fdb_query_cache.get(bridgename)
567 if not fdbs:
568 fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
569 if not fdbs:
570 return
571 self._bridge_fdb_query_cache[bridgename] = fdbs
572 return fdbs.get(vlan)
573
574 def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
575 """ If the device is a bridge, make sure the addresses
576 are in the bridge """
577 if '.' in ifaceobj.name:
578 (bridgename, vlan) = ifaceobj.name.split('.')
579 if self.ipcmd.bridge_is_vlan_aware(bridgename):
580 fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
581 if not fdb_addrs or hwaddress not in fdb_addrs:
582 return False
583 return True
584
0582f185 585 def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None):
15ef32ea
RP
586 runningaddrsdict = None
587 if not self.ipcmd.link_exists(ifaceobj.name):
588 self.logger.debug('iface %s not found' %ifaceobj.name)
589 return
16d854b4 590 addr_method = ifaceobj.addr_method
15ef32ea
RP
591 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
592 'mtu', self.ipcmd.link_get_mtu)
428206bf 593 hwaddress = self._get_hwaddress(ifaceobj)
8e113d63 594 if hwaddress:
2876ca35 595 rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
8e113d63
RP
596 if not rhwaddress or rhwaddress != hwaddress:
597 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
598 1)
599 elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
600 # XXX: hw address is not in bridge
601 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
602 1)
603 ifaceobjcurr.status_str = 'bridge fdb error'
604 else:
605 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
606 0)
15ef32ea
RP
607 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
608 'alias', self.ipcmd.link_get_alias)
609 # compare addresses
16d854b4
RP
610 if addr_method == 'dhcp':
611 return
82908a2d
JF
612 addrs = utils.get_normalized_ip_addr(ifaceobj.name,
613 self._get_iface_addresses(ifaceobj))
15ef32ea 614 runningaddrsdict = self.ipcmd.addr_get(ifaceobj.name)
a794fb31
BR
615 # if anycast address is configured on 'lo' and is in running config
616 # add it to addrs so that query_check doesn't fail
82908a2d 617 anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip'))
a794fb31
BR
618 if anycast_addr:
619 anycast_addr = anycast_addr+'/32'
620 if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr):
621 addrs.append(anycast_addr)
15ef32ea
RP
622
623 # Set ifaceobjcurr method and family
624 ifaceobjcurr.addr_method = ifaceobj.addr_method
625 ifaceobjcurr.addr_family = ifaceobj.addr_family
626 if not runningaddrsdict and not addrs:
627 return
628 runningaddrs = runningaddrsdict.keys() if runningaddrsdict else []
629 if runningaddrs != addrs:
630 runningaddrsset = set(runningaddrs) if runningaddrs else set([])
631 addrsset = set(addrs) if addrs else set([])
632 if (ifaceobj.flags & iface.HAS_SIBLINGS):
633 if not addrsset:
634 return
635 # only check for addresses present in running config
636 addrsdiff = addrsset.difference(runningaddrsset)
637 for addr in addrs:
638 if addr in addrsdiff:
639 ifaceobjcurr.update_config_with_status('address',
640 addr, 1)
641 else:
642 ifaceobjcurr.update_config_with_status('address',
643 addr, 0)
644 else:
645 addrsdiff = addrsset.symmetric_difference(runningaddrsset)
646 for addr in addrsset.union(runningaddrsset):
647 if addr in addrsdiff:
648 ifaceobjcurr.update_config_with_status('address',
649 addr, 1)
650 else:
651 ifaceobjcurr.update_config_with_status('address',
652 addr, 0)
653 elif addrs:
654 [ifaceobjcurr.update_config_with_status('address',
655 addr, 0) for addr in addrs]
656 #XXXX Check broadcast address, scope, etc
657 return
658
0582f185 659 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
15ef32ea
RP
660 if not self.ipcmd.link_exists(ifaceobjrunning.name):
661 self.logger.debug('iface %s not found' %ifaceobjrunning.name)
15ef32ea
RP
662 return
663 dhclientcmd = dhclient()
664 if (dhclientcmd.is_running(ifaceobjrunning.name) or
665 dhclientcmd.is_running6(ifaceobjrunning.name)):
666 # If dhcp is configured on the interface, we skip it
84f33af6 667 return
15ef32ea
RP
668 isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
669 if isloopback:
670 default_addrs = ['127.0.0.1/8', '::1/128']
004d1e65 671 ifaceobjrunning.addr_family.append('inet')
15ef32ea
RP
672 ifaceobjrunning.addr_method = 'loopback'
673 else:
674 default_addrs = []
675 runningaddrsdict = self.ipcmd.addr_get(ifaceobjrunning.name)
676 if runningaddrsdict:
677 [ifaceobjrunning.update_config('address', addr)
678 for addr, addrattrs in runningaddrsdict.items()
679 if addr not in default_addrs]
680 mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
681 if (mtu and
682 (ifaceobjrunning.name == 'lo' and mtu != '16436') or
683 (ifaceobjrunning.name != 'lo' and
684 mtu != self.get_mod_subattr('mtu', 'default'))):
685 ifaceobjrunning.update_config('mtu', mtu)
686 alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
84f33af6 687 if alias:
15ef32ea
RP
688 ifaceobjrunning.update_config('alias', alias)
689
8d1e346f 690
15ef32ea
RP
691 _run_ops = {'up' : _up,
692 'down' : _down,
693 'query-checkcurr' : _query_check,
694 'query-running' : _query_running }
695
696 def get_ops(self):
697 """ returns list of ops supported by this module """
698 return self._run_ops.keys()
699
700 def _init_command_handlers(self):
701 if not self.ipcmd:
fc5e1735 702 self.ipcmd = iproute2()
15ef32ea 703
6e16e5ae 704 def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None):
15ef32ea
RP
705 """ run address configuration on the interface object passed as argument
706
707 Args:
708 **ifaceobj** (object): iface object
709
710 **operation** (str): any of 'up', 'down', 'query-checkcurr',
711 'query-running'
712 Kwargs:
713 query_ifaceobj (object): query check ifaceobject. This is only
714 valid when op is 'query-checkcurr'. It is an object same as
715 ifaceobj, but contains running attribute values and its config
716 status. The modules can use it to return queried running state
717 of interfaces. status is success if the running state is same
718 as user required state in ifaceobj. error otherwise.
719 """
8e113d63
RP
720 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
721 return
15ef32ea
RP
722 op_handler = self._run_ops.get(operation)
723 if not op_handler:
724 return
15ef32ea
RP
725 self._init_command_handlers()
726 if operation == 'query-checkcurr':
0582f185
RP
727 op_handler(self, ifaceobj, query_ifaceobj,
728 ifaceobj_getfunc=ifaceobj_getfunc)
15ef32ea 729 else:
0582f185
RP
730 op_handler(self, ifaceobj,
731 ifaceobj_getfunc=ifaceobj_getfunc)