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