]>
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 | ||
9087e727 JF |
7 | import os |
8 | ||
15ef32ea | 9 | try: |
c6370b56 | 10 | from ipaddr import IPNetwork, IPv4Network, IPv6Network, IPv4Address, IPv6Address |
15ef32ea RP |
11 | from sets import Set |
12 | from ifupdown.iface import * | |
82908a2d | 13 | from ifupdown.utils import utils |
15ef32ea RP |
14 | from ifupdownaddons.modulebase import moduleBase |
15 | from ifupdownaddons.iproute2 import iproute2 | |
16 | from ifupdownaddons.dhclient import dhclient | |
84f33af6 | 17 | import ifupdown.policymanager as policymanager |
2864d6f3 | 18 | from ifupdown.netlink import netlink |
0582f185 | 19 | import ifupdown.ifupdownconfig as ifupdownConfig |
fc5e1735 | 20 | import ifupdown.ifupdownflags as ifupdownflags |
0232d1bb | 21 | import ifupdown.statemanager as statemanager |
15ef32ea RP |
22 | except ImportError, e: |
23 | raise ImportError (str(e) + "- required module not found") | |
24 | ||
25 | class address(moduleBase): | |
26 | """ ifupdown2 addon module to configure address, mtu, hwaddress, alias | |
27 | (description) on an interface """ | |
28 | ||
29 | _modinfo = {'mhelp' : 'address configuration module for interfaces', | |
30 | 'attrs': { | |
31 | 'address' : | |
32 | {'help' : 'ipv4 or ipv6 addresses', | |
482b2fab | 33 | 'validvals' : ['<ipv4/prefixlen>', '<ipv6/prefixlen>'], |
c6370b56 | 34 | 'multiline' : True, |
15ef32ea RP |
35 | 'example' : ['address 10.0.12.3/24', |
36 | 'address 2000:1000:1000:1000:3::5/128']}, | |
37 | 'netmask' : | |
38 | {'help': 'netmask', | |
39 | 'example' : ['netmask 255.255.255.0'], | |
40 | 'compat' : True}, | |
41 | 'broadcast' : | |
42 | {'help': 'broadcast address', | |
482b2fab | 43 | 'validvals' : ['<ipv4>', ], |
15ef32ea RP |
44 | 'example' : ['broadcast 10.0.1.255']}, |
45 | 'scope' : | |
46 | {'help': 'scope', | |
c6370b56 | 47 | 'validvals' : ['universe', 'site', 'link', 'host', 'nowhere'], |
15ef32ea RP |
48 | 'example' : ['scope host']}, |
49 | 'preferred-lifetime' : | |
50 | {'help': 'preferred lifetime', | |
c6370b56 | 51 | 'validrange' : ['0', '65535'], |
15ef32ea RP |
52 | 'example' : ['preferred-lifetime forever', |
53 | 'preferred-lifetime 10']}, | |
54 | 'gateway' : | |
55 | {'help': 'default gateway', | |
482b2fab | 56 | 'validvals' : ['<ipv4>', '<ipv6>'], |
2ed2adeb | 57 | 'multiline' : True, |
15ef32ea RP |
58 | 'example' : ['gateway 255.255.255.0']}, |
59 | 'mtu' : | |
60 | { 'help': 'interface mtu', | |
c6370b56 | 61 | 'validrange' : ['552', '9216'], |
15ef32ea RP |
62 | 'example' : ['mtu 1600'], |
63 | 'default' : '1500'}, | |
64 | 'hwaddress' : | |
65 | {'help' : 'hw address', | |
c6370b56 | 66 | 'validvals' : ['<mac>',], |
15ef32ea RP |
67 | 'example': ['hwaddress 44:38:39:00:27:b8']}, |
68 | 'alias' : | |
69 | { 'help': 'description/alias', | |
394e68b5 RP |
70 | 'example' : ['alias testnetwork']}, |
71 | 'address-purge' : | |
72 | { 'help': 'purge existing addresses. By default ' + | |
73 | 'any existing ip addresses on an interface are ' + | |
74 | 'purged to match persistant addresses in the ' + | |
75 | 'interfaces file. Set this attribute to \'no\'' + | |
76 | 'if you want to preserve existing addresses', | |
c6370b56 | 77 | 'validvals' : ['yes', 'no'], |
394e68b5 | 78 | 'default' : 'yes', |
a794fb31 BR |
79 | 'example' : ['address-purge yes/no']}, |
80 | 'clagd-vxlan-anycast-ip' : | |
81 | { 'help' : 'Anycast local IP address for ' + | |
82 | 'dual connected VxLANs', | |
482b2fab | 83 | 'validvals' : ['<ipv4>', ], |
a794fb31 | 84 | 'example' : ['clagd-vxlan-anycast-ip 36.0.0.11']}}} |
15ef32ea RP |
85 | |
86 | def __init__(self, *args, **kargs): | |
87 | moduleBase.__init__(self, *args, **kargs) | |
88 | self.ipcmd = None | |
8e113d63 | 89 | self._bridge_fdb_query_cache = {} |
84f33af6 | 90 | self.default_mtu = policymanager.policymanager_api.get_attr_default(module_name=self.__class__.__name__, attr='mtu') |
9f30b2cc RP |
91 | self.max_mtu = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='max_mtu') |
92 | ||
93 | if not self.default_mtu: | |
94 | self.default_mtu = '1500' | |
95 | ||
96 | self.logger.info('address: using default mtu %s' %self.default_mtu) | |
97 | ||
98 | if self.max_mtu: | |
99 | self.logger.info('address: using max mtu %s' %self.max_mtu) | |
15ef32ea | 100 | |
09096420 | 101 | def syntax_check(self, ifaceobj, ifaceobj_getfunc=None): |
22b49c28 | 102 | return (self.syntax_check_multiple_gateway(ifaceobj) |
09096420 RP |
103 | and self.syntax_check_addr_allowed_on(ifaceobj, True) |
104 | and self.syntax_check_mtu(ifaceobj, ifaceobj_getfunc)) | |
105 | ||
106 | def syntax_check_mtu(self, ifaceobj, ifaceobj_getfunc): | |
107 | mtu = ifaceobj.get_attr_value_first('mtu') | |
108 | if mtu: | |
109 | return self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc, | |
110 | syntaxcheck=True) | |
111 | return True | |
22b49c28 JF |
112 | |
113 | def syntax_check_addr_allowed_on(self, ifaceobj, syntax_check=False): | |
114 | if ifaceobj.get_attr_value('address'): | |
115 | return utils.is_addr_ip_allowed_on(ifaceobj, syntax_check=syntax_check) | |
116 | return True | |
117 | ||
38365d4a JF |
118 | def _syntax_check_multiple_gateway(self, family, found, addr, type_obj): |
119 | if type(IPNetwork(addr)) == type_obj: | |
120 | if found: | |
121 | raise Exception('%s: multiple gateways for %s family' | |
122 | % (addr, family)) | |
123 | return True | |
124 | return False | |
125 | ||
22b49c28 | 126 | def syntax_check_multiple_gateway(self, ifaceobj): |
38365d4a JF |
127 | result = True |
128 | inet = False | |
129 | inet6 = False | |
130 | gateways = ifaceobj.get_attr_value('gateway') | |
131 | for addr in gateways if gateways else []: | |
132 | try: | |
133 | if self._syntax_check_multiple_gateway('inet', inet, addr, | |
134 | IPv4Network): | |
135 | inet = True | |
136 | if self._syntax_check_multiple_gateway('inet6', inet6, addr, | |
137 | IPv6Network): | |
138 | inet6 = True | |
139 | except Exception as e: | |
140 | self.logger.warning('%s: address: %s' % (ifaceobj.name, str(e))) | |
141 | result = False | |
142 | return result | |
143 | ||
75afe2a7 RP |
144 | def _address_valid(self, addrs): |
145 | if not addrs: | |
146 | return False | |
147 | if any(map(lambda a: True if a[:7] != '0.0.0.0' | |
148 | else False, addrs)): | |
149 | return True | |
150 | return False | |
151 | ||
428206bf | 152 | def _get_hwaddress(self, ifaceobj): |
2876ca35 | 153 | hwaddress = ifaceobj.get_attr_value_first('hwaddress') |
428206bf JF |
154 | if hwaddress and hwaddress.startswith("ether"): |
155 | hwaddress = hwaddress[5:].strip() | |
156 | return hwaddress | |
157 | ||
158 | def _process_bridge(self, ifaceobj, up): | |
159 | hwaddress = self._get_hwaddress(ifaceobj) | |
75afe2a7 RP |
160 | addrs = ifaceobj.get_attr_value_first('address') |
161 | is_vlan_dev_on_vlan_aware_bridge = False | |
162 | is_bridge = self.ipcmd.is_bridge(ifaceobj.name) | |
163 | if not is_bridge: | |
164 | if '.' in ifaceobj.name: | |
165 | (bridgename, vlan) = ifaceobj.name.split('.') | |
166 | is_vlan_dev_on_vlan_aware_bridge = self.ipcmd.bridge_is_vlan_aware(bridgename) | |
8c2c9f26 RP |
167 | if ((is_bridge and not self.ipcmd.bridge_is_vlan_aware(ifaceobj.name)) |
168 | or is_vlan_dev_on_vlan_aware_bridge): | |
75afe2a7 RP |
169 | if self._address_valid(addrs): |
170 | if up: | |
171 | self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name + | |
172 | '/arp_accept', '1') | |
173 | else: | |
174 | self.write_file('/proc/sys/net/ipv4/conf/%s' %ifaceobj.name + | |
175 | '/arp_accept', '0') | |
176 | if hwaddress and is_vlan_dev_on_vlan_aware_bridge: | |
177 | if up: | |
178 | self.ipcmd.bridge_fdb_add(bridgename, hwaddress, vlan) | |
179 | else: | |
180 | self.ipcmd.bridge_fdb_del(bridgename, hwaddress, vlan) | |
cb46a208 | 181 | |
0582f185 RP |
182 | def _get_anycast_addr(self, ifaceobjlist): |
183 | for ifaceobj in ifaceobjlist: | |
184 | anycast_addr = ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip') | |
185 | if anycast_addr: | |
186 | anycast_addr = anycast_addr+'/32' | |
187 | return anycast_addr | |
188 | return None | |
189 | ||
190 | def _inet_address_convert_to_cidr(self, ifaceobjlist): | |
15ef32ea | 191 | newaddrs = [] |
0582f185 RP |
192 | newaddr_attrs = {} |
193 | ||
194 | for ifaceobj in ifaceobjlist: | |
195 | addrs = ifaceobj.get_attr_value('address') | |
196 | if not addrs: | |
197 | continue | |
198 | ||
22b49c28 JF |
199 | if not self.syntax_check_addr_allowed_on(ifaceobj, |
200 | syntax_check=False): | |
0582f185 | 201 | return (False, newaddrs, newaddr_attrs) |
15ef32ea RP |
202 | # If user address is not in CIDR notation, convert them to CIDR |
203 | for addr_index in range(0, len(addrs)): | |
204 | addr = addrs[addr_index] | |
205 | if '/' in addr: | |
206 | newaddrs.append(addr) | |
207 | continue | |
494d31d2 | 208 | newaddr = addr |
15ef32ea RP |
209 | netmask = ifaceobj.get_attr_value_n('netmask', addr_index) |
210 | if netmask: | |
211 | prefixlen = IPNetwork('%s' %addr + | |
212 | '/%s' %netmask).prefixlen | |
0582f185 | 213 | newaddr = addr + '/%s' %prefixlen |
42ae7838 ST |
214 | else: |
215 | # we are here because there is no slash (/xx) and no netmask | |
216 | # just let IPNetwork handle the ipv4 or ipv6 address mask | |
217 | prefixlen = IPNetwork(addr).prefixlen | |
218 | newaddr = addr + '/%s' %prefixlen | |
0582f185 | 219 | newaddrs.append(newaddr) |
15ef32ea | 220 | |
0582f185 RP |
221 | attrs = {} |
222 | for a in ['broadcast', 'pointopoint', 'scope', | |
223 | 'preferred-lifetime']: | |
224 | aval = ifaceobj.get_attr_value_n(a, addr_index) | |
225 | if aval: | |
72c964c2 | 226 | attrs[a] = aval |
0582f185 RP |
227 | |
228 | if attrs: | |
229 | newaddr_attrs[newaddr]= attrs | |
230 | return (True, newaddrs, newaddr_attrs) | |
231 | ||
77021aa1 RP |
232 | def _inet_address_list_config(self, ifaceobj, newaddrs, newaddr_attrs): |
233 | for addr_index in range(0, len(newaddrs)): | |
234 | try: | |
235 | if newaddr_attrs: | |
236 | self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index], | |
237 | newaddr_attrs.get(newaddrs[addr_index], | |
238 | {}).get('broadcast'), | |
239 | newaddr_attrs.get(newaddrs[addr_index], | |
240 | {}).get('pointopoint'), | |
241 | newaddr_attrs.get(newaddrs[addr_index], | |
242 | {}).get('scope'), | |
243 | newaddr_attrs.get(newaddrs[addr_index], | |
244 | {}).get('preferred-lifetime')) | |
245 | else: | |
246 | self.ipcmd.addr_add(ifaceobj.name, newaddrs[addr_index]) | |
247 | except Exception, e: | |
bf3eda91 | 248 | self.log_error(str(e), ifaceobj) |
77021aa1 RP |
249 | |
250 | def _inet_address_config(self, ifaceobj, ifaceobj_getfunc=None, | |
251 | force_reapply=False): | |
0582f185 RP |
252 | squash_addr_config = (True if \ |
253 | ifupdownConfig.config.get('addr_config_squash', \ | |
254 | '0') == '1' else False) | |
255 | ||
256 | if (squash_addr_config and | |
257 | not (ifaceobj.flags & ifaceobj.YOUNGEST_SIBLING)): | |
258 | return | |
259 | ||
260 | purge_addresses = ifaceobj.get_attr_value_first('address-purge') | |
261 | if not purge_addresses: | |
262 | purge_addresses = 'yes' | |
263 | ||
264 | if squash_addr_config and ifaceobj.flags & iface.HAS_SIBLINGS: | |
265 | ifaceobjlist = ifaceobj_getfunc(ifaceobj.name) | |
266 | else: | |
267 | ifaceobjlist = [ifaceobj] | |
268 | ||
269 | (addr_supported, newaddrs, newaddr_attrs) = self._inet_address_convert_to_cidr(ifaceobjlist) | |
82908a2d | 270 | newaddrs = utils.get_normalized_ip_addr(ifaceobj.name, newaddrs) |
0582f185 RP |
271 | if not addr_supported: |
272 | return | |
273 | if (not squash_addr_config and (ifaceobj.flags & iface.HAS_SIBLINGS)): | |
274 | # if youngest sibling and squash addr is not set | |
275 | # print a warning that addresses will not be purged | |
276 | if (ifaceobj.flags & iface.YOUNGEST_SIBLING): | |
277 | self.logger.warn('%s: interface has multiple ' %ifaceobj.name + | |
278 | 'iface stanzas, skip purging existing addresses') | |
279 | purge_addresses = 'no' | |
280 | ||
fc5e1735 | 281 | if not ifupdownflags.flags.PERFMODE and purge_addresses == 'yes': |
0582f185 RP |
282 | # if perfmode is not set and purge addresses is not set to 'no' |
283 | # lets purge addresses not in the config | |
82908a2d | 284 | runningaddrs = utils.get_normalized_ip_addr(ifaceobj.name, self.ipcmd.addr_get(ifaceobj.name, details=False)) |
0582f185 | 285 | |
a794fb31 BR |
286 | # if anycast address is configured on 'lo' and is in running config |
287 | # add it to newaddrs so that ifreload doesn't wipe it out | |
82908a2d | 288 | anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, self._get_anycast_addr(ifaceobjlist)) |
0582f185 | 289 | |
a794fb31 BR |
290 | if runningaddrs and anycast_addr and anycast_addr in runningaddrs: |
291 | newaddrs.append(anycast_addr) | |
15ef32ea | 292 | if newaddrs == runningaddrs: |
77021aa1 RP |
293 | if force_reapply: |
294 | self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs) | |
15ef32ea RP |
295 | return |
296 | try: | |
297 | # if primary address is not same, there is no need to keep any. | |
298 | # reset all addresses | |
299 | if (newaddrs and runningaddrs and | |
300 | (newaddrs[0] != runningaddrs[0])): | |
301 | self.ipcmd.del_addr_all(ifaceobj.name) | |
302 | else: | |
303 | self.ipcmd.del_addr_all(ifaceobj.name, newaddrs) | |
304 | except Exception, e: | |
305 | self.log_warn(str(e)) | |
306 | if not newaddrs: | |
307 | return | |
77021aa1 | 308 | self._inet_address_list_config(ifaceobj, newaddrs, newaddr_attrs) |
15ef32ea | 309 | |
0232d1bb N |
310 | def _add_delete_gateway(self, ifaceobj, gateways=[], prev_gw=[]): |
311 | vrf = ifaceobj.get_attr_value_first('vrf') | |
312 | metric = ifaceobj.get_attr_value_first('metric') | |
313 | for del_gw in list(set(prev_gw) - set(gateways)): | |
314 | try: | |
315 | self.ipcmd.route_del_gateway(ifaceobj.name, del_gw, vrf, metric) | |
316 | except: | |
317 | pass | |
318 | for add_gw in list(set(gateways) - set(prev_gw)): | |
319 | try: | |
320 | self.ipcmd.route_add_gateway(ifaceobj.name, add_gw, vrf) | |
321 | except: | |
322 | pass | |
323 | ||
324 | def _get_prev_gateway(self, ifaceobj, gateways): | |
325 | ipv = [] | |
326 | saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(ifaceobj.name) | |
327 | if not saved_ifaceobjs: | |
328 | return ipv | |
329 | prev_gateways = saved_ifaceobjs[0].get_attr_value('gateway') | |
330 | if not prev_gateways: | |
331 | return ipv | |
332 | return prev_gateways | |
333 | ||
8d1e346f RP |
334 | def _check_mtu_config(self, ifaceobj, mtu, ifaceobj_getfunc, syntaxcheck=False): |
335 | retval = True | |
336 | if (ifaceobj.link_kind & ifaceLinkKind.BRIDGE): | |
337 | if syntaxcheck: | |
338 | self.logger.warn('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name) | |
339 | retval = False | |
340 | else: | |
9f30b2cc | 341 | self.logger.info('%s: bridge inherits mtu from its ports. There is no need to assign mtu on a bridge' %ifaceobj.name) |
8d1e346f RP |
342 | elif ifaceobj_getfunc: |
343 | if ((ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and | |
344 | ifaceobj.upperifaces): | |
9f30b2cc RP |
345 | masterobj = ifaceobj_getfunc(ifaceobj.upperifaces[0]) |
346 | if masterobj: | |
347 | master_mtu = masterobj[0].get_attr_value_first('mtu') | |
348 | if master_mtu and master_mtu != mtu: | |
8d1e346f RP |
349 | if syntaxcheck: |
350 | self.logger.warn('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu)) | |
351 | retval = False | |
352 | else: | |
353 | self.logger.info('%s: bond slave mtu %s is different from bond master %s mtu %s. There is no need to configure mtu on a bond slave.' %(ifaceobj.name, mtu, masterobj[0].name, master_mtu)) | |
354 | elif ((ifaceobj.link_kind & ifaceLinkKind.VLAN) and | |
355 | ifaceobj.lowerifaces): | |
356 | lowerobj = ifaceobj_getfunc(ifaceobj.lowerifaces[0]) | |
357 | if lowerobj: | |
358 | if syntaxcheck: | |
359 | lowerdev_mtu = lowerobj[0].get_attr_value_first('mtu') | |
360 | else: | |
361 | lowerdev_mtu = self.ipcmd.link_get_mtu(lowerobj[0].name) | |
362 | if lowerdev_mtu and int(mtu) > int(lowerdev_mtu): | |
363 | self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' | |
364 | %(ifaceobj.name, mtu, lowerobj[0].name, lowerdev_mtu)) | |
365 | retval = False | |
366 | elif (not lowerobj[0].link_kind and | |
367 | not (lowerobj[0].link_privflags & ifaceLinkPrivFlags.LOOPBACK) and | |
368 | self.default_mtu and (int(mtu) > int(self.default_mtu))): | |
369 | # only check default mtu on lower device which is a physical interface | |
370 | self.logger.warn('%s: vlan dev mtu %s is greater than lower realdev %s mtu %s' | |
371 | %(ifaceobj.name, mtu, lowerobj[0].name, self.default_mtu)) | |
372 | retval = False | |
9f30b2cc RP |
373 | if self.max_mtu and mtu > self.max_mtu: |
374 | self.logger.warn('%s: specified mtu %s is greater than max mtu %s' | |
375 | %(ifaceobj.name, mtu, self.max_mtu)) | |
8d1e346f RP |
376 | retval = False |
377 | return retval | |
378 | ||
379 | def _propagate_mtu_to_upper_devs(self, ifaceobj, mtu, ifaceobj_getfunc): | |
380 | if (not ifaceobj.upperifaces or | |
381 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) or | |
382 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE) or | |
383 | (ifaceobj.link_privflags & ifaceLinkPrivFlags.BRIDGE_PORT)): | |
384 | return | |
385 | for u in ifaceobj.upperifaces: | |
386 | upperobjs = ifaceobj_getfunc(u) | |
387 | if (not upperobjs or | |
388 | not (upperobjs[0].link_kind & ifaceLinkKind.VLAN)): | |
389 | continue | |
390 | # only adjust mtu for vlan devices on ifaceobj | |
391 | umtu = upperobjs[0].get_attr_value_first('mtu') | |
392 | if not umtu: | |
393 | running_mtu = self.ipcmd.link_get_mtu(upperobjs[0].name) | |
394 | if not running_mtu or (running_mtu != mtu): | |
395 | self.ipcmd.link_set(u, 'mtu', mtu) | |
396 | ||
397 | def _process_mtu_config(self, ifaceobj, ifaceobj_getfunc): | |
398 | mtu = ifaceobj.get_attr_value_first('mtu') | |
399 | if mtu: | |
400 | if not self._check_mtu_config(ifaceobj, mtu, ifaceobj_getfunc): | |
401 | return | |
402 | running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) | |
403 | if not running_mtu or (running_mtu and running_mtu != mtu): | |
404 | self.ipcmd.link_set(ifaceobj.name, 'mtu', mtu) | |
405 | if (not ifupdownflags.flags.ALL and | |
406 | not ifaceobj.link_kind and | |
407 | ifupdownConfig.config.get('adjust_logical_dev_mtu', '1') != '0'): | |
408 | # This is additional cost to us, so do it only when | |
409 | # ifupdown2 is called on a particular interface and | |
410 | # it is a physical interface | |
411 | self._propagate_mtu_to_upper_devs(ifaceobj, mtu, ifaceobj_getfunc) | |
9f30b2cc RP |
412 | return |
413 | ||
414 | if ifaceobj.link_kind: | |
415 | # bonds and vxlan devices need an explicit set of mtu. | |
416 | # bridges don't need mtu set | |
417 | if (ifaceobj.link_kind & ifaceLinkKind.BOND or | |
418 | ifaceobj.link_kind & ifaceLinkKind.VXLAN): | |
419 | running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) | |
420 | if (self.default_mtu and running_mtu != self.default_mtu): | |
421 | self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) | |
422 | return | |
423 | if (ifupdownConfig.config.get('adjust_logical_dev_mtu', '1') != '0' | |
424 | and ifaceobj.lowerifaces): | |
425 | # set vlan interface mtu to lower device mtu | |
426 | if (ifaceobj.link_kind & ifaceLinkKind.VLAN): | |
427 | lower_iface_mtu = self.ipcmd.link_get_mtu(ifaceobj.lowerifaces[0], refresh=True) | |
428 | if not lower_iface_mtu == self.ipcmd.link_get_mtu(ifaceobj.name): | |
429 | self.ipcmd.link_set_mtu(ifaceobj.name, lower_iface_mtu) | |
430 | ||
431 | elif (not (ifaceobj.name == 'lo') and not ifaceobj.link_kind and | |
432 | not (ifaceobj.link_privflags & ifaceLinkPrivFlags.BOND_SLAVE) and | |
433 | self.default_mtu): | |
434 | # logical devices like bridges and vlan devices rely on mtu | |
435 | # from their lower devices. ie mtu travels from | |
436 | # lower devices to upper devices. For bonds mtu travels from | |
437 | # upper to lower devices. running mtu depends on upper and | |
438 | # lower device mtu. With all this implicit mtu | |
439 | # config by the kernel in play, we try to be cautious here | |
440 | # on which devices we want to reset mtu to default. | |
441 | # essentially only physical interfaces which are not bond slaves | |
442 | running_mtu = self.ipcmd.link_get_mtu(ifaceobj.name) | |
443 | if running_mtu != self.default_mtu: | |
444 | self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) | |
445 | ||
0582f185 | 446 | def _up(self, ifaceobj, ifaceobj_getfunc=None): |
15ef32ea RP |
447 | if not self.ipcmd.link_exists(ifaceobj.name): |
448 | return | |
68d9fee0 | 449 | addr_method = ifaceobj.addr_method |
77021aa1 | 450 | force_reapply = False |
15ef32ea RP |
451 | try: |
452 | # release any stale dhcp addresses if present | |
fc5e1735 | 453 | if (addr_method != "dhcp" and not ifupdownflags.flags.PERFMODE and |
15ef32ea RP |
454 | not (ifaceobj.flags & iface.HAS_SIBLINGS)): |
455 | # if not running in perf mode and ifaceobj does not have | |
456 | # any sibling iface objects, kill any stale dhclient | |
457 | # processes | |
75afe2a7 | 458 | dhclientcmd = dhclient() |
7c1135ea | 459 | if dhclientcmd.is_running(ifaceobj.name): |
15ef32ea RP |
460 | # release any dhcp leases |
461 | dhclientcmd.release(ifaceobj.name) | |
77021aa1 | 462 | force_reapply = True |
7c1135ea | 463 | elif dhclientcmd.is_running6(ifaceobj.name): |
15ef32ea | 464 | dhclientcmd.release6(ifaceobj.name) |
77021aa1 | 465 | force_reapply = True |
15ef32ea RP |
466 | except: |
467 | pass | |
8e113d63 | 468 | |
15ef32ea | 469 | self.ipcmd.batch_start() |
68d9fee0 | 470 | if addr_method != "dhcp": |
77021aa1 RP |
471 | self._inet_address_config(ifaceobj, ifaceobj_getfunc, |
472 | force_reapply) | |
9f30b2cc | 473 | self._process_mtu_config(ifaceobj, ifaceobj_getfunc) |
13e22530 | 474 | |
15ef32ea RP |
475 | alias = ifaceobj.get_attr_value_first('alias') |
476 | if alias: | |
8e113d63 | 477 | self.ipcmd.link_set_alias(ifaceobj.name, alias) |
093ffa00 JF |
478 | try: |
479 | self.ipcmd.batch_commit() | |
480 | except Exception as e: | |
03092649 | 481 | self.log_error('%s: %s' % (ifaceobj.name, str(e)), ifaceobj, raise_error=False) |
264dcaa0 | 482 | |
68d9fee0 | 483 | try: |
707aeb73 JF |
484 | hwaddress = self._get_hwaddress(ifaceobj) |
485 | if hwaddress: | |
486 | running_hwaddress = None | |
487 | if not ifupdownflags.flags.PERFMODE: # system is clean | |
488 | running_hwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) | |
489 | if hwaddress != running_hwaddress: | |
490 | slave_down = False | |
491 | netlink.link_set_updown(ifaceobj.name, "down") | |
492 | if ifaceobj.link_kind & ifaceLinkKind.BOND: | |
493 | # if bond, down all the slaves | |
494 | if ifaceobj.lowerifaces: | |
495 | for l in ifaceobj.lowerifaces: | |
496 | netlink.link_set_updown(l, "down") | |
497 | slave_down = True | |
498 | try: | |
499 | self.ipcmd.link_set(ifaceobj.name, 'address', hwaddress) | |
500 | finally: | |
501 | netlink.link_set_updown(ifaceobj.name, "up") | |
502 | if slave_down: | |
503 | for l in ifaceobj.lowerifaces: | |
504 | netlink.link_set_updown(l, "up") | |
505 | ||
68d9fee0 RP |
506 | # Handle special things on a bridge |
507 | self._process_bridge(ifaceobj, True) | |
508 | except Exception, e: | |
bf3eda91 | 509 | self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj) |
cb46a208 | 510 | |
68d9fee0 | 511 | if addr_method != "dhcp": |
0232d1bb N |
512 | gateways = ifaceobj.get_attr_value('gateway') |
513 | if not gateways: | |
514 | gateways = [] | |
515 | prev_gw = self._get_prev_gateway(ifaceobj, gateways) | |
516 | self._add_delete_gateway(ifaceobj, gateways, prev_gw) | |
517 | return | |
15ef32ea | 518 | |
0582f185 | 519 | def _down(self, ifaceobj, ifaceobj_getfunc=None): |
15ef32ea RP |
520 | try: |
521 | if not self.ipcmd.link_exists(ifaceobj.name): | |
522 | return | |
68d9fee0 RP |
523 | addr_method = ifaceobj.addr_method |
524 | if addr_method != "dhcp": | |
aa052170 N |
525 | if ifaceobj.get_attr_value_first('address-purge')=='no': |
526 | addrlist = ifaceobj.get_attr_value('address') | |
527 | for addr in addrlist: | |
528 | self.ipcmd.addr_del(ifaceobj.name, addr) | |
529 | #self.ipcmd.addr_del(ifaceobj.name, ifaceobj.get_attr_value('address')[0]) | |
530 | else: | |
531 | self.ipcmd.del_addr_all(ifaceobj.name) | |
84f33af6 | 532 | mtu = ifaceobj.get_attr_value_first('mtu') |
00c12960 RP |
533 | if (not ifaceobj.link_kind and mtu and |
534 | self.default_mtu and (mtu != self.default_mtu)): | |
84f33af6 | 535 | self.ipcmd.link_set(ifaceobj.name, 'mtu', self.default_mtu) |
15ef32ea RP |
536 | alias = ifaceobj.get_attr_value_first('alias') |
537 | if alias: | |
9087e727 | 538 | filename = '/sys/class/net/%s/ifalias' %ifaceobj.name |
a4a53f4b | 539 | self.logger.info('executing echo "" > %s' %filename) |
9087e727 | 540 | os.system('echo "" > %s' %filename) |
75afe2a7 RP |
541 | # XXX hwaddress reset cannot happen because we dont know last |
542 | # address. | |
543 | ||
544 | # Handle special things on a bridge | |
545 | self._process_bridge(ifaceobj, False) | |
15ef32ea | 546 | except Exception, e: |
bcf11b14 RP |
547 | self.logger.debug('%s : %s' %(ifaceobj.name, str(e))) |
548 | pass | |
15ef32ea RP |
549 | |
550 | def _get_iface_addresses(self, ifaceobj): | |
551 | addrlist = ifaceobj.get_attr_value('address') | |
552 | outaddrlist = [] | |
553 | ||
554 | if not addrlist: return None | |
555 | for addrindex in range(0, len(addrlist)): | |
556 | addr = addrlist[addrindex] | |
557 | netmask = ifaceobj.get_attr_value_n('netmask', addrindex) | |
558 | if netmask: | |
559 | prefixlen = IPNetwork('%s' %addr + | |
560 | '/%s' %netmask).prefixlen | |
561 | addr = addr + '/%s' %prefixlen | |
562 | outaddrlist.append(addr) | |
563 | return outaddrlist | |
564 | ||
8e113d63 RP |
565 | def _get_bridge_fdbs(self, bridgename, vlan): |
566 | fdbs = self._bridge_fdb_query_cache.get(bridgename) | |
567 | if not fdbs: | |
568 | fdbs = self.ipcmd.bridge_fdb_show_dev(bridgename) | |
569 | if not fdbs: | |
570 | return | |
571 | self._bridge_fdb_query_cache[bridgename] = fdbs | |
572 | return fdbs.get(vlan) | |
573 | ||
574 | def _check_addresses_in_bridge(self, ifaceobj, hwaddress): | |
575 | """ If the device is a bridge, make sure the addresses | |
576 | are in the bridge """ | |
577 | if '.' in ifaceobj.name: | |
578 | (bridgename, vlan) = ifaceobj.name.split('.') | |
579 | if self.ipcmd.bridge_is_vlan_aware(bridgename): | |
580 | fdb_addrs = self._get_bridge_fdbs(bridgename, vlan) | |
581 | if not fdb_addrs or hwaddress not in fdb_addrs: | |
582 | return False | |
583 | return True | |
584 | ||
0582f185 | 585 | def _query_check(self, ifaceobj, ifaceobjcurr, ifaceobj_getfunc=None): |
15ef32ea RP |
586 | runningaddrsdict = None |
587 | if not self.ipcmd.link_exists(ifaceobj.name): | |
588 | self.logger.debug('iface %s not found' %ifaceobj.name) | |
589 | return | |
16d854b4 | 590 | addr_method = ifaceobj.addr_method |
15ef32ea RP |
591 | self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, |
592 | 'mtu', self.ipcmd.link_get_mtu) | |
428206bf | 593 | hwaddress = self._get_hwaddress(ifaceobj) |
8e113d63 | 594 | if hwaddress: |
2876ca35 | 595 | rhwaddress = self.ipcmd.link_get_hwaddress(ifaceobj.name) |
8e113d63 RP |
596 | if not rhwaddress or rhwaddress != hwaddress: |
597 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, | |
598 | 1) | |
599 | elif not self._check_addresses_in_bridge(ifaceobj, hwaddress): | |
600 | # XXX: hw address is not in bridge | |
601 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, | |
602 | 1) | |
603 | ifaceobjcurr.status_str = 'bridge fdb error' | |
604 | else: | |
605 | ifaceobjcurr.update_config_with_status('hwaddress', rhwaddress, | |
606 | 0) | |
15ef32ea RP |
607 | self.query_n_update_ifaceobjcurr_attr(ifaceobj, ifaceobjcurr, |
608 | 'alias', self.ipcmd.link_get_alias) | |
609 | # compare addresses | |
16d854b4 RP |
610 | if addr_method == 'dhcp': |
611 | return | |
82908a2d JF |
612 | addrs = utils.get_normalized_ip_addr(ifaceobj.name, |
613 | self._get_iface_addresses(ifaceobj)) | |
15ef32ea | 614 | runningaddrsdict = self.ipcmd.addr_get(ifaceobj.name) |
a794fb31 BR |
615 | # if anycast address is configured on 'lo' and is in running config |
616 | # add it to addrs so that query_check doesn't fail | |
82908a2d | 617 | anycast_addr = utils.get_normalized_ip_addr(ifaceobj.name, ifaceobj.get_attr_value_first('clagd-vxlan-anycast-ip')) |
a794fb31 BR |
618 | if anycast_addr: |
619 | anycast_addr = anycast_addr+'/32' | |
620 | if runningaddrsdict and anycast_addr and runningaddrsdict.get(anycast_addr): | |
621 | addrs.append(anycast_addr) | |
15ef32ea RP |
622 | |
623 | # Set ifaceobjcurr method and family | |
624 | ifaceobjcurr.addr_method = ifaceobj.addr_method | |
625 | ifaceobjcurr.addr_family = ifaceobj.addr_family | |
626 | if not runningaddrsdict and not addrs: | |
627 | return | |
628 | runningaddrs = runningaddrsdict.keys() if runningaddrsdict else [] | |
629 | if runningaddrs != addrs: | |
630 | runningaddrsset = set(runningaddrs) if runningaddrs else set([]) | |
631 | addrsset = set(addrs) if addrs else set([]) | |
632 | if (ifaceobj.flags & iface.HAS_SIBLINGS): | |
633 | if not addrsset: | |
634 | return | |
635 | # only check for addresses present in running config | |
636 | addrsdiff = addrsset.difference(runningaddrsset) | |
637 | for addr in addrs: | |
638 | if addr in addrsdiff: | |
639 | ifaceobjcurr.update_config_with_status('address', | |
640 | addr, 1) | |
641 | else: | |
642 | ifaceobjcurr.update_config_with_status('address', | |
643 | addr, 0) | |
644 | else: | |
645 | addrsdiff = addrsset.symmetric_difference(runningaddrsset) | |
646 | for addr in addrsset.union(runningaddrsset): | |
647 | if addr in addrsdiff: | |
648 | ifaceobjcurr.update_config_with_status('address', | |
649 | addr, 1) | |
650 | else: | |
651 | ifaceobjcurr.update_config_with_status('address', | |
652 | addr, 0) | |
653 | elif addrs: | |
654 | [ifaceobjcurr.update_config_with_status('address', | |
655 | addr, 0) for addr in addrs] | |
656 | #XXXX Check broadcast address, scope, etc | |
657 | return | |
658 | ||
0582f185 | 659 | def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None): |
15ef32ea RP |
660 | if not self.ipcmd.link_exists(ifaceobjrunning.name): |
661 | self.logger.debug('iface %s not found' %ifaceobjrunning.name) | |
15ef32ea RP |
662 | return |
663 | dhclientcmd = dhclient() | |
664 | if (dhclientcmd.is_running(ifaceobjrunning.name) or | |
665 | dhclientcmd.is_running6(ifaceobjrunning.name)): | |
666 | # If dhcp is configured on the interface, we skip it | |
84f33af6 | 667 | return |
15ef32ea RP |
668 | isloopback = self.ipcmd.link_isloopback(ifaceobjrunning.name) |
669 | if isloopback: | |
670 | default_addrs = ['127.0.0.1/8', '::1/128'] | |
004d1e65 | 671 | ifaceobjrunning.addr_family.append('inet') |
15ef32ea RP |
672 | ifaceobjrunning.addr_method = 'loopback' |
673 | else: | |
674 | default_addrs = [] | |
675 | runningaddrsdict = self.ipcmd.addr_get(ifaceobjrunning.name) | |
676 | if runningaddrsdict: | |
677 | [ifaceobjrunning.update_config('address', addr) | |
678 | for addr, addrattrs in runningaddrsdict.items() | |
679 | if addr not in default_addrs] | |
680 | mtu = self.ipcmd.link_get_mtu(ifaceobjrunning.name) | |
681 | if (mtu and | |
682 | (ifaceobjrunning.name == 'lo' and mtu != '16436') or | |
683 | (ifaceobjrunning.name != 'lo' and | |
684 | mtu != self.get_mod_subattr('mtu', 'default'))): | |
685 | ifaceobjrunning.update_config('mtu', mtu) | |
686 | alias = self.ipcmd.link_get_alias(ifaceobjrunning.name) | |
84f33af6 | 687 | if alias: |
15ef32ea RP |
688 | ifaceobjrunning.update_config('alias', alias) |
689 | ||
8d1e346f | 690 | |
15ef32ea RP |
691 | _run_ops = {'up' : _up, |
692 | 'down' : _down, | |
693 | 'query-checkcurr' : _query_check, | |
694 | 'query-running' : _query_running } | |
695 | ||
696 | def get_ops(self): | |
697 | """ returns list of ops supported by this module """ | |
698 | return self._run_ops.keys() | |
699 | ||
700 | def _init_command_handlers(self): | |
701 | if not self.ipcmd: | |
fc5e1735 | 702 | self.ipcmd = iproute2() |
15ef32ea | 703 | |
6e16e5ae | 704 | def run(self, ifaceobj, operation, query_ifaceobj=None, ifaceobj_getfunc=None): |
15ef32ea RP |
705 | """ run address configuration on the interface object passed as argument |
706 | ||
707 | Args: | |
708 | **ifaceobj** (object): iface object | |
709 | ||
710 | **operation** (str): any of 'up', 'down', 'query-checkcurr', | |
711 | 'query-running' | |
712 | Kwargs: | |
713 | query_ifaceobj (object): query check ifaceobject. This is only | |
714 | valid when op is 'query-checkcurr'. It is an object same as | |
715 | ifaceobj, but contains running attribute values and its config | |
716 | status. The modules can use it to return queried running state | |
717 | of interfaces. status is success if the running state is same | |
718 | as user required state in ifaceobj. error otherwise. | |
719 | """ | |
8e113d63 RP |
720 | if ifaceobj.type == ifaceType.BRIDGE_VLAN: |
721 | return | |
15ef32ea RP |
722 | op_handler = self._run_ops.get(operation) |
723 | if not op_handler: | |
724 | return | |
15ef32ea RP |
725 | self._init_command_handlers() |
726 | if operation == 'query-checkcurr': | |
0582f185 RP |
727 | op_handler(self, ifaceobj, query_ifaceobj, |
728 | ifaceobj_getfunc=ifaceobj_getfunc) | |
15ef32ea | 729 | else: |
0582f185 RP |
730 | op_handler(self, ifaceobj, |
731 | ifaceobj_getfunc=ifaceobj_getfunc) |