]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdownaddons/LinkUtils.py
LinkUtils: tunnel_change: do not purge the cache on tunnel change
[mirror_ifupdown2.git] / ifupdown2 / ifupdownaddons / LinkUtils.py
CommitLineData
d486dd0d
JF
1#!/usr/bin/python
2#
3# Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4# Author: Roopa Prabhu, roopa@cumulusnetworks.com
5# Julien Fortin, julien@cumulusnetworks.com
6#
7
8import os
9import re
10import glob
11import shlex
12import signal
007cae35 13import socket
d486dd0d
JF
14import subprocess
15
16from ipaddr import IPNetwork, IPv6Network
17
18try:
19 import ifupdown2.ifupdown.statemanager as statemanager
20 import ifupdown2.ifupdown.ifupdownflags as ifupdownflags
21
22 from ifupdown2.nlmanager.nlmanager import Link, Route
23
24 from ifupdown2.ifupdown.iface import *
25 from ifupdown2.ifupdown.utils import utils
26 from ifupdown2.ifupdown.netlink import netlink
27
28 from ifupdown2.ifupdownaddons.utilsbase import utilsBase
29 from ifupdown2.ifupdownaddons.cache import linkCache, MSTPAttrsCache
30except ImportError:
31 import ifupdown.ifupdownflags as ifupdownflags
32 import ifupdown.statemanager as statemanager
33
34 from nlmanager.nlmanager import Link, Route
35
36 from ifupdown.iface import *
37 from ifupdown.utils import utils
38 from ifupdown.netlink import netlink
39
40 from ifupdownaddons.utilsbase import utilsBase
41 from ifupdownaddons.cache import linkCache, MSTPAttrsCache
42
43
44class LinkUtils(utilsBase):
45 """
46 This class contains helper methods to cache and manipulate interfaces through
47 non-netlink APIs (sysfs, iproute2, brctl...)
48 """
49 _CACHE_FILL_DONE = False
50 VXLAN_UDP_PORT = 4789
51
52 ipbatchbuf = ''
53 ipbatch = False
54 ipbatch_pause = False
55
56 bridge_utils_is_installed = os.path.exists(utils.brctl_cmd)
57 bridge_utils_missing_warning = True
58
dfaa8a2d
JF
59 DEFAULT_IP_METRIC = 1024
60 ADDR_METRIC_SUPPORT = None
61
d486dd0d
JF
62 def __init__(self, *args, **kargs):
63 utilsBase.__init__(self, *args, **kargs)
64
65 self.supported_command = {
66 '%s -c -json vlan show' % utils.bridge_cmd: True,
67 'showmcqv4src': True
68 }
69 self.bridge_vlan_cache = {}
70 self.bridge_vlan_cache_fill_done = False
71
72 if not ifupdownflags.flags.PERFMODE and not LinkUtils._CACHE_FILL_DONE:
73 self._fill_cache()
74
dfaa8a2d
JF
75 if LinkUtils.ADDR_METRIC_SUPPORT is None:
76 try:
77 cmd = [utils.ip_cmd, 'addr', 'help']
78 self.logger.info('executing %s addr help' % utils.ip_cmd)
79
80 process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
81 stdout, stderr = process.communicate()
82 LinkUtils.ADDR_METRIC_SUPPORT = '[ metric METRIC ]' in stderr or ''
83 self.logger.info('address metric support: %s' % ('OK' if LinkUtils.ADDR_METRIC_SUPPORT else 'KO'))
84 except Exception:
85 LinkUtils.ADDR_METRIC_SUPPORT = False
86 self.logger.info('address metric support: KO')
87
88 @classmethod
89 def addr_metric_support(cls):
90 return cls.ADDR_METRIC_SUPPORT
91
92 @classmethod
93 def get_default_ip_metric(cls):
94 return cls.DEFAULT_IP_METRIC
95
d486dd0d
JF
96 @classmethod
97 def reset(cls):
98 LinkUtils._CACHE_FILL_DONE = False
99 LinkUtils.ipbatchbuf = ''
100 LinkUtils.ipbatch = False
101 LinkUtils.ipbatch_pause = False
102
103 def _fill_cache(self):
104 if not LinkUtils._CACHE_FILL_DONE:
105 self._link_fill()
106 self._addr_fill()
107 LinkUtils._CACHE_FILL_DONE = True
108 return True
109 return False
110
111 @staticmethod
112 def _get_vland_id(citems, i, warn):
113 try:
114 sub = citems[i:]
115 index = sub.index('id')
116 int(sub[index + 1])
117 return sub[index + 1]
118 except:
119 if warn:
120 raise Exception('invalid use of \'vlan\' keyword')
121 return None
122
123 def _link_fill(self, ifacename=None, refresh=False):
124 """ fills cache with link information
125
126 if ifacename argument given, fill cache for ifacename, else
127 fill cache for all interfaces in the system
128 """
129
130 if LinkUtils._CACHE_FILL_DONE and not refresh:
131 return
132 try:
133 # if ifacename already present, return
134 if (ifacename and not refresh and
135 linkCache.get_attr([ifacename, 'ifflag'])):
136 return
137 except:
138 pass
139
140 if True:
141 try:
142 [linkCache.update_attrdict([ifname], linkattrs)
143 for ifname, linkattrs in netlink.link_dump(ifacename).items()]
144 except Exception as e:
145 self.logger.info('%s' % str(e))
146 # this netlink call replaces the call to _link_fill_iproute2_cmd()
147 # We shouldn't have netlink calls in the iproute2 module, this will
148 # be removed in the future. We plan to release, a flexible backend
149 # (netlink+iproute2) by default we will use netlink backend but with
150 # a CLI arg we can switch to iproute2 backend.
151 # Until we decide to create this "backend" switch capability,
152 # we have to put the netlink call inside the iproute2 module.
153 else:
154 self._link_fill_iproute2_cmd(ifacename, refresh)
155
156 self._fill_bond_info(ifacename)
157 self._fill_bridge_info(ifacename)
158
159 def _fill_bridge_info(self, ifacename):
160
161 if True: # netlink
162 brports = {}
163
164 if ifacename:
165 cache_dict = {ifacename: linkCache.links.get(ifacename, {})}
166 else:
167 cache_dict = linkCache.links
168
169 for ifname, obj in cache_dict.items():
170 slave_kind = obj.get('slave_kind')
171 if not slave_kind and slave_kind != 'bridge':
172 continue
173
174 info_slave_data = obj.get('info_slave_data')
175 if not info_slave_data:
176 continue
177
178 ifla_master = obj.get('master')
179 if not ifla_master:
180 raise Exception('No master associated with bridge port %s' % ifname)
181
182 for nl_attr in [
183 Link.IFLA_BRPORT_STATE,
184 Link.IFLA_BRPORT_COST,
185 Link.IFLA_BRPORT_PRIORITY,
186 ]:
187 if nl_attr not in info_slave_data and LinkUtils.bridge_utils_is_installed:
188 self._fill_bridge_info_brctl()
189 return
190
191 brport_attrs = {
192 'pathcost': str(info_slave_data.get(Link.IFLA_BRPORT_COST, 0)),
193 'fdelay': format(float(info_slave_data.get(Link.IFLA_BRPORT_FORWARD_DELAY_TIMER, 0) / 100), '.2f'),
194 'portmcrouter': str(info_slave_data.get(Link.IFLA_BRPORT_MULTICAST_ROUTER, 0)),
195 'portmcfl': str(info_slave_data.get(Link.IFLA_BRPORT_FAST_LEAVE, 0)),
196 'portprio': str(info_slave_data.get(Link.IFLA_BRPORT_PRIORITY, 0)),
197 'unicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_UNICAST_FLOOD, 0)),
198 'multicast-flood': str(info_slave_data.get(Link.IFLA_BRPORT_MCAST_FLOOD, 0)),
199 'learning': str(info_slave_data.get(Link.IFLA_BRPORT_LEARNING, 0)),
200 'arp-nd-suppress': str(info_slave_data.get(Link.IFLA_BRPORT_ARP_SUPPRESS, 0))
201 }
202
203 if ifla_master in brports:
204 brports[ifla_master][ifname] = brport_attrs
205 else:
206 brports[ifla_master] = {ifname: brport_attrs}
207
208 linkCache.update_attrdict([ifla_master, 'linkinfo', 'ports'], brports[ifla_master])
209 else:
210 if LinkUtils.bridge_utils_is_installed:
211 self._fill_bridge_info_brctl()
212
213 def _fill_bridge_info_brctl(self):
214 brctlout = utils.exec_command('%s show' % utils.brctl_cmd)
215 if not brctlout:
216 return
217
218 for bline in brctlout.splitlines()[1:]:
219 bitems = bline.split()
220 if len(bitems) < 2:
221 continue
222 try:
223 linkCache.update_attrdict([bitems[0], 'linkinfo'],
224 {'stp': bitems[2]})
225 except KeyError:
226 linkCache.update_attrdict([bitems[0]],
227 {'linkinfo': {'stp': bitems[2]}})
228 self._bridge_attrs_fill(bitems[0])
229
230 def _bridge_attrs_fill(self, bridgename):
231 battrs = {}
232 bports = {}
233
234 brout = utils.exec_command('%s showstp %s' % (utils.brctl_cmd, bridgename))
235 chunks = re.split(r'\n\n', brout, maxsplit=0, flags=re.MULTILINE)
236
237 try:
238 # Get all bridge attributes
239 broutlines = chunks[0].splitlines()
240 # battrs['pathcost'] = broutlines[3].split('path cost')[1].strip()
241
242 try:
243 battrs['maxage'] = broutlines[4].split('bridge max age')[
244 1].strip().replace('.00', '')
245 except:
246 pass
247
248 try:
249 battrs['hello'] = broutlines[5].split('bridge hello time')[
250 1].strip().replace('.00', '')
251 except:
252 pass
253
254 try:
255 battrs['fd'] = broutlines[6].split('bridge forward delay')[
256 1].strip().replace('.00', '')
257 except:
258 pass
259
260 try:
261 battrs['ageing'] = broutlines[7].split('ageing time')[
262 1].strip().replace('.00', '')
263 except:
264 pass
265
266 try:
267 battrs['mcrouter'] = broutlines[12].split('mc router')[
268 1].strip().split('\t\t\t')[0]
269 except:
270 pass
271
272 try:
273 battrs['bridgeprio'] = self.read_file_oneline(
274 '/sys/class/net/%s/bridge/priority' % bridgename)
275 except:
276 pass
277
278 try:
279 battrs['vlan-protocol'] = VlanProtocols.ID_TO_ETHERTYPES[
280 self.read_file_oneline(
281 '/sys/class/net/%s/bridge/vlan_protocol' % bridgename)]
282 except:
283 pass
284
285 try:
286 battrs.update(self._bridge_get_mcattrs_from_sysfs(bridgename))
287 except:
288 pass
289
290 # XXX: comment this out until mc attributes become available
291 # with brctl again
292 # battrs['hashel'] = broutlines[10].split('hash elasticity')[1].split()[0].strip()
293 # battrs['hashmax'] = broutlines[10].split('hash max')[1].strip()
294 # battrs['mclmc'] = broutlines[11].split('mc last member count')[1].split()[0].strip()
295 # battrs['mciqc'] = broutlines[11].split('mc init query count')[1].strip()
296 # battrs['mcrouter'] = broutlines[12].split('mc router')[1].split()[0].strip()
297 ##battrs['mcsnoop'] = broutlines[12].split('mc snooping')[1].strip()
298 # battrs['mclmt'] = broutlines[13].split('mc last member timer')[1].split()[0].strip()
299 except Exception, e:
300 self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
301 pass
302
303 linkCache.update_attrdict([bridgename, 'linkinfo'], battrs)
304 for cidx in range(1, len(chunks)):
305 bpout = chunks[cidx].lstrip('\n')
306 if not bpout or bpout[0] == ' ':
307 continue
308 bplines = bpout.splitlines()
309 pname = bplines[0].split()[0]
310 bportattrs = {}
311 try:
312 bportattrs['pathcost'] = bplines[2].split(
313 'path cost')[1].strip()
314 bportattrs['fdelay'] = bplines[4].split(
315 'forward delay timer')[1].strip()
316 bportattrs['portmcrouter'] = self.read_file_oneline(
317 '/sys/class/net/%s/brport/multicast_router' % pname)
318 bportattrs['portmcfl'] = self.read_file_oneline(
319 '/sys/class/net/%s/brport/multicast_fast_leave' % pname)
320 bportattrs['portprio'] = self.read_file_oneline(
321 '/sys/class/net/%s/brport/priority' % pname)
322 bportattrs['unicast-flood'] = self.read_file_oneline(
323 '/sys/class/net/%s/brport/unicast_flood' % pname)
324 bportattrs['multicast-flood'] = self.read_file_oneline(
325 '/sys/class/net/%s/brport/multicast_flood' % pname)
326 bportattrs['learning'] = self.read_file_oneline(
327 '/sys/class/net/%s/brport/learning' % pname)
328 bportattrs['arp-nd-suppress'] = self.read_file_oneline(
329 '/sys/class/net/%s/brport/neigh_suppress' % pname)
330 # bportattrs['mcrouters'] = bplines[6].split('mc router')[1].split()[0].strip()
331 # bportattrs['mc fast leave'] = bplines[6].split('mc fast leave')[1].strip()
332 except Exception, e:
333 self.logger.warn('%s: error while processing bridge attributes: %s' % (bridgename, str(e)))
334 bports[pname] = bportattrs
335 linkCache.update_attrdict([bridgename, 'linkinfo', 'ports'], bports)
336
337 _bridge_sysfs_mcattrs = {
338 'mclmc': 'multicast_last_member_count',
339 'mcrouter': 'multicast_router',
340 'mcsnoop': 'multicast_snooping',
341 'mcsqc': 'multicast_startup_query_count',
342 'mcqifaddr': 'multicast_query_use_ifaddr',
343 'mcquerier': 'multicast_querier',
344 'hashel': 'hash_elasticity',
345 'hashmax': 'hash_max',
346 'mclmi': 'multicast_last_member_interval',
347 'mcmi': 'multicast_membership_interval',
348 'mcqpi': 'multicast_querier_interval',
349 'mcqi': 'multicast_query_interval',
350 'mcqri': 'multicast_query_response_interval',
351 'mcsqi': 'multicast_startup_query_interval',
352 'igmp-version': 'multicast_igmp_version',
353 'mld-version': 'multicast_mld_version',
354 'vlan-stats': 'vlan_stats_enabled',
355 'mcstats': 'multicast_stats_enabled',
356 }
357
358 def _bridge_get_mcattrs_from_sysfs(self, bridgename):
359 mcattrsdivby100 = ['mclmi', 'mcmi', 'mcqpi', 'mcqi', 'mcqri', 'mcsqi']
360 mcattrs = {}
361
362 for m, s in self._bridge_sysfs_mcattrs.items():
363 n = self.read_file_oneline('/sys/class/net/%s/bridge/%s' % (bridgename, s))
364 if m in mcattrsdivby100:
365 try:
366 v = int(n) / 100
367 mcattrs[m] = str(v)
368 except Exception, e:
369 self.logger.warn('error getting mc attr %s (%s)' % (m, str(e)))
370 pass
371 else:
372 mcattrs[m] = n
373 return mcattrs
374
375 def _fill_bond_info(self, ifacename):
376 bonding_masters = self.read_file_oneline('/sys/class/net/bonding_masters')
377 if not bonding_masters:
378 return
379
380 bond_masters_list = bonding_masters.split()
381
382 if ifacename:
383 if ifacename in bond_masters_list:
384 bond_masters_list = [ifacename]
385 else:
386 # we want to refresh this interface only if it's a bond master
387 return
388
389 for bondname in bond_masters_list:
390 try:
391 if bondname not in linkCache.links:
392 linkCache.set_attr([bondname], {'linkinfo': {}})
393 linkCache.set_attr([bondname, 'linkinfo', 'slaves'],
394 self.read_file_oneline('/sys/class/net/%s/bonding/slaves'
395 % bondname).split())
396 try:
397 # if some attribute are missing we try to get the bond attributes via sysfs
398 bond_linkinfo = linkCache.links[bondname]['linkinfo']
399 for attr in [Link.IFLA_BOND_MODE, Link.IFLA_BOND_XMIT_HASH_POLICY, Link.IFLA_BOND_MIN_LINKS]:
400 if attr not in bond_linkinfo:
401 self._fill_bond_info_sysfs(bondname)
402 # after we fill in the cache we can continue to the next bond
403 break
404 except:
405 self._fill_bond_info_sysfs(bondname)
406
407 except Exception as e:
408 self.logger.debug('LinkUtils: bond cache error: %s' % str(e))
409
410 def _fill_bond_info_sysfs(self, bondname):
411 try:
412 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS],
413 self.read_file_oneline(
414 '/sys/class/net/%s/bonding/min_links'
415 % bondname))
416 except Exception as e:
417 self.logger.debug(str(e))
418
419 try:
420 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MODE],
421 self.read_file_oneline('/sys/class/net/%s/bonding/mode'
422 % bondname).split()[0])
423 except Exception as e:
424 self.logger.debug(str(e))
425 try:
426 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY],
427 self.read_file_oneline(
428 '/sys/class/net/%s/bonding/xmit_hash_policy'
429 % bondname).split()[0])
430 except Exception as e:
431 self.logger.debug(str(e))
432 try:
433 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE],
434 self.read_file_oneline('/sys/class/net/%s/bonding/lacp_rate'
435 % bondname).split()[1])
436 except Exception as e:
437 self.logger.debug(str(e))
438 try:
439 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO],
440 self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_sys_prio'
441 % bondname))
442 except Exception as e:
443 self.logger.debug(str(e))
444 try:
445 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM],
446 self.read_file_oneline('/sys/class/net/%s/bonding/ad_actor_system'
447 % bondname))
448 except Exception as e:
449 self.logger.debug(str(e))
450 try:
451 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS],
452 self.read_file_oneline('/sys/class/net/%s/bonding/lacp_bypass'
453 % bondname).split()[1])
454 except Exception as e:
455 self.logger.debug(str(e))
456 try:
457 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY],
458 self.read_file_oneline('/sys/class/net/%s/bonding/updelay'
459 % bondname))
460 except Exception as e:
461 self.logger.debug(str(e))
462 try:
463 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY],
464 self.read_file_oneline('/sys/class/net/%s/bonding/downdelay'
465 % bondname))
466 except Exception as e:
467 self.logger.debug(str(e))
468
469 try:
470 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER],
471 self.read_file_oneline('/sys/class/net/%s/bonding/use_carrier' % bondname))
472 except Exception as e:
473 self.logger.debug(str(e))
474
475 try:
476 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON],
477 self.read_file_oneline('/sys/class/net/%s/bonding/miimon' % bondname))
478 except Exception as e:
479 self.logger.debug(str(e))
480
481 try:
482 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF],
483 self.read_file_oneline('/sys/class/net/%s/bonding/num_unsol_na' % bondname))
484 except Exception as e:
485 self.logger.debug(str(e))
486
487 try:
488 linkCache.set_attr([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF],
489 self.read_file_oneline('/sys/class/net/%s/bonding/num_grat_arp' % bondname))
490 except Exception as e:
491 self.logger.debug(str(e))
492
493
494 def _link_fill_iproute2_cmd(self, ifacename=None, refresh=False):
495 warn = True
496 linkout = {}
497 if LinkUtils._CACHE_FILL_DONE and not refresh:
498 return
499 try:
500 # if ifacename already present, return
501 if (ifacename and not refresh and
502 linkCache.get_attr([ifacename, 'ifflag'])):
503 return
504 except:
505 pass
506 cmdout = self.link_show(ifacename=ifacename)
507 if not cmdout:
508 return
509 for c in cmdout.splitlines():
510 citems = c.split()
511 ifnamenlink = citems[1].split('@')
512 if len(ifnamenlink) > 1:
513 ifname = ifnamenlink[0]
514 iflink = ifnamenlink[1].strip(':')
515 else:
516 ifname = ifnamenlink[0].strip(':')
517 iflink = None
518 linkattrs = dict()
519 linkattrs['link'] = iflink
520 linkattrs['ifindex'] = citems[0].strip(':')
521 flags = citems[2].strip('<>').split(',')
522 linkattrs['flags'] = flags
523 linkattrs['ifflag'] = 'UP' if 'UP' in flags else 'DOWN'
524 for i in range(0, len(citems)):
525 try:
526 if citems[i] == 'mtu':
527 linkattrs['mtu'] = citems[i + 1]
528 elif citems[i] == 'state':
529 linkattrs['state'] = citems[i + 1]
530 elif citems[i] == 'link/ether':
531 linkattrs['hwaddress'] = citems[i + 1]
33ebe60a 532 elif citems[i] in ['link/gre', 'link/ipip', 'link/sit', 'link/gre6', 'link/tunnel6', 'gretap']:
22aa65c7 533 linkattrs['kind'] = 'tunnel'
b2a87fe8 534 tunattrs = {'mode': citems[i].split('/')[-1],
22aa65c7
MW
535 'endpoint' : None,
536 'local' : None,
537 'ttl' : None,
538 'physdev' : None}
b2a87fe8 539 for j in range(i, len(citems)):
22aa65c7
MW
540 if citems[j] == 'local':
541 tunattrs['local'] = citems[j + 1]
542 elif citems[j] == 'remote':
543 tunattrs['endpoint'] = citems[j + 1]
544 elif citems[j] == 'ttl':
545 tunattrs['ttl'] = citems[j + 1]
546 elif citems[j] == 'dev':
547 tunattrs['physdev'] = citems[j + 1]
6039c4d7
JF
548 elif citems[j] in ['vti', 'vti6', 'ip6gre', 'ipip6', 'ip6ip6']:
549 tunattrs['mode'] = citems[j]
22aa65c7
MW
550 linkattrs['linkinfo'] = tunattrs
551 break
77054f7f
SA
552 elif citems[i] == 'link/ppp':
553 linkattrs['kind'] = 'ppp'
d486dd0d
JF
554 elif citems[i] == 'vlan':
555 vlanid = self._get_vland_id(citems, i, warn)
556 if vlanid:
557 linkattrs['linkinfo'] = {'vlanid': vlanid}
558 linkattrs['kind'] = 'vlan'
559 elif citems[i] == 'dummy':
560 linkattrs['kind'] = 'dummy'
561 elif citems[i] == 'vxlan' and citems[i + 1] == 'id':
562 linkattrs['kind'] = 'vxlan'
563 vattrs = {'vxlanid': citems[i + 2],
564 'svcnode': None,
565 'remote': [],
566 'ageing': citems[i + 2],
567 'learning': 'on'}
568 for j in range(i + 2, len(citems)):
569 if citems[j] == 'local':
570 vattrs['local'] = citems[j + 1]
571 elif citems[j] == 'remote':
572 vattrs['svcnode'] = citems[j + 1]
573 elif citems[j] == 'ageing':
574 vattrs['ageing'] = citems[j + 1]
575 elif citems[j] == 'nolearning':
576 vattrs['learning'] = 'off'
a382b488
JF
577 elif citems[j] == 'dev':
578 vattrs['physdev'] = citems[j + 1]
d486dd0d
JF
579 linkattrs['linkinfo'] = vattrs
580 break
581 elif citems[i] == 'vrf' and citems[i + 1] == 'table':
582 vattrs = {'table': citems[i + 2]}
583 linkattrs['linkinfo'] = vattrs
584 linkattrs['kind'] = 'vrf'
585 linkCache.vrfs[ifname] = vattrs
586 break
587 elif citems[i] == 'veth':
588 linkattrs['kind'] = 'veth'
589 elif citems[i] == 'vrf_slave':
590 linkattrs['slave_kind'] = 'vrf_slave'
591 break
592 elif citems[i] == 'macvlan' and citems[i + 1] == 'mode':
593 linkattrs['kind'] = 'macvlan'
594 except Exception as e:
595 if warn:
596 self.logger.debug('%s: parsing error: id, mtu, state, '
597 'link/ether, vlan, dummy, vxlan, local, '
598 'remote, ageing, nolearning, vrf, table, '
599 'vrf_slave are reserved keywords: %s' %
600 (ifname, str(e)))
601 warn = False
602 # linkattrs['alias'] = self.read_file_oneline(
603 # '/sys/class/net/%s/ifalias' %ifname)
604 linkout[ifname] = linkattrs
605 [linkCache.update_attrdict([ifname], linkattrs)
606 for ifname, linkattrs in linkout.items()]
607
608 @staticmethod
609 def _addr_filter(ifname, addr, scope=None):
610 default_addrs = ['127.0.0.1/8', '::1/128', '0.0.0.0']
611 if ifname == 'lo' and addr in default_addrs:
612 return True
613 if scope and scope == 'link':
614 return True
615 return False
616
617 def _addr_fill(self, ifacename=None, refresh=False):
618 """ fills cache with address information
619
620 if ifacename argument given, fill cache for ifacename, else
621 fill cache for all interfaces in the system
622 """
623 if LinkUtils._CACHE_FILL_DONE and not refresh:
624 return
625 try:
626 # Check if ifacename is already full, in which case, return
627 if ifacename and not refresh:
628 linkCache.get_attr([ifacename, 'addrs'])
629 return
630 except:
631 pass
632
633 if True:
634 try:
635 [linkCache.update_attrdict([ifname], linkattrs)
636 for ifname, linkattrs in netlink.addr_dump(ifname=ifacename).items()]
637 except Exception as e:
638 self.logger.info(str(e))
639
640 # this netlink call replaces the call to _addr_fill_iproute2_cmd()
641 # We shouldn't have netlink calls in the iproute2 module, this will
642 # be removed in the future. We plan to release, a flexible backend
643 # (netlink+iproute2) by default we will use netlink backend but with
644 # a CLI arg we can switch to iproute2 backend.
645 # Until we decide to create this "backend" switch capability,
646 # we have to put the netlink call inside the iproute2 module.
647
648 else:
649 self._addr_fill_iproute2_cmd(ifacename, refresh)
650
651 def _addr_fill_iproute2_cmd(self, ifacename=None, refresh=False):
652 """ fills cache with address information
653
654 if ifacename argument given, fill cache for ifacename, else
655 fill cache for all interfaces in the system
656 """
657 linkout = {}
658 if LinkUtils._CACHE_FILL_DONE and not refresh:
659 return
660 try:
661 # Check if ifacename is already full, in which case, return
662 if ifacename and not refresh:
663 linkCache.get_attr([ifacename, 'addrs'])
664 return
665 except:
666 pass
667 cmdout = self.addr_show(ifacename=ifacename)
668 if not cmdout:
669 return
670 for c in cmdout.splitlines():
671 citems = c.split()
672 ifnamenlink = citems[1].split('@')
673 if len(ifnamenlink) > 1:
674 ifname = ifnamenlink[0]
675 else:
676 ifname = ifnamenlink[0].strip(':')
677 if not linkout.get(ifname):
678 linkattrs = dict()
679 linkattrs['addrs'] = OrderedDict({})
680 try:
681 linkout[ifname].update(linkattrs)
682 except KeyError:
683 linkout[ifname] = linkattrs
684 if citems[2] == 'inet':
685 if self._addr_filter(ifname, citems[3], scope=citems[5]):
686 continue
687 addrattrs = dict()
688 addrattrs['scope'] = citems[5]
689 addrattrs['type'] = 'inet'
690 linkout[ifname]['addrs'][citems[3]] = addrattrs
691 elif citems[2] == 'inet6':
692 if self._addr_filter(ifname, citems[3], scope=citems[5]):
693 continue
694 if citems[5] == 'link':
695 continue # skip 'link' addresses
696 addrattrs = dict()
697 addrattrs['scope'] = citems[5]
698 addrattrs['type'] = 'inet6'
699 linkout[ifname]['addrs'][citems[3]] = addrattrs
700 [linkCache.update_attrdict([ifname], linkattrs)
701 for ifname, linkattrs in linkout.items()]
702
703 def cache_get(self, t, attrlist, refresh=False):
704 return self._cache_get(t, attrlist, refresh)
705
706 def _cache_get(self, t, attrlist, refresh=False):
707 try:
708 if ifupdownflags.flags.DRYRUN:
709 return False
710 if ifupdownflags.flags.CACHE:
711 if self._fill_cache():
712 # if we filled the cache, return new data
713 return linkCache.get_attr(attrlist)
714 if not refresh:
715 return linkCache.get_attr(attrlist)
716 if t == 'link':
717 self._link_fill(attrlist[0], refresh)
718 elif t == 'addr':
719 self._addr_fill(attrlist[0], refresh)
720 else:
721 self._link_fill(attrlist[0], refresh)
722 self._addr_fill(attrlist[0], refresh)
723 return linkCache.get_attr(attrlist)
724 except Exception, e:
725 self.logger.debug('_cache_get(%s) : [%s]' % (str(attrlist), str(e)))
726 return None
727
728 def cache_check(self, attrlist, value, refresh=False):
729 return self._cache_check('link', attrlist, value, refresh=refresh)
730
731 def _cache_check(self, t, attrlist, value, refresh=False):
732 try:
733 return self._cache_get(t, attrlist, refresh) == value
734 except Exception, e:
735 self.logger.debug('_cache_check(%s) : [%s]'
736 % (str(attrlist), str(e)))
737 return False
738
739 def cache_update(self, attrlist, value):
740 return self._cache_update(attrlist, value)
741
742 @staticmethod
743 def _cache_update(attrlist, value):
744 if ifupdownflags.flags.DRYRUN:
745 return
746 try:
747 if attrlist[-1] == 'slaves':
748 linkCache.append_to_attrlist(attrlist, value)
749 return
750 linkCache.set_attr(attrlist, value)
751 except:
752 pass
753
754 @staticmethod
755 def _cache_delete(attrlist, value=None):
756 if ifupdownflags.flags.DRYRUN:
757 return
758 try:
759 if value:
760 linkCache.remove_from_attrlist(attrlist, value)
761 else:
762 linkCache.del_attr(attrlist)
763 except:
764 pass
765
766 @staticmethod
767 def _cache_invalidate():
768 linkCache.invalidate()
769 LinkUtils._CACHE_FILL_DONE = False
770
771 @staticmethod
772 def batch_start():
773 LinkUtils.ipbatcbuf = ''
774 LinkUtils.ipbatch = True
775 LinkUtils.ipbatch_pause = False
776
777 @staticmethod
778 def add_to_batch(cmd):
779 LinkUtils.ipbatchbuf += cmd + '\n'
780
781 @staticmethod
782 def batch_pause():
783 LinkUtils.ipbatch_pause = True
784
785 @staticmethod
786 def batch_resume():
787 LinkUtils.ipbatch_pause = False
788
789 def batch_commit(self):
790 if not LinkUtils.ipbatchbuf:
791 LinkUtils.ipbatchbuf = ''
792 LinkUtils.ipbatch = False
793 LinkUtils.ipbatch_pause = False
794 return
795 try:
796 utils.exec_command('%s -force -batch -' % utils.ip_cmd,
797 stdin=self.ipbatchbuf)
798 except:
799 raise
800 finally:
801 LinkUtils.ipbatchbuf = ''
802 LinkUtils.ipbatch = False
803 LinkUtils.ipbatch_pause = False
804
805 def bridge_batch_commit(self):
806 if not LinkUtils.ipbatchbuf:
807 LinkUtils.ipbatchbuf = ''
808 LinkUtils.ipbatch = False
809 LinkUtils.ipbatch_pause = False
810 return
811 try:
812 utils.exec_command('%s -force -batch -'
813 % utils.bridge_cmd, stdin=self.ipbatchbuf)
814 except:
815 raise
816 finally:
817 LinkUtils.ipbatchbuf = ''
818 LinkUtils.ipbatch = False
819 LinkUtils.ipbatch_pause = False
820
821 def addr_show(self, ifacename=None):
822 if ifacename:
823 if not self.link_exists(ifacename):
824 return
825 return utils.exec_commandl([utils.ip_cmd,
826 '-o', 'addr', 'show', 'dev', ifacename])
827 else:
828 return utils.exec_commandl([utils.ip_cmd,
829 '-o', 'addr', 'show'])
830
831 @staticmethod
832 def link_show(ifacename=None):
833 if ifacename:
834 return utils.exec_commandl([utils.ip_cmd,
835 '-o', '-d', 'link', 'show', 'dev', ifacename])
836 else:
837 return utils.exec_commandl([utils.ip_cmd,
838 '-o', '-d', 'link', 'show'])
839
840 def addr_add(self, ifacename, address, broadcast=None,
dfaa8a2d 841 peer=None, scope=None, preferred_lifetime=None, metric=None):
d486dd0d
JF
842 if not address:
843 return
844 cmd = 'addr add %s' % address
845 if broadcast:
846 cmd += ' broadcast %s' % broadcast
847 if peer:
848 cmd += ' peer %s' % peer
849 if scope:
850 cmd += ' scope %s' % scope
851 if preferred_lifetime:
852 cmd += ' preferred_lft %s' % preferred_lifetime
853 cmd += ' dev %s' % ifacename
dfaa8a2d
JF
854
855 if metric:
856 cmd += ' metric %s' % metric
857
d486dd0d
JF
858 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
859 self.add_to_batch(cmd)
860 else:
861 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
862 self._cache_update([ifacename, 'addrs', address], {})
863
864 def addr_del(self, ifacename, address, broadcast=None,
865 peer=None, scope=None):
866 """ Delete ipv4 address """
867 if not address:
868 return
869 if not self._cache_get('addr', [ifacename, 'addrs', address]):
870 return
871 cmd = 'addr del %s' % address
872 if broadcast:
873 cmd += 'broadcast %s' % broadcast
874 if peer:
875 cmd += 'peer %s' % peer
876 if scope:
877 cmd += 'scope %s' % scope
878 cmd += ' dev %s' % ifacename
879 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
880 self._cache_delete([ifacename, 'addrs', address])
881
882 def addr_flush(self, ifacename):
883 cmd = 'addr flush dev %s' % ifacename
884 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
885 self.add_to_batch(cmd)
886 else:
887 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
888 self._cache_delete([ifacename, 'addrs'])
889
890 def del_addr_all(self, ifacename, skip_addrs=[]):
891 if not skip_addrs:
892 skip_addrs = []
893 runningaddrsdict = self.get_running_addrs(ifname=ifacename)
894 try:
895 # XXX: ignore errors. Fix this to delete secondary addresses
896 # first
897 [self.addr_del(ifacename, a) for a in
898 set(runningaddrsdict.keys()).difference(skip_addrs)]
899 except:
900 # ignore errors
901 pass
902
903 def addr_get(self, ifacename, details=True, refresh=False):
904 addrs = self._cache_get('addr', [ifacename, 'addrs'], refresh=refresh)
905 if not addrs:
906 return None
907 if details:
908 return addrs
909 return addrs.keys()
910
911 def get_running_addrs(self, ifaceobj=None, ifname=None, details=True, addr_virtual_ifaceobj=None):
912 """
913 We now support addr with link scope. Since the kernel may add it's
914 own link address to some interfaces we need to filter them out and
915 make sure we only deal with the addresses set by ifupdown2.
916
917 To do so we look at the previous configuration made by ifupdown2
918 (with the help of the statemanager) together with the addresses
919 specified by the user in /etc/network/interfaces, these addresses
920 are then compared to the running state of the intf (ip addr show)
921 made via a netlink addr dump.
922 For each configured addresses of scope link, we check if it was
923 previously configured by ifupdown2 to create a final set of the
924 addresses watched by ifupdown2
925 """
926 if not ifaceobj and not ifname:
927 return None
928
929 config_addrs = set()
930
931 if ifaceobj:
932 interface_name = ifaceobj.name
933 else:
934 interface_name = ifname
935
936 if addr_virtual_ifaceobj:
937 for virtual in addr_virtual_ifaceobj.get_attr_value('address-virtual') or []:
938 for ip in virtual.split():
939 try:
940 IPNetwork(ip)
941 config_addrs.add(ip)
942 except:
943 pass
944
945 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(addr_virtual_ifaceobj.name)
946 for saved_ifaceobj in saved_ifaceobjs or []:
947 for virtual in saved_ifaceobj.get_attr_value('address-virtual') or []:
948 for ip in virtual.split():
949 try:
950 IPNetwork(ip)
951 config_addrs.add(ip)
952 except:
953 pass
954 else:
955 if ifaceobj:
956 for addr in ifaceobj.get_attr_value('address') or []:
957 config_addrs.add(addr)
958
959 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(interface_name)
960 for saved_ifaceobj in saved_ifaceobjs or []:
961 for addr in saved_ifaceobj.get_attr_value('address') or []:
962 config_addrs.add(addr)
963
964 running_addrs = OrderedDict()
965 cached_addrs = self.addr_get(interface_name)
966 if cached_addrs:
967 for addr, addr_details in cached_addrs.items():
968 try:
969 scope = int(addr_details['scope'])
970 except Exception:
971 try:
972 d = {}
973 addr_obj = IPNetwork(addr)
974 if isinstance(addr_obj, IPv6Network):
975 d['family'] = 'inet6'
976 else:
977 d['family'] = 'inet'
978 running_addrs[addr] = d
979 except:
980 running_addrs[addr] = {}
981 continue
982 if (scope & Route.RT_SCOPE_LINK and addr in config_addrs) or not scope & Route.RT_SCOPE_LINK:
983 running_addrs[addr] = addr_details
984 else:
985 return None
986
987 if details:
988 return running_addrs
989 return running_addrs.keys()
990
991 @staticmethod
992 def compare_user_config_vs_running_state(running_addrs, user_addrs):
993 ip4 = []
994 ip6 = []
995
996 for ip in user_addrs or []:
997 obj = IPNetwork(ip)
998
999 if type(obj) == IPv6Network:
1000 ip6.append(obj)
1001 else:
1002 ip4.append(obj)
1003
1004 running_ipobj = []
1005 for ip in running_addrs or []:
1006 running_ipobj.append(IPNetwork(ip))
1007
1008 return running_ipobj == (ip4 + ip6)
1009
dfaa8a2d 1010 def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None):
d486dd0d
JF
1011 # purges address
1012 if purge_existing:
1013 # if perfmode is not set and also if iface has no sibling
1014 # objects, purge addresses that are not present in the new
1015 # config
1016 runningaddrs = self.get_running_addrs(
1017 ifname=ifacename,
1018 details=False,
1019 addr_virtual_ifaceobj=ifaceobj
1020 )
1021 addrs = utils.get_normalized_ip_addr(ifacename, addrs)
1022
1023 if self.compare_user_config_vs_running_state(runningaddrs, addrs):
1024 return
1025 try:
1026 # if primary address is not same, there is no need to keep any.
1027 # reset all addresses
1028 if (addrs and runningaddrs and
1029 (addrs[0] != runningaddrs[0])):
1030 self.del_addr_all(ifacename)
1031 else:
1032 self.del_addr_all(ifacename, addrs)
1033 except Exception, e:
1034 self.logger.warning('%s: %s' % (ifacename, str(e)))
1035 for a in addrs:
1036 try:
dfaa8a2d 1037 self.addr_add(ifacename, a, metric=metric)
d486dd0d
JF
1038 except Exception, e:
1039 self.logger.error(str(e))
1040
1041 def _link_set_ifflag(self, ifacename, value):
1042 # Dont look at the cache, the cache may have stale value
1043 # because link status can be changed by external
1044 # entity (One such entity is ifupdown main program)
1045 cmd = 'link set dev %s %s' % (ifacename, value.lower())
1046 if LinkUtils.ipbatch:
1047 self.add_to_batch(cmd)
1048 else:
1049 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1050
1051 def link_up(self, ifacename):
1052 self._link_set_ifflag(ifacename, 'UP')
1053
1054 def link_down(self, ifacename):
1055 self._link_set_ifflag(ifacename, 'DOWN')
1056
1057 def link_set(self, ifacename, key, value=None,
1058 force=False, t=None, state=None):
1059 if not force:
1060 if (key not in ['master', 'nomaster'] and
1061 self._cache_check('link', [ifacename, key], value)):
1062 return
1063 cmd = 'link set dev %s' % ifacename
1064 if t:
1065 cmd += ' type %s' % t
1066 cmd += ' %s' % key
1067 if value:
1068 cmd += ' %s' % value
1069 if state:
1070 cmd += ' %s' % state
1071 if LinkUtils.ipbatch:
1072 self.add_to_batch(cmd)
1073 else:
1074 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1075 if key not in ['master', 'nomaster']:
1076 self._cache_update([ifacename, key], value)
1077
1078 def link_set_hwaddress(self, ifacename, hwaddress, force=False):
1079 if not force:
1080 if self._cache_check('link', [ifacename, 'hwaddress'], hwaddress):
1081 return
1082 self.link_down(ifacename)
1083 cmd = 'link set dev %s address %s' % (ifacename, hwaddress)
1084 if LinkUtils.ipbatch:
1085 self.add_to_batch(cmd)
1086 else:
1087 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1088 self.link_up(ifacename)
1089 self._cache_update([ifacename, 'hwaddress'], hwaddress)
1090
1091 def link_set_mtu(self, ifacename, mtu):
1092 if ifupdownflags.flags.DRYRUN:
1093 return True
1094 if not mtu or not ifacename: return
1095 self.write_file('/sys/class/net/%s/mtu' % ifacename, mtu)
1096 self._cache_update([ifacename, 'mtu'], mtu)
1097
1098 def link_set_alias(self, ifacename, alias):
1099 self.write_file('/sys/class/net/%s/ifalias' % ifacename,
1100 '\n' if not alias else alias)
1101
1102 def link_get_alias(self, ifacename):
1103 return self.read_file_oneline('/sys/class/net/%s/ifalias'
1104 % ifacename)
1105
1106 def link_isloopback(self, ifacename):
1107 flags = self._cache_get('link', [ifacename, 'flags'])
1108 if not flags:
1109 return
1110 if 'LOOPBACK' in flags:
1111 return True
1112 return False
1113
1114 def link_get_status(self, ifacename):
1115 return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
1116
1117 @staticmethod
9d505185 1118 def route_add_gateway(ifacename, gateway, vrf=None, metric=None, onlink=True):
d486dd0d
JF
1119 if not gateway:
1120 return
1121 if not vrf:
1122 cmd = '%s route add default via %s proto kernel' % (utils.ip_cmd,
1123 gateway)
1124 else:
1125 cmd = ('%s route add table %s default via %s proto kernel' %
1126 (utils.ip_cmd, vrf, gateway))
1127 # Add metric
1128 if metric:
1129 cmd += 'metric %s' % metric
1130 cmd += ' dev %s' % ifacename
9d505185
JF
1131
1132 if onlink:
1133 cmd += " onlink"
1134
d486dd0d
JF
1135 utils.exec_command(cmd)
1136
1137 @staticmethod
1138 def route_del_gateway(ifacename, gateway, vrf=None, metric=None):
1139 # delete default gw
1140 if not gateway:
1141 return
1142 if not vrf:
1143 cmd = ('%s route del default via %s proto kernel' %
1144 (utils.ip_cmd, gateway))
1145 else:
1146 cmd = ('%s route del table %s default via %s proto kernel' %
1147 (utils.ip_cmd, vrf, gateway))
1148 if metric:
1149 cmd += ' metric %s' % metric
1150 cmd += ' dev %s' % ifacename
1151 utils.exec_command(cmd)
1152
1153 @staticmethod
1154 def _get_vrf_id(ifacename):
1155 try:
1156 return linkCache.vrfs[ifacename]['table']
1157 except KeyError:
1158 dump = netlink.link_dump(ifacename)
1159
1160 [linkCache.update_attrdict([ifname], linkattrs)
1161 for ifname, linkattrs in dump.items()]
1162
1163 if dump and dump.get(ifacename, {}).get('kind') == 'vrf':
1164 vrf_table = dump.get(ifacename, {}).get('linkinfo', {}).get('table')
1165 linkCache.vrfs[ifacename] = {'table': vrf_table}
1166 return vrf_table
1167
1168 return None
1169
1170 def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips):
1171 vrf_table = None
1172
1173 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
1174 try:
1175 for upper_iface in ifaceobj.upperifaces:
1176 vrf_table = self._get_vrf_id(upper_iface)
1177 if vrf_table:
1178 break
1179 except:
1180 pass
1181
1182 ip_route_del = []
1183 for ip in ips:
1184 ip_network_obj = IPNetwork(ip)
1185
1186 if type(ip_network_obj) == IPv6Network:
1187 route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
1188
1189 if vrf_table:
1190 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1191 LinkUtils.add_to_batch('route del %s table %s dev %s' % (route_prefix, vrf_table, macvlan_ifacename))
1192 else:
1193 utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'table', vrf_table, 'dev', macvlan_ifacename])
1194 else:
1195 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1196 LinkUtils.add_to_batch('route del %s dev %s' % (route_prefix, macvlan_ifacename))
1197 else:
1198 utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'dev', macvlan_ifacename])
1199 ip_route_del.append((route_prefix, vrf_table))
1200
1201 for ip, vrf_table in ip_route_del:
1202 if vrf_table:
1203 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1204 LinkUtils.add_to_batch('route add %s table %s dev %s proto kernel metric 9999' % (ip, vrf_table, macvlan_ifacename))
1205 else:
1206 utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'table', vrf_table, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
1207 else:
1208 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1209 LinkUtils.add_to_batch('route add %s dev %s proto kernel metric 9999' % (ip, macvlan_ifacename))
1210 else:
1211 utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
1212
1213 def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid):
1214 if self.link_exists(vlan_device_name):
1215 return
1216 utils.exec_command('%s link add link %s name %s type vlan id %d' %
1217 (utils.ip_cmd,
1218 vlan_raw_device, vlan_device_name, vlanid))
1219 self._cache_update([vlan_device_name], {})
1220
1221 def link_create_vlan_from_name(self, vlan_device_name):
1222 v = vlan_device_name.split('.')
1223 if len(v) != 2:
1224 self.logger.warn('invalid vlan device name %s' % vlan_device_name)
1225 return
1226 self.link_create_vlan(vlan_device_name, v[0], v[1])
1227
1228 def link_create_macvlan(self, name, linkdev, mode='private'):
1229 if self.link_exists(name):
1230 return
1231 cmd = ('link add link %s' % linkdev +
1232 ' name %s' % name +
1233 ' type macvlan mode %s' % mode)
1234 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1235 self.add_to_batch(cmd)
1236 else:
1237 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1238 self._cache_update([name], {})
1239
1240 def get_vxlan_peers(self, dev, svcnodeip):
1241 cmd = '%s fdb show brport %s' % (utils.bridge_cmd,
1242 dev)
1243 cur_peers = []
1244 try:
1245 ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
1246 utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
1247 output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout)
1248 ps.wait()
1249 utils.disable_subprocess_signal_forwarding(signal.SIGINT)
1250 try:
1251 ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+')
1252 for l in output.split('\n'):
1253 m = ppat.search(l)
1254 if m and m.group(1) != svcnodeip:
1255 cur_peers.append(m.group(1))
1256 except:
1257 self.logger.warn('error parsing ip link output')
1258 except subprocess.CalledProcessError as e:
1259 if e.returncode != 1:
1260 self.logger.error(str(e))
1261 finally:
1262 utils.disable_subprocess_signal_forwarding(signal.SIGINT)
1263
1264 return cur_peers
1265
33ebe60a
SA
1266 def tunnel_create(self, tunnelname, mode, attrs={}):
1267 """ generic link_create function """
1268 if self.link_exists(tunnelname):
1269 return
f40c6294
SA
1270
1271 cmd = ''
1272 if '6' in mode:
1273 cmd = ' -6 '
1274
6039c4d7
JF
1275 if mode in ['gretap']:
1276 cmd += 'link add %s type %s' % (tunnelname, mode)
535b0c8d 1277 else:
6039c4d7 1278 cmd += 'tunnel add %s mode %s' % (tunnelname, mode)
535b0c8d 1279
33ebe60a
SA
1280 if attrs:
1281 for k, v in attrs.iteritems():
6039c4d7 1282 cmd += ' %s' % k
33ebe60a 1283 if v:
6039c4d7 1284 cmd += ' %s' % v
33ebe60a
SA
1285 if self.ipbatch and not self.ipbatch_pause:
1286 self.add_to_batch(cmd)
1287 else:
1288 utils.exec_command('ip %s' % cmd)
1289 self._cache_update([tunnelname], {})
1290
1291 def tunnel_change(self, tunnelname, attrs={}):
1292 """ tunnel change function """
1293 if not self.link_exists(tunnelname):
1294 return
1295 cmd = 'tunnel change'
1296 cmd += ' %s' %(tunnelname)
1297 if attrs:
1298 for k, v in attrs.iteritems():
1299 cmd += ' %s' %k
1300 if v:
1301 cmd += ' %s' %v
1302 if self.ipbatch and not self.ipbatch_pause:
1303 self.add_to_batch(cmd)
1304 else:
1305 utils.exec_command('ip %s' % cmd)
33ebe60a 1306
d486dd0d
JF
1307 def link_create_vxlan(self, name, vxlanid,
1308 localtunnelip=None,
1309 svcnodeip=None,
1310 remoteips=None,
1311 learning='on',
1312 ageing=None,
1313 anycastip=None):
1314 if svcnodeip and remoteips:
1315 raise Exception("svcnodeip and remoteip is mutually exclusive")
1316 args = ''
1317 if svcnodeip:
1318 args += ' remote %s' % svcnodeip
1319 if ageing:
1320 args += ' ageing %s' % ageing
1321 if learning == 'off':
1322 args += ' nolearning'
1323
1324 if self.link_exists(name):
1325 cmd = 'link set dev %s type vxlan dstport %d' % (name, LinkUtils.VXLAN_UDP_PORT)
1326 vxlanattrs = self.get_vxlandev_attrs(name)
1327 # on ifreload do not overwrite anycast_ip to individual ip if clagd
1328 # has modified
1329 if vxlanattrs:
1330 running_localtunnelip = vxlanattrs.get('local')
1331 if anycastip and running_localtunnelip and anycastip == running_localtunnelip:
1332 localtunnelip = running_localtunnelip
1333 running_svcnode = vxlanattrs.get('svcnode')
1334 if running_svcnode and not svcnodeip:
1335 args += ' noremote'
1336 else:
1337 cmd = 'link add dev %s type vxlan id %s dstport %d' % (name, vxlanid, LinkUtils.VXLAN_UDP_PORT)
1338
1339 if localtunnelip:
1340 args += ' local %s' % localtunnelip
1341 cmd += args
1342
1343 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1344 self.add_to_batch(cmd)
1345 else:
1346 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1347
1348 # XXX: update linkinfo correctly
1349 #self._cache_update([name], {})
1350
1351 @staticmethod
1352 def link_exists(ifacename):
1353 if ifupdownflags.flags.DRYRUN:
1354 return True
1355 return os.path.exists('/sys/class/net/%s' % ifacename)
1356
9b653c85
JF
1357 @staticmethod
1358 def link_exists_nodryrun(ifname):
1359 return os.path.exists('/sys/class/net/%s' % ifname)
1360
d486dd0d
JF
1361 def link_get_ifindex(self, ifacename):
1362 if ifupdownflags.flags.DRYRUN:
1363 return True
1364 return self.read_file_oneline('/sys/class/net/%s/ifindex' % ifacename)
1365
1366 def is_vlan_device_by_name(self, ifacename):
1367 if re.search(r'\.', ifacename):
1368 return True
1369 return False
1370
1371 @staticmethod
1372 def link_add_macvlan(ifname, macvlan_ifacename):
1373 utils.exec_commandl(['ip', 'link', 'add', 'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', 'private'])
1374
1375 @staticmethod
1376 def route_add(route):
1377 utils.exec_command('%s route add %s' % (utils.ip_cmd,
1378 route))
1379
1380 @staticmethod
1381 def route6_add(route):
1382 utils.exec_command('%s -6 route add %s' % (utils.ip_cmd,
1383 route))
1384
1385 def get_vlandev_attrs(self, ifacename):
1386 return (self._cache_get('link', [ifacename, 'link']),
1387 self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']),
1388 self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol']))
1389
1390 def get_vlan_protocol(self, ifacename):
1391 return self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol'])
1392
1393 def get_vxlandev_attrs(self, ifacename):
1394 return self._cache_get('link', [ifacename, 'linkinfo'])
1395
1396 def get_vxlandev_learning(self, ifacename):
1397 return self._cache_get('link', [ifacename, 'linkinfo', Link.IFLA_VXLAN_LEARNING])
1398
1399 def set_vxlandev_learning(self, ifacename, learn):
1400 if learn == 'on':
1401 utils.exec_command('%s link set dev %s type vxlan learning' %
1402 (utils.ip_cmd, ifacename))
1403 self._cache_update([ifacename, 'linkinfo', 'learning'], 'on')
1404 else:
1405 utils.exec_command('%s link set dev %s type vxlan nolearning' %
1406 (utils.ip_cmd, ifacename))
1407 self._cache_update([ifacename, 'linkinfo', 'learning'], 'off')
1408
1409 def link_get_linkinfo_attrs(self, ifacename):
1410 return self._cache_get('link', [ifacename, 'linkinfo'])
1411
1412 def link_get_mtu(self, ifacename, refresh=False):
1413 return self._cache_get('link', [ifacename, 'mtu'], refresh=refresh)
1414
1415 def link_get_mtu_sysfs(self, ifacename):
1416 return self.read_file_oneline('/sys/class/net/%s/mtu'
1417 % ifacename)
1418
1419 def link_get_kind(self, ifacename):
1420 return self._cache_get('link', [ifacename, 'kind'])
1421
1422 def link_get_slave_kind(self, ifacename):
1423 return self._cache_get('link', [ifacename, 'slave_kind'])
1424
1425 def link_get_hwaddress(self, ifacename):
1426 address = self._cache_get('link', [ifacename, 'hwaddress'])
1427 # newly created logical interface addresses dont end up in the cache
1428 # read hwaddress from sysfs file for these interfaces
1429 if not address:
1430 address = self.read_file_oneline('/sys/class/net/%s/address'
1431 % ifacename)
1432 return address
1433
1434 def link_create(self, ifacename, t, attrs={}):
1435 """ generic link_create function """
1436 if self.link_exists(ifacename):
1437 return
1438 cmd = 'link add'
1439 cmd += ' name %s type %s' % (ifacename, t)
1440 if attrs:
1441 for k, v in attrs.iteritems():
1442 cmd += ' %s' % k
1443 if v:
1444 cmd += ' %s' % v
1445 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1446 self.add_to_batch(cmd)
1447 else:
1448 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1449 self._cache_update([ifacename], {})
1450
1451 def link_delete(self, ifacename):
1452 if not self.link_exists(ifacename):
1453 return
1454 cmd = 'link del %s' % ifacename
1455 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1456 self.add_to_batch(cmd)
1457 else:
1458 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1459 self._cache_invalidate()
1460
1461 def link_get_master(self, ifacename):
1462 sysfs_master_path = '/sys/class/net/%s/master' % ifacename
1463 if os.path.exists(sysfs_master_path):
1464 link_path = os.readlink(sysfs_master_path)
1465 if link_path:
1466 return os.path.basename(link_path)
1467 else:
1468 return None
1469 else:
1470 return self._cache_get('link', [ifacename, 'master'])
1471
1472 def get_brport_peer_link(self, bridgename):
1473 try:
1474 return self._cache_get('link', [bridgename, 'info_slave_data', Link.IFLA_BRPORT_PEER_LINK])
1475 except:
1476 return None
1477
1478 @staticmethod
1479 def bridge_port_vids_add(bridgeportname, vids):
1480 [utils.exec_command('%s vlan add vid %s dev %s' %
1481 (utils.bridge_cmd,
1482 v, bridgeportname)) for v in vids]
1483
1484 @staticmethod
1485 def bridge_port_vids_del(bridgeportname, vids):
1486 if not vids:
1487 return
1488 [utils.exec_command('%s vlan del vid %s dev %s' %
1489 (utils.bridge_cmd,
1490 v, bridgeportname)) for v in vids]
1491
1492 @staticmethod
1493 def bridge_port_vids_flush(bridgeportname, vid):
1494 utils.exec_command('%s vlan del vid %s dev %s' %
1495 (utils.bridge_cmd,
1496 vid, bridgeportname))
1497
1498 @staticmethod
1499 def bridge_port_vids_get(bridgeportname):
1500 bridgeout = utils.exec_command('%s vlan show dev %s' %
1501 (utils.bridge_cmd,
1502 bridgeportname))
1503 if not bridgeout:
1504 return []
1505 brvlanlines = bridgeout.readlines()[2:]
1506 vids = [l.strip() for l in brvlanlines]
1507 return [v for v in vids if v]
1508
1509 @staticmethod
1510 def bridge_port_vids_get_all():
1511 brvlaninfo = {}
1512 bridgeout = utils.exec_command('%s -c vlan show'
1513 % utils.bridge_cmd)
1514 if not bridgeout:
1515 return brvlaninfo
1516 brvlanlines = bridgeout.splitlines()
1517 brportname = None
1518 for l in brvlanlines[1:]:
1519 if l and not l.startswith(' ') and not l.startswith('\t'):
1520 attrs = l.split()
1521 brportname = attrs[0].strip()
1522 brvlaninfo[brportname] = {'pvid': None, 'vlan': []}
1523 l = ' '.join(attrs[1:])
1524 if not brportname or not l:
1525 continue
1526 l = l.strip()
1527 if 'PVID' in l:
1528 brvlaninfo[brportname]['pvid'] = l.split()[0]
1529 elif 'Egress Untagged' not in l:
1530 brvlaninfo[brportname]['vlan'].append(l)
1531 return brvlaninfo
1532
1533 def bridge_port_vids_get_all_json(self):
1534 if not self.supported_command['%s -c -json vlan show'
1535 % utils.bridge_cmd]:
1536 return {}
1537 brvlaninfo = {}
1538 try:
1539 bridgeout = utils.exec_command('%s -c -json vlan show'
1540 % utils.bridge_cmd)
1541 except:
1542 self.supported_command['%s -c -json vlan show'
1543 % utils.bridge_cmd] = False
1544 self.logger.info('%s -c -json vlan show: skipping unsupported command'
1545 % utils.bridge_cmd)
1546 try:
1547 return self.get_bridge_vlan_nojson()
1548 except Exception as e:
1549 self.logger.info('bridge: get_bridge_vlan_nojson: %s' % str(e))
1550 return {}
1551
1552 if not bridgeout: return brvlaninfo
1553 try:
52340202 1554 vlan_json = json.loads(bridgeout, encoding="utf-8")
d486dd0d
JF
1555 except Exception, e:
1556 self.logger.info('json loads failed with (%s)' % str(e))
1557 return {}
52340202
JF
1558
1559 try:
1560 if isinstance(vlan_json, list):
1561 # newer iproute2 version changed the bridge vlan show output
1562 # ifupdown2 relies on the previous format, we have the convert
1563 # data into old format
1564 bridge_port_vids = dict()
1565
1566 for intf in vlan_json:
1567 bridge_port_vids[intf["ifname"]] = intf["vlans"]
1568
1569 return bridge_port_vids
1570 else:
1571 # older iproute2 version have different ways to dump vlans
1572 # ifupdown2 prefers the following syntax:
1573 # {
1574 # "vx-1002": [{
1575 # "vlan": 1002,
1576 # "flags": ["PVID", "Egress Untagged"]
1577 # }
1578 # ],
1579 # "vx-1004": [{
1580 # "vlan": 1004,
1581 # "flags": ["PVID", "Egress Untagged"]
1582 # }]
1583 # }
1584 return vlan_json
1585 except Exception as e:
1586 self.logger.debug("bridge vlan show: Unknown json output: %s" % str(e))
1587 return vlan_json
d486dd0d
JF
1588
1589 @staticmethod
1590 def get_bridge_vlan_nojson():
1591 vlan_json = {}
1592 bridgeout = utils.exec_commandl([utils.bridge_cmd, '-c', 'vlan', 'show'])
1593 if bridgeout:
1594 output = [line.split('\n') for line in bridgeout.split('\n\n')]
1595 output[0] = output[0][1:]
1596 for line in output:
1597 current_swp = None
1598 if not line:
1599 continue
1600 for entry in line:
1601 if not entry:
1602 continue
1603 prefix, vlan = entry.split('\t')
1604 if prefix:
1605 current_swp = prefix
1606 vlan_json[prefix] = []
1607 v = {}
1608 vlan = vlan[1:]
1609 try:
1610 v['vlan'] = int(vlan)
1611 except:
1612 try:
1613 if '-' in vlan:
1614 start, end = vlan.split('-')
1615 if ' ' in end:
1616 end = end[0:end.index(' ')]
1617 v['vlan'] = int(start)
1618 v['vlanEnd'] = int(end)
1619 else:
1620 v['vlan'] = int(vlan[0:vlan.index(' ')])
1621 flags = []
1622 if 'PVID' in vlan:
1623 flags.append('PVID')
1624 if 'Egress Untagged' in vlan:
1625 flags.append('Egress Untagged')
1626 v['flags'] = flags
1627 except:
1628 continue
1629 vlan_json[current_swp].append(v)
1630 return vlan_json
1631
1632 def bridge_vlan_cache_get(self, ifacename, refresh=False):
1633 if not self.bridge_vlan_cache_fill_done or refresh:
1634 self.bridge_vlan_cache = self.bridge_port_vids_get_all_json()
1635 self.bridge_vlan_cache_fill_done = True
1636 return self.bridge_vlan_cache.get(ifacename, {})
1637
1638 def bridge_vlan_get_pvid(self, ifacename, refresh=False):
1639 pvid = 0
1640
1641 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
1642 v = vinfo.get('vlan')
1643 pvid = v if 'PVID' in vinfo.get('flags', []) else 0
1644 if pvid:
1645 return pvid
1646 return pvid
1647
1648 def bridge_vlan_get_vids(self, ifacename, refresh=False):
1649 vids = []
1650
1651 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
1652 v = vinfo.get('vlan')
1653 ispvid = True if 'PVID' in vinfo.get('flags', []) else False
1654 if ispvid:
1655 pvid = v if 'PVID' in vinfo.get('flags', []) else 0
1656 if pvid == 1:
1657 continue
1658 vEnd = vinfo.get('vlanEnd')
1659 if vEnd:
1660 vids.extend(range(v, vEnd + 1))
1661 else:
1662 vids.append(v)
1663 return vids
1664
1665 def bridge_vlan_get_vids_n_pvid(self, ifacename, refresh=False):
1666 vids = []
1667 pvid = 0
1668
1669 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
1670 v = vinfo.get('vlan')
1671 ispvid = True if 'PVID' in vinfo.get('flags', []) else False
1672 if ispvid:
1673 pvid = v if 'PVID' in vinfo.get('flags', []) else 0
1674 vEnd = vinfo.get('vlanEnd')
1675 if vEnd:
1676 vids.extend(range(v, vEnd + 1))
1677 else:
1678 vids.append(v)
1679 return vids, pvid
1680
1681 def bridge_port_pvid_add(self, bridgeportname, pvid):
1682 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1683 self.add_to_batch('vlan add vid %s untagged pvid dev %s' %
1684 (pvid, bridgeportname))
1685 else:
1686 utils.exec_command('%s vlan add vid %s untagged pvid dev %s' %
1687 (utils.bridge_cmd,
1688 pvid, bridgeportname))
1689
1690 def bridge_port_pvid_del(self, bridgeportname, pvid):
1691 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1692 self.add_to_batch('vlan del vid %s untagged pvid dev %s' %
1693 (pvid, bridgeportname))
1694 else:
1695 utils.exec_command('%s vlan del vid %s untagged pvid dev %s' %
1696 (utils.bridge_cmd,
1697 pvid, bridgeportname))
1698
1699 def bridge_port_pvids_get(self, bridgeportname):
1700 return self.read_file_oneline('/sys/class/net/%s/brport/pvid'
1701 % bridgeportname)
1702
1703 def bridge_vids_add(self, bridgeportname, vids, bridge=True):
1704 target = 'self' if bridge else ''
1705 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1706 [self.add_to_batch('vlan add vid %s dev %s %s' %
1707 (v, bridgeportname, target)) for v in vids]
1708 else:
1709 [utils.exec_command('%s vlan add vid %s dev %s %s' %
1710 (utils.bridge_cmd,
1711 v, bridgeportname, target)) for v in vids]
1712
1713 def bridge_vids_del(self, bridgeportname, vids, bridge=True):
1714 target = 'self' if bridge else ''
1715 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1716 [self.add_to_batch('vlan del vid %s dev %s %s' %
1717 (v, bridgeportname, target)) for v in vids]
1718 else:
1719 [utils.exec_command('%s vlan del vid %s dev %s %s' %
1720 (utils.bridge_cmd,
1721 v, bridgeportname, target)) for v in vids]
1722
1723 @staticmethod
1724 def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
1725 target = 'self' if bridge else ''
1726 vlan_str = ''
1727 if vlan:
1728 vlan_str = 'vlan %s ' % vlan
1729
1730 dst_str = ''
1731 if remote:
1732 dst_str = 'dst %s ' % remote
1733
1734 utils.exec_command('%s fdb replace %s dev %s %s %s %s' %
1735 (utils.bridge_cmd,
1736 address, dev, vlan_str, target, dst_str))
1737
1738 @staticmethod
1739 def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None):
1740 target = 'self' if bridge else ''
1741 vlan_str = ''
1742 if vlan:
1743 vlan_str = 'vlan %s ' % vlan
1744
1745 dst_str = ''
1746 if remote:
1747 dst_str = 'dst %s ' % remote
1748
1749 utils.exec_command('%s fdb append %s dev %s %s %s %s' %
1750 (utils.bridge_cmd,
1751 address, dev, vlan_str, target, dst_str))
1752
1753 @staticmethod
1754 def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None):
1755 target = 'self' if bridge else ''
1756 vlan_str = ''
1757 if vlan:
1758 vlan_str = 'vlan %s ' % vlan
1759
1760 dst_str = ''
1761 if remote:
1762 dst_str = 'dst %s ' % remote
1763 utils.exec_command('%s fdb del %s dev %s %s %s %s' %
1764 (utils.bridge_cmd,
1765 address, dev, vlan_str, target, dst_str))
1766
1767 def bridge_is_vlan_aware(self, bridgename):
1768 filename = '/sys/class/net/%s/bridge/vlan_filtering' % bridgename
1769 if os.path.exists(filename) and self.read_file_oneline(filename) == '1':
1770 return True
1771 return False
1772
1773 @staticmethod
1774 def bridge_port_get_bridge_name(bridgeport):
1775 filename = '/sys/class/net/%s/brport/bridge' % bridgeport
1776 try:
1777 return os.path.basename(os.readlink(filename))
1778 except:
1779 return None
1780
1781 @staticmethod
1782 def bridge_port_exists(bridge, bridgeportname):
1783 try:
1784 return os.path.exists('/sys/class/net/%s/brif/%s'
1785 % (bridge, bridgeportname))
1786 except Exception:
1787 return False
1788
1789 def bridge_fdb_show_dev(self, dev):
1790 try:
1791 fdbs = {}
1792 output = utils.exec_command('%s fdb show dev %s'
1793 % (utils.bridge_cmd, dev))
1794 if output:
1795 for fdb_entry in output.splitlines():
1796 try:
1797 entries = fdb_entry.split()
1798 fdbs.setdefault(entries[2], []).append(entries[0])
1799 except:
1800 self.logger.debug('%s: invalid fdb line \'%s\''
1801 % (dev, fdb_entry))
1802 return fdbs
1803 except Exception:
1804 return None
1805
1806 @staticmethod
1807 def is_bridge(bridge):
1808 return os.path.exists('/sys/class/net/%s/bridge' % bridge)
1809
1810 def is_link_up(self, ifacename):
1811 ret = False
1812 try:
1813 flags = self.read_file_oneline('/sys/class/net/%s/flags' % ifacename)
1814 iflags = int(flags, 16)
1815 if iflags & 0x0001:
1816 ret = True
1817 except:
1818 ret = False
1819 return ret
1820
aa895ecd 1821 def ip_route_get_dev(self, prefix, vrf_master=None):
d486dd0d 1822 try:
aa895ecd
JF
1823 if vrf_master:
1824 cmd = '%s route get %s vrf %s' % (utils.ip_cmd, prefix, vrf_master)
1825 else:
1826 cmd = '%s route get %s' % (utils.ip_cmd, prefix)
1827
1828 output = utils.exec_command(cmd)
d486dd0d
JF
1829 if output:
1830 rline = output.splitlines()[0]
1831 if rline:
1832 rattrs = rline.split()
1833 return rattrs[rattrs.index('dev') + 1]
1834 except Exception, e:
1835 self.logger.debug('ip_route_get_dev: failed .. %s' % str(e))
1836 return None
1837
1838 @staticmethod
1839 def link_get_lowers(ifacename):
1840 try:
1841 lowers = glob.glob("/sys/class/net/%s/lower_*" % ifacename)
1842 if not lowers:
1843 return []
1844 return [os.path.basename(l)[6:] for l in lowers]
1845 except:
1846 return []
1847
1848 @staticmethod
1849 def link_get_uppers(ifacename):
1850 try:
1851 uppers = glob.glob("/sys/class/net/%s/upper_*" % ifacename)
1852 if not uppers:
1853 return None
1854 return [os.path.basename(u)[6:] for u in uppers]
1855 except Exception:
1856 return None
1857
1858 def link_get_vrfs(self):
1859 if not LinkUtils._CACHE_FILL_DONE:
1860 self._fill_cache()
1861 return linkCache.vrfs
1862
1863 @staticmethod
1864 def cache_get_info_slave(attrlist):
1865 try:
1866 return linkCache.get_attr(attrlist)
1867 except:
1868 return None
1869
1870 def get_brport_learning(self, ifacename):
1871 learn = self.read_file_oneline('/sys/class/net/%s/brport/learning'
1872 % ifacename)
1873 if learn and learn == '1':
1874 return 'on'
1875 else:
1876 return 'off'
1877
1878 def get_brport_learning_bool(self, ifacename):
1879 return utils.get_boolean_from_string(self.read_file_oneline('/sys/class/net/%s/brport/learning' % ifacename))
1880
1881 def set_brport_learning(self, ifacename, learn):
1882 if learn == 'off':
1883 return self.write_file('/sys/class/net/%s/brport/learning'
1884 % ifacename, '0')
1885 else:
1886 return self.write_file('/sys/class/net/%s/brport/learning'
1887 % ifacename, '1')
1888
1889 #################################################################################
1890 ################################### BOND UTILS ##################################
1891 #################################################################################
1892
1893 def _link_cache_get(self, attrlist, refresh=False):
1894 return self._cache_get('link', attrlist, refresh)
1895
1896 def cache_delete(self, attrlist, value=None):
1897 return self._cache_delete(attrlist, value)
1898
1899 def link_cache_get(self, attrlist, refresh=False):
1900 return self._link_cache_get(attrlist, refresh)
1901
1902 def link_cache_check(self, attrlist, value, refresh=False):
1903 return self._link_cache_check(attrlist, value, refresh)
1904
1905 def _link_cache_check(self, attrlist, value, refresh=False):
1906 try:
1907 return self._link_cache_get(attrlist, refresh) == value
1908 except Exception, e:
1909 self.logger.debug('_cache_check(%s) : [%s]'
1910 % (str(attrlist), str(e)))
1911 pass
1912 return False
1913
1914 bondcmd_attrmap = {
1915 Link.IFLA_BOND_MODE: 'mode',
1916 Link.IFLA_BOND_MIIMON: 'miimon',
1917 Link.IFLA_BOND_USE_CARRIER: 'use_carrier',
1918 Link.IFLA_BOND_AD_LACP_RATE: 'lacp_rate',
1919 Link.IFLA_BOND_XMIT_HASH_POLICY: 'xmit_hash_policy',
1920 Link.IFLA_BOND_MIN_LINKS: 'min_links',
1921 Link.IFLA_BOND_NUM_PEER_NOTIF: 'num_grat_arp',
1922 Link.IFLA_BOND_AD_ACTOR_SYSTEM: 'ad_actor_system',
1923 Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: 'ad_actor_sys_prio',
1924 Link.IFLA_BOND_AD_LACP_BYPASS: 'lacp_bypass',
1925 Link.IFLA_BOND_UPDELAY: 'updelay',
1926 Link.IFLA_BOND_DOWNDELAY: 'downdelay',
1927 }
1928
1929 def bond_set_attrs_nl(self, bondname, ifla_info_data):
1930 bond_attr_name = 'None' # for log purpose (in case an exception raised)
1931 for nl_attr, value in ifla_info_data.items():
1932 try:
1933 bond_attr_name = self.bondcmd_attrmap[nl_attr]
1934 file_path = '/sys/class/net/%s/bonding/%s' % (bondname, bond_attr_name)
1935 if os.path.exists(file_path):
1936 self.write_file(file_path, str(value))
1937 except Exception as e:
1938 exception_str = '%s: %s %s: %s' % (bondname, bond_attr_name, value, str(e))
1939 if ifupdownflags.flags.FORCE:
1940 self.logger.warning(exception_str)
1941 else:
1942 self.logger.debug(exception_str)
1943
1944 def bond_set_attrs(self, bondname, attrdict, prehook):
1945 for attrname, attrval in attrdict.items():
1946 if (self._link_cache_check([bondname, 'linkinfo',
1947 attrname], attrval)):
1948 continue
1949 if (attrname == 'mode'
1950 or attrname == 'xmit_hash_policy'
1951 or attrname == 'lacp_rate' or attrname == 'min_links'):
1952 if prehook:
1953 prehook(bondname)
1954 try:
1955 if ((attrname not in ['lacp_rate',
1956 'lacp_bypass']) or
1957 self._link_cache_check([bondname, 'linkinfo', 'mode'], '802.3ad',
1958 True)):
1959 self.write_file('/sys/class/net/%s/bonding/%s'
1960 % (bondname, attrname), attrval)
1961 except Exception, e:
1962 if ifupdownflags.flags.FORCE:
1963 self.logger.warn(str(e))
1964 pass
1965 else:
1966 raise
1967
1968 def bond_set_use_carrier(self, bondname, use_carrier):
1969 if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
1970 return
1971 if (self._link_cache_check([bondname, 'linkinfo', 'use_carrier'],
1972 use_carrier)):
1973 return
1974 self.write_file('/sys/class/net/%s' % bondname +
1975 '/bonding/use_carrier', use_carrier)
1976 self._cache_update([bondname, 'linkinfo',
1977 'use_carrier'], use_carrier)
1978
1979 def bond_get_use_carrier(self, bondname):
1980 return self._link_cache_get([bondname, 'linkinfo', 'use_carrier'])
1981
1982 def bond_get_use_carrier_nl(self, bondname):
1983 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])
1984
1985 def bond_set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
1986 valid_values = ['layer2', 'layer3+4', 'layer2+3']
1987 if not hash_policy:
1988 return
1989 if hash_policy not in valid_values:
1990 raise Exception('invalid hash policy value %s' % hash_policy)
1991 if (self._link_cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
1992 hash_policy)):
1993 return
1994 if prehook:
1995 prehook(bondname)
1996 self.write_file('/sys/class/net/%s' % bondname +
1997 '/bonding/xmit_hash_policy', hash_policy)
1998 self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
1999 hash_policy)
2000
2001 def bond_get_xmit_hash_policy(self, bondname):
2002 return self._link_cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
2003
2004 def bond_get_xmit_hash_policy_nl(self, bondname):
2005 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])
2006
2007 def bond_set_miimon(self, bondname, miimon):
2008 if (self._link_cache_check([bondname, 'linkinfo', 'miimon'],
2009 miimon)):
2010 return
2011 self.write_file('/sys/class/net/%s' % bondname +
2012 '/bonding/miimon', miimon)
2013 self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
2014
2015 def bond_get_miimon(self, bondname):
2016 return self._link_cache_get([bondname, 'linkinfo', 'miimon'])
2017
2018 def bond_get_miimon_nl(self, bondname):
2019 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON])
2020
2021 def bond_set_mode(self, bondname, mode, prehook=None):
2022 valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
2023 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
2024 if not mode:
2025 return
2026 if mode not in valid_modes:
2027 raise Exception('invalid mode %s' % mode)
2028 if (self._link_cache_check([bondname, 'linkinfo', 'mode'],
2029 mode)):
2030 return
2031 if prehook:
2032 prehook(bondname)
2033 self.write_file('/sys/class/net/%s' % bondname + '/bonding/mode', mode)
2034 self._cache_update([bondname, 'linkinfo', 'mode'], mode)
2035
2036 def bond_get_mode(self, bondname):
2037 return self._link_cache_get([bondname, 'linkinfo', 'mode'])
2038
2039 def bond_get_mode_nl(self, bondname):
2040 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])
2041
2042 def bond_set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
2043 if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
2044 return
2045 if (self._link_cache_check([bondname, 'linkinfo', 'lacp_rate'],
2046 lacp_rate)):
2047 return
2048 if prehook:
2049 prehook(bondname)
2050 try:
2051 self.write_file('/sys/class/net/%s' % bondname +
2052 '/bonding/lacp_rate', lacp_rate)
2053 except:
2054 raise
2055 finally:
2056 if posthook:
2057 prehook(bondname)
2058 self._cache_update([bondname, 'linkinfo',
2059 'lacp_rate'], lacp_rate)
2060
2061 def bond_get_lacp_rate(self, bondname):
2062 return self._link_cache_get([bondname, 'linkinfo', 'lacp_rate'])
2063
2064 def bond_get_lacp_rate_nl(self, bondname):
2065 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])
2066
2067 def bond_set_lacp_bypass_allow(self, bondname, allow, prehook=None, posthook=None):
2068 if self._link_cache_check([bondname, 'linkinfo', 'lacp_bypass'], allow):
2069 return
2070 if prehook:
2071 prehook(bondname)
2072 try:
2073 self.write_file('/sys/class/net/%s' % bondname +
2074 '/bonding/lacp_bypass', allow)
2075 except:
2076 raise
2077 finally:
2078 if posthook:
2079 posthook(bondname)
2080 self._cache_update([bondname, 'linkinfo',
2081 'lacp_bypass'], allow)
2082
2083 def bond_get_lacp_bypass_allow(self, bondname):
2084 return self._link_cache_get([bondname, 'linkinfo', 'lacp_bypass'])
2085
2086 def bond_get_lacp_bypass_allow_nl(self, bondname):
2087 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])
2088
2089 def bond_set_min_links(self, bondname, min_links, prehook=None):
2090 if (self._link_cache_check([bondname, 'linkinfo', 'min_links'],
2091 min_links)):
2092 return
2093 if prehook:
2094 prehook(bondname)
2095 self.write_file('/sys/class/net/%s/bonding/min_links' % bondname,
2096 min_links)
2097 self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
2098
2099 def bond_get_min_links(self, bondname):
2100 return self._link_cache_get([bondname, 'linkinfo', 'min_links'])
2101
2102 def get_min_links_nl(self, bondname):
2103 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
2104
2105 def bond_get_ad_actor_system(self, bondname):
2106 return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_system'])
2107
2108 def bond_get_ad_actor_system_nl(self, bondname):
2109 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM])
2110
2111 def bond_get_ad_actor_sys_prio(self, bondname):
2112 return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_sys_prio'])
2113
2114 def bond_get_ad_actor_sys_prio_nl(self, bondname):
2115 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO])
2116
2117 def bond_get_num_unsol_na(self, bondname):
2118 return self._link_cache_get([bondname, 'linkinfo', 'num_unsol_na'])
2119
2120 def bond_get_num_unsol_na_nl(self, bondname):
2121 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
2122
2123 def bond_get_num_grat_arp(self, bondname):
2124 return self._link_cache_get([bondname, 'linkinfo', 'num_grat_arp'])
2125
2126 def bond_get_num_grat_arp_nl(self, bondname):
2127 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
2128
2129 def bond_get_updelay(self, bondname):
2130 return self._link_cache_get([bondname, 'linkinfo', 'updelay'])
2131
2132 def bond_get_updelay_nl(self, bondname):
2133 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY])
2134
2135 def bond_get_downdelay(self, bondname):
2136 return self._link_cache_get([bondname, 'linkinfo', 'downdelay'])
2137
2138 def bond_get_downdelay_nl(self, bondname):
2139 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
2140
2141 def bond_enslave_slave(self, bondname, slave, prehook=None, posthook=None):
2142 slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
2143 if slaves and slave in slaves:
2144 return
2145 if prehook:
2146 prehook(slave)
2147 self.write_file('/sys/class/net/%s' % bondname +
2148 '/bonding/slaves', '+' + slave)
2149 if posthook:
2150 posthook(slave)
2151 self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
2152
2153 def bond_remove_slave(self, bondname, slave):
2154 slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
2155 if not slaves or slave not in slaves:
2156 return
2157 sysfs_bond_path = ('/sys/class/net/%s' % bondname +
2158 '/bonding/slaves')
2159 if not os.path.exists(sysfs_bond_path):
2160 return
2161 self.write_file(sysfs_bond_path, '-' + slave)
2162 self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
2163
2164 def bond_remove_slaves_all(self, bondname):
2165 if not self._link_cache_get([bondname, 'linkinfo', 'slaves']):
2166 return
2167 slaves = None
2168 sysfs_bond_path = ('/sys/class/net/%s' % bondname +
2169 '/bonding/slaves')
2170 try:
2171 with open(sysfs_bond_path, 'r') as f:
2172 slaves = f.readline().strip().split()
2173 except IOError, e:
2174 raise Exception('error reading slaves of bond %s (%s)' % (bondname, str(e)))
2175 for slave in slaves:
2176 self.link_down(slave)
2177 try:
2178 self.bond_remove_slave(bondname, slave)
2179 except Exception, e:
2180 if not ifupdownflags.flags.FORCE:
2181 raise Exception('error removing slave %s from bond %s (%s)' % (slave, bondname, str(e)))
2182 else:
2183 pass
2184 self._cache_delete([bondname, 'linkinfo', 'slaves'])
2185
2186 @staticmethod
2187 def bond_load_bonding_module():
2188 return utils.exec_command('%s -q bonding' % utils.modprobe_cmd)
2189
2190 def create_bond(self, bondname):
2191 if self.bond_exists(bondname):
2192 return
2193 # load_bonding_module() has already been run
2194 self.write_file('/sys/class/net/bonding_masters', '+' + bondname)
2195 self._cache_update([bondname], {})
2196
2197 def delete_bond(self, bondname):
2198 if not os.path.exists('/sys/class/net/%s' % bondname):
2199 return
2200 self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
2201 self._cache_delete([bondname])
2202
2203 def bond_get_slaves(self, bondname):
2204 slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
2205 if slaves:
2206 return list(slaves)
2207 slavefile = '/sys/class/net/%s/bonding/slaves' % bondname
2208 if os.path.exists(slavefile):
2209 buf = self.read_file_oneline(slavefile)
2210 if buf:
2211 slaves = buf.split()
2212 if not slaves:
2213 return []
2214 self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
2215 return list(slaves)
2216
2217 def bond_slave_exists(self, bond, slave):
2218 slaves = self.bond_get_slaves(bond)
2219 if not slaves:
2220 return False
2221 return slave in slaves
2222
2223 @staticmethod
2224 def bond_exists(bondname):
2225 return os.path.exists('/sys/class/net/%s/bonding' % bondname)
2226
2227 #################################################################################
2228 ################################## BRIDGE UTILS #################################
2229 #################################################################################
2230
2231 def create_bridge(self, bridgename):
2232 if not LinkUtils.bridge_utils_is_installed:
2233 return
2234 if self.bridge_exists(bridgename):
2235 return
2236 utils.exec_command('%s addbr %s' % (utils.brctl_cmd, bridgename))
2237 self._cache_update([bridgename], {})
2238
2239 def delete_bridge(self, bridgename):
2240 if not LinkUtils.bridge_utils_is_installed:
2241 return
2242 if not self.bridge_exists(bridgename):
2243 return
2244 utils.exec_command('%s delbr %s' % (utils.brctl_cmd, bridgename))
2245 self._cache_invalidate()
2246
2247 def add_bridge_port(self, bridgename, bridgeportname):
2248 """ Add port to bridge """
2249 if not LinkUtils.bridge_utils_is_installed:
2250 return
2251 ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
2252 if ports and ports.get(bridgeportname):
2253 return
2254 utils.exec_command('%s addif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
2255 self._cache_update([bridgename, 'linkinfo', 'ports', bridgeportname], {})
2256
2257 def delete_bridge_port(self, bridgename, bridgeportname):
2258 """ Delete port from bridge """
2259 if not LinkUtils.bridge_utils_is_installed:
2260 return
2261 ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
2262 if not ports or not ports.get(bridgeportname):
2263 return
2264 utils.exec_command('%s delif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
2265 self._cache_delete([bridgename, 'linkinfo', 'ports', 'bridgeportname'])
2266
2267 def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict):
2268 portattrs = self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname])
2269 if portattrs == None:
2270 portattrs = {}
2271 for k, v in attrdict.iteritems():
2272 if ifupdownflags.flags.CACHE:
2273 curval = portattrs.get(k)
2274 if curval and curval == v:
2275 continue
2276 if k == 'unicast-flood':
2277 self.write_file('/sys/class/net/%s/brport/unicast_flood' % bridgeportname, v)
2278 elif k == 'multicast-flood':
2279 self.write_file('/sys/class/net/%s/brport/multicast_flood' % bridgeportname, v)
2280 elif k == 'learning':
2281 self.write_file('/sys/class/net/%s/brport/learning' % bridgeportname, v)
2282 elif k == 'arp-nd-suppress':
2283 self.write_file('/sys/class/net/%s/brport/neigh_suppress' % bridgeportname, v)
2284 else:
2285 if not LinkUtils.bridge_utils_is_installed:
2286 continue
2287 utils.exec_command('%s set%s %s %s %s' % (utils.brctl_cmd, k, bridgename, bridgeportname, v))
2288
2289 def set_bridgeport_attr(self, bridgename, bridgeportname,
2290 attrname, attrval):
2291 if not LinkUtils.bridge_utils_is_installed:
2292 return
2293 if self._link_cache_check([bridgename, 'linkinfo', 'ports', bridgeportname, attrname], attrval):
2294 return
2295 utils.exec_command('%s set%s %s %s %s' %
2296 (utils.brctl_cmd,
2297 attrname,
2298 bridgename,
2299 bridgeportname,
2300 attrval))
2301
2302 def set_bridge_attrs(self, bridgename, attrdict):
2303 for k, v in attrdict.iteritems():
2304 if not v:
2305 continue
2306 if self._link_cache_check([bridgename, 'linkinfo', k], v):
2307 continue
2308 try:
2309 if k == 'igmp-version':
2310 self.write_file('/sys/class/net/%s/bridge/'
2311 'multicast_igmp_version' % bridgename, v)
2312 elif k == 'mld-version':
2313 self.write_file('/sys/class/net/%s/bridge/'
2314 'multicast_mld_version' % bridgename, v)
2315 elif k == 'vlan-protocol':
2316 self.write_file('/sys/class/net/%s/bridge/'
2317 'vlan_protocol' % bridgename,
2318 VlanProtocols.ETHERTYPES_TO_ID.get(v.upper(),
2319 None))
2320 elif k == 'vlan-stats':
2321 self.write_file('/sys/class/net/%s/bridge/'
2322 'vlan_stats_enabled' % bridgename, v)
2323 elif k == 'mcstats':
2324 self.write_file('/sys/class/net/%s/bridge/'
2325 'multicast_stats_enabled' % bridgename, v)
2326 else:
2327 if not LinkUtils.bridge_utils_is_installed:
2328 continue
2329 cmd = ('%s set%s %s %s' %
2330 (utils.brctl_cmd, k, bridgename, v))
2331 utils.exec_command(cmd)
2332 except Exception, e:
2333 self.logger.warn('%s: %s' % (bridgename, str(e)))
2334 pass
2335
2336 def set_bridge_attr(self, bridgename, attrname, attrval):
2337 if self._link_cache_check([bridgename, 'linkinfo', attrname], attrval):
2338 return
2339 if attrname == 'igmp-version':
2340 self.write_file('/sys/class/net/%s/bridge/multicast_igmp_version'
2341 % bridgename, attrval)
2342 elif attrname == 'mld-version':
2343 self.write_file('/sys/class/net/%s/bridge/multicast_mld_version'
2344 % bridgename, attrval)
2345 elif attrname == 'vlan-protocol':
2346 self.write_file('/sys/class/net/%s/bridge/vlan_protocol'
2347 % bridgename, VlanProtocols.ETHERTYPES_TO_ID[attrval.upper()])
2348 elif attrname == 'vlan-stats':
2349 self.write_file('/sys/class/net/%s/bridge/vlan_stats_enabled'
2350 % bridgename, attrval)
2351 elif attrname == 'mcstats':
2352 self.write_file('/sys/class/net/%s/bridge/multicast_stats_enabled'
2353 % bridgename, attrval)
2354 else:
2355 if not LinkUtils.bridge_utils_is_installed:
2356 return
2357 cmd = '%s set%s %s %s' % (utils.brctl_cmd,
2358 attrname, bridgename, attrval)
2359 utils.exec_command(cmd)
2360
2361 def get_bridge_attrs(self, bridgename):
2362 attrs = self._link_cache_get([bridgename, 'linkinfo'])
2363 no_ints_attrs = {}
2364 for key, value in attrs.items():
2365 if type(key) == str:
2366 no_ints_attrs[key] = value
2367 return no_ints_attrs
2368
2369 def get_bridgeport_attrs(self, bridgename, bridgeportname):
2370 return self._link_cache_get([bridgename, 'linkinfo', 'ports',
2371 bridgeportname])
2372
2373 def get_bridgeport_attr(self, bridgename, bridgeportname, attrname):
2374 return self._link_cache_get([bridgename, 'linkinfo', 'ports',
2375 bridgeportname, attrname])
2376
2377 @staticmethod
2378 def bridge_set_stp(bridge, stp_state):
2379 if not LinkUtils.bridge_utils_is_installed:
2380 return
2381 utils.exec_command('%s stp %s %s' % (utils.brctl_cmd, bridge, stp_state))
2382
2383 def bridge_get_stp(self, bridge):
2384 sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' % bridge
2385 if not os.path.exists(sysfs_stpstate):
2386 return 'error'
2387 stpstate = self.read_file_oneline(sysfs_stpstate)
2388 if not stpstate:
2389 return 'error'
2390 try:
2391 if int(stpstate) > 0:
2392 return 'yes'
2393 elif int(stpstate) == 0:
2394 return 'no'
2395 except:
2396 return 'unknown'
2397
2398 @staticmethod
2399 def _conv_value_to_user(s):
2400 try:
2401 ret = int(s) / 100
2402 return '%d' % ret
2403 except:
2404 return None
2405
2406 def read_value_from_sysfs(self, filename, preprocess_func):
2407 value = self.read_file_oneline(filename)
2408 if not value:
2409 return None
2410 return preprocess_func(value)
2411
2412 @staticmethod
2413 def bridge_set_ageing(bridge, ageing):
2414 if not LinkUtils.bridge_utils_is_installed:
2415 return
2416 utils.exec_command('%s setageing %s %s' % (utils.brctl_cmd, bridge, ageing))
2417
2418 def bridge_get_ageing(self, bridge):
2419 return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time'
2420 % bridge, self._conv_value_to_user)
2421
2422 @staticmethod
2423 def set_bridgeprio(bridge, prio):
2424 if not LinkUtils.bridge_utils_is_installed:
2425 return
2426 utils.exec_command('%s setbridgeprio %s %s' % (utils.brctl_cmd, bridge, prio))
2427
2428 def get_bridgeprio(self, bridge):
2429 return self.read_file_oneline(
2430 '/sys/class/net/%s/bridge/priority' % bridge)
2431
2432 @staticmethod
2433 def bridge_set_fd(bridge, fd):
2434 if not LinkUtils.bridge_utils_is_installed:
2435 return
2436 utils.exec_command('%s setfd %s %s' % (utils.brctl_cmd, bridge, fd))
2437
2438 def bridge_get_fd(self, bridge):
2439 return self.read_value_from_sysfs(
2440 '/sys/class/net/%s/bridge/forward_delay'
2441 % bridge, self._conv_value_to_user)
2442
2443 def bridge_set_gcint(self, bridge, gcint):
2444 raise Exception('set_gcint not implemented')
2445
2446 @staticmethod
2447 def bridge_set_hello(bridge, hello):
2448 if not LinkUtils.bridge_utils_is_installed:
2449 return
2450 utils.exec_command('%s sethello %s %s' % (utils.brctl_cmd, bridge, hello))
2451
2452 def bridge_get_hello(self, bridge):
2453 return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time'
2454 % bridge, self._conv_value_to_user)
2455
2456 @staticmethod
2457 def bridge_set_maxage(bridge, maxage):
2458 if not LinkUtils.bridge_utils_is_installed:
2459 return
2460 utils.exec_command('%s setmaxage %s %s' % (utils.brctl_cmd, bridge, maxage))
2461
2462 def bridge_get_maxage(self, bridge):
2463 return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age'
2464 % bridge, self._conv_value_to_user)
2465
2466 @staticmethod
2467 def bridge_set_pathcost(bridge, port, pathcost):
2468 if not LinkUtils.bridge_utils_is_installed:
2469 return
2470 utils.exec_command('%s setpathcost %s %s %s' % (utils.brctl_cmd, bridge, port, pathcost))
2471
2472 def bridge_get_pathcost(self, bridge, port):
2473 return self.read_file_oneline('/sys/class/net/%s/brport/path_cost'
2474 % port)
2475
2476 @staticmethod
2477 def bridge_set_portprio(bridge, port, prio):
2478 if not LinkUtils.bridge_utils_is_installed:
2479 return
2480 utils.exec_command('%s setportprio %s %s %s' % (utils.brctl_cmd, bridge, port, prio))
2481
2482 def bridge_get_portprio(self, bridge, port):
2483 return self.read_file_oneline('/sys/class/net/%s/brport/priority'
2484 % port)
2485
2486 @staticmethod
2487 def bridge_set_hashmax(bridge, hashmax):
2488 if not LinkUtils.bridge_utils_is_installed:
2489 return
2490 utils.exec_command('%s sethashmax %s %s' % (utils.brctl_cmd, bridge, hashmax))
2491
2492 def bridge_get_hashmax(self, bridge):
2493 return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max'
2494 % bridge)
2495
2496 @staticmethod
2497 def bridge_set_hashel(bridge, hashel):
2498 if not LinkUtils.bridge_utils_is_installed:
2499 return
2500 utils.exec_command('%s sethashel %s %s' % (utils.brctl_cmd, bridge, hashel))
2501
2502 def bridge_get_hashel(self, bridge):
2503 return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity'
2504 % bridge)
2505
2506 @staticmethod
2507 def bridge_set_mclmc(bridge, mclmc):
2508 if not LinkUtils.bridge_utils_is_installed:
2509 return
2510 utils.exec_command('%s setmclmc %s %s' % (utils.brctl_cmd, bridge, mclmc))
2511
2512 def bridge_get_mclmc(self, bridge):
2513 return self.read_file_oneline(
2514 '/sys/class/net/%s/bridge/multicast_last_member_count'
2515 % bridge)
2516
2517 @staticmethod
2518 def bridge_set_mcrouter(bridge, mcrouter):
2519 if not LinkUtils.bridge_utils_is_installed:
2520 return
2521 utils.exec_command('%s setmcrouter %s %s' % (utils.brctl_cmd, bridge, mcrouter))
2522
2523 def bridge_get_mcrouter(self, bridge):
2524 return self.read_file_oneline(
2525 '/sys/class/net/%s/bridge/multicast_router' % bridge)
2526
2527 @staticmethod
2528 def bridge_set_mcsnoop(bridge, mcsnoop):
2529 if not LinkUtils.bridge_utils_is_installed:
2530 return
2531 utils.exec_command('%s setmcsnoop %s %s' % (utils.brctl_cmd, bridge, mcsnoop))
2532
2533 def bridge_get_mcsnoop(self, bridge):
2534 return self.read_file_oneline(
2535 '/sys/class/net/%s/bridge/multicast_snooping' % bridge)
2536
2537 @staticmethod
2538 def bridge_set_mcsqc(bridge, mcsqc):
2539 if not LinkUtils.bridge_utils_is_installed:
2540 return
2541 utils.exec_command('%s setmcsqc %s %s' % (utils.brctl_cmd, bridge, mcsqc))
2542
2543 def bridge_get_mcsqc(self, bridge):
2544 return self.read_file_oneline(
2545 '/sys/class/net/%s/bridge/multicast_startup_query_count'
2546 % bridge)
2547
2548 @staticmethod
2549 def bridge_set_mcqifaddr(bridge, mcqifaddr):
2550 if not LinkUtils.bridge_utils_is_installed:
2551 return
2552 utils.exec_command('%s setmcqifaddr %s %s' % (utils.brctl_cmd, bridge, mcqifaddr))
2553
2554 def bridge_get_mcqifaddr(self, bridge):
2555 return self.read_file_oneline(
2556 '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr'
2557 % bridge)
2558
2559 @staticmethod
2560 def bridge_set_mcquerier(bridge, mcquerier):
2561 if not LinkUtils.bridge_utils_is_installed:
2562 return
2563 utils.exec_command('%s setmcquerier %s %s' % (utils.brctl_cmd, bridge, mcquerier))
2564
2565 def bridge_get_mcquerier(self, bridge):
2566 return self.read_file_oneline(
2567 '/sys/class/net/%s/bridge/multicast_querier' % bridge)
2568
2569 def bridge_set_mcqv4src(self, bridge, vlan, mcquerier):
2570 try:
2571 vlan = int(vlan)
2572 except:
2573 self.logger.info('%s: set mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
2574 return
2575 if vlan == 0 or vlan > 4095:
2576 self.logger.warn('mcqv4src vlan \'%d\' invalid range' % vlan)
2577 return
2578
2579 ip = mcquerier.split('.')
2580 if len(ip) != 4:
2581 self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
2582 return
2583 for k in ip:
2584 if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
2585 self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
2586 return
2587
2588 if not LinkUtils.bridge_utils_is_installed:
2589 return
2590
2591 utils.exec_command('%s setmcqv4src %s %d %s' %
2592 (utils.brctl_cmd, bridge, vlan, mcquerier))
2593
2594 def bridge_del_mcqv4src(self, bridge, vlan):
2595 if not LinkUtils.bridge_utils_is_installed:
2596 return
2597 try:
2598 vlan = int(vlan)
2599 except:
2600 self.logger.info('%s: del mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
2601 return
2602 utils.exec_command('%s delmcqv4src %s %d' % (utils.brctl_cmd, bridge, vlan))
2603
2604 def bridge_get_mcqv4src(self, bridge, vlan=None):
2605 if not LinkUtils.bridge_utils_is_installed:
2606 return {}
2607 if not self.supported_command['showmcqv4src']:
2608 return {}
2609 mcqv4src = {}
2610 try:
2611 mcqout = utils.exec_command('%s showmcqv4src %s' %
2612 (utils.brctl_cmd, bridge))
2613 except Exception as e:
2614 s = str(e).lower()
2615 if 'never heard' in s:
2616 msg = ('%s showmcqv4src: skipping unsupported command'
2617 % utils.brctl_cmd)
2618 self.logger.info(msg)
2619 self.supported_command['showmcqv4src'] = False
2620 return {}
2621 raise
2622 if not mcqout:
2623 return {}
2624 mcqlines = mcqout.splitlines()
2625 for l in mcqlines[1:]:
2626 l = l.strip()
2627 k, d, v = l.split('\t')
2628 if not k or not v:
2629 continue
2630 mcqv4src[k] = v
2631 if vlan:
2632 return mcqv4src.get(vlan)
2633 return mcqv4src
2634
d00f5278
JF
2635 def bridge_get_mcqv4src_sysfs(self, bridge, vlan=None):
2636 if not LinkUtils.bridge_utils_is_installed:
2637 return {}
2638 if not self.supported_command['showmcqv4src']:
2639 return {}
2640 if ifupdownflags.flags.PERFMODE:
2641 return {}
2642 mcqv4src = {}
2643 try:
2644 filename = '/sys/class/net/%s/bridge/multicast_v4_queriers' % bridge
2645 if os.path.exists(filename):
2646 for line in self.read_file(filename) or []:
2647 vlan_id, ip = line.split('=')
2648 mcqv4src[vlan_id] = ip.strip()
2649 except Exception as e:
2650 s = str(e).lower()
2651 msg = ('%s showmcqv4src: skipping unsupported command'
2652 % utils.brctl_cmd)
2653 self.logger.info(msg)
2654 self.supported_command['showmcqv4src'] = False
2655 return {}
2656 if vlan:
2657 return mcqv4src.get(vlan)
2658 return mcqv4src
2659
d486dd0d
JF
2660 @staticmethod
2661 def bridge_set_mclmi(bridge, mclmi):
2662 if not LinkUtils.bridge_utils_is_installed:
2663 return
2664 utils.exec_command('%s setmclmi %s %s' % (utils.brctl_cmd, bridge, mclmi))
2665
2666 def bridge_get_mclmi(self, bridge):
2667 return self.read_file_oneline(
2668 '/sys/class/net/%s/bridge/multicast_last_member_interval'
2669 % bridge)
2670
2671 @staticmethod
2672 def bridge_set_mcmi(bridge, mcmi):
2673 if not LinkUtils.bridge_utils_is_installed:
2674 return
2675 utils.exec_command('%s setmcmi %s %s' % (utils.brctl_cmd, bridge, mcmi))
2676
2677 def bridge_get_mcmi(self, bridge):
2678 return self.read_file_oneline(
2679 '/sys/class/net/%s/bridge/multicast_membership_interval'
2680 % bridge)
2681
2682 @staticmethod
2683 def bridge_exists(bridge):
2684 return os.path.exists('/sys/class/net/%s/bridge' % bridge)
2685
2686 @staticmethod
2687 def is_bridge_port(ifacename):
2688 return os.path.exists('/sys/class/net/%s/brport' % ifacename)
2689
2690 @staticmethod
2691 def bridge_port_exists(bridge, bridgeportname):
2692 try:
2693 return os.path.exists('/sys/class/net/%s/brif/%s' % (bridge, bridgeportname))
2694 except:
2695 return False
2696
2697 @staticmethod
2698 def get_bridge_ports(bridgename):
2699 try:
2700 return os.listdir('/sys/class/net/%s/brif/' % bridgename)
2701 except:
2702 return []
3fc54eef 2703
c4cc1f86
JF
2704 def reset_addr_cache(self, ifname):
2705 try:
2706 linkCache.links[ifname]['addrs'] = {}
2707 self.logger.debug('%s: reset address cache' % ifname)
2708 except:
2709 pass
2710
007cae35
JF
2711 def get_ipv6_addrgen_mode(self, ifname):
2712 try:
2713 return self._cache_get('link', [ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE]
2714 except:
2715 # default to 0 (eui64)
2716 return 0
2717
2718 def ipv6_addrgen(self, ifname, addrgen, link_created):
2719 try:
2720 # IFLA_INET6_ADDR_GEN_MODE values:
2721 # 0 = eui64
2722 # 1 = none
2723 if self._link_cache_get([ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] == addrgen:
2724 self.logger.debug('%s: ipv6 addrgen already %s' % (ifname, 'off' if addrgen else 'on'))
2725 return
8329d2b8 2726
8329d2b8
JF
2727 disabled_ipv6 = self.read_file_oneline('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % ifname)
2728 if not disabled_ipv6 or int(disabled_ipv6) == 1:
2729 self.logger.info('%s: cannot set addrgen: ipv6 is disabled on this device' % ifname)
2730 return
2731
d4019ee9
JF
2732 if int(self._link_cache_get([ifname, 'mtu'])) < 1280:
2733 self.logger.info('%s: ipv6 addrgen is disabled on device with MTU '
2734 'lower than 1280: cannot set addrgen %s' % (ifname, 'off' if addrgen else 'on'))
2735 return
8329d2b8
JF
2736 except (KeyError, TypeError):
2737 self.logger.debug('%s: ipv6 addrgen probably not supported or disabled on this device' % ifname)
2738 return
2739 except Exception:
007cae35
JF
2740 pass
2741
2742 if not link_created:
2743 # When setting addrgenmode it is necessary to flap the macvlan
2744 # device. After flapping the device we also need to re-add all
2745 # the user configuration. The best way to add the user config
2746 # is to flush our internal address cache
2747 self.reset_addr_cache(ifname)
2748
2749 cmd = 'link set dev %s addrgenmode %s' % (ifname, 'none' if addrgen else 'eui64')
3fc54eef
JF
2750
2751 is_link_up = self.is_link_up(ifname)
2752
2753 if is_link_up:
2754 self.link_down(ifname)
2755
2756 if LinkUtils.ipbatch:
2757 self.add_to_batch(cmd)
2758 else:
2759 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
2760
2761 if is_link_up:
2762 self.link_up(ifname)