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