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