]> git.proxmox.com Git - mirror_ifupdown2.git/blame - ifupdown2/ifupdownaddons/LinkUtils.py
addons: addressvirtual: create VRRP macvlans in bridge mode
[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:
5bc963f0
JF
937 for attr_name in ["address-virtual", "vrrp"]:
938 for virtual in addr_virtual_ifaceobj.get_attr_value(attr_name) or []:
d486dd0d
JF
939 for ip in virtual.split():
940 try:
941 IPNetwork(ip)
942 config_addrs.add(ip)
943 except:
944 pass
5bc963f0
JF
945
946 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(addr_virtual_ifaceobj.name)
947 for saved_ifaceobj in saved_ifaceobjs or []:
948 for virtual in saved_ifaceobj.get_attr_value(attr_name) or []:
949 for ip in virtual.split():
950 try:
951 IPNetwork(ip)
952 config_addrs.add(ip)
953 except:
954 pass
d486dd0d
JF
955 else:
956 if ifaceobj:
957 for addr in ifaceobj.get_attr_value('address') or []:
958 config_addrs.add(addr)
959
960 saved_ifaceobjs = statemanager.statemanager_api.get_ifaceobjs(interface_name)
961 for saved_ifaceobj in saved_ifaceobjs or []:
962 for addr in saved_ifaceobj.get_attr_value('address') or []:
963 config_addrs.add(addr)
964
965 running_addrs = OrderedDict()
966 cached_addrs = self.addr_get(interface_name)
967 if cached_addrs:
968 for addr, addr_details in cached_addrs.items():
969 try:
970 scope = int(addr_details['scope'])
971 except Exception:
972 try:
973 d = {}
974 addr_obj = IPNetwork(addr)
975 if isinstance(addr_obj, IPv6Network):
976 d['family'] = 'inet6'
977 else:
978 d['family'] = 'inet'
979 running_addrs[addr] = d
980 except:
981 running_addrs[addr] = {}
982 continue
983 if (scope & Route.RT_SCOPE_LINK and addr in config_addrs) or not scope & Route.RT_SCOPE_LINK:
984 running_addrs[addr] = addr_details
985 else:
986 return None
987
988 if details:
989 return running_addrs
990 return running_addrs.keys()
991
992 @staticmethod
993 def compare_user_config_vs_running_state(running_addrs, user_addrs):
994 ip4 = []
995 ip6 = []
996
997 for ip in user_addrs or []:
998 obj = IPNetwork(ip)
999
1000 if type(obj) == IPv6Network:
1001 ip6.append(obj)
1002 else:
1003 ip4.append(obj)
1004
1005 running_ipobj = []
1006 for ip in running_addrs or []:
1007 running_ipobj.append(IPNetwork(ip))
1008
1009 return running_ipobj == (ip4 + ip6)
1010
dfaa8a2d 1011 def addr_add_multiple(self, ifaceobj, ifacename, addrs, purge_existing=False, metric=None):
d486dd0d
JF
1012 # purges address
1013 if purge_existing:
1014 # if perfmode is not set and also if iface has no sibling
1015 # objects, purge addresses that are not present in the new
1016 # config
1017 runningaddrs = self.get_running_addrs(
1018 ifname=ifacename,
1019 details=False,
1020 addr_virtual_ifaceobj=ifaceobj
1021 )
1022 addrs = utils.get_normalized_ip_addr(ifacename, addrs)
1023
1024 if self.compare_user_config_vs_running_state(runningaddrs, addrs):
1025 return
1026 try:
1027 # if primary address is not same, there is no need to keep any.
1028 # reset all addresses
1029 if (addrs and runningaddrs and
1030 (addrs[0] != runningaddrs[0])):
1031 self.del_addr_all(ifacename)
1032 else:
1033 self.del_addr_all(ifacename, addrs)
1034 except Exception, e:
1035 self.logger.warning('%s: %s' % (ifacename, str(e)))
1036 for a in addrs:
1037 try:
dfaa8a2d 1038 self.addr_add(ifacename, a, metric=metric)
d486dd0d
JF
1039 except Exception, e:
1040 self.logger.error(str(e))
1041
1042 def _link_set_ifflag(self, ifacename, value):
1043 # Dont look at the cache, the cache may have stale value
1044 # because link status can be changed by external
1045 # entity (One such entity is ifupdown main program)
1046 cmd = 'link set dev %s %s' % (ifacename, value.lower())
1047 if LinkUtils.ipbatch:
1048 self.add_to_batch(cmd)
1049 else:
1050 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1051
1052 def link_up(self, ifacename):
1053 self._link_set_ifflag(ifacename, 'UP')
1054
1055 def link_down(self, ifacename):
1056 self._link_set_ifflag(ifacename, 'DOWN')
1057
1058 def link_set(self, ifacename, key, value=None,
1059 force=False, t=None, state=None):
1060 if not force:
1061 if (key not in ['master', 'nomaster'] and
1062 self._cache_check('link', [ifacename, key], value)):
1063 return
1064 cmd = 'link set dev %s' % ifacename
1065 if t:
1066 cmd += ' type %s' % t
1067 cmd += ' %s' % key
1068 if value:
1069 cmd += ' %s' % value
1070 if state:
1071 cmd += ' %s' % state
1072 if LinkUtils.ipbatch:
1073 self.add_to_batch(cmd)
1074 else:
1075 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1076 if key not in ['master', 'nomaster']:
1077 self._cache_update([ifacename, key], value)
1078
1079 def link_set_hwaddress(self, ifacename, hwaddress, force=False):
1080 if not force:
1081 if self._cache_check('link', [ifacename, 'hwaddress'], hwaddress):
1082 return
1083 self.link_down(ifacename)
1084 cmd = 'link set dev %s address %s' % (ifacename, hwaddress)
1085 if LinkUtils.ipbatch:
1086 self.add_to_batch(cmd)
1087 else:
1088 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1089 self.link_up(ifacename)
1090 self._cache_update([ifacename, 'hwaddress'], hwaddress)
1091
1092 def link_set_mtu(self, ifacename, mtu):
1093 if ifupdownflags.flags.DRYRUN:
1094 return True
1095 if not mtu or not ifacename: return
1096 self.write_file('/sys/class/net/%s/mtu' % ifacename, mtu)
1097 self._cache_update([ifacename, 'mtu'], mtu)
1098
1099 def link_set_alias(self, ifacename, alias):
1100 self.write_file('/sys/class/net/%s/ifalias' % ifacename,
1101 '\n' if not alias else alias)
1102
1103 def link_get_alias(self, ifacename):
1104 return self.read_file_oneline('/sys/class/net/%s/ifalias'
1105 % ifacename)
1106
1107 def link_isloopback(self, ifacename):
1108 flags = self._cache_get('link', [ifacename, 'flags'])
1109 if not flags:
1110 return
1111 if 'LOOPBACK' in flags:
1112 return True
1113 return False
1114
1115 def link_get_status(self, ifacename):
1116 return self._cache_get('link', [ifacename, 'ifflag'], refresh=True)
1117
1118 @staticmethod
9d505185 1119 def route_add_gateway(ifacename, gateway, vrf=None, metric=None, onlink=True):
d486dd0d
JF
1120 if not gateway:
1121 return
1122 if not vrf:
1123 cmd = '%s route add default via %s proto kernel' % (utils.ip_cmd,
1124 gateway)
1125 else:
1126 cmd = ('%s route add table %s default via %s proto kernel' %
1127 (utils.ip_cmd, vrf, gateway))
1128 # Add metric
1129 if metric:
1130 cmd += 'metric %s' % metric
1131 cmd += ' dev %s' % ifacename
9d505185
JF
1132
1133 if onlink:
1134 cmd += " onlink"
1135
d486dd0d
JF
1136 utils.exec_command(cmd)
1137
1138 @staticmethod
1139 def route_del_gateway(ifacename, gateway, vrf=None, metric=None):
1140 # delete default gw
1141 if not gateway:
1142 return
1143 if not vrf:
1144 cmd = ('%s route del default via %s proto kernel' %
1145 (utils.ip_cmd, gateway))
1146 else:
1147 cmd = ('%s route del table %s default via %s proto kernel' %
1148 (utils.ip_cmd, vrf, gateway))
1149 if metric:
1150 cmd += ' metric %s' % metric
1151 cmd += ' dev %s' % ifacename
1152 utils.exec_command(cmd)
1153
1154 @staticmethod
1155 def _get_vrf_id(ifacename):
1156 try:
1157 return linkCache.vrfs[ifacename]['table']
1158 except KeyError:
1159 dump = netlink.link_dump(ifacename)
1160
1161 [linkCache.update_attrdict([ifname], linkattrs)
1162 for ifname, linkattrs in dump.items()]
1163
1164 if dump and dump.get(ifacename, {}).get('kind') == 'vrf':
1165 vrf_table = dump.get(ifacename, {}).get('linkinfo', {}).get('table')
1166 linkCache.vrfs[ifacename] = {'table': vrf_table}
1167 return vrf_table
1168
1169 return None
1170
1171 def fix_ipv6_route_metric(self, ifaceobj, macvlan_ifacename, ips):
1172 vrf_table = None
1173
1174 if ifaceobj.link_privflags & ifaceLinkPrivFlags.VRF_SLAVE:
1175 try:
1176 for upper_iface in ifaceobj.upperifaces:
1177 vrf_table = self._get_vrf_id(upper_iface)
1178 if vrf_table:
1179 break
1180 except:
1181 pass
1182
1183 ip_route_del = []
1184 for ip in ips:
1185 ip_network_obj = IPNetwork(ip)
1186
1187 if type(ip_network_obj) == IPv6Network:
1188 route_prefix = '%s/%d' % (ip_network_obj.network, ip_network_obj.prefixlen)
1189
1190 if vrf_table:
1191 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1192 LinkUtils.add_to_batch('route del %s table %s dev %s' % (route_prefix, vrf_table, macvlan_ifacename))
1193 else:
1194 utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'table', vrf_table, 'dev', macvlan_ifacename])
1195 else:
1196 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1197 LinkUtils.add_to_batch('route del %s dev %s' % (route_prefix, macvlan_ifacename))
1198 else:
1199 utils.exec_commandl([utils.ip_cmd, 'route', 'del', route_prefix, 'dev', macvlan_ifacename])
1200 ip_route_del.append((route_prefix, vrf_table))
1201
1202 for ip, vrf_table in ip_route_del:
1203 if vrf_table:
1204 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1205 LinkUtils.add_to_batch('route add %s table %s dev %s proto kernel metric 9999' % (ip, vrf_table, macvlan_ifacename))
1206 else:
1207 utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'table', vrf_table, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
1208 else:
1209 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1210 LinkUtils.add_to_batch('route add %s dev %s proto kernel metric 9999' % (ip, macvlan_ifacename))
1211 else:
1212 utils.exec_commandl([utils.ip_cmd, 'route', 'add', ip, 'dev', macvlan_ifacename, 'proto', 'kernel' 'metric', '9999'])
1213
1214 def link_create_vlan(self, vlan_device_name, vlan_raw_device, vlanid):
1215 if self.link_exists(vlan_device_name):
1216 return
1217 utils.exec_command('%s link add link %s name %s type vlan id %d' %
1218 (utils.ip_cmd,
1219 vlan_raw_device, vlan_device_name, vlanid))
1220 self._cache_update([vlan_device_name], {})
1221
1222 def link_create_vlan_from_name(self, vlan_device_name):
1223 v = vlan_device_name.split('.')
1224 if len(v) != 2:
1225 self.logger.warn('invalid vlan device name %s' % vlan_device_name)
1226 return
1227 self.link_create_vlan(vlan_device_name, v[0], v[1])
1228
1229 def link_create_macvlan(self, name, linkdev, mode='private'):
1230 if self.link_exists(name):
1231 return
1232 cmd = ('link add link %s' % linkdev +
1233 ' name %s' % name +
1234 ' type macvlan mode %s' % mode)
1235 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1236 self.add_to_batch(cmd)
1237 else:
1238 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1239 self._cache_update([name], {})
1240
1241 def get_vxlan_peers(self, dev, svcnodeip):
1242 cmd = '%s fdb show brport %s' % (utils.bridge_cmd,
1243 dev)
1244 cur_peers = []
1245 try:
1246 ps = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, close_fds=False)
1247 utils.enable_subprocess_signal_forwarding(ps, signal.SIGINT)
1248 output = subprocess.check_output(('grep', '00:00:00:00:00:00'), stdin=ps.stdout)
1249 ps.wait()
1250 utils.disable_subprocess_signal_forwarding(signal.SIGINT)
1251 try:
1252 ppat = re.compile('\s+dst\s+(\d+.\d+.\d+.\d+)\s+')
1253 for l in output.split('\n'):
1254 m = ppat.search(l)
1255 if m and m.group(1) != svcnodeip:
1256 cur_peers.append(m.group(1))
1257 except:
1258 self.logger.warn('error parsing ip link output')
1259 except subprocess.CalledProcessError as e:
1260 if e.returncode != 1:
1261 self.logger.error(str(e))
1262 finally:
1263 utils.disable_subprocess_signal_forwarding(signal.SIGINT)
1264
1265 return cur_peers
1266
33ebe60a
SA
1267 def tunnel_create(self, tunnelname, mode, attrs={}):
1268 """ generic link_create function """
1269 if self.link_exists(tunnelname):
1270 return
f40c6294
SA
1271
1272 cmd = ''
1273 if '6' in mode:
1274 cmd = ' -6 '
1275
6039c4d7
JF
1276 if mode in ['gretap']:
1277 cmd += 'link add %s type %s' % (tunnelname, mode)
535b0c8d 1278 else:
6039c4d7 1279 cmd += 'tunnel add %s mode %s' % (tunnelname, mode)
535b0c8d 1280
33ebe60a
SA
1281 if attrs:
1282 for k, v in attrs.iteritems():
6039c4d7 1283 cmd += ' %s' % k
33ebe60a 1284 if v:
6039c4d7 1285 cmd += ' %s' % v
33ebe60a
SA
1286 if self.ipbatch and not self.ipbatch_pause:
1287 self.add_to_batch(cmd)
1288 else:
1289 utils.exec_command('ip %s' % cmd)
1290 self._cache_update([tunnelname], {})
1291
1292 def tunnel_change(self, tunnelname, attrs={}):
1293 """ tunnel change function """
1294 if not self.link_exists(tunnelname):
1295 return
1296 cmd = 'tunnel change'
1297 cmd += ' %s' %(tunnelname)
1298 if attrs:
1299 for k, v in attrs.iteritems():
1300 cmd += ' %s' %k
1301 if v:
1302 cmd += ' %s' %v
1303 if self.ipbatch and not self.ipbatch_pause:
1304 self.add_to_batch(cmd)
1305 else:
1306 utils.exec_command('ip %s' % cmd)
33ebe60a 1307
d486dd0d
JF
1308 def link_create_vxlan(self, name, vxlanid,
1309 localtunnelip=None,
1310 svcnodeip=None,
1311 remoteips=None,
1312 learning='on',
1313 ageing=None,
ec25a08c
JF
1314 anycastip=None,
1315 ttl=None):
d486dd0d
JF
1316 if svcnodeip and remoteips:
1317 raise Exception("svcnodeip and remoteip is mutually exclusive")
1318 args = ''
1319 if svcnodeip:
1320 args += ' remote %s' % svcnodeip
1321 if ageing:
1322 args += ' ageing %s' % ageing
1323 if learning == 'off':
1324 args += ' nolearning'
ec25a08c
JF
1325 if ttl is not None:
1326 args += ' ttl %s' % ttl
d486dd0d
JF
1327
1328 if self.link_exists(name):
1329 cmd = 'link set dev %s type vxlan dstport %d' % (name, LinkUtils.VXLAN_UDP_PORT)
1330 vxlanattrs = self.get_vxlandev_attrs(name)
1331 # on ifreload do not overwrite anycast_ip to individual ip if clagd
1332 # has modified
1333 if vxlanattrs:
1334 running_localtunnelip = vxlanattrs.get('local')
1335 if anycastip and running_localtunnelip and anycastip == running_localtunnelip:
1336 localtunnelip = running_localtunnelip
1337 running_svcnode = vxlanattrs.get('svcnode')
1338 if running_svcnode and not svcnodeip:
1339 args += ' noremote'
1340 else:
1341 cmd = 'link add dev %s type vxlan id %s dstport %d' % (name, vxlanid, LinkUtils.VXLAN_UDP_PORT)
1342
1343 if localtunnelip:
1344 args += ' local %s' % localtunnelip
1345 cmd += args
1346
1347 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1348 self.add_to_batch(cmd)
1349 else:
1350 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1351
1352 # XXX: update linkinfo correctly
1353 #self._cache_update([name], {})
1354
1355 @staticmethod
1356 def link_exists(ifacename):
1357 if ifupdownflags.flags.DRYRUN:
1358 return True
1359 return os.path.exists('/sys/class/net/%s' % ifacename)
1360
9b653c85
JF
1361 @staticmethod
1362 def link_exists_nodryrun(ifname):
1363 return os.path.exists('/sys/class/net/%s' % ifname)
1364
d486dd0d
JF
1365 def link_get_ifindex(self, ifacename):
1366 if ifupdownflags.flags.DRYRUN:
1367 return True
1368 return self.read_file_oneline('/sys/class/net/%s/ifindex' % ifacename)
1369
1370 def is_vlan_device_by_name(self, ifacename):
1371 if re.search(r'\.', ifacename):
1372 return True
1373 return False
1374
1375 @staticmethod
e588acb7
JF
1376 def link_add_macvlan(ifname, macvlan_ifacename, mode):
1377 utils.exec_commandl(['ip', 'link', 'add', 'link', ifname, 'name', macvlan_ifacename, 'type', 'macvlan', 'mode', mode])
d486dd0d
JF
1378
1379 @staticmethod
1380 def route_add(route):
1381 utils.exec_command('%s route add %s' % (utils.ip_cmd,
1382 route))
1383
1384 @staticmethod
1385 def route6_add(route):
1386 utils.exec_command('%s -6 route add %s' % (utils.ip_cmd,
1387 route))
1388
1389 def get_vlandev_attrs(self, ifacename):
1390 return (self._cache_get('link', [ifacename, 'link']),
1391 self._cache_get('link', [ifacename, 'linkinfo', 'vlanid']),
1392 self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol']))
1393
1394 def get_vlan_protocol(self, ifacename):
1395 return self._cache_get('link', [ifacename, 'linkinfo', 'vlan_protocol'])
1396
1397 def get_vxlandev_attrs(self, ifacename):
1398 return self._cache_get('link', [ifacename, 'linkinfo'])
1399
1400 def get_vxlandev_learning(self, ifacename):
1401 return self._cache_get('link', [ifacename, 'linkinfo', Link.IFLA_VXLAN_LEARNING])
1402
1403 def set_vxlandev_learning(self, ifacename, learn):
1404 if learn == 'on':
1405 utils.exec_command('%s link set dev %s type vxlan learning' %
1406 (utils.ip_cmd, ifacename))
1407 self._cache_update([ifacename, 'linkinfo', 'learning'], 'on')
1408 else:
1409 utils.exec_command('%s link set dev %s type vxlan nolearning' %
1410 (utils.ip_cmd, ifacename))
1411 self._cache_update([ifacename, 'linkinfo', 'learning'], 'off')
1412
1413 def link_get_linkinfo_attrs(self, ifacename):
1414 return self._cache_get('link', [ifacename, 'linkinfo'])
1415
1416 def link_get_mtu(self, ifacename, refresh=False):
1417 return self._cache_get('link', [ifacename, 'mtu'], refresh=refresh)
1418
1419 def link_get_mtu_sysfs(self, ifacename):
1420 return self.read_file_oneline('/sys/class/net/%s/mtu'
1421 % ifacename)
1422
1423 def link_get_kind(self, ifacename):
1424 return self._cache_get('link', [ifacename, 'kind'])
1425
1426 def link_get_slave_kind(self, ifacename):
1427 return self._cache_get('link', [ifacename, 'slave_kind'])
1428
1429 def link_get_hwaddress(self, ifacename):
1430 address = self._cache_get('link', [ifacename, 'hwaddress'])
1431 # newly created logical interface addresses dont end up in the cache
1432 # read hwaddress from sysfs file for these interfaces
1433 if not address:
1434 address = self.read_file_oneline('/sys/class/net/%s/address'
1435 % ifacename)
1436 return address
1437
1438 def link_create(self, ifacename, t, attrs={}):
1439 """ generic link_create function """
1440 if self.link_exists(ifacename):
1441 return
1442 cmd = 'link add'
1443 cmd += ' name %s type %s' % (ifacename, t)
1444 if attrs:
1445 for k, v in attrs.iteritems():
1446 cmd += ' %s' % k
1447 if v:
1448 cmd += ' %s' % v
1449 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1450 self.add_to_batch(cmd)
1451 else:
1452 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1453 self._cache_update([ifacename], {})
1454
1455 def link_delete(self, ifacename):
1456 if not self.link_exists(ifacename):
1457 return
1458 cmd = 'link del %s' % ifacename
1459 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1460 self.add_to_batch(cmd)
1461 else:
1462 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
1463 self._cache_invalidate()
1464
1465 def link_get_master(self, ifacename):
1466 sysfs_master_path = '/sys/class/net/%s/master' % ifacename
1467 if os.path.exists(sysfs_master_path):
1468 link_path = os.readlink(sysfs_master_path)
1469 if link_path:
1470 return os.path.basename(link_path)
1471 else:
1472 return None
1473 else:
1474 return self._cache_get('link', [ifacename, 'master'])
1475
1476 def get_brport_peer_link(self, bridgename):
1477 try:
1478 return self._cache_get('link', [bridgename, 'info_slave_data', Link.IFLA_BRPORT_PEER_LINK])
1479 except:
1480 return None
1481
1482 @staticmethod
1483 def bridge_port_vids_add(bridgeportname, vids):
1484 [utils.exec_command('%s vlan add vid %s dev %s' %
1485 (utils.bridge_cmd,
1486 v, bridgeportname)) for v in vids]
1487
1488 @staticmethod
1489 def bridge_port_vids_del(bridgeportname, vids):
1490 if not vids:
1491 return
1492 [utils.exec_command('%s vlan del vid %s dev %s' %
1493 (utils.bridge_cmd,
1494 v, bridgeportname)) for v in vids]
1495
1496 @staticmethod
1497 def bridge_port_vids_flush(bridgeportname, vid):
1498 utils.exec_command('%s vlan del vid %s dev %s' %
1499 (utils.bridge_cmd,
1500 vid, bridgeportname))
1501
1502 @staticmethod
1503 def bridge_port_vids_get(bridgeportname):
1504 bridgeout = utils.exec_command('%s vlan show dev %s' %
1505 (utils.bridge_cmd,
1506 bridgeportname))
1507 if not bridgeout:
1508 return []
1509 brvlanlines = bridgeout.readlines()[2:]
1510 vids = [l.strip() for l in brvlanlines]
1511 return [v for v in vids if v]
1512
1513 @staticmethod
1514 def bridge_port_vids_get_all():
1515 brvlaninfo = {}
1516 bridgeout = utils.exec_command('%s -c vlan show'
1517 % utils.bridge_cmd)
1518 if not bridgeout:
1519 return brvlaninfo
1520 brvlanlines = bridgeout.splitlines()
1521 brportname = None
1522 for l in brvlanlines[1:]:
1523 if l and not l.startswith(' ') and not l.startswith('\t'):
1524 attrs = l.split()
1525 brportname = attrs[0].strip()
1526 brvlaninfo[brportname] = {'pvid': None, 'vlan': []}
1527 l = ' '.join(attrs[1:])
1528 if not brportname or not l:
1529 continue
1530 l = l.strip()
1531 if 'PVID' in l:
1532 brvlaninfo[brportname]['pvid'] = l.split()[0]
1533 elif 'Egress Untagged' not in l:
1534 brvlaninfo[brportname]['vlan'].append(l)
1535 return brvlaninfo
1536
1537 def bridge_port_vids_get_all_json(self):
1538 if not self.supported_command['%s -c -json vlan show'
1539 % utils.bridge_cmd]:
1540 return {}
1541 brvlaninfo = {}
1542 try:
1543 bridgeout = utils.exec_command('%s -c -json vlan show'
1544 % utils.bridge_cmd)
1545 except:
1546 self.supported_command['%s -c -json vlan show'
1547 % utils.bridge_cmd] = False
1548 self.logger.info('%s -c -json vlan show: skipping unsupported command'
1549 % utils.bridge_cmd)
1550 try:
1551 return self.get_bridge_vlan_nojson()
1552 except Exception as e:
1553 self.logger.info('bridge: get_bridge_vlan_nojson: %s' % str(e))
1554 return {}
1555
1556 if not bridgeout: return brvlaninfo
1557 try:
52340202 1558 vlan_json = json.loads(bridgeout, encoding="utf-8")
d486dd0d
JF
1559 except Exception, e:
1560 self.logger.info('json loads failed with (%s)' % str(e))
1561 return {}
52340202
JF
1562
1563 try:
1564 if isinstance(vlan_json, list):
1565 # newer iproute2 version changed the bridge vlan show output
1566 # ifupdown2 relies on the previous format, we have the convert
1567 # data into old format
1568 bridge_port_vids = dict()
1569
1570 for intf in vlan_json:
1571 bridge_port_vids[intf["ifname"]] = intf["vlans"]
1572
1573 return bridge_port_vids
1574 else:
1575 # older iproute2 version have different ways to dump vlans
1576 # ifupdown2 prefers the following syntax:
1577 # {
1578 # "vx-1002": [{
1579 # "vlan": 1002,
1580 # "flags": ["PVID", "Egress Untagged"]
1581 # }
1582 # ],
1583 # "vx-1004": [{
1584 # "vlan": 1004,
1585 # "flags": ["PVID", "Egress Untagged"]
1586 # }]
1587 # }
1588 return vlan_json
1589 except Exception as e:
1590 self.logger.debug("bridge vlan show: Unknown json output: %s" % str(e))
1591 return vlan_json
d486dd0d
JF
1592
1593 @staticmethod
1594 def get_bridge_vlan_nojson():
1595 vlan_json = {}
1596 bridgeout = utils.exec_commandl([utils.bridge_cmd, '-c', 'vlan', 'show'])
1597 if bridgeout:
1598 output = [line.split('\n') for line in bridgeout.split('\n\n')]
1599 output[0] = output[0][1:]
1600 for line in output:
1601 current_swp = None
1602 if not line:
1603 continue
1604 for entry in line:
1605 if not entry:
1606 continue
1607 prefix, vlan = entry.split('\t')
1608 if prefix:
1609 current_swp = prefix
1610 vlan_json[prefix] = []
1611 v = {}
1612 vlan = vlan[1:]
1613 try:
1614 v['vlan'] = int(vlan)
1615 except:
1616 try:
1617 if '-' in vlan:
1618 start, end = vlan.split('-')
1619 if ' ' in end:
1620 end = end[0:end.index(' ')]
1621 v['vlan'] = int(start)
1622 v['vlanEnd'] = int(end)
1623 else:
1624 v['vlan'] = int(vlan[0:vlan.index(' ')])
1625 flags = []
1626 if 'PVID' in vlan:
1627 flags.append('PVID')
1628 if 'Egress Untagged' in vlan:
1629 flags.append('Egress Untagged')
1630 v['flags'] = flags
1631 except:
1632 continue
1633 vlan_json[current_swp].append(v)
1634 return vlan_json
1635
1636 def bridge_vlan_cache_get(self, ifacename, refresh=False):
1637 if not self.bridge_vlan_cache_fill_done or refresh:
1638 self.bridge_vlan_cache = self.bridge_port_vids_get_all_json()
1639 self.bridge_vlan_cache_fill_done = True
1640 return self.bridge_vlan_cache.get(ifacename, {})
1641
1642 def bridge_vlan_get_pvid(self, ifacename, refresh=False):
1643 pvid = 0
1644
1645 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
1646 v = vinfo.get('vlan')
1647 pvid = v if 'PVID' in vinfo.get('flags', []) else 0
1648 if pvid:
1649 return pvid
1650 return pvid
1651
1652 def bridge_vlan_get_vids(self, ifacename, refresh=False):
1653 vids = []
1654
1655 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
1656 v = vinfo.get('vlan')
1657 ispvid = True if 'PVID' in vinfo.get('flags', []) else False
1658 if ispvid:
1659 pvid = v if 'PVID' in vinfo.get('flags', []) else 0
1660 if pvid == 1:
1661 continue
1662 vEnd = vinfo.get('vlanEnd')
1663 if vEnd:
1664 vids.extend(range(v, vEnd + 1))
1665 else:
1666 vids.append(v)
1667 return vids
1668
1669 def bridge_vlan_get_vids_n_pvid(self, ifacename, refresh=False):
1670 vids = []
1671 pvid = 0
1672
1673 for vinfo in self.bridge_vlan_cache_get(ifacename, refresh):
1674 v = vinfo.get('vlan')
1675 ispvid = True if 'PVID' in vinfo.get('flags', []) else False
1676 if ispvid:
1677 pvid = v if 'PVID' in vinfo.get('flags', []) else 0
1678 vEnd = vinfo.get('vlanEnd')
1679 if vEnd:
1680 vids.extend(range(v, vEnd + 1))
1681 else:
1682 vids.append(v)
1683 return vids, pvid
1684
1685 def bridge_port_pvid_add(self, bridgeportname, pvid):
1686 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1687 self.add_to_batch('vlan add vid %s untagged pvid dev %s' %
1688 (pvid, bridgeportname))
1689 else:
1690 utils.exec_command('%s vlan add vid %s untagged pvid dev %s' %
1691 (utils.bridge_cmd,
1692 pvid, bridgeportname))
1693
1694 def bridge_port_pvid_del(self, bridgeportname, pvid):
1695 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1696 self.add_to_batch('vlan del vid %s untagged pvid dev %s' %
1697 (pvid, bridgeportname))
1698 else:
1699 utils.exec_command('%s vlan del vid %s untagged pvid dev %s' %
1700 (utils.bridge_cmd,
1701 pvid, bridgeportname))
1702
1703 def bridge_port_pvids_get(self, bridgeportname):
1704 return self.read_file_oneline('/sys/class/net/%s/brport/pvid'
1705 % bridgeportname)
1706
1707 def bridge_vids_add(self, bridgeportname, vids, bridge=True):
1708 target = 'self' if bridge else ''
1709 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1710 [self.add_to_batch('vlan add vid %s dev %s %s' %
1711 (v, bridgeportname, target)) for v in vids]
1712 else:
1713 [utils.exec_command('%s vlan add vid %s dev %s %s' %
1714 (utils.bridge_cmd,
1715 v, bridgeportname, target)) for v in vids]
1716
1717 def bridge_vids_del(self, bridgeportname, vids, bridge=True):
1718 target = 'self' if bridge else ''
1719 if LinkUtils.ipbatch and not LinkUtils.ipbatch_pause:
1720 [self.add_to_batch('vlan del vid %s dev %s %s' %
1721 (v, bridgeportname, target)) for v in vids]
1722 else:
1723 [utils.exec_command('%s vlan del vid %s dev %s %s' %
1724 (utils.bridge_cmd,
1725 v, bridgeportname, target)) for v in vids]
1726
1727 @staticmethod
1728 def bridge_fdb_add(dev, address, vlan=None, bridge=True, remote=None):
1729 target = 'self' if bridge else ''
1730 vlan_str = ''
1731 if vlan:
1732 vlan_str = 'vlan %s ' % vlan
1733
1734 dst_str = ''
1735 if remote:
1736 dst_str = 'dst %s ' % remote
1737
1738 utils.exec_command('%s fdb replace %s dev %s %s %s %s' %
1739 (utils.bridge_cmd,
1740 address, dev, vlan_str, target, dst_str))
1741
1742 @staticmethod
1743 def bridge_fdb_append(dev, address, vlan=None, bridge=True, remote=None):
1744 target = 'self' if bridge else ''
1745 vlan_str = ''
1746 if vlan:
1747 vlan_str = 'vlan %s ' % vlan
1748
1749 dst_str = ''
1750 if remote:
1751 dst_str = 'dst %s ' % remote
1752
1753 utils.exec_command('%s fdb append %s dev %s %s %s %s' %
1754 (utils.bridge_cmd,
1755 address, dev, vlan_str, target, dst_str))
1756
1757 @staticmethod
1758 def bridge_fdb_del(dev, address, vlan=None, bridge=True, remote=None):
1759 target = 'self' if bridge else ''
1760 vlan_str = ''
1761 if vlan:
1762 vlan_str = 'vlan %s ' % vlan
1763
1764 dst_str = ''
1765 if remote:
1766 dst_str = 'dst %s ' % remote
1767 utils.exec_command('%s fdb del %s dev %s %s %s %s' %
1768 (utils.bridge_cmd,
1769 address, dev, vlan_str, target, dst_str))
1770
1771 def bridge_is_vlan_aware(self, bridgename):
1772 filename = '/sys/class/net/%s/bridge/vlan_filtering' % bridgename
1773 if os.path.exists(filename) and self.read_file_oneline(filename) == '1':
1774 return True
1775 return False
1776
1777 @staticmethod
1778 def bridge_port_get_bridge_name(bridgeport):
1779 filename = '/sys/class/net/%s/brport/bridge' % bridgeport
1780 try:
1781 return os.path.basename(os.readlink(filename))
1782 except:
1783 return None
1784
1785 @staticmethod
1786 def bridge_port_exists(bridge, bridgeportname):
1787 try:
1788 return os.path.exists('/sys/class/net/%s/brif/%s'
1789 % (bridge, bridgeportname))
1790 except Exception:
1791 return False
1792
1793 def bridge_fdb_show_dev(self, dev):
1794 try:
1795 fdbs = {}
1796 output = utils.exec_command('%s fdb show dev %s'
1797 % (utils.bridge_cmd, dev))
1798 if output:
1799 for fdb_entry in output.splitlines():
1800 try:
1801 entries = fdb_entry.split()
1802 fdbs.setdefault(entries[2], []).append(entries[0])
1803 except:
1804 self.logger.debug('%s: invalid fdb line \'%s\''
1805 % (dev, fdb_entry))
1806 return fdbs
1807 except Exception:
1808 return None
1809
1810 @staticmethod
1811 def is_bridge(bridge):
1812 return os.path.exists('/sys/class/net/%s/bridge' % bridge)
1813
1814 def is_link_up(self, ifacename):
1815 ret = False
1816 try:
1817 flags = self.read_file_oneline('/sys/class/net/%s/flags' % ifacename)
1818 iflags = int(flags, 16)
1819 if iflags & 0x0001:
1820 ret = True
1821 except:
1822 ret = False
1823 return ret
1824
aa895ecd 1825 def ip_route_get_dev(self, prefix, vrf_master=None):
d486dd0d 1826 try:
aa895ecd
JF
1827 if vrf_master:
1828 cmd = '%s route get %s vrf %s' % (utils.ip_cmd, prefix, vrf_master)
1829 else:
1830 cmd = '%s route get %s' % (utils.ip_cmd, prefix)
1831
1832 output = utils.exec_command(cmd)
d486dd0d
JF
1833 if output:
1834 rline = output.splitlines()[0]
1835 if rline:
1836 rattrs = rline.split()
1837 return rattrs[rattrs.index('dev') + 1]
1838 except Exception, e:
1839 self.logger.debug('ip_route_get_dev: failed .. %s' % str(e))
1840 return None
1841
1842 @staticmethod
1843 def link_get_lowers(ifacename):
1844 try:
1845 lowers = glob.glob("/sys/class/net/%s/lower_*" % ifacename)
1846 if not lowers:
1847 return []
1848 return [os.path.basename(l)[6:] for l in lowers]
1849 except:
1850 return []
1851
1852 @staticmethod
1853 def link_get_uppers(ifacename):
1854 try:
1855 uppers = glob.glob("/sys/class/net/%s/upper_*" % ifacename)
1856 if not uppers:
1857 return None
1858 return [os.path.basename(u)[6:] for u in uppers]
1859 except Exception:
1860 return None
1861
1862 def link_get_vrfs(self):
1863 if not LinkUtils._CACHE_FILL_DONE:
1864 self._fill_cache()
1865 return linkCache.vrfs
1866
1867 @staticmethod
1868 def cache_get_info_slave(attrlist):
1869 try:
1870 return linkCache.get_attr(attrlist)
1871 except:
1872 return None
1873
1874 def get_brport_learning(self, ifacename):
1875 learn = self.read_file_oneline('/sys/class/net/%s/brport/learning'
1876 % ifacename)
1877 if learn and learn == '1':
1878 return 'on'
1879 else:
1880 return 'off'
1881
1882 def get_brport_learning_bool(self, ifacename):
1883 return utils.get_boolean_from_string(self.read_file_oneline('/sys/class/net/%s/brport/learning' % ifacename))
1884
1885 def set_brport_learning(self, ifacename, learn):
1886 if learn == 'off':
1887 return self.write_file('/sys/class/net/%s/brport/learning'
1888 % ifacename, '0')
1889 else:
1890 return self.write_file('/sys/class/net/%s/brport/learning'
1891 % ifacename, '1')
1892
1893 #################################################################################
1894 ################################### BOND UTILS ##################################
1895 #################################################################################
1896
1897 def _link_cache_get(self, attrlist, refresh=False):
1898 return self._cache_get('link', attrlist, refresh)
1899
1900 def cache_delete(self, attrlist, value=None):
1901 return self._cache_delete(attrlist, value)
1902
1903 def link_cache_get(self, attrlist, refresh=False):
1904 return self._link_cache_get(attrlist, refresh)
1905
1906 def link_cache_check(self, attrlist, value, refresh=False):
1907 return self._link_cache_check(attrlist, value, refresh)
1908
1909 def _link_cache_check(self, attrlist, value, refresh=False):
1910 try:
1911 return self._link_cache_get(attrlist, refresh) == value
1912 except Exception, e:
1913 self.logger.debug('_cache_check(%s) : [%s]'
1914 % (str(attrlist), str(e)))
1915 pass
1916 return False
1917
1918 bondcmd_attrmap = {
1919 Link.IFLA_BOND_MODE: 'mode',
1920 Link.IFLA_BOND_MIIMON: 'miimon',
1921 Link.IFLA_BOND_USE_CARRIER: 'use_carrier',
1922 Link.IFLA_BOND_AD_LACP_RATE: 'lacp_rate',
1923 Link.IFLA_BOND_XMIT_HASH_POLICY: 'xmit_hash_policy',
1924 Link.IFLA_BOND_MIN_LINKS: 'min_links',
1925 Link.IFLA_BOND_NUM_PEER_NOTIF: 'num_grat_arp',
1926 Link.IFLA_BOND_AD_ACTOR_SYSTEM: 'ad_actor_system',
1927 Link.IFLA_BOND_AD_ACTOR_SYS_PRIO: 'ad_actor_sys_prio',
1928 Link.IFLA_BOND_AD_LACP_BYPASS: 'lacp_bypass',
1929 Link.IFLA_BOND_UPDELAY: 'updelay',
1930 Link.IFLA_BOND_DOWNDELAY: 'downdelay',
1931 }
1932
1933 def bond_set_attrs_nl(self, bondname, ifla_info_data):
1934 bond_attr_name = 'None' # for log purpose (in case an exception raised)
1935 for nl_attr, value in ifla_info_data.items():
1936 try:
1937 bond_attr_name = self.bondcmd_attrmap[nl_attr]
1938 file_path = '/sys/class/net/%s/bonding/%s' % (bondname, bond_attr_name)
1939 if os.path.exists(file_path):
1940 self.write_file(file_path, str(value))
1941 except Exception as e:
1942 exception_str = '%s: %s %s: %s' % (bondname, bond_attr_name, value, str(e))
1943 if ifupdownflags.flags.FORCE:
1944 self.logger.warning(exception_str)
1945 else:
1946 self.logger.debug(exception_str)
1947
1948 def bond_set_attrs(self, bondname, attrdict, prehook):
1949 for attrname, attrval in attrdict.items():
1950 if (self._link_cache_check([bondname, 'linkinfo',
1951 attrname], attrval)):
1952 continue
1953 if (attrname == 'mode'
1954 or attrname == 'xmit_hash_policy'
1955 or attrname == 'lacp_rate' or attrname == 'min_links'):
1956 if prehook:
1957 prehook(bondname)
1958 try:
1959 if ((attrname not in ['lacp_rate',
1960 'lacp_bypass']) or
1961 self._link_cache_check([bondname, 'linkinfo', 'mode'], '802.3ad',
1962 True)):
1963 self.write_file('/sys/class/net/%s/bonding/%s'
1964 % (bondname, attrname), attrval)
1965 except Exception, e:
1966 if ifupdownflags.flags.FORCE:
1967 self.logger.warn(str(e))
1968 pass
1969 else:
1970 raise
1971
1972 def bond_set_use_carrier(self, bondname, use_carrier):
1973 if not use_carrier or (use_carrier != '0' and use_carrier != '1'):
1974 return
1975 if (self._link_cache_check([bondname, 'linkinfo', 'use_carrier'],
1976 use_carrier)):
1977 return
1978 self.write_file('/sys/class/net/%s' % bondname +
1979 '/bonding/use_carrier', use_carrier)
1980 self._cache_update([bondname, 'linkinfo',
1981 'use_carrier'], use_carrier)
1982
1983 def bond_get_use_carrier(self, bondname):
1984 return self._link_cache_get([bondname, 'linkinfo', 'use_carrier'])
1985
1986 def bond_get_use_carrier_nl(self, bondname):
1987 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_USE_CARRIER])
1988
1989 def bond_set_xmit_hash_policy(self, bondname, hash_policy, prehook=None):
1990 valid_values = ['layer2', 'layer3+4', 'layer2+3']
1991 if not hash_policy:
1992 return
1993 if hash_policy not in valid_values:
1994 raise Exception('invalid hash policy value %s' % hash_policy)
1995 if (self._link_cache_check([bondname, 'linkinfo', 'xmit_hash_policy'],
1996 hash_policy)):
1997 return
1998 if prehook:
1999 prehook(bondname)
2000 self.write_file('/sys/class/net/%s' % bondname +
2001 '/bonding/xmit_hash_policy', hash_policy)
2002 self._cache_update([bondname, 'linkinfo', 'xmit_hash_policy'],
2003 hash_policy)
2004
2005 def bond_get_xmit_hash_policy(self, bondname):
2006 return self._link_cache_get([bondname, 'linkinfo', 'xmit_hash_policy'])
2007
2008 def bond_get_xmit_hash_policy_nl(self, bondname):
2009 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_XMIT_HASH_POLICY])
2010
2011 def bond_set_miimon(self, bondname, miimon):
2012 if (self._link_cache_check([bondname, 'linkinfo', 'miimon'],
2013 miimon)):
2014 return
2015 self.write_file('/sys/class/net/%s' % bondname +
2016 '/bonding/miimon', miimon)
2017 self._cache_update([bondname, 'linkinfo', 'miimon'], miimon)
2018
2019 def bond_get_miimon(self, bondname):
2020 return self._link_cache_get([bondname, 'linkinfo', 'miimon'])
2021
2022 def bond_get_miimon_nl(self, bondname):
2023 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIIMON])
2024
2025 def bond_set_mode(self, bondname, mode, prehook=None):
2026 valid_modes = ['balance-rr', 'active-backup', 'balance-xor',
2027 'broadcast', '802.3ad', 'balance-tlb', 'balance-alb']
2028 if not mode:
2029 return
2030 if mode not in valid_modes:
2031 raise Exception('invalid mode %s' % mode)
2032 if (self._link_cache_check([bondname, 'linkinfo', 'mode'],
2033 mode)):
2034 return
2035 if prehook:
2036 prehook(bondname)
2037 self.write_file('/sys/class/net/%s' % bondname + '/bonding/mode', mode)
2038 self._cache_update([bondname, 'linkinfo', 'mode'], mode)
2039
2040 def bond_get_mode(self, bondname):
2041 return self._link_cache_get([bondname, 'linkinfo', 'mode'])
2042
2043 def bond_get_mode_nl(self, bondname):
2044 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MODE])
2045
2046 def bond_set_lacp_rate(self, bondname, lacp_rate, prehook=None, posthook=None):
2047 if not lacp_rate or (lacp_rate != '0' and lacp_rate != '1'):
2048 return
2049 if (self._link_cache_check([bondname, 'linkinfo', 'lacp_rate'],
2050 lacp_rate)):
2051 return
2052 if prehook:
2053 prehook(bondname)
2054 try:
2055 self.write_file('/sys/class/net/%s' % bondname +
2056 '/bonding/lacp_rate', lacp_rate)
2057 except:
2058 raise
2059 finally:
2060 if posthook:
2061 prehook(bondname)
2062 self._cache_update([bondname, 'linkinfo',
2063 'lacp_rate'], lacp_rate)
2064
2065 def bond_get_lacp_rate(self, bondname):
2066 return self._link_cache_get([bondname, 'linkinfo', 'lacp_rate'])
2067
2068 def bond_get_lacp_rate_nl(self, bondname):
2069 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_RATE])
2070
2071 def bond_set_lacp_bypass_allow(self, bondname, allow, prehook=None, posthook=None):
2072 if self._link_cache_check([bondname, 'linkinfo', 'lacp_bypass'], allow):
2073 return
2074 if prehook:
2075 prehook(bondname)
2076 try:
2077 self.write_file('/sys/class/net/%s' % bondname +
2078 '/bonding/lacp_bypass', allow)
2079 except:
2080 raise
2081 finally:
2082 if posthook:
2083 posthook(bondname)
2084 self._cache_update([bondname, 'linkinfo',
2085 'lacp_bypass'], allow)
2086
2087 def bond_get_lacp_bypass_allow(self, bondname):
2088 return self._link_cache_get([bondname, 'linkinfo', 'lacp_bypass'])
2089
2090 def bond_get_lacp_bypass_allow_nl(self, bondname):
2091 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_LACP_BYPASS])
2092
2093 def bond_set_min_links(self, bondname, min_links, prehook=None):
2094 if (self._link_cache_check([bondname, 'linkinfo', 'min_links'],
2095 min_links)):
2096 return
2097 if prehook:
2098 prehook(bondname)
2099 self.write_file('/sys/class/net/%s/bonding/min_links' % bondname,
2100 min_links)
2101 self._cache_update([bondname, 'linkinfo', 'min_links'], min_links)
2102
2103 def bond_get_min_links(self, bondname):
2104 return self._link_cache_get([bondname, 'linkinfo', 'min_links'])
2105
2106 def get_min_links_nl(self, bondname):
2107 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_MIN_LINKS])
2108
2109 def bond_get_ad_actor_system(self, bondname):
2110 return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_system'])
2111
2112 def bond_get_ad_actor_system_nl(self, bondname):
2113 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYSTEM])
2114
2115 def bond_get_ad_actor_sys_prio(self, bondname):
2116 return self._link_cache_get([bondname, 'linkinfo', 'ad_actor_sys_prio'])
2117
2118 def bond_get_ad_actor_sys_prio_nl(self, bondname):
2119 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_AD_ACTOR_SYS_PRIO])
2120
2121 def bond_get_num_unsol_na(self, bondname):
2122 return self._link_cache_get([bondname, 'linkinfo', 'num_unsol_na'])
2123
2124 def bond_get_num_unsol_na_nl(self, bondname):
2125 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
2126
2127 def bond_get_num_grat_arp(self, bondname):
2128 return self._link_cache_get([bondname, 'linkinfo', 'num_grat_arp'])
2129
2130 def bond_get_num_grat_arp_nl(self, bondname):
2131 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_NUM_PEER_NOTIF])
2132
2133 def bond_get_updelay(self, bondname):
2134 return self._link_cache_get([bondname, 'linkinfo', 'updelay'])
2135
2136 def bond_get_updelay_nl(self, bondname):
2137 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_UPDELAY])
2138
2139 def bond_get_downdelay(self, bondname):
2140 return self._link_cache_get([bondname, 'linkinfo', 'downdelay'])
2141
2142 def bond_get_downdelay_nl(self, bondname):
2143 return self._link_cache_get([bondname, 'linkinfo', Link.IFLA_BOND_DOWNDELAY])
2144
2145 def bond_enslave_slave(self, bondname, slave, prehook=None, posthook=None):
2146 slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
2147 if slaves and slave in slaves:
2148 return
2149 if prehook:
2150 prehook(slave)
2151 self.write_file('/sys/class/net/%s' % bondname +
2152 '/bonding/slaves', '+' + slave)
2153 if posthook:
2154 posthook(slave)
2155 self._cache_update([bondname, 'linkinfo', 'slaves'], slave)
2156
2157 def bond_remove_slave(self, bondname, slave):
2158 slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
2159 if not slaves or slave not in slaves:
2160 return
2161 sysfs_bond_path = ('/sys/class/net/%s' % bondname +
2162 '/bonding/slaves')
2163 if not os.path.exists(sysfs_bond_path):
2164 return
2165 self.write_file(sysfs_bond_path, '-' + slave)
2166 self._cache_delete([bondname, 'linkinfo', 'slaves'], slave)
2167
2168 def bond_remove_slaves_all(self, bondname):
2169 if not self._link_cache_get([bondname, 'linkinfo', 'slaves']):
2170 return
2171 slaves = None
2172 sysfs_bond_path = ('/sys/class/net/%s' % bondname +
2173 '/bonding/slaves')
2174 try:
2175 with open(sysfs_bond_path, 'r') as f:
2176 slaves = f.readline().strip().split()
2177 except IOError, e:
2178 raise Exception('error reading slaves of bond %s (%s)' % (bondname, str(e)))
2179 for slave in slaves:
2180 self.link_down(slave)
2181 try:
2182 self.bond_remove_slave(bondname, slave)
2183 except Exception, e:
2184 if not ifupdownflags.flags.FORCE:
2185 raise Exception('error removing slave %s from bond %s (%s)' % (slave, bondname, str(e)))
2186 else:
2187 pass
2188 self._cache_delete([bondname, 'linkinfo', 'slaves'])
2189
2190 @staticmethod
2191 def bond_load_bonding_module():
2192 return utils.exec_command('%s -q bonding' % utils.modprobe_cmd)
2193
2194 def create_bond(self, bondname):
2195 if self.bond_exists(bondname):
2196 return
2197 # load_bonding_module() has already been run
2198 self.write_file('/sys/class/net/bonding_masters', '+' + bondname)
2199 self._cache_update([bondname], {})
2200
2201 def delete_bond(self, bondname):
2202 if not os.path.exists('/sys/class/net/%s' % bondname):
2203 return
2204 self.write_file('/sys/class/net/bonding_masters', '-' + bondname)
2205 self._cache_delete([bondname])
2206
2207 def bond_get_slaves(self, bondname):
2208 slaves = self._link_cache_get([bondname, 'linkinfo', 'slaves'])
2209 if slaves:
2210 return list(slaves)
2211 slavefile = '/sys/class/net/%s/bonding/slaves' % bondname
2212 if os.path.exists(slavefile):
2213 buf = self.read_file_oneline(slavefile)
2214 if buf:
2215 slaves = buf.split()
2216 if not slaves:
2217 return []
2218 self._cache_update([bondname, 'linkinfo', 'slaves'], slaves)
2219 return list(slaves)
2220
2221 def bond_slave_exists(self, bond, slave):
2222 slaves = self.bond_get_slaves(bond)
2223 if not slaves:
2224 return False
2225 return slave in slaves
2226
2227 @staticmethod
2228 def bond_exists(bondname):
2229 return os.path.exists('/sys/class/net/%s/bonding' % bondname)
2230
2231 #################################################################################
2232 ################################## BRIDGE UTILS #################################
2233 #################################################################################
2234
2235 def create_bridge(self, bridgename):
2236 if not LinkUtils.bridge_utils_is_installed:
2237 return
2238 if self.bridge_exists(bridgename):
2239 return
2240 utils.exec_command('%s addbr %s' % (utils.brctl_cmd, bridgename))
2241 self._cache_update([bridgename], {})
2242
2243 def delete_bridge(self, bridgename):
2244 if not LinkUtils.bridge_utils_is_installed:
2245 return
2246 if not self.bridge_exists(bridgename):
2247 return
2248 utils.exec_command('%s delbr %s' % (utils.brctl_cmd, bridgename))
2249 self._cache_invalidate()
2250
2251 def add_bridge_port(self, bridgename, bridgeportname):
2252 """ Add port to bridge """
2253 if not LinkUtils.bridge_utils_is_installed:
2254 return
2255 ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
2256 if ports and ports.get(bridgeportname):
2257 return
2258 utils.exec_command('%s addif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
2259 self._cache_update([bridgename, 'linkinfo', 'ports', bridgeportname], {})
2260
2261 def delete_bridge_port(self, bridgename, bridgeportname):
2262 """ Delete port from bridge """
2263 if not LinkUtils.bridge_utils_is_installed:
2264 return
2265 ports = self._link_cache_get([bridgename, 'linkinfo', 'ports'])
2266 if not ports or not ports.get(bridgeportname):
2267 return
2268 utils.exec_command('%s delif %s %s' % (utils.brctl_cmd, bridgename, bridgeportname))
2269 self._cache_delete([bridgename, 'linkinfo', 'ports', 'bridgeportname'])
2270
2271 def set_bridgeport_attrs(self, bridgename, bridgeportname, attrdict):
2272 portattrs = self._link_cache_get([bridgename, 'linkinfo', 'ports', bridgeportname])
2273 if portattrs == None:
2274 portattrs = {}
2275 for k, v in attrdict.iteritems():
2276 if ifupdownflags.flags.CACHE:
2277 curval = portattrs.get(k)
2278 if curval and curval == v:
2279 continue
2280 if k == 'unicast-flood':
2281 self.write_file('/sys/class/net/%s/brport/unicast_flood' % bridgeportname, v)
2282 elif k == 'multicast-flood':
2283 self.write_file('/sys/class/net/%s/brport/multicast_flood' % bridgeportname, v)
2284 elif k == 'learning':
2285 self.write_file('/sys/class/net/%s/brport/learning' % bridgeportname, v)
2286 elif k == 'arp-nd-suppress':
2287 self.write_file('/sys/class/net/%s/brport/neigh_suppress' % bridgeportname, v)
2288 else:
2289 if not LinkUtils.bridge_utils_is_installed:
2290 continue
2291 utils.exec_command('%s set%s %s %s %s' % (utils.brctl_cmd, k, bridgename, bridgeportname, v))
2292
2293 def set_bridgeport_attr(self, bridgename, bridgeportname,
2294 attrname, attrval):
2295 if not LinkUtils.bridge_utils_is_installed:
2296 return
2297 if self._link_cache_check([bridgename, 'linkinfo', 'ports', bridgeportname, attrname], attrval):
2298 return
2299 utils.exec_command('%s set%s %s %s %s' %
2300 (utils.brctl_cmd,
2301 attrname,
2302 bridgename,
2303 bridgeportname,
2304 attrval))
2305
2306 def set_bridge_attrs(self, bridgename, attrdict):
2307 for k, v in attrdict.iteritems():
2308 if not v:
2309 continue
2310 if self._link_cache_check([bridgename, 'linkinfo', k], v):
2311 continue
2312 try:
2313 if k == 'igmp-version':
2314 self.write_file('/sys/class/net/%s/bridge/'
2315 'multicast_igmp_version' % bridgename, v)
2316 elif k == 'mld-version':
2317 self.write_file('/sys/class/net/%s/bridge/'
2318 'multicast_mld_version' % bridgename, v)
2319 elif k == 'vlan-protocol':
2320 self.write_file('/sys/class/net/%s/bridge/'
2321 'vlan_protocol' % bridgename,
2322 VlanProtocols.ETHERTYPES_TO_ID.get(v.upper(),
2323 None))
2324 elif k == 'vlan-stats':
2325 self.write_file('/sys/class/net/%s/bridge/'
2326 'vlan_stats_enabled' % bridgename, v)
2327 elif k == 'mcstats':
2328 self.write_file('/sys/class/net/%s/bridge/'
2329 'multicast_stats_enabled' % bridgename, v)
2330 else:
2331 if not LinkUtils.bridge_utils_is_installed:
2332 continue
2333 cmd = ('%s set%s %s %s' %
2334 (utils.brctl_cmd, k, bridgename, v))
2335 utils.exec_command(cmd)
2336 except Exception, e:
2337 self.logger.warn('%s: %s' % (bridgename, str(e)))
2338 pass
2339
2340 def set_bridge_attr(self, bridgename, attrname, attrval):
2341 if self._link_cache_check([bridgename, 'linkinfo', attrname], attrval):
2342 return
2343 if attrname == 'igmp-version':
2344 self.write_file('/sys/class/net/%s/bridge/multicast_igmp_version'
2345 % bridgename, attrval)
2346 elif attrname == 'mld-version':
2347 self.write_file('/sys/class/net/%s/bridge/multicast_mld_version'
2348 % bridgename, attrval)
2349 elif attrname == 'vlan-protocol':
2350 self.write_file('/sys/class/net/%s/bridge/vlan_protocol'
2351 % bridgename, VlanProtocols.ETHERTYPES_TO_ID[attrval.upper()])
2352 elif attrname == 'vlan-stats':
2353 self.write_file('/sys/class/net/%s/bridge/vlan_stats_enabled'
2354 % bridgename, attrval)
2355 elif attrname == 'mcstats':
2356 self.write_file('/sys/class/net/%s/bridge/multicast_stats_enabled'
2357 % bridgename, attrval)
2358 else:
2359 if not LinkUtils.bridge_utils_is_installed:
2360 return
2361 cmd = '%s set%s %s %s' % (utils.brctl_cmd,
2362 attrname, bridgename, attrval)
2363 utils.exec_command(cmd)
2364
2365 def get_bridge_attrs(self, bridgename):
2366 attrs = self._link_cache_get([bridgename, 'linkinfo'])
2367 no_ints_attrs = {}
2368 for key, value in attrs.items():
2369 if type(key) == str:
2370 no_ints_attrs[key] = value
2371 return no_ints_attrs
2372
2373 def get_bridgeport_attrs(self, bridgename, bridgeportname):
2374 return self._link_cache_get([bridgename, 'linkinfo', 'ports',
2375 bridgeportname])
2376
2377 def get_bridgeport_attr(self, bridgename, bridgeportname, attrname):
2378 return self._link_cache_get([bridgename, 'linkinfo', 'ports',
2379 bridgeportname, attrname])
2380
2381 @staticmethod
2382 def bridge_set_stp(bridge, stp_state):
2383 if not LinkUtils.bridge_utils_is_installed:
2384 return
2385 utils.exec_command('%s stp %s %s' % (utils.brctl_cmd, bridge, stp_state))
2386
2387 def bridge_get_stp(self, bridge):
2388 sysfs_stpstate = '/sys/class/net/%s/bridge/stp_state' % bridge
2389 if not os.path.exists(sysfs_stpstate):
2390 return 'error'
2391 stpstate = self.read_file_oneline(sysfs_stpstate)
2392 if not stpstate:
2393 return 'error'
2394 try:
2395 if int(stpstate) > 0:
2396 return 'yes'
2397 elif int(stpstate) == 0:
2398 return 'no'
2399 except:
2400 return 'unknown'
2401
2402 @staticmethod
2403 def _conv_value_to_user(s):
2404 try:
2405 ret = int(s) / 100
2406 return '%d' % ret
2407 except:
2408 return None
2409
2410 def read_value_from_sysfs(self, filename, preprocess_func):
2411 value = self.read_file_oneline(filename)
2412 if not value:
2413 return None
2414 return preprocess_func(value)
2415
2416 @staticmethod
2417 def bridge_set_ageing(bridge, ageing):
2418 if not LinkUtils.bridge_utils_is_installed:
2419 return
2420 utils.exec_command('%s setageing %s %s' % (utils.brctl_cmd, bridge, ageing))
2421
2422 def bridge_get_ageing(self, bridge):
2423 return self.read_value_from_sysfs('/sys/class/net/%s/bridge/ageing_time'
2424 % bridge, self._conv_value_to_user)
2425
2426 @staticmethod
2427 def set_bridgeprio(bridge, prio):
2428 if not LinkUtils.bridge_utils_is_installed:
2429 return
2430 utils.exec_command('%s setbridgeprio %s %s' % (utils.brctl_cmd, bridge, prio))
2431
2432 def get_bridgeprio(self, bridge):
2433 return self.read_file_oneline(
2434 '/sys/class/net/%s/bridge/priority' % bridge)
2435
2436 @staticmethod
2437 def bridge_set_fd(bridge, fd):
2438 if not LinkUtils.bridge_utils_is_installed:
2439 return
2440 utils.exec_command('%s setfd %s %s' % (utils.brctl_cmd, bridge, fd))
2441
2442 def bridge_get_fd(self, bridge):
2443 return self.read_value_from_sysfs(
2444 '/sys/class/net/%s/bridge/forward_delay'
2445 % bridge, self._conv_value_to_user)
2446
2447 def bridge_set_gcint(self, bridge, gcint):
2448 raise Exception('set_gcint not implemented')
2449
2450 @staticmethod
2451 def bridge_set_hello(bridge, hello):
2452 if not LinkUtils.bridge_utils_is_installed:
2453 return
2454 utils.exec_command('%s sethello %s %s' % (utils.brctl_cmd, bridge, hello))
2455
2456 def bridge_get_hello(self, bridge):
2457 return self.read_value_from_sysfs('/sys/class/net/%s/bridge/hello_time'
2458 % bridge, self._conv_value_to_user)
2459
2460 @staticmethod
2461 def bridge_set_maxage(bridge, maxage):
2462 if not LinkUtils.bridge_utils_is_installed:
2463 return
2464 utils.exec_command('%s setmaxage %s %s' % (utils.brctl_cmd, bridge, maxage))
2465
2466 def bridge_get_maxage(self, bridge):
2467 return self.read_value_from_sysfs('/sys/class/net/%s/bridge/max_age'
2468 % bridge, self._conv_value_to_user)
2469
2470 @staticmethod
2471 def bridge_set_pathcost(bridge, port, pathcost):
2472 if not LinkUtils.bridge_utils_is_installed:
2473 return
2474 utils.exec_command('%s setpathcost %s %s %s' % (utils.brctl_cmd, bridge, port, pathcost))
2475
2476 def bridge_get_pathcost(self, bridge, port):
2477 return self.read_file_oneline('/sys/class/net/%s/brport/path_cost'
2478 % port)
2479
2480 @staticmethod
2481 def bridge_set_portprio(bridge, port, prio):
2482 if not LinkUtils.bridge_utils_is_installed:
2483 return
2484 utils.exec_command('%s setportprio %s %s %s' % (utils.brctl_cmd, bridge, port, prio))
2485
2486 def bridge_get_portprio(self, bridge, port):
2487 return self.read_file_oneline('/sys/class/net/%s/brport/priority'
2488 % port)
2489
2490 @staticmethod
2491 def bridge_set_hashmax(bridge, hashmax):
2492 if not LinkUtils.bridge_utils_is_installed:
2493 return
2494 utils.exec_command('%s sethashmax %s %s' % (utils.brctl_cmd, bridge, hashmax))
2495
2496 def bridge_get_hashmax(self, bridge):
2497 return self.read_file_oneline('/sys/class/net/%s/bridge/hash_max'
2498 % bridge)
2499
2500 @staticmethod
2501 def bridge_set_hashel(bridge, hashel):
2502 if not LinkUtils.bridge_utils_is_installed:
2503 return
2504 utils.exec_command('%s sethashel %s %s' % (utils.brctl_cmd, bridge, hashel))
2505
2506 def bridge_get_hashel(self, bridge):
2507 return self.read_file_oneline('/sys/class/net/%s/bridge/hash_elasticity'
2508 % bridge)
2509
2510 @staticmethod
2511 def bridge_set_mclmc(bridge, mclmc):
2512 if not LinkUtils.bridge_utils_is_installed:
2513 return
2514 utils.exec_command('%s setmclmc %s %s' % (utils.brctl_cmd, bridge, mclmc))
2515
2516 def bridge_get_mclmc(self, bridge):
2517 return self.read_file_oneline(
2518 '/sys/class/net/%s/bridge/multicast_last_member_count'
2519 % bridge)
2520
2521 @staticmethod
2522 def bridge_set_mcrouter(bridge, mcrouter):
2523 if not LinkUtils.bridge_utils_is_installed:
2524 return
2525 utils.exec_command('%s setmcrouter %s %s' % (utils.brctl_cmd, bridge, mcrouter))
2526
2527 def bridge_get_mcrouter(self, bridge):
2528 return self.read_file_oneline(
2529 '/sys/class/net/%s/bridge/multicast_router' % bridge)
2530
2531 @staticmethod
2532 def bridge_set_mcsnoop(bridge, mcsnoop):
2533 if not LinkUtils.bridge_utils_is_installed:
2534 return
2535 utils.exec_command('%s setmcsnoop %s %s' % (utils.brctl_cmd, bridge, mcsnoop))
2536
2537 def bridge_get_mcsnoop(self, bridge):
2538 return self.read_file_oneline(
2539 '/sys/class/net/%s/bridge/multicast_snooping' % bridge)
2540
2541 @staticmethod
2542 def bridge_set_mcsqc(bridge, mcsqc):
2543 if not LinkUtils.bridge_utils_is_installed:
2544 return
2545 utils.exec_command('%s setmcsqc %s %s' % (utils.brctl_cmd, bridge, mcsqc))
2546
2547 def bridge_get_mcsqc(self, bridge):
2548 return self.read_file_oneline(
2549 '/sys/class/net/%s/bridge/multicast_startup_query_count'
2550 % bridge)
2551
2552 @staticmethod
2553 def bridge_set_mcqifaddr(bridge, mcqifaddr):
2554 if not LinkUtils.bridge_utils_is_installed:
2555 return
2556 utils.exec_command('%s setmcqifaddr %s %s' % (utils.brctl_cmd, bridge, mcqifaddr))
2557
2558 def bridge_get_mcqifaddr(self, bridge):
2559 return self.read_file_oneline(
2560 '/sys/class/net/%s/bridge/multicast_startup_query_use_ifaddr'
2561 % bridge)
2562
2563 @staticmethod
2564 def bridge_set_mcquerier(bridge, mcquerier):
2565 if not LinkUtils.bridge_utils_is_installed:
2566 return
2567 utils.exec_command('%s setmcquerier %s %s' % (utils.brctl_cmd, bridge, mcquerier))
2568
2569 def bridge_get_mcquerier(self, bridge):
2570 return self.read_file_oneline(
2571 '/sys/class/net/%s/bridge/multicast_querier' % bridge)
2572
2573 def bridge_set_mcqv4src(self, bridge, vlan, mcquerier):
2574 try:
2575 vlan = int(vlan)
2576 except:
2577 self.logger.info('%s: set mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
2578 return
2579 if vlan == 0 or vlan > 4095:
2580 self.logger.warn('mcqv4src vlan \'%d\' invalid range' % vlan)
2581 return
2582
2583 ip = mcquerier.split('.')
2584 if len(ip) != 4:
2585 self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
2586 return
2587 for k in ip:
2588 if not k.isdigit() or int(k, 10) < 0 or int(k, 10) > 255:
2589 self.logger.warn('mcqv4src \'%s\' invalid IPv4 address' % mcquerier)
2590 return
2591
2592 if not LinkUtils.bridge_utils_is_installed:
2593 return
2594
2595 utils.exec_command('%s setmcqv4src %s %d %s' %
2596 (utils.brctl_cmd, bridge, vlan, mcquerier))
2597
2598 def bridge_del_mcqv4src(self, bridge, vlan):
2599 if not LinkUtils.bridge_utils_is_installed:
2600 return
2601 try:
2602 vlan = int(vlan)
2603 except:
2604 self.logger.info('%s: del mcqv4src vlan: invalid parameter %s: %s' %(bridge, vlan, str(e)))
2605 return
2606 utils.exec_command('%s delmcqv4src %s %d' % (utils.brctl_cmd, bridge, vlan))
2607
2608 def bridge_get_mcqv4src(self, bridge, vlan=None):
2609 if not LinkUtils.bridge_utils_is_installed:
2610 return {}
2611 if not self.supported_command['showmcqv4src']:
2612 return {}
2613 mcqv4src = {}
2614 try:
2615 mcqout = utils.exec_command('%s showmcqv4src %s' %
2616 (utils.brctl_cmd, bridge))
2617 except Exception as e:
2618 s = str(e).lower()
2619 if 'never heard' in s:
2620 msg = ('%s showmcqv4src: skipping unsupported command'
2621 % utils.brctl_cmd)
2622 self.logger.info(msg)
2623 self.supported_command['showmcqv4src'] = False
2624 return {}
2625 raise
2626 if not mcqout:
2627 return {}
2628 mcqlines = mcqout.splitlines()
2629 for l in mcqlines[1:]:
2630 l = l.strip()
2631 k, d, v = l.split('\t')
2632 if not k or not v:
2633 continue
2634 mcqv4src[k] = v
2635 if vlan:
2636 return mcqv4src.get(vlan)
2637 return mcqv4src
2638
d00f5278
JF
2639 def bridge_get_mcqv4src_sysfs(self, bridge, vlan=None):
2640 if not LinkUtils.bridge_utils_is_installed:
2641 return {}
2642 if not self.supported_command['showmcqv4src']:
2643 return {}
2644 if ifupdownflags.flags.PERFMODE:
2645 return {}
2646 mcqv4src = {}
2647 try:
2648 filename = '/sys/class/net/%s/bridge/multicast_v4_queriers' % bridge
2649 if os.path.exists(filename):
2650 for line in self.read_file(filename) or []:
2651 vlan_id, ip = line.split('=')
2652 mcqv4src[vlan_id] = ip.strip()
2653 except Exception as e:
2654 s = str(e).lower()
2655 msg = ('%s showmcqv4src: skipping unsupported command'
2656 % utils.brctl_cmd)
2657 self.logger.info(msg)
2658 self.supported_command['showmcqv4src'] = False
2659 return {}
2660 if vlan:
2661 return mcqv4src.get(vlan)
2662 return mcqv4src
2663
d486dd0d
JF
2664 @staticmethod
2665 def bridge_set_mclmi(bridge, mclmi):
2666 if not LinkUtils.bridge_utils_is_installed:
2667 return
2668 utils.exec_command('%s setmclmi %s %s' % (utils.brctl_cmd, bridge, mclmi))
2669
2670 def bridge_get_mclmi(self, bridge):
2671 return self.read_file_oneline(
2672 '/sys/class/net/%s/bridge/multicast_last_member_interval'
2673 % bridge)
2674
2675 @staticmethod
2676 def bridge_set_mcmi(bridge, mcmi):
2677 if not LinkUtils.bridge_utils_is_installed:
2678 return
2679 utils.exec_command('%s setmcmi %s %s' % (utils.brctl_cmd, bridge, mcmi))
2680
2681 def bridge_get_mcmi(self, bridge):
2682 return self.read_file_oneline(
2683 '/sys/class/net/%s/bridge/multicast_membership_interval'
2684 % bridge)
2685
2686 @staticmethod
2687 def bridge_exists(bridge):
2688 return os.path.exists('/sys/class/net/%s/bridge' % bridge)
2689
2690 @staticmethod
2691 def is_bridge_port(ifacename):
2692 return os.path.exists('/sys/class/net/%s/brport' % ifacename)
2693
2694 @staticmethod
2695 def bridge_port_exists(bridge, bridgeportname):
2696 try:
2697 return os.path.exists('/sys/class/net/%s/brif/%s' % (bridge, bridgeportname))
2698 except:
2699 return False
2700
2701 @staticmethod
2702 def get_bridge_ports(bridgename):
2703 try:
2704 return os.listdir('/sys/class/net/%s/brif/' % bridgename)
2705 except:
2706 return []
3fc54eef 2707
c4cc1f86
JF
2708 def reset_addr_cache(self, ifname):
2709 try:
2710 linkCache.links[ifname]['addrs'] = {}
2711 self.logger.debug('%s: reset address cache' % ifname)
2712 except:
2713 pass
2714
007cae35
JF
2715 def get_ipv6_addrgen_mode(self, ifname):
2716 try:
2717 return self._cache_get('link', [ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE]
2718 except:
2719 # default to 0 (eui64)
2720 return 0
2721
2722 def ipv6_addrgen(self, ifname, addrgen, link_created):
2723 try:
2724 # IFLA_INET6_ADDR_GEN_MODE values:
2725 # 0 = eui64
2726 # 1 = none
2727 if self._link_cache_get([ifname, 'af_spec', socket.AF_INET6])[Link.IFLA_INET6_ADDR_GEN_MODE] == addrgen:
2728 self.logger.debug('%s: ipv6 addrgen already %s' % (ifname, 'off' if addrgen else 'on'))
2729 return
8329d2b8 2730
8329d2b8
JF
2731 disabled_ipv6 = self.read_file_oneline('/proc/sys/net/ipv6/conf/%s/disable_ipv6' % ifname)
2732 if not disabled_ipv6 or int(disabled_ipv6) == 1:
2733 self.logger.info('%s: cannot set addrgen: ipv6 is disabled on this device' % ifname)
2734 return
2735
d4019ee9
JF
2736 if int(self._link_cache_get([ifname, 'mtu'])) < 1280:
2737 self.logger.info('%s: ipv6 addrgen is disabled on device with MTU '
2738 'lower than 1280: cannot set addrgen %s' % (ifname, 'off' if addrgen else 'on'))
2739 return
8329d2b8
JF
2740 except (KeyError, TypeError):
2741 self.logger.debug('%s: ipv6 addrgen probably not supported or disabled on this device' % ifname)
2742 return
2743 except Exception:
007cae35
JF
2744 pass
2745
2746 if not link_created:
2747 # When setting addrgenmode it is necessary to flap the macvlan
2748 # device. After flapping the device we also need to re-add all
2749 # the user configuration. The best way to add the user config
2750 # is to flush our internal address cache
2751 self.reset_addr_cache(ifname)
2752
2753 cmd = 'link set dev %s addrgenmode %s' % (ifname, 'none' if addrgen else 'eui64')
3fc54eef
JF
2754
2755 is_link_up = self.is_link_up(ifname)
2756
2757 if is_link_up:
2758 self.link_down(ifname)
2759
2760 if LinkUtils.ipbatch:
2761 self.add_to_batch(cmd)
2762 else:
2763 utils.exec_command('%s %s' % (utils.ip_cmd, cmd))
2764
2765 if is_link_up:
2766 self.link_up(ifname)