]> git.proxmox.com Git - mirror_ifupdown2.git/blob - ifupdown2/addons/address.py
947487f1dd55ecbb467f370f839e35edc3b49cfc
[mirror_ifupdown2.git] / ifupdown2 / 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 try:
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
14 except ImportError, e:
15 raise ImportError (str(e) + "- required module not found")
16
17 class 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',
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']}}}
62
63 def __init__(self, *args, **kargs):
64 moduleBase.__init__(self, *args, **kargs)
65 self.ipcmd = None
66 self._bridge_fdb_query_cache = {}
67
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 and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name))
86 or is_vlan_dev_on_vlan_aware_bridge):
87 if self._address_valid(addrs):
88 if up:
89 self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
90 '/arp_accept', '1')
91 else:
92 self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name +
93 '/arp_accept', '0')
94 if hwaddress and is_vlan_dev_on_vlan_aware_bridge:
95 if up:
96 self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan)
97 else:
98 self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan)
99
100 def _inet_address_config(self, ifaceobj):
101 purge_addresses = ifaceobj.get_attr_value_first('address-purge')
102 if not purge_addresses:
103 purge_addresses = 'yes'
104 newaddrs = []
105 addrs = ifaceobj.get_attr_value('address')
106 if addrs:
107 # If user address is not in CIDR notation, convert them to CIDR
108 for addr_index in range(0, len(addrs)):
109 addr = addrs[addr_index]
110 if '/' in addr:
111 newaddrs.append(addr)
112 continue
113 netmask = ifaceobj.get_attr_value_n('netmask', addr_index)
114 if netmask:
115 prefixlen = IPNetwork('%s' %addr +
116 '/%s' %netmask).prefixlen
117 newaddrs.append(addr + '/%s' %prefixlen)
118 else:
119 newaddrs.append(addr)
120
121 if (not self.PERFMODE and
122 not (ifaceobj.flags & iface.HAS_SIBLINGS) and
123 purge_addresses == 'yes'):
124 # if perfmode is not set and also if iface has no sibling
125 # objects, purge addresses that are not present in the new
126 # config
127 runningaddrs = self.ipcmd.addr_get(ifaceobj.name, details=False)
128 if newaddrs == runningaddrs:
129 return
130 try:
131 # if primary address is not same, there is no need to keep any.
132 # reset all addresses
133 if (newaddrs and runningaddrs and
134 (newaddrs[0] != runningaddrs[0])):
135 self.ipcmd.del_addr_all(ifaceobj.name)
136 else:
137 self.ipcmd.del_addr_all(ifaceobj.name, newaddrs)
138 except Exception, e:
139 self.log_warn(str(e))
140 if not newaddrs:
141 return
142 for addr_index in range(0, len(newaddrs)):
143 try:
144 self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index],
145 ifaceobj.get_attr_value_n('broadcast', addr_index),
146 ifaceobj.get_attr_value_n('pointopoint',addr_index),
147 ifaceobj.get_attr_value_n('scope', addr_index),
148 ifaceobj.get_attr_value_n('preferred-lifetime', addr_index))
149 except Exception, e:
150 self.log_error(str(e))
151
152 def _up(self, ifaceobj):
153 if not self.ipcmd.link_exists(ifaceobj.name):
154 return
155 addr_method = ifaceobj.addr_method
156 try:
157 # release any stale dhcp addresses if present
158 if (addr_method != "dhcp" and not self.PERFMODE and
159 not (ifaceobj.flags & iface.HAS_SIBLINGS)):
160 # if not running in perf mode and ifaceobj does not have
161 # any sibling iface objects, kill any stale dhclient
162 # processes
163 dhclientcmd = dhclient()
164 if dhclient.is_running(ifaceobj.name):
165 # release any dhcp leases
166 dhclientcmd.release(ifaceobj.name)
167 elif dhclient.is_running6(ifaceobj.name):
168 dhclientcmd.release6(ifaceobj.name)
169 except:
170 pass
171
172 self.ipcmd.batch_start()
173 if addr_method != "dhcp":
174 self._inet_address_config(ifaceobj)
175 mtu = ifaceobj.get_attr_value_first('mtu')
176 if mtu:
177 self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu)
178 alias = ifaceobj.get_attr_value_first('alias')
179 if alias:
180 self.ipcmd.link_set_alias(ifaceobj.name, alias)
181 hwaddress = ifaceobj.get_attr_value_first('hwaddress')
182 if hwaddress:
183 self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress)
184 self.ipcmd.batch_commit()
185
186 try:
187 # Handle special things on a bridge
188 self._process_bridge(ifaceobj, True)
189 except Exception, e:
190 self.log_warn('%s: %s' %(ifaceobj.name, str(e)))
191 pass
192
193 if addr_method != "dhcp":
194 self.ipcmd.route_add_gateway(ifaceobj.name,
195 ifaceobj.get_attr_value_first('gateway'))
196
197 def _down(self, ifaceobj):
198 try:
199 if not self.ipcmd.link_exists(ifaceobj.name):
200 return
201 addr_method = ifaceobj.addr_method
202 if addr_method != "dhcp":
203 self.ipcmd.route_del_gateway(ifaceobj.name,
204 ifaceobj.get_attr_value_first('gateway'),
205 ifaceobj.get_attr_value_first('metric'))
206 self.ipcmd.del_addr_all(ifaceobj.name)
207 alias = ifaceobj.get_attr_value_first('alias')
208 if alias:
209 self.ipcmd.link_set(ifaceobj.name, 'alias', "\'\'")
210 # XXX hwaddress reset cannot happen because we dont know last
211 # address.
212
213 # Handle special things on a bridge
214 self._process_bridge(ifaceobj, False)
215 except Exception, e:
216 self.logger.debug('%s : %s' %(ifaceobj.name, str(e)))
217 pass
218
219 def _get_iface_addresses(self, ifaceobj):
220 addrlist = ifaceobj.get_attr_value('address')
221 outaddrlist = []
222
223 if not addrlist: return None
224 for addrindex in range(0, len(addrlist)):
225 addr = addrlist[addrindex]
226 netmask = ifaceobj.get_attr_value_n('netmask', addrindex)
227 if netmask:
228 prefixlen = IPNetwork('%s' %addr +
229 '/%s' %netmask).prefixlen
230 addr = addr + '/%s' %prefixlen
231 outaddrlist.append(addr)
232 return outaddrlist
233
234 def _get_bridge_fdbs(self, bridgename, vlan):
235 fdbs = self._bridge_fdb_query_cache.get(bridgename)
236 if not fdbs:
237 fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename)
238 if not fdbs:
239 return
240 self._bridge_fdb_query_cache[bridgename] = fdbs
241 return fdbs.get(vlan)
242
243 def _check_addresses_in_bridge(self, ifaceobj, hwaddress):
244 """ If the device is a bridge, make sure the addresses
245 are in the bridge """
246 if '.' in ifaceobj.name:
247 (bridgename, vlan) = ifaceobj.name.split('.')
248 if self.ipcmd.bridge_is_vlan_aware(bridgename):
249 fdb_addrs = self._get_bridge_fdbs(bridgename, vlan)
250 if not fdb_addrs or hwaddress not in fdb_addrs:
251 return False
252 return True
253
254 def _query_check(self, ifaceobj, ifaceobjcurr):
255 runningaddrsdict = None
256 if not self.ipcmd.link_exists(ifaceobj.name):
257 self.logger.debug('iface %s not found' %ifaceobj.name)
258 return
259 addr_method = ifaceobj.addr_method
260 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
261 'mtu', self.ipcmd.link_get_mtu)
262 hwaddress = ifaceobj.get_attr_value_first('hwaddress')
263 if hwaddress:
264 rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name)
265 if not rhwaddress or rhwaddress != hwaddress:
266 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
267 1)
268 elif not self._check_addresses_in_bridge(ifaceobj, hwaddress):
269 # XXX: hw address is not in bridge
270 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
271 1)
272 ifaceobjcurr.status_str = 'bridge fdb error'
273 else:
274 ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress,
275 0)
276 self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr,
277 'alias', self.ipcmd.link_get_alias)
278 # compare addresses
279 if addr_method == 'dhcp':
280 return
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 return
324 dhclientcmd = dhclient()
325 if (dhclientcmd.is_running(ifaceobjrunning.name) or
326 dhclientcmd.is_running6(ifaceobjrunning.name)):
327 # If dhcp is configured on the interface, we skip it
328 return
329 isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name)
330 if isloopback:
331 default_addrs = ['127.0.0.1/8', '::1/128']
332 ifaceobjrunning.addr_family = 'inet'
333 ifaceobjrunning.addr_method = 'loopback'
334 else:
335 default_addrs = []
336 runningaddrsdict = self.ipcmd.addr_get(ifaceobjrunning.name)
337 if runningaddrsdict:
338 [ifaceobjrunning.update_config('address', addr)
339 for addr, addrattrs in runningaddrsdict.items()
340 if addr not in default_addrs]
341 mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name)
342 if (mtu and
343 (ifaceobjrunning.name == 'lo' and mtu != '16436') or
344 (ifaceobjrunning.name != 'lo' and
345 mtu != self.get_mod_subattr('mtu', 'default'))):
346 ifaceobjrunning.update_config('mtu', mtu)
347 alias = self.ipcmd.link_get_alias(ifaceobjrunning.name)
348 if alias:
349 ifaceobjrunning.update_config('alias', alias)
350
351 _run_ops = {'up' : _up,
352 'down' : _down,
353 'query-checkcurr' : _query_check,
354 'query-running' : _query_running }
355
356 def get_ops(self):
357 """ returns list of ops supported by this module """
358 return self._run_ops.keys()
359
360 def _init_command_handlers(self):
361 if not self.ipcmd:
362 self.ipcmd = iproute2(**self.get_flags())
363
364 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
365 """ run address configuration on the interface object passed as argument
366
367 Args:
368 **ifaceobj** (object): iface object
369
370 **operation** (str): any of 'up', 'down', 'query-checkcurr',
371 'query-running'
372 Kwargs:
373 query_ifaceobj (object): query check ifaceobject. This is only
374 valid when op is 'query-checkcurr'. It is an object same as
375 ifaceobj, but contains running attribute values and its config
376 status. The modules can use it to return queried running state
377 of interfaces. status is success if the running state is same
378 as user required state in ifaceobj. error otherwise.
379 """
380 if ifaceobj.type == ifaceType.BRIDGE_VLAN:
381 return
382 op_handler = self._run_ops.get(operation)
383 if not op_handler:
384 return
385 self._init_command_handlers()
386 if operation == 'query-checkcurr':
387 op_handler(self, ifaceobj, query_ifaceobj)
388 else:
389 op_handler(self, ifaceobj)