3 # Copyright 2014-2017 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
16 import ifupdown2
.ifupdown
.policymanager
as policymanager
17 import ifupdown2
.ifupdown
.ifupdownflags
as ifupdownflags
18 from ifupdown2
.ifupdown
.statemanager
import statemanager_api
as statemanager
20 from ifupdown2
.ifupdown
.iface
import *
21 from ifupdown2
.ifupdown
.utils
import utils
22 from ifupdown2
.ifupdown
.netlink
import netlink
24 from ifupdown2
.ifupdownaddons
.dhclient
import dhclient
25 from ifupdown2
.ifupdownaddons
.utilsbase
import *
26 from ifupdown2
.ifupdownaddons
.LinkUtils
import LinkUtils
27 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
29 import ifupdown
.policymanager
as policymanager
30 import ifupdown
.ifupdownflags
as ifupdownflags
31 from ifupdown
.statemanager
import statemanager_api
as statemanager
33 from ifupdown
.iface
import *
34 from ifupdown
.utils
import utils
35 from ifupdown
.netlink
import netlink
37 from ifupdownaddons
.dhclient
import dhclient
38 from ifupdownaddons
.utilsbase
import *
39 from ifupdownaddons
.LinkUtils
import LinkUtils
40 from ifupdownaddons
.modulebase
import moduleBase
46 class vrf(moduleBase
):
47 """ ifupdown2 addon module to configure vrfs """
48 _modinfo
= { 'mhelp' : 'vrf configuration module',
51 {'help' : 'vrf device routing table id. key to ' +
52 'creating a vrf device. ' +
53 'Table id is either \'auto\' or '+
54 '\'valid routing table id\'',
55 'validvals': ['auto', '<number>'],
56 'example': ['vrf-table auto', 'vrf-table 1001']},
58 {'help' : 'vrf the interface is part of.',
59 'validvals': ['<text>'],
60 'example': ['vrf blue']}}}
62 iproute2_vrf_filename
= '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
63 iproute2_vrf_filehdr
= '# This file is autogenerated by ifupdown2.\n' + \
64 '# It contains the vrf name to table mapping.\n' + \
65 '# Reserved table range %s %s\n'
66 VRF_TABLE_START
= 1001
69 system_reserved_rt_tables
= {'255' : 'local', '254' : 'main',
70 '253' : 'default', '0' : 'unspec'}
72 def __init__(self
, *args
, **kargs
):
73 moduleBase
.__init
__(self
, *args
, **kargs
)
76 self
.dhclientcmd
= None
77 self
.name
= self
.__class
__.__name
__
78 self
.vrf_mgmt_devname
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-mgmt-devname')
80 self
.user_reserved_vrf_table
= []
82 if (ifupdownflags
.flags
.PERFMODE
and
83 not (self
.vrf_mgmt_devname
and os
.path
.exists('/sys/class/net/%s'
84 %self
.vrf_mgmt_devname
))):
85 # if perf mode is set (PERFMODE is set at boot), and this is the first
86 # time we are calling ifup at boot (check for mgmt vrf existance at
87 # boot, make sure this is really the first invocation at boot.
88 # ifup is called with PERFMODE at boot multiple times (once for mgmt vrf
89 # and the second time with all auto interfaces). We want to delete
90 # the map file only the first time. This is to avoid accidently
91 # deleting map file with a valid mgmt vrf entry
92 if os
.path
.exists(self
.iproute2_vrf_filename
):
94 self
.logger
.info('vrf: removing file %s'
95 %self
.iproute2_vrf_filename
)
96 os
.remove(self
.iproute2_vrf_filename
)
98 self
.logger
.debug('vrf: removing file failed (%s)'
101 ip_rules
= utils
.exec_command('%s rule show'
102 %utils
.ip_cmd
).splitlines()
103 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
105 self
.ip_rule_cache
= []
106 self
.logger
.warn('vrf: cache v4: %s' % str(e
))
109 ip_rules
= utils
.exec_command('%s -6 rule show'
110 %utils
.ip_cmd
).splitlines()
111 self
.ip6_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
113 self
.ip6_rule_cache
= []
114 self
.logger
.warn('vrf: cache v6: %s' % str(e
))
116 #self.logger.debug("vrf: ip rule cache")
117 #self.logger.info(self.ip_rule_cache)
119 #self.logger.info("vrf: ip -6 rule cache")
120 #self.logger.info(self.ip6_rule_cache)
122 self
.l3mdev_checked
= False
123 self
.l3mdev4_rule
= False
124 if self
._l3mdev
_rule
(self
.ip_rule_cache
):
125 self
.l3mdev4_rule
= True
126 self
.l3mdev_checked
= True
127 self
.l3mdev6_rule
= False
128 if self
._l3mdev
_rule
(self
.ip6_rule_cache
):
129 self
.l3mdev6_rule
= True
130 self
.l3mdev_checked
= True
131 self
._iproute
2_vrf
_map
_initialized
= False
132 self
.iproute2_vrf_map
= {}
133 self
.iproute2_vrf_map_fd
= None
134 self
.iproute2_vrf_map_sync_to_disk
= False
136 self
.vrf_table_id_start
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-start')
137 if not self
.vrf_table_id_start
:
138 self
.vrf_table_id_start
= self
.VRF_TABLE_START
139 self
.vrf_table_id_end
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-end')
140 if not self
.vrf_table_id_end
:
141 self
.vrf_table_id_end
= self
.VRF_TABLE_END
143 self
._modinfo
['attrs']['vrf-table']['validrange'] = [
144 str(self
.vrf_table_id_start
),
145 str(self
.vrf_table_id_end
)
148 self
.vrf_max_count
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-max-count')
150 self
.vrf_fix_local_table
= True
152 self
.vrf_helper
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-helper')
153 self
.vrf_close_socks_on_down
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-close-socks-on-down')
154 self
.warn_on_vrf_map_write_err
= True
156 def _check_vrf_table_id(self
, ifaceobj
):
157 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
160 if (vrf_table
!= 'auto' and
161 (int(vrf_table
) < self
.vrf_table_id_start
or
162 int(vrf_table
) > self
.vrf_table_id_end
)):
163 self
.logger
.error('%s: vrf table id %s out of reserved range [%d,%d]'
166 self
.vrf_table_id_start
,
167 self
.vrf_table_id_end
))
171 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
172 if ifaceobj
.link_kind
& ifaceLinkKind
.VRF
:
174 check_vrf_table_id
= self
._check
_vrf
_table
_id
(ifaceobj
)
175 check_vrf_sys_names
= self
._check
_vrf
_system
_reserved
_names
(ifaceobj
)
176 return check_vrf_table_id
and check_vrf_sys_names
177 except Exception as e
:
178 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
182 def _check_vrf_system_reserved_names(self
, ifaceobj
):
183 system_reserved_names
= self
.system_reserved_rt_tables
.values()
184 if ifaceobj
.name
in system_reserved_names
:
185 self
.log_error('cannot use system reserved %s vrf names'
186 % (str(system_reserved_names
)), ifaceobj
)
190 def _iproute2_vrf_map_initialize(self
, writetodisk
=True):
191 if self
._iproute
2_vrf
_map
_initialized
:
194 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
195 self
.iproute2_vrf_map
= {}
196 iproute2_vrf_map_force_rewrite
= False
197 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
198 if os
.path
.exists(self
.iproute2_vrf_filename
):
199 with
open(self
.iproute2_vrf_filename
, 'r+') as vrf_map_fd
:
200 lines
= vrf_map_fd
.readlines()
206 (table
, vrf_name
) = l
.strip().split()
207 if self
.iproute2_vrf_map
.get(int(table
)):
208 # looks like the existing file has
209 # duplicate entries, force rewrite of the
211 iproute2_vrf_map_force_rewrite
= True
213 self
.iproute2_vrf_map
[int(table
)] = vrf_name
215 self
.logger
.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l
, str(e
)))
218 vrfs
= self
.ipcmd
.link_get_vrfs()
221 for v
, lattrs
in vrfs
.iteritems():
222 table
= lattrs
.get('table', None)
224 running_vrf_map
[int(table
)] = v
226 if (not running_vrf_map
or (running_vrf_map
!= self
.iproute2_vrf_map
)):
227 self
.iproute2_vrf_map
= running_vrf_map
228 iproute2_vrf_map_force_rewrite
= True
230 self
.iproute2_vrf_map_fd
= None
232 if iproute2_vrf_map_force_rewrite
:
233 # reopen the file and rewrite the map
234 self
._iproute
2_vrf
_map
_open
(True, False)
236 self
._iproute
2_vrf
_map
_open
(False, True)
238 self
.iproute2_vrf_map_sync_to_disk
= False
239 atexit
.register(self
._iproute
2_vrf
_map
_sync
_to
_disk
)
241 self
.logger
.info("vrf: dumping iproute2_vrf_map")
242 self
.logger
.info(self
.iproute2_vrf_map
)
244 last_used_vrf_table
= None
245 for t
in range(self
.vrf_table_id_start
,
246 self
.vrf_table_id_end
):
247 if not self
.iproute2_vrf_map
.get(t
):
249 last_used_vrf_table
= t
250 self
.last_used_vrf_table
= last_used_vrf_table
251 self
._iproute
2_vrf
_map
_initialized
= True
252 self
.vrf_count
= len(self
.iproute2_vrf_map
)
254 def _iproute2_map_warn(self
, errstr
):
255 if self
.warn_on_vrf_map_write_err
:
256 if not os
.path
.exists('/etc/iproute2/rt_tables.d/'):
257 self
.logger
.info('unable to save iproute2 vrf to table ' +
258 'map (%s)\n' %errstr
)
259 self
.logger
.info('cannot find /etc/iproute2/rt_tables.d.' +
260 ' pls check if your iproute2 version' +
261 ' supports rt_tables.d')
263 self
.logger
.warn('unable to open iproute2 vrf to table ' +
264 'map (%s)\n' %errstr
)
265 self
.warn_on_vrf_map_write_err
= False
267 def _iproute2_vrf_map_sync_to_disk(self
):
268 if (ifupdownflags
.flags
.DRYRUN
or
269 not self
.iproute2_vrf_map_sync_to_disk
):
271 self
.logger
.info('vrf: syncing table map to %s'
272 %self
.iproute2_vrf_filename
)
274 with
open(self
.iproute2_vrf_filename
, 'w') as f
:
275 f
.write(self
.iproute2_vrf_filehdr
%(self
.vrf_table_id_start
,
276 self
.vrf_table_id_end
))
277 for t
, v
in self
.iproute2_vrf_map
.iteritems():
278 f
.write('%s %s\n' %(t
, v
))
281 self
._iproute
2_map
_warn
(str(e
))
284 def _iproute2_vrf_map_open(self
, sync_vrfs
=False, append
=False):
285 self
.logger
.info('vrf: syncing table map to %s'
286 %self
.iproute2_vrf_filename
)
287 if ifupdownflags
.flags
.DRYRUN
:
289 fmode
= 'a+' if append
else 'w'
291 self
.iproute2_vrf_map_fd
= open(self
.iproute2_vrf_filename
,
293 fcntl
.fcntl(self
.iproute2_vrf_map_fd
, fcntl
.F_SETFD
, fcntl
.FD_CLOEXEC
)
295 self
._iproute
2_map
_warn
(str(e
))
300 self
.iproute2_vrf_map_fd
.write(self
.iproute2_vrf_filehdr
301 %(self
.vrf_table_id_start
,
302 self
.vrf_table_id_end
))
303 for t
, v
in self
.iproute2_vrf_map
.iteritems():
304 self
.iproute2_vrf_map_fd
.write('%s %s\n' %(t
, v
))
305 self
.iproute2_vrf_map_fd
.flush()
307 def _is_vrf(self
, ifaceobj
):
308 if ifaceobj
.get_attr_value_first('vrf-table'):
312 def get_upper_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
313 """ Returns list of interfaces dependent on ifaceobj """
315 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
317 ifaceobj
.link_type
= ifaceLinkType
.LINK_MASTER
318 ifaceobj
.link_kind |
= ifaceLinkKind
.VRF
319 ifaceobj
.role |
= ifaceRole
.MASTER
321 if vrf_table
!= 'auto':
322 # if the user didn't specify auto we need to store the desired
323 # vrf tables ids, in case the configuration has both auto and
324 # hardcoded vrf-table ids. We need to create them all without
326 self
.user_reserved_vrf_table
.append(int(vrf_table
))
328 vrf_iface_name
= ifaceobj
.get_attr_value_first('vrf')
329 if not vrf_iface_name
:
331 ifaceobj
.link_type
= ifaceLinkType
.LINK_SLAVE
332 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.VRF_SLAVE
334 return [vrf_iface_name
]
336 def get_upper_ifacenames_running(self
, ifaceobj
):
339 def _get_iproute2_vrf_table(self
, vrf_dev_name
):
340 for t
, v
in self
.iproute2_vrf_map
.iteritems():
341 if v
== vrf_dev_name
:
345 def _get_avail_vrf_table_id(self
):
346 if self
.last_used_vrf_table
== None:
347 table_id_start
= self
.vrf_table_id_start
349 table_id_start
= self
.last_used_vrf_table
+ 1
350 for t
in range(table_id_start
, self
.vrf_table_id_end
):
351 if (not self
.iproute2_vrf_map
.get(t
)
352 and t
not in self
.user_reserved_vrf_table
):
353 self
.last_used_vrf_table
= t
357 def _iproute2_is_vrf_tableid_inuse(self
, vrfifaceobj
, table_id
):
358 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
359 if old_vrf_name
and old_vrf_name
!= vrfifaceobj
.name
:
360 self
.log_error('table id %s already assigned to vrf dev %s'
361 %(table_id
, old_vrf_name
), vrfifaceobj
)
363 def _iproute2_vrf_table_entry_add(self
, vrfifaceobj
, table_id
):
364 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
366 self
.iproute2_vrf_map
[int(table_id
)] = vrfifaceobj
.name
367 if self
.iproute2_vrf_map_fd
:
368 self
.iproute2_vrf_map_fd
.write('%s %s\n'
369 %(table_id
, vrfifaceobj
.name
))
370 self
.iproute2_vrf_map_fd
.flush()
373 if old_vrf_name
!= vrfifaceobj
.name
:
374 self
.log_error('table id %d already assigned to vrf dev %s'
375 %(table_id
, old_vrf_name
))
377 def _iproute2_vrf_table_entry_del(self
, table_id
):
379 # with any del of vrf map, we need to force sync to disk
380 self
.iproute2_vrf_map_sync_to_disk
= True
381 del self
.iproute2_vrf_map
[int(table_id
)]
383 self
.logger
.info('vrf: iproute2 vrf map del failed for %s (%s)'
387 def _is_vrf_dev(self
, ifacename
):
388 # Look at iproute2 map for now.
389 # If it was a master we knew about,
390 # it is definately there
391 if ifacename
in self
.iproute2_vrf_map
.values():
395 def _is_dhcp_slave(self
, ifaceobj
):
396 if (not ifaceobj
.addr_method
or
397 (ifaceobj
.addr_method
!= 'dhcp' and
398 ifaceobj
.addr_method
!= 'dhcp6')):
402 def _up_vrf_slave_without_master(self
, ifacename
, vrfname
, ifaceobj
, vrf_master_objs
, ifaceobj_getfunc
=None):
403 """ If we have a vrf slave that has dhcp configured, bring up the
404 vrf master now. This is needed because vrf has special handling
405 in dhclient hook which requires the vrf master to be present """
407 if len(ifaceobj
.upperifaces
) > 1 and ifaceobj_getfunc
:
408 for upper_iface
in ifaceobj
.upperifaces
:
409 upper_ifaceobjs
= ifaceobj_getfunc(upper_iface
)
412 for upper_obj
in upper_ifaceobjs
:
413 if upper_obj
.link_kind
& ifaceLinkKind
.VRF
:
414 vrf_master
= upper_obj
.name
416 elif ifaceobj
.upperifaces
:
417 vrf_master
= ifaceobj
.upperifaces
[0]
419 self
.logger
.warn('%s: vrf master not found' %ifacename
)
421 if os
.path
.exists('/sys/class/net/%s' %vrf_master
):
422 self
.logger
.info('%s: vrf master %s exists returning'
423 %(ifacename
, vrf_master
))
425 self
.logger
.info('%s: bringing up vrf master %s'
426 %(ifacename
, vrf_master
))
427 for mobj
in vrf_master_objs
:
428 vrf_table
= mobj
.get_attr_value_first('vrf-table')
430 if vrf_table
== 'auto':
431 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
433 self
.log_error('%s: unable to get an auto table id'
434 %mobj
.name
, ifaceobj
)
435 self
.logger
.info('%s: table id auto: selected table id %s\n'
436 %(mobj
.name
, vrf_table
))
438 self
._up
_vrf
_dev
(mobj
, vrf_table
, False)
442 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
443 self
.ipcmd
.link_set(ifacename
, 'master', vrfname
)
444 self
.enable_ipv6(ifacename
)
447 def enable_ipv6(self
, ifname
):
449 Only enable ipv6 on former bridge port
450 - workaround for intf moved from bridge port to VRF slave
453 self
.write_file("/proc/sys/net/ipv6/conf/%s/disable_ipv6" % ifname
, "0")
455 self
.logger
.info(str(e
))
457 def _down_dhcp_slave(self
, ifaceobj
, vrfname
):
459 dhclient_cmd_prefix
= None
460 if (vrfname
and self
.vrf_exec_cmd_prefix
and
461 self
.ipcmd
.link_exists(vrfname
)):
462 dhclient_cmd_prefix
= '%s %s' %(self
.vrf_exec_cmd_prefix
,
464 self
.dhclientcmd
.release(ifaceobj
.name
, dhclient_cmd_prefix
)
466 # ignore any dhclient release errors
469 def _handle_existing_connections(self
, ifaceobj
, vrfname
):
470 if not ifaceobj
or ifupdownflags
.flags
.PERFMODE
:
472 if (self
.vrf_mgmt_devname
and
473 self
.vrf_mgmt_devname
== vrfname
):
474 self
._kill
_ssh
_connections
(ifaceobj
.name
)
475 if self
._is
_dhcp
_slave
(ifaceobj
):
476 self
._down
_dhcp
_slave
(ifaceobj
, vrfname
)
478 def _up_vrf_slave(self
, ifacename
, vrfname
, ifaceobj
=None,
479 ifaceobj_getfunc
=None, vrf_exists
=False):
482 if vrf_exists
or self
.ipcmd
.link_exists(vrfname
):
483 uppers
= self
.ipcmd
.link_get_uppers(ifacename
)
484 if not uppers
or vrfname
not in uppers
:
485 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
486 self
.ipcmd
.link_set(ifacename
, 'master', vrfname
)
487 self
.enable_ipv6(ifacename
)
489 vrf_master_objs
= ifaceobj_getfunc(vrfname
)
490 if not vrf_master_objs
:
491 # this is the case where vrf is assigned to an interface
492 # but user has not provided a vrf interface.
493 # people expect you to warn them but go ahead with the
494 # rest of the config on that interface
495 netlink
.link_set_updown(ifacename
, "up")
496 self
.log_error('vrf master ifaceobj %s not found'
499 if (ifupdownflags
.flags
.ALL
or
500 ifupdownflags
.flags
.WITH_DEPENDS
or
501 (ifupdownflags
.flags
.CLASS
and
502 ifaceobj
.classes
and vrf_master_objs
[0].classes
and
503 Set(ifaceobj
.classes
).intersection(vrf_master_objs
[0].classes
))):
504 self
._up
_vrf
_slave
_without
_master
(ifacename
, vrfname
,
509 master_exists
= False
511 master_exists
= False
513 if not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
514 netlink
.link_set_updown(ifacename
, "up")
516 self
.log_error('vrf %s not around, skipping vrf config'
517 %(vrfname), ifaceobj
)
519 self
.log_error('%s: %s' %(ifacename
, str(e
)), ifaceobj
)
521 def _del_vrf_rules(self
, vrf_dev_name
, vrf_table
):
523 ip_rule_out_format
= '%s: from all %s %s lookup %s'
524 ip_rule_cmd
= '%s %s rule del pref %s %s %s table %s'
526 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
527 if rule
in self
.ip_rule_cache
:
528 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
529 '', pref
, 'oif', vrf_dev_name
,
531 utils
.exec_command(rule_cmd
)
533 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
534 if rule
in self
.ip_rule_cache
:
535 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
536 '', pref
, 'iif', vrf_dev_name
,
538 utils
.exec_command(rule_cmd
)
540 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
541 if rule
in self
.ip6_rule_cache
:
542 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
543 '-6', pref
, 'oif', vrf_dev_name
,
545 utils
.exec_command(rule_cmd
)
547 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
548 if rule
in self
.ip6_rule_cache
:
549 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
550 '-6', pref
, 'iif', vrf_dev_name
,
552 utils
.exec_command(rule_cmd
)
554 def _l3mdev_rule(self
, ip_rules
):
555 for rule
in ip_rules
:
556 if not re
.search(r
"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
562 def _rule_cache_fill(self
):
563 ip_rules
= utils
.exec_command('%s rule show'
564 %utils
.ip_cmd
).splitlines()
565 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
566 self
.l3mdev4_rule
= self
._l3mdev
_rule
(self
.ip_rule_cache
)
567 ip_rules
= utils
.exec_command('%s -6 rule show'
568 %utils
.ip_cmd
).splitlines()
569 self
.ip6_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
570 self
.l3mdev6_rule
= self
._l3mdev
_rule
(self
.ip6_rule_cache
)
572 def _add_vrf_rules(self
, vrf_dev_name
, vrf_table
):
574 ip_rule_out_format
= '%s: from all %s %s lookup %s'
575 ip_rule_cmd
= '%s %s rule add pref %s %s %s table %s'
576 if self
.vrf_fix_local_table
:
577 self
.vrf_fix_local_table
= False
578 rule
= '0: from all lookup local'
579 if rule
in self
.ip_rule_cache
:
581 utils
.exec_command('%s rule del pref 0'
583 utils
.exec_command('%s rule add pref 32765 table local'
586 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
588 if rule
in self
.ip6_rule_cache
:
590 utils
.exec_command('%s -6 rule del pref 0'
592 utils
.exec_command('%s -6 rule add pref 32765 table local'
595 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
598 if not self
.l3mdev_checked
:
599 self
._rule
_cache
_fill
()
600 self
.l3mdev_checked
= True
602 #200: from all oif blue lookup blue
603 #200: from all iif blue lookup blue
605 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
606 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
607 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
608 '', pref
, 'oif', vrf_dev_name
,
610 utils
.exec_command(rule_cmd
)
612 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
613 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
614 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
615 '', pref
, 'iif', vrf_dev_name
,
617 utils
.exec_command(rule_cmd
)
619 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
620 if not self
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
621 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
622 '-6', pref
, 'oif', vrf_dev_name
,
624 utils
.exec_command(rule_cmd
)
626 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
627 if not self
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
628 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
629 '-6', pref
, 'iif', vrf_dev_name
,
631 utils
.exec_command(rule_cmd
)
633 def _is_address_virtual_slaves(self
, vrfobj
, config_vrfslaves
,
635 # Address virtual lines on a vrf slave will create
636 # macvlan devices on the vrf slave and enslave them
637 # to the vrf master. This function checks if the
638 # vrf slave is such a macvlan interface.
639 # XXX: additional possible checks that can be done here
641 # - check if it is also a macvlan device of the
642 # format <vrf_slave>-v<int> created by the
643 # address virtual module
644 vrfslave_lowers
= self
.ipcmd
.link_get_lowers(vrfslave
)
646 if vrfslave_lowers
[0] in config_vrfslaves
:
650 def _add_vrf_slaves(self
, ifaceobj
, ifaceobj_getfunc
=None):
651 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
652 config_slaves
= ifaceobj
.lowerifaces
653 if not config_slaves
and not running_slaves
:
656 if not config_slaves
: config_slaves
= []
657 if not running_slaves
: running_slaves
= []
658 add_slaves
= set(config_slaves
).difference(set(running_slaves
))
659 del_slaves
= set(running_slaves
).difference(set(config_slaves
))
663 if not self
.ipcmd
.link_exists(s
):
667 sobj
= ifaceobj_getfunc(s
)
668 self
._up
_vrf
_slave
(s
, ifaceobj
.name
,
669 sobj
[0] if sobj
else None,
670 ifaceobj_getfunc
, True)
672 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
677 if self
._is
_address
_virtual
_slaves
(ifaceobj
,
682 sobj
= ifaceobj_getfunc(s
)
683 self
._down
_vrf
_slave
(s
, sobj
[0] if sobj
else None,
686 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
688 if ifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
689 for s
in config_slaves
:
691 for slave_ifaceobj
in ifaceobj_getfunc(s
) or []:
692 if slave_ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
693 raise Exception("link-down yes: keeping VRF slave down")
694 netlink
.link_set_updown(s
, "up")
696 self
.logger
.debug("%s: %s" % (s
, str(e
)))
699 def _set_vrf_dev_processed_flag(self
, ifaceobj
):
700 ifaceobj
.module_flags
[self
.name
] = \
701 ifaceobj
.module_flags
.setdefault(self
.name
, 0) | \
702 vrfPrivFlags
.PROCESSED
704 def _check_vrf_dev_processed_flag(self
, ifaceobj
):
705 if (ifaceobj
.module_flags
.get(self
.name
, 0) & vrfPrivFlags
.PROCESSED
):
709 def _create_vrf_dev(self
, ifaceobj
, vrf_table
):
710 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
711 self
._check
_vrf
_system
_reserved
_names
(ifaceobj
)
713 if self
.vrf_count
== self
.vrf_max_count
:
714 self
.log_error('max vrf count %d hit...not '
715 'creating vrf' % self
.vrf_count
, ifaceobj
)
716 if vrf_table
== 'auto':
717 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
719 self
.log_error('unable to get an auto table id', ifaceobj
)
720 self
.logger
.info('%s: table id auto: selected table id %s\n'
721 %(ifaceobj
.name
, vrf_table
))
723 self
._iproute
2_is
_vrf
_tableid
_inuse
(ifaceobj
, vrf_table
)
724 if ifaceobj
.name
in self
.system_reserved_rt_tables
.keys():
725 self
.log_error('cannot use system reserved %s table ids'
726 %(str(self
.system_reserved_rt_tables
.keys())),
729 if not vrf_table
.isdigit():
730 self
.log_error('vrf-table must be an integer or \'auto\'', ifaceobj
)
732 # XXX: If we decide to not allow vrf id usages out of
733 # the reserved ifupdown range, then uncomment this code.
735 if (int(vrf_table
) < self
.vrf_table_id_start
or
736 int(vrf_table
) > self
.vrf_table_id_end
):
737 self
.log_error('vrf table id %s out of reserved range [%d,%d]'
739 self
.vrf_table_id_start
,
740 self
.vrf_table_id_end
), ifaceobj
)
742 self
.ipcmd
.link_create(ifaceobj
.name
, 'vrf',
743 {'table' : '%s' %vrf_table
})
745 self
.log_error('create failed (%s)\n' % str(e
), ifaceobj
)
746 if vrf_table
!= 'auto':
747 self
._iproute
2_vrf
_table
_entry
_add
(ifaceobj
, vrf_table
)
749 if vrf_table
== 'auto':
750 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
751 if not vrf_table
and not ifupdownflags
.flags
.DRYRUN
:
752 self
.log_error('unable to get vrf table id', ifaceobj
)
754 # if the device exists, check if table id is same
755 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
757 running_table
= vrfdev_attrs
.get('table', None)
758 if vrf_table
!= running_table
:
759 self
.log_error('cannot change vrf table id,running table id'
760 ' %s is different from config id %s'
761 % (running_table
, vrf_table
), ifaceobj
)
764 def _up_vrf_helper(self
, ifaceobj
, vrf_table
):
766 if ifupdownflags
.flags
.PERFMODE
:
769 utils
.exec_command('%s create %s %s %s' %
775 def _up_vrf_dev(self
, ifaceobj
, vrf_table
, add_slaves
=True,
776 ifaceobj_getfunc
=None):
778 # if vrf dev is already processed return. This can happen
779 # if we the slave was configured before.
780 # see self._up_vrf_slave_without_master
781 if self
._check
_vrf
_dev
_processed
_flag
(ifaceobj
):
785 vrf_table
= self
._create
_vrf
_dev
(ifaceobj
, vrf_table
)
787 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
790 self
._add
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
791 self
._up
_vrf
_helper
(ifaceobj
, vrf_table
)
793 self
._add
_vrf
_slaves
(ifaceobj
, ifaceobj_getfunc
)
794 self
._set
_vrf
_dev
_processed
_flag
(ifaceobj
)
796 if not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
797 netlink
.link_set_updown(ifaceobj
.name
, "up")
799 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
801 def _kill_ssh_connections(self
, ifacename
):
803 runningaddrsdict
= self
.ipcmd
.get_running_addrs(None, ifacename
)
804 if not runningaddrsdict
:
806 iplist
= [i
.split('/', 1)[0] for i
in runningaddrsdict
.keys()]
811 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
812 #users:(("sshd",pid=2528,fd=3))
813 cmdl
= [utils
.ss_cmd
, '-t', '-p']
814 for line
in utils
.exec_commandl(cmdl
).splitlines():
815 citems
= line
.split()
818 addr
= citems
[3].split('%')[0]
819 elif ':ssh' in citems
[3]:
820 addr
= citems
[3].split(':')[0]
825 proc
.append(citems
[5].split(',')[1].split('=')[1])
830 # outpt of '/usr/bin/pstree -Aps <pid>':
831 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
832 # get the above output to following format
833 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
834 pstree
= list(reversed(utils
.exec_command('%s -Aps %s' %
835 (utils
.pstree_cmd
, os
.getpid())).strip().split('---')))
836 for index
, process
in enumerate(pstree
):
837 # check the parent of SSH process to make sure
838 # we don't kill SSH server or systemd process
839 if 'sshd' in process
and 'sshd' in pstree
[index
+ 1]:
840 pid
= filter(lambda x
: x
.isdigit(), process
)
842 self
.logger
.info("%s: killing active ssh sessions: %s"
843 %(ifacename
, str(proc
)))
845 if ifupdownflags
.flags
.DRYRUN
:
850 os
.kill(int(id), signal
.SIGINT
)
854 # Kill current SSH client
859 self
.logger
.info("fork error : %s [%d]" % (e
.strerror
, e
.errno
))
860 if (forkret
== 0): # The first child.
863 self
.logger
.info("%s: ifreload continuing in the background" %ifacename
)
864 except OSError, (err_no
, err_message
):
865 self
.logger
.info("os.setsid failed: errno=%d: %s" % (err_no
, err_message
))
866 self
.logger
.info("pid=%d pgid=%d" % (os
.getpid(), os
.getpgid(0)))
868 self
.logger
.info("%s: killing our session: %s"
869 %(ifacename
, str(proc
)))
870 os
.kill(int(pid
), signal
.SIGINT
)
875 self
.logger
.info('%s: %s' %(ifacename
, str(e
)))
877 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
879 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
881 self
._iproute
2_vrf
_map
_initialize
()
882 # This is a vrf device
883 self
._up
_vrf
_dev
(ifaceobj
, vrf_table
, True, ifaceobj_getfunc
)
885 vrf
= ifaceobj
.get_attr_value_first('vrf')
887 self
._iproute
2_vrf
_map
_initialize
()
888 # This is a vrf slave
889 self
._up
_vrf
_slave
(ifaceobj
.name
, vrf
, ifaceobj
,
891 elif not ifupdownflags
.flags
.PERFMODE
:
892 # check if we were a slave before
893 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
895 self
._iproute
2_vrf
_map
_initialize
()
896 if self
._is
_vrf
_dev
(master
):
897 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
,
900 self
.log_error(str(e
), ifaceobj
)
902 def _down_vrf_helper(self
, ifaceobj
, vrf_table
):
904 if ifupdownflags
.flags
.PERFMODE
:
907 utils
.exec_command('%s delete %s %s %s' %
913 def _close_sockets(self
, ifaceobj
, ifindex
):
914 if not self
.vrf_close_socks_on_down
:
918 utils
.exec_command('%s -aK \"dev == %s\"'
919 %(utils
.ss_cmd
, ifindex
))
921 self
.logger
.info('%s: closing socks using ss'
922 ' failed (%s)\n' %(ifaceobj
.name
, str(e
)))
925 def _down_vrf_dev(self
, ifaceobj
, vrf_table
, ifaceobj_getfunc
=None):
927 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
930 if vrf_table
== 'auto':
931 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
933 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
935 for s
in running_slaves
:
937 sobj
= ifaceobj_getfunc(s
)
939 self
._handle
_existing
_connections
(sobj
[0]
943 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
946 self
.ipcmd
.addr_flush(s
)
947 netlink
.link_set_updown(s
, "down")
949 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
953 self
._down
_vrf
_helper
(ifaceobj
, vrf_table
)
955 self
.logger
.warn('%s: %s' %(ifaceobj
.name
, str(e
)))
959 self
._del
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
961 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
964 ifindex
= self
.ipcmd
.link_get_ifindex(ifaceobj
.name
)
968 self
.ipcmd
.link_delete(ifaceobj
.name
)
970 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
973 self
._close
_sockets
(ifaceobj
, ifindex
)
976 self
._iproute
2_vrf
_table
_entry
_del
(vrf_table
)
978 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
982 def _down_vrf_slave(self
, ifacename
, ifaceobj
=None, vrfname
=None):
984 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
985 self
.ipcmd
.link_set(ifacename
, 'nomaster')
986 # Down this slave only if it is a slave ifupdown2 manages.
987 # we dont want to down slaves that maybe up'ed by
988 # somebody else. One such example is a macvlan device
989 # which ifupdown2 addressvirtual addon module auto creates
991 netlink
.link_set_updown(ifacename
, "down")
993 self
.logger
.warn('%s: %s' %(ifacename
, str(e
)))
995 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
997 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
999 self
._iproute
2_vrf
_map
_initialize
()
1000 self
._down
_vrf
_dev
(ifaceobj
, vrf_table
, ifaceobj_getfunc
)
1002 vrf
= ifaceobj
.get_attr_value_first('vrf')
1004 self
._iproute
2_vrf
_map
_initialize
()
1005 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
, None)
1006 except Exception, e
:
1007 self
.log_warn(str(e
))
1009 def _query_check_vrf_slave(self
, ifaceobj
, ifaceobjcurr
, vrf
):
1011 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
1012 if not master
or master
!= vrf
:
1013 ifaceobjcurr
.update_config_with_status('vrf', str(master
), 1)
1015 ifaceobjcurr
.update_config_with_status('vrf', master
, 0)
1016 except Exception, e
:
1017 self
.log_error(str(e
), ifaceobjcurr
)
1019 def _query_check_vrf_dev(self
, ifaceobj
, ifaceobjcurr
, vrf_table
):
1021 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
1022 self
.logger
.info('%s: vrf: does not exist' %(ifaceobj
.name
))
1024 if vrf_table
== 'auto':
1025 config_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
1027 config_table
= vrf_table
1028 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
1029 if not vrfdev_attrs
:
1030 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
1032 running_table
= vrfdev_attrs
.get('table')
1033 if not running_table
:
1034 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
1036 if config_table
!= running_table
:
1037 ifaceobjcurr
.update_config_with_status('vrf-table',
1040 ifaceobjcurr
.update_config_with_status('vrf-table',
1042 if not ifupdownflags
.flags
.WITHDEFAULTS
:
1046 utils
.exec_command('%s verify %s %s'
1048 ifaceobj
.name
, config_table
))
1049 ifaceobjcurr
.update_config_with_status('vrf-helper',
1054 except Exception, e
:
1055 ifaceobjcurr
.update_config_with_status('vrf-helper',
1061 except Exception, e
:
1062 self
.log_warn(str(e
))
1064 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
1066 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
1068 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
1069 self
._query
_check
_vrf
_dev
(ifaceobj
, ifaceobjcurr
, vrf_table
)
1071 vrf
= ifaceobj
.get_attr_value_first('vrf')
1073 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
1074 self
._query
_check
_vrf
_slave
(ifaceobj
, ifaceobjcurr
, vrf
)
1075 except Exception, e
:
1076 self
.log_warn(str(e
))
1078 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
1080 kind
= self
.ipcmd
.link_get_kind(ifaceobjrunning
.name
)
1082 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobjrunning
.name
)
1084 running_table
= vrfdev_attrs
.get('table')
1086 ifaceobjrunning
.update_config('vrf-table',
1089 slave_kind
= self
.ipcmd
.link_get_slave_kind(ifaceobjrunning
.name
)
1090 if slave_kind
== 'vrf_slave':
1091 vrf
= self
.ipcmd
.link_get_master(ifaceobjrunning
.name
)
1093 ifaceobjrunning
.update_config('vrf', vrf
)
1094 except Exception, e
:
1095 self
.log_warn(str(e
))
1097 def _query(self
, ifaceobj
, **kwargs
):
1098 if not self
.vrf_helper
:
1100 if (ifaceobj
.link_kind
& ifaceLinkKind
.VRF
):
1101 ifaceobj
.update_config('vrf-helper', '%s %s' %(self
.vrf_helper
,
1104 _run_ops
= {'pre-up' : _up
,
1105 'post-down' : _down
,
1106 'query-running' : _query_running
,
1107 'query-checkcurr' : _query_check
,
1111 """ returns list of ops supported by this module """
1112 return self
._run
_ops
.keys()
1114 def _init_command_handlers(self
):
1116 self
.ipcmd
= self
.bondcmd
= LinkUtils()
1117 if not self
.dhclientcmd
:
1118 self
.dhclientcmd
= dhclient()
1120 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1121 ifaceobj_getfunc
=None, **extra_args
):
1122 """ run bond configuration on the interface object passed as argument
1125 **ifaceobj** (object): iface object
1127 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1131 **query_ifaceobj** (object): query check ifaceobject. This is only
1132 valid when op is 'query-checkcurr'. It is an object same as
1133 ifaceobj, but contains running attribute values and its config
1134 status. The modules can use it to return queried running state
1135 of interfaces. status is success if the running state is same
1136 as user required state in ifaceobj. error otherwise.
1138 op_handler
= self
._run
_ops
.get(operation
)
1141 self
._init
_command
_handlers
()
1142 if operation
== 'query-checkcurr':
1143 op_handler(self
, ifaceobj
, query_ifaceobj
)
1145 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)