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