]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/address.py
ifupdown2: fix a few regex parsing error messages
[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
7try:
8 from ipaddr import IPNetwork
9 from sets import Set
10 from ifupdown.iface import *
11 from ifupdownaddons.modulebase import moduleBase
12 from ifupdownaddons.iproute2 import iproute2
13 from ifupdownaddons.dhclient import dhclient
264dcaa0 14 import ifupdown.rtnetlink_api as rtnetlink_api
15ef32ea
RP
15except ImportError, e:
16 raise ImportError (str(e) + "- required module not found")
17
18class address(moduleBase):
19 """ ifupdown2 addon module to configure address, mtu, hwaddress, alias
20 (description) on an interface """
21
22 _modinfo = {'mhelp' : 'address configuration module for interfaces',
23 'attrs': {
24 'address' :
25 {'help' : 'ipv4 or ipv6 addresses',
26 'example' : ['address 10.0.12.3/24',
27 'address 2000:1000:1000:1000:3::5/128']},
28 'netmask' :
29 {'help': 'netmask',
30 'example' : ['netmask 255.255.255.0'],
31 'compat' : True},
32 'broadcast' :
33 {'help': 'broadcast address',
34 'example' : ['broadcast 10.0.1.255']},
35 'scope' :
36 {'help': 'scope',
37 'example' : ['scope host']},
38 'preferred-lifetime' :
39 {'help': 'preferred lifetime',
40 'example' : ['preferred-lifetime forever',
41 'preferred-lifetime 10']},
42 'gateway' :
43 {'help': 'default gateway',
44 'example' : ['gateway 255.255.255.0']},
45 'mtu' :
46 { 'help': 'interface mtu',
47 'example' : ['mtu 1600'],
48 'default' : '1500'},
49 'hwaddress' :
50 {'help' : 'hw address',
51 'example': ['hwaddress 44:38:39:00:27:b8']},
52 'alias' :
53 { 'help': 'description/alias',
394e68b5
RP
54 'example' : ['alias testnetwork']},
55 'address-purge' :
56 { 'help': 'purge existing addresses. By default ' +
57 'any existing ip addresses on an interface are ' +
58 'purged to match persistant addresses in the ' +
59 'interfaces file. Set this attribute to \'no\'' +
60 'if you want to preserve existing addresses',
61 'default' : 'yes',
a794fb31
BR
62 'example' : ['address-purge yes/no']},
63 'clagd-vxlan-anycast-ip' :
64 { 'help' : 'Anycast local IP address for ' +
65 'dual connected VxLANs',
66 'example' : ['clagd-vxlan-anycast-ip 36.0.0.11']}}}
15ef32ea
RP
67
68 def __init__(self, *args, **kargs):
69 moduleBase.__init__(self, *args, **kargs)
70 self.ipcmd = None
8e113d63 71 self._bridge_fdb_query_cache = {}
15ef32ea 72
75afe2a7
RP
73 def _address_valid(self, addrs):
74 if not addrs:
75 return False
76 if any(map(lambda a: True if a[:7] != '0.0.0.0'
77 else False, addrs)):
78 return True
79 return False
80
81 def _process_bridge(self, ifaceobj, up):
82 hwaddress = ifaceobj.get_attr_value_first('hwaddress')
83 addrs = ifaceobj.get_attr_value_first('address')
84 is_vlan_dev_on_vlan_aware_bridge = False
85 is_bridge = self.ipcmd.is_bridge(ifaceobj.name)
86 if not is_bridge:
87 if '.' in ifaceobj.name:
88 (bridgename, vlan) = ifaceobj.name.split('.')
89 is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename)
8c2c9f26
RP
90 if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
91 or is_vlan_dev_on_vlan_aware_bridge):
75afe2a7
RP
92 if self._address_valid(addrs):
93 if up:
94 self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
95 '/arp_accept', '1')
96 else:
97 self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
98 '/arp_accept', '0')
99 if hwaddress and is_vlan_dev_on_vlan_aware_bridge:
100 if up:
101 self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
102 else:
103 self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
cb46a208 104
15ef32ea 105 def _inet_address_config(self, ifaceobj):
394e68b5
RP
106 purge_addresses = ifaceobj.get_attr_value_first('address-purge')
107 if not purge_addresses:
108 purge_addresses = 'yes'
15ef32ea
RP
109 newaddrs = []
110 addrs = ifaceobj.get_attr_value('address')
111 if addrs:
7f67f3e5
ST
112 if (ifaceobj.role & ifaceRole.SLAVE) or \
113 (ifaceobj.link_kind & ifaceLinkKind.BRIDGE_VLAN_AWARE):
e6a66e79
RP
114 # we must not configure an IP address if the interface is
115 # enslaved or is a VLAN AWARE BRIDGE
116 self.logger.info('%s: ignoring ip address. Interface is '
117 'enslaved or a vlan aware bridge and cannot'
118 ' have an IP Address' %(ifaceobj.name))
0a3bee28 119 return
15ef32ea
RP
120 # If user address is not in CIDR notation, convert them to CIDR
121 for addr_index in range(0, len(addrs)):
122 addr = addrs[addr_index]
123 if '/' in addr:
124 newaddrs.append(addr)
125 continue
126 netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
127 if netmask:
128 prefixlen = IPNetwork('%s' %addr +
129 '/%s' %netmask).prefixlen
130 newaddrs.append(addr + '/%s' %prefixlen)
131 else:
132 newaddrs.append(addr)
133
394e68b5
RP
134 if (not self.PERFMODE and
135 not (ifaceobj.flags & iface.HAS_SIBLINGS) and
136 purge_addresses == 'yes'):
15ef32ea
RP
137 # if perfmode is not set and also if iface has no sibling
138 # objects, purge addresses that are not present in the new
139 # config
140 runningaddrs = self.ipcmd.addr_get(ifaceobj.name, details=False)
a794fb31
BR
141 # if anycast address is configured on 'lo' and is in running config
142 # add it to newaddrs so that ifreload doesn't wipe it out
143 anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')
144 if anycast_addr:
145 anycast_addr = anycast_addr+'/32'
146 if runningaddrs and anycast_addr and anycast_addr in runningaddrs:
147 newaddrs.append(anycast_addr)
15ef32ea
RP
148 if newaddrs == runningaddrs:
149 return
150 try:
151 # if primary address is not same, there is no need to keep any.
152 # reset all addresses
153 if (newaddrs and runningaddrs and
154 (newaddrs[0] != runningaddrs[0])):
155 self.ipcmd.del_addr_all(ifaceobj.name)
156 else:
157 self.ipcmd.del_addr_all(ifaceobj.name, newaddrs)
158 except Exception, e:
159 self.log_warn(str(e))
160 if not newaddrs:
161 return
162 for addr_index in range(0, len(newaddrs)):
163 try:
164 self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
165 ifaceobj.get_attr_value_n('broadcast', addr_index),
166 ifaceobj.get_attr_value_n('pointopoint',addr_index),
167 ifaceobj.get_attr_value_n('scope', addr_index),
168 ifaceobj.get_attr_value_n('preferred-lifetime', addr_index))
169 except Exception, e:
170 self.log_error(str(e))
171
172 def _up(self, ifaceobj):
173 if not self.ipcmd.link_exists(ifaceobj.name):
174 return
68d9fee0 175 addr_method = ifaceobj.addr_method
15ef32ea
RP
176 try:
177 # release any stale dhcp addresses if present
68d9fee0 178 if (addr_method != "dhcp" and not self.PERFMODE and
15ef32ea
RP
179 not (ifaceobj.flags & iface.HAS_SIBLINGS)):
180 # if not running in perf mode and ifaceobj does not have
181 # any sibling iface objects, kill any stale dhclient
182 # processes
75afe2a7 183 dhclientcmd = dhclient()
15ef32ea
RP
184 if dhclient.is_running(ifaceobj.name):
185 # release any dhcp leases
186 dhclientcmd.release(ifaceobj.name)
187 elif dhclient.is_running6(ifaceobj.name):
188 dhclientcmd.release6(ifaceobj.name)
189 except:
190 pass
8e113d63 191
15ef32ea 192 self.ipcmd.batch_start()
68d9fee0
RP
193 if addr_method != "dhcp":
194 self._inet_address_config(ifaceobj)
15ef32ea
RP
195 mtu = ifaceobj.get_attr_value_first('mtu')
196 if mtu:
8e113d63 197 self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu)
15ef32ea
RP
198 alias = ifaceobj.get_attr_value_first('alias')
199 if alias:
8e113d63 200 self.ipcmd.link_set_alias(ifaceobj.name, alias)
264dcaa0
RP
201 self.ipcmd.batch_commit()
202
cb46a208
RP
203 hwaddress = ifaceobj.get_attr_value_first('hwaddress')
204 if hwaddress:
264dcaa0
RP
205 running_hwaddress = None
206 if not self.PERFMODE: # system is clean
207 running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
208 if hwaddress != running_hwaddress:
209 slave_down = False
210 rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "down")
211 if ifaceobj.link_kind & ifaceLinkKind.BOND:
212 # if bond, down all the slaves
213 if ifaceobj.lowerifaces:
214 for l in ifaceobj.lowerifaces:
215 rtnetlink_api.rtnl_api.link_set(l, "down")
216 slave_down = True
217 try:
218 self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
219 finally:
220 rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
221 if slave_down:
222 for l in ifaceobj.lowerifaces:
223 rtnetlink_api.rtnl_api.link_set(l, "up")
cb46a208 224
68d9fee0
RP
225 try:
226 # Handle special things on a bridge
227 self._process_bridge(ifaceobj, True)
228 except Exception, e:
229 self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
230 pass
cb46a208 231
68d9fee0
RP
232 if addr_method != "dhcp":
233 self.ipcmd.route_add_gateway(ifaceobj.name,
234 ifaceobj.get_attr_value_first('gateway'))
15ef32ea
RP
235
236 def _down(self, ifaceobj):
237 try:
238 if not self.ipcmd.link_exists(ifaceobj.name):
239 return
68d9fee0
RP
240 addr_method = ifaceobj.addr_method
241 if addr_method != "dhcp":
242 self.ipcmd.route_del_gateway(ifaceobj.name,
15ef32ea
RP
243 ifaceobj.get_attr_value_first('gateway'),
244 ifaceobj.get_attr_value_first('metric'))
68d9fee0 245 self.ipcmd.del_addr_all(ifaceobj.name)
15ef32ea
RP
246 alias = ifaceobj.get_attr_value_first('alias')
247 if alias:
248 self.ipcmd.link_set(ifaceobj.name, 'alias', "\'\'")
75afe2a7
RP
249 # XXX hwaddress reset cannot happen because we dont know last
250 # address.
251
252 # Handle special things on a bridge
253 self._process_bridge(ifaceobj, False)
15ef32ea 254 except Exception, e:
bcf11b14
RP
255 self.logger.debug('%s : %s' %(ifaceobj.name, str(e)))
256 pass
15ef32ea
RP
257
258 def _get_iface_addresses(self, ifaceobj):
259 addrlist = ifaceobj.get_attr_value('address')
260 outaddrlist = []
261
262 if not addrlist: return None
263 for addrindex in range(0, len(addrlist)):
264 addr = addrlist[addrindex]
265 netmask = ifaceobj.get_attr_value_n('netmask', addrindex)
266 if netmask:
267 prefixlen = IPNetwork('%s' %addr +
268 '/%s' %netmask).prefixlen
269 addr = addr + '/%s' %prefixlen
270 outaddrlist.append(addr)
271 return outaddrlist
272
8e113d63
RP
273 def _get_bridge_fdbs(self, bridgename, vlan):
274 fdbs = self._bridge_fdb_query_cache.get(bridgename)
275 if not fdbs:
276 fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
277 if not fdbs:
278 return
279 self._bridge_fdb_query_cache[bridgename] = fdbs
280 return fdbs.get(vlan)
281
282 def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
283 """ If the device is a bridge, make sure the addresses
284 are in the bridge """
285 if '.' in ifaceobj.name:
286 (bridgename, vlan) = ifaceobj.name.split('.')
287 if self.ipcmd.bridge_is_vlan_aware(bridgename):
288 fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
289 if not fdb_addrs or hwaddress not in fdb_addrs:
290 return False
291 return True
292
15ef32ea
RP
293 def _query_check(self, ifaceobj, ifaceobjcurr):
294 runningaddrsdict = None
295 if not self.ipcmd.link_exists(ifaceobj.name):
296 self.logger.debug('iface %s not found' %ifaceobj.name)
297 return
16d854b4 298 addr_method = ifaceobj.addr_method
15ef32ea
RP
299 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
300 'mtu', self.ipcmd.link_get_mtu)
8e113d63
RP
301 hwaddress = ifaceobj.get_attr_value_first('hwaddress')
302 if hwaddress:
303 rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
304 if not rhwaddress or rhwaddress != hwaddress:
305 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
306 1)
307 elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
308 # XXX: hw address is not in bridge
309 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
310 1)
311 ifaceobjcurr.status_str = 'bridge fdb error'
312 else:
313 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
314 0)
15ef32ea
RP
315 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
316 'alias', self.ipcmd.link_get_alias)
317 # compare addresses
16d854b4
RP
318 if addr_method == 'dhcp':
319 return
15ef32ea
RP
320 addrs = self._get_iface_addresses(ifaceobj)
321 runningaddrsdict = self.ipcmd.addr_get(ifaceobj.name)
a794fb31
BR
322 # if anycast address is configured on 'lo' and is in running config
323 # add it to addrs so that query_check doesn't fail
324 anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')
325 if anycast_addr:
326 anycast_addr = anycast_addr+'/32'
327 if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr):
328 addrs.append(anycast_addr)
15ef32ea
RP
329
330 # Set ifaceobjcurr method and family
331 ifaceobjcurr.addr_method = ifaceobj.addr_method
332 ifaceobjcurr.addr_family = ifaceobj.addr_family
333 if not runningaddrsdict and not addrs:
334 return
335 runningaddrs = runningaddrsdict.keys() if runningaddrsdict else []
336 if runningaddrs != addrs:
337 runningaddrsset = set(runningaddrs) if runningaddrs else set([])
338 addrsset = set(addrs) if addrs else set([])
339 if (ifaceobj.flags & iface.HAS_SIBLINGS):
340 if not addrsset:
341 return
342 # only check for addresses present in running config
343 addrsdiff = addrsset.difference(runningaddrsset)
344 for addr in addrs:
345 if addr in addrsdiff:
346 ifaceobjcurr.update_config_with_status('address',
347 addr, 1)
348 else:
349 ifaceobjcurr.update_config_with_status('address',
350 addr, 0)
351 else:
352 addrsdiff = addrsset.symmetric_difference(runningaddrsset)
353 for addr in addrsset.union(runningaddrsset):
354 if addr in addrsdiff:
355 ifaceobjcurr.update_config_with_status('address',
356 addr, 1)
357 else:
358 ifaceobjcurr.update_config_with_status('address',
359 addr, 0)
360 elif addrs:
361 [ifaceobjcurr.update_config_with_status('address',
362 addr, 0) for addr in addrs]
363 #XXXX Check broadcast address, scope, etc
364 return
365
366 def _query_running(self, ifaceobjrunning):
367 if not self.ipcmd.link_exists(ifaceobjrunning.name):
368 self.logger.debug('iface %s not found' %ifaceobjrunning.name)
15ef32ea
RP
369 return
370 dhclientcmd = dhclient()
371 if (dhclientcmd.is_running(ifaceobjrunning.name) or
372 dhclientcmd.is_running6(ifaceobjrunning.name)):
373 # If dhcp is configured on the interface, we skip it
374 return
375 isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
376 if isloopback:
377 default_addrs = ['127.0.0.1/8', '::1/128']
378 ifaceobjrunning.addr_family = 'inet'
379 ifaceobjrunning.addr_method = 'loopback'
380 else:
381 default_addrs = []
382 runningaddrsdict = self.ipcmd.addr_get(ifaceobjrunning.name)
383 if runningaddrsdict:
384 [ifaceobjrunning.update_config('address', addr)
385 for addr, addrattrs in runningaddrsdict.items()
386 if addr not in default_addrs]
387 mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
388 if (mtu and
389 (ifaceobjrunning.name == 'lo' and mtu != '16436') or
390 (ifaceobjrunning.name != 'lo' and
391 mtu != self.get_mod_subattr('mtu', 'default'))):
392 ifaceobjrunning.update_config('mtu', mtu)
393 alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
394 if alias:
395 ifaceobjrunning.update_config('alias', alias)
396
397 _run_ops = {'up' : _up,
398 'down' : _down,
399 'query-checkcurr' : _query_check,
400 'query-running' : _query_running }
401
402 def get_ops(self):
403 """ returns list of ops supported by this module """
404 return self._run_ops.keys()
405
406 def _init_command_handlers(self):
407 if not self.ipcmd:
408 self.ipcmd = iproute2(**self.get_flags())
409
84ca006f 410 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
15ef32ea
RP
411 """ run address configuration on the interface object passed as argument
412
413 Args:
414 **ifaceobj** (object): iface object
415
416 **operation** (str): any of 'up', 'down', 'query-checkcurr',
417 'query-running'
418 Kwargs:
419 query_ifaceobj (object): query check ifaceobject. This is only
420 valid when op is 'query-checkcurr'. It is an object same as
421 ifaceobj, but contains running attribute values and its config
422 status. The modules can use it to return queried running state
423 of interfaces. status is success if the running state is same
424 as user required state in ifaceobj. error otherwise.
425 """
8e113d63
RP
426 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
427 return
15ef32ea
RP
428 op_handler = self._run_ops.get(operation)
429 if not op_handler:
430 return
15ef32ea
RP
431 self._init_command_handlers()
432 if operation == 'query-checkcurr':
433 op_handler(self, ifaceobj, query_ifaceobj)
434 else:
435 op_handler(self, ifaceobj)