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