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