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
19 from ifupdown2
.ifupdown
.iface
import *
20 from ifupdown2
.ifupdown
.utils
import utils
21 from ifupdown2
.ifupdown
.netlink
import netlink
23 from ifupdown2
.ifupdownaddons
.dhclient
import dhclient
24 from ifupdown2
.ifupdownaddons
.utilsbase
import *
25 from ifupdown2
.ifupdownaddons
.LinkUtils
import LinkUtils
26 from ifupdown2
.ifupdownaddons
.modulebase
import moduleBase
28 import ifupdown
.policymanager
as policymanager
29 import ifupdown
.ifupdownflags
as ifupdownflags
31 from ifupdown
.iface
import *
32 from ifupdown
.utils
import utils
33 from ifupdown
.netlink
import netlink
35 from ifupdownaddons
.dhclient
import dhclient
36 from ifupdownaddons
.utilsbase
import *
37 from ifupdownaddons
.LinkUtils
import LinkUtils
38 from ifupdownaddons
.modulebase
import moduleBase
44 class vrf(moduleBase
):
45 """ ifupdown2 addon module to configure vrfs """
46 _modinfo
= { 'mhelp' : 'vrf configuration module',
49 {'help' : 'vrf device routing table id. key to ' +
50 'creating a vrf device. ' +
51 'Table id is either \'auto\' or '+
52 '\'valid routing table id\'',
53 'validvals': ['auto', '<number>'],
54 'example': ['vrf-table auto', 'vrf-table 1001']},
56 {'help' : 'vrf the interface is part of.',
57 'validvals': ['<text>'],
58 'example': ['vrf blue']}}}
60 iproute2_vrf_filename
= '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
61 iproute2_vrf_filehdr
= '# This file is autogenerated by ifupdown2.\n' + \
62 '# It contains the vrf name to table mapping.\n' + \
63 '# Reserved table range %s %s\n'
64 VRF_TABLE_START
= 1001
67 system_reserved_rt_tables
= {'255' : 'local', '254' : 'main',
68 '253' : 'default', '0' : 'unspec'}
70 def __init__(self
, *args
, **kargs
):
71 moduleBase
.__init
__(self
, *args
, **kargs
)
74 self
.dhclientcmd
= None
75 self
.name
= self
.__class
__.__name
__
76 self
.vrf_mgmt_devname
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-mgmt-devname')
78 self
.user_reserved_vrf_table
= []
80 if (ifupdownflags
.flags
.PERFMODE
and
81 not (self
.vrf_mgmt_devname
and os
.path
.exists('/sys/class/net/%s'
82 %self
.vrf_mgmt_devname
))):
83 # if perf mode is set (PERFMODE is set at boot), and this is the first
84 # time we are calling ifup at boot (check for mgmt vrf existance at
85 # boot, make sure this is really the first invocation at boot.
86 # ifup is called with PERFMODE at boot multiple times (once for mgmt vrf
87 # and the second time with all auto interfaces). We want to delete
88 # the map file only the first time. This is to avoid accidently
89 # deleting map file with a valid mgmt vrf entry
90 if os
.path
.exists(self
.iproute2_vrf_filename
):
92 self
.logger
.info('vrf: removing file %s'
93 %self
.iproute2_vrf_filename
)
94 os
.remove(self
.iproute2_vrf_filename
)
96 self
.logger
.debug('vrf: removing file failed (%s)'
99 ip_rules
= utils
.exec_command('%s rule show'
100 %utils
.ip_cmd
).splitlines()
101 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
103 self
.ip_rule_cache
= []
104 self
.logger
.warn('vrf: cache v4: %s' % str(e
))
107 ip_rules
= utils
.exec_command('%s -6 rule show'
108 %utils
.ip_cmd
).splitlines()
109 self
.ip6_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
111 self
.ip6_rule_cache
= []
112 self
.logger
.warn('vrf: cache v6: %s' % str(e
))
114 #self.logger.debug("vrf: ip rule cache")
115 #self.logger.info(self.ip_rule_cache)
117 #self.logger.info("vrf: ip -6 rule cache")
118 #self.logger.info(self.ip6_rule_cache)
120 self
.l3mdev_checked
= False
121 self
.l3mdev4_rule
= False
122 if self
._l3mdev
_rule
(self
.ip_rule_cache
):
123 self
.l3mdev4_rule
= True
124 self
.l3mdev_checked
= True
125 self
.l3mdev6_rule
= False
126 if self
._l3mdev
_rule
(self
.ip6_rule_cache
):
127 self
.l3mdev6_rule
= True
128 self
.l3mdev_checked
= True
129 self
._iproute
2_vrf
_map
_initialized
= False
130 self
.iproute2_vrf_map
= {}
131 self
.iproute2_vrf_map_fd
= None
132 self
.iproute2_vrf_map_sync_to_disk
= False
134 self
.vrf_table_id_start
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-start')
135 if not self
.vrf_table_id_start
:
136 self
.vrf_table_id_start
= self
.VRF_TABLE_START
137 self
.vrf_table_id_end
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-end')
138 if not self
.vrf_table_id_end
:
139 self
.vrf_table_id_end
= self
.VRF_TABLE_END
141 self
._modinfo
['attrs']['vrf-table']['validrange'] = [
142 str(self
.vrf_table_id_start
),
143 str(self
.vrf_table_id_end
)
146 self
.vrf_max_count
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-max-count')
148 self
.vrf_fix_local_table
= True
150 self
.vrf_helper
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-helper')
151 self
.vrf_close_socks_on_down
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-close-socks-on-down')
152 self
.warn_on_vrf_map_write_err
= True
154 def _check_vrf_table_id(self
, ifaceobj
):
155 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
158 if (vrf_table
!= 'auto' and
159 (int(vrf_table
) < self
.vrf_table_id_start
or
160 int(vrf_table
) > self
.vrf_table_id_end
)):
161 self
.logger
.error('%s: vrf table id %s out of reserved range [%d,%d]'
164 self
.vrf_table_id_start
,
165 self
.vrf_table_id_end
))
169 def syntax_check(self
, ifaceobj
, ifaceobj_getfunc
):
170 if ifaceobj
.link_kind
& ifaceLinkKind
.VRF
:
172 check_vrf_table_id
= self
._check
_vrf
_table
_id
(ifaceobj
)
173 check_vrf_sys_names
= self
._check
_vrf
_system
_reserved
_names
(ifaceobj
)
174 return check_vrf_table_id
and check_vrf_sys_names
175 except Exception as e
:
176 self
.logger
.error('%s: %s' % (ifaceobj
.name
, str(e
)))
180 def _check_vrf_system_reserved_names(self
, ifaceobj
):
181 system_reserved_names
= self
.system_reserved_rt_tables
.values()
182 if ifaceobj
.name
in system_reserved_names
:
183 self
.log_error('cannot use system reserved %s vrf names'
184 % (str(system_reserved_names
)), ifaceobj
)
188 def _iproute2_vrf_map_initialize(self
, writetodisk
=True):
189 if self
._iproute
2_vrf
_map
_initialized
:
192 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
193 self
.iproute2_vrf_map
= {}
194 iproute2_vrf_map_force_rewrite
= False
195 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
196 if os
.path
.exists(self
.iproute2_vrf_filename
):
197 with
open(self
.iproute2_vrf_filename
, 'r+') as vrf_map_fd
:
198 lines
= vrf_map_fd
.readlines()
204 (table
, vrf_name
) = l
.strip().split()
205 if self
.iproute2_vrf_map
.get(int(table
)):
206 # looks like the existing file has
207 # duplicate entries, force rewrite of the
209 iproute2_vrf_map_force_rewrite
= True
211 self
.iproute2_vrf_map
[int(table
)] = vrf_name
213 self
.logger
.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l
, str(e
)))
216 vrfs
= self
.ipcmd
.link_get_vrfs()
219 for v
, lattrs
in vrfs
.iteritems():
220 table
= lattrs
.get('table', None)
222 running_vrf_map
[int(table
)] = v
224 if (not running_vrf_map
or (running_vrf_map
!= self
.iproute2_vrf_map
)):
225 self
.iproute2_vrf_map
= running_vrf_map
226 iproute2_vrf_map_force_rewrite
= True
228 self
.iproute2_vrf_map_fd
= None
230 if iproute2_vrf_map_force_rewrite
:
231 # reopen the file and rewrite the map
232 self
._iproute
2_vrf
_map
_open
(True, False)
234 self
._iproute
2_vrf
_map
_open
(False, True)
236 self
.iproute2_vrf_map_sync_to_disk
= False
237 atexit
.register(self
._iproute
2_vrf
_map
_sync
_to
_disk
)
239 self
.logger
.info("vrf: dumping iproute2_vrf_map")
240 self
.logger
.info(self
.iproute2_vrf_map
)
242 last_used_vrf_table
= None
243 for t
in range(self
.vrf_table_id_start
,
244 self
.vrf_table_id_end
):
245 if not self
.iproute2_vrf_map
.get(t
):
247 last_used_vrf_table
= t
248 self
.last_used_vrf_table
= last_used_vrf_table
249 self
._iproute
2_vrf
_map
_initialized
= True
250 self
.vrf_count
= len(self
.iproute2_vrf_map
)
252 def _iproute2_map_warn(self
, errstr
):
253 if self
.warn_on_vrf_map_write_err
:
254 if not os
.path
.exists('/etc/iproute2/rt_tables.d/'):
255 self
.logger
.info('unable to save iproute2 vrf to table ' +
256 'map (%s)\n' %errstr
)
257 self
.logger
.info('cannot find /etc/iproute2/rt_tables.d.' +
258 ' pls check if your iproute2 version' +
259 ' supports rt_tables.d')
261 self
.logger
.warn('unable to open iproute2 vrf to table ' +
262 'map (%s)\n' %errstr
)
263 self
.warn_on_vrf_map_write_err
= False
265 def _iproute2_vrf_map_sync_to_disk(self
):
266 if (ifupdownflags
.flags
.DRYRUN
or
267 not self
.iproute2_vrf_map_sync_to_disk
):
269 self
.logger
.info('vrf: syncing table map to %s'
270 %self
.iproute2_vrf_filename
)
272 with
open(self
.iproute2_vrf_filename
, 'w') as f
:
273 f
.write(self
.iproute2_vrf_filehdr
%(self
.vrf_table_id_start
,
274 self
.vrf_table_id_end
))
275 for t
, v
in self
.iproute2_vrf_map
.iteritems():
276 f
.write('%s %s\n' %(t
, v
))
279 self
._iproute
2_map
_warn
(str(e
))
282 def _iproute2_vrf_map_open(self
, sync_vrfs
=False, append
=False):
283 self
.logger
.info('vrf: syncing table map to %s'
284 %self
.iproute2_vrf_filename
)
285 if ifupdownflags
.flags
.DRYRUN
:
287 fmode
= 'a+' if append
else 'w'
289 self
.iproute2_vrf_map_fd
= open(self
.iproute2_vrf_filename
,
291 fcntl
.fcntl(self
.iproute2_vrf_map_fd
, fcntl
.F_SETFD
, fcntl
.FD_CLOEXEC
)
293 self
._iproute
2_map
_warn
(str(e
))
298 self
.iproute2_vrf_map_fd
.write(self
.iproute2_vrf_filehdr
299 %(self
.vrf_table_id_start
,
300 self
.vrf_table_id_end
))
301 for t
, v
in self
.iproute2_vrf_map
.iteritems():
302 self
.iproute2_vrf_map_fd
.write('%s %s\n' %(t
, v
))
303 self
.iproute2_vrf_map_fd
.flush()
305 def _is_vrf(self
, ifaceobj
):
306 if ifaceobj
.get_attr_value_first('vrf-table'):
310 def get_upper_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
311 """ Returns list of interfaces dependent on ifaceobj """
313 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
315 ifaceobj
.link_type
= ifaceLinkType
.LINK_MASTER
316 ifaceobj
.link_kind |
= ifaceLinkKind
.VRF
317 ifaceobj
.role |
= ifaceRole
.MASTER
319 if vrf_table
!= 'auto':
320 # if the user didn't specify auto we need to store the desired
321 # vrf tables ids, in case the configuration has both auto and
322 # hardcoded vrf-table ids. We need to create them all without
324 self
.user_reserved_vrf_table
.append(int(vrf_table
))
326 vrf_iface_name
= ifaceobj
.get_attr_value_first('vrf')
327 if not vrf_iface_name
:
329 ifaceobj
.link_type
= ifaceLinkType
.LINK_SLAVE
330 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.VRF_SLAVE
332 return [vrf_iface_name
]
334 def get_upper_ifacenames_running(self
, ifaceobj
):
337 def _get_iproute2_vrf_table(self
, vrf_dev_name
):
338 for t
, v
in self
.iproute2_vrf_map
.iteritems():
339 if v
== vrf_dev_name
:
343 def _get_avail_vrf_table_id(self
):
344 if self
.last_used_vrf_table
== None:
345 table_id_start
= self
.vrf_table_id_start
347 table_id_start
= self
.last_used_vrf_table
+ 1
348 for t
in range(table_id_start
, self
.vrf_table_id_end
):
349 if (not self
.iproute2_vrf_map
.get(t
)
350 and t
not in self
.user_reserved_vrf_table
):
351 self
.last_used_vrf_table
= t
355 def _iproute2_is_vrf_tableid_inuse(self
, vrfifaceobj
, table_id
):
356 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
357 if old_vrf_name
and old_vrf_name
!= vrfifaceobj
.name
:
358 self
.log_error('table id %s already assigned to vrf dev %s'
359 %(table_id
, old_vrf_name
), vrfifaceobj
)
361 def _iproute2_vrf_table_entry_add(self
, vrfifaceobj
, table_id
):
362 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
364 self
.iproute2_vrf_map
[int(table_id
)] = vrfifaceobj
.name
365 if self
.iproute2_vrf_map_fd
:
366 self
.iproute2_vrf_map_fd
.write('%s %s\n'
367 %(table_id
, vrfifaceobj
.name
))
368 self
.iproute2_vrf_map_fd
.flush()
371 if old_vrf_name
!= vrfifaceobj
.name
:
372 self
.log_error('table id %d already assigned to vrf dev %s'
373 %(table_id
, old_vrf_name
))
375 def _iproute2_vrf_table_entry_del(self
, table_id
):
377 # with any del of vrf map, we need to force sync to disk
378 self
.iproute2_vrf_map_sync_to_disk
= True
379 del self
.iproute2_vrf_map
[int(table_id
)]
381 self
.logger
.info('vrf: iproute2 vrf map del failed for %s (%s)'
385 def _is_vrf_dev(self
, ifacename
):
386 # Look at iproute2 map for now.
387 # If it was a master we knew about,
388 # it is definately there
389 if ifacename
in self
.iproute2_vrf_map
.values():
393 def _is_dhcp_slave(self
, ifaceobj
):
394 if (not ifaceobj
.addr_method
or
395 (ifaceobj
.addr_method
!= 'dhcp' and
396 ifaceobj
.addr_method
!= 'dhcp6')):
400 def _up_vrf_slave_without_master(self
, ifacename
, vrfname
, ifaceobj
, vrf_master_objs
, ifaceobj_getfunc
=None):
401 """ If we have a vrf slave that has dhcp configured, bring up the
402 vrf master now. This is needed because vrf has special handling
403 in dhclient hook which requires the vrf master to be present """
405 if len(ifaceobj
.upperifaces
) > 1 and ifaceobj_getfunc
:
406 for upper_iface
in ifaceobj
.upperifaces
:
407 upper_ifaceobjs
= ifaceobj_getfunc(upper_iface
)
410 for upper_obj
in upper_ifaceobjs
:
411 if upper_obj
.link_kind
& ifaceLinkKind
.VRF
:
412 vrf_master
= upper_obj
.name
414 elif ifaceobj
.upperifaces
:
415 vrf_master
= ifaceobj
.upperifaces
[0]
417 self
.logger
.warn('%s: vrf master not found' %ifacename
)
419 if os
.path
.exists('/sys/class/net/%s' %vrf_master
):
420 self
.logger
.info('%s: vrf master %s exists returning'
421 %(ifacename
, vrf_master
))
423 self
.logger
.info('%s: bringing up vrf master %s'
424 %(ifacename
, vrf_master
))
425 for mobj
in vrf_master_objs
:
426 vrf_table
= mobj
.get_attr_value_first('vrf-table')
428 if vrf_table
== 'auto':
429 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
431 self
.log_error('%s: unable to get an auto table id'
432 %mobj
.name
, ifaceobj
)
433 self
.logger
.info('%s: table id auto: selected table id %s\n'
434 %(mobj
.name
, vrf_table
))
436 self
._up
_vrf
_dev
(mobj
, vrf_table
, False)
440 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
441 self
.ipcmd
.link_set(ifacename
, 'master', vrfname
)
444 def _down_dhcp_slave(self
, ifaceobj
, vrfname
):
446 dhclient_cmd_prefix
= None
447 if (vrfname
and self
.vrf_exec_cmd_prefix
and
448 self
.ipcmd
.link_exists(vrfname
)):
449 dhclient_cmd_prefix
= '%s %s' %(self
.vrf_exec_cmd_prefix
,
451 self
.dhclientcmd
.release(ifaceobj
.name
, dhclient_cmd_prefix
)
453 # ignore any dhclient release errors
456 def _handle_existing_connections(self
, ifaceobj
, vrfname
):
457 if not ifaceobj
or ifupdownflags
.flags
.PERFMODE
:
459 if (self
.vrf_mgmt_devname
and
460 self
.vrf_mgmt_devname
== vrfname
):
461 self
._kill
_ssh
_connections
(ifaceobj
.name
)
462 if self
._is
_dhcp
_slave
(ifaceobj
):
463 self
._down
_dhcp
_slave
(ifaceobj
, vrfname
)
465 def _up_vrf_slave(self
, ifacename
, vrfname
, ifaceobj
=None,
466 ifaceobj_getfunc
=None, vrf_exists
=False):
469 if vrf_exists
or self
.ipcmd
.link_exists(vrfname
):
470 uppers
= self
.ipcmd
.link_get_uppers(ifacename
)
471 if not uppers
or vrfname
not in uppers
:
472 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
473 self
.ipcmd
.link_set(ifacename
, 'master', vrfname
)
475 vrf_master_objs
= ifaceobj_getfunc(vrfname
)
476 if not vrf_master_objs
:
477 # this is the case where vrf is assigned to an interface
478 # but user has not provided a vrf interface.
479 # people expect you to warn them but go ahead with the
480 # rest of the config on that interface
481 netlink
.link_set_updown(ifacename
, "up")
482 self
.log_error('vrf master ifaceobj %s not found'
485 if (ifupdownflags
.flags
.ALL
or
486 ifupdownflags
.flags
.WITH_DEPENDS
or
487 (ifupdownflags
.flags
.CLASS
and
488 ifaceobj
.classes
and vrf_master_objs
[0].classes
and
489 Set(ifaceobj
.classes
).intersection(vrf_master_objs
[0].classes
))):
490 self
._up
_vrf
_slave
_without
_master
(ifacename
, vrfname
,
495 master_exists
= False
497 master_exists
= False
499 if not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
500 netlink
.link_set_updown(ifacename
, "up")
502 self
.log_error('vrf %s not around, skipping vrf config'
503 %(vrfname), ifaceobj
)
505 self
.log_error('%s: %s' %(ifacename
, str(e
)), ifaceobj
)
507 def _del_vrf_rules(self
, vrf_dev_name
, vrf_table
):
509 ip_rule_out_format
= '%s: from all %s %s lookup %s'
510 ip_rule_cmd
= '%s %s rule del pref %s %s %s table %s'
512 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
513 if rule
in self
.ip_rule_cache
:
514 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
515 '', pref
, 'oif', vrf_dev_name
,
517 utils
.exec_command(rule_cmd
)
519 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
520 if rule
in self
.ip_rule_cache
:
521 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
522 '', pref
, 'iif', vrf_dev_name
,
524 utils
.exec_command(rule_cmd
)
526 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
527 if rule
in self
.ip6_rule_cache
:
528 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
529 '-6', 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
.ip6_rule_cache
:
535 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
536 '-6', pref
, 'iif', vrf_dev_name
,
538 utils
.exec_command(rule_cmd
)
540 def _l3mdev_rule(self
, ip_rules
):
541 for rule
in ip_rules
:
542 if not re
.search(r
"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
548 def _rule_cache_fill(self
):
549 ip_rules
= utils
.exec_command('%s rule show'
550 %utils
.ip_cmd
).splitlines()
551 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
552 self
.l3mdev4_rule
= self
._l3mdev
_rule
(self
.ip_rule_cache
)
553 ip_rules
= utils
.exec_command('%s -6 rule show'
554 %utils
.ip_cmd
).splitlines()
555 self
.ip6_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
556 self
.l3mdev6_rule
= self
._l3mdev
_rule
(self
.ip6_rule_cache
)
558 def _add_vrf_rules(self
, vrf_dev_name
, vrf_table
):
560 ip_rule_out_format
= '%s: from all %s %s lookup %s'
561 ip_rule_cmd
= '%s %s rule add pref %s %s %s table %s'
562 if self
.vrf_fix_local_table
:
563 self
.vrf_fix_local_table
= False
564 rule
= '0: from all lookup local'
565 if rule
in self
.ip_rule_cache
:
567 utils
.exec_command('%s rule del pref 0'
569 utils
.exec_command('%s rule add pref 32765 table local'
572 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
574 if rule
in self
.ip6_rule_cache
:
576 utils
.exec_command('%s -6 rule del pref 0'
578 utils
.exec_command('%s -6 rule add pref 32765 table local'
581 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
584 if not self
.l3mdev_checked
:
585 self
._rule
_cache
_fill
()
586 self
.l3mdev_checked
= True
588 #200: from all oif blue lookup blue
589 #200: from all iif blue lookup blue
591 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
592 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
593 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
594 '', pref
, 'oif', vrf_dev_name
,
596 utils
.exec_command(rule_cmd
)
598 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
599 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
600 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
601 '', pref
, 'iif', vrf_dev_name
,
603 utils
.exec_command(rule_cmd
)
605 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
606 if not self
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
607 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
608 '-6', 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
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
614 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
615 '-6', pref
, 'iif', vrf_dev_name
,
617 utils
.exec_command(rule_cmd
)
619 def _is_address_virtual_slaves(self
, vrfobj
, config_vrfslaves
,
621 # Address virtual lines on a vrf slave will create
622 # macvlan devices on the vrf slave and enslave them
623 # to the vrf master. This function checks if the
624 # vrf slave is such a macvlan interface.
625 # XXX: additional possible checks that can be done here
627 # - check if it is also a macvlan device of the
628 # format <vrf_slave>-v<int> created by the
629 # address virtual module
630 vrfslave_lowers
= self
.ipcmd
.link_get_lowers(vrfslave
)
632 if vrfslave_lowers
[0] in config_vrfslaves
:
636 def _add_vrf_slaves(self
, ifaceobj
, ifaceobj_getfunc
=None):
637 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
638 config_slaves
= ifaceobj
.lowerifaces
639 if not config_slaves
and not running_slaves
:
642 if not config_slaves
: config_slaves
= []
643 if not running_slaves
: running_slaves
= []
644 add_slaves
= set(config_slaves
).difference(set(running_slaves
))
645 del_slaves
= set(running_slaves
).difference(set(config_slaves
))
649 if not self
.ipcmd
.link_exists(s
):
653 sobj
= ifaceobj_getfunc(s
)
654 self
._up
_vrf
_slave
(s
, ifaceobj
.name
,
655 sobj
[0] if sobj
else None,
656 ifaceobj_getfunc
, True)
658 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
663 if self
._is
_address
_virtual
_slaves
(ifaceobj
,
668 sobj
= ifaceobj_getfunc(s
)
669 self
._down
_vrf
_slave
(s
, sobj
[0] if sobj
else None,
672 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
674 if ifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
675 for s
in config_slaves
:
677 for slave_ifaceobj
in ifaceobj_getfunc(s
) or []:
678 if slave_ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
679 raise Exception("link-down yes: keeping VRF slave down")
680 netlink
.link_set_updown(s
, "up")
682 self
.logger
.debug("%s: %s" % (s
, str(e
)))
685 def _set_vrf_dev_processed_flag(self
, ifaceobj
):
686 ifaceobj
.module_flags
[self
.name
] = \
687 ifaceobj
.module_flags
.setdefault(self
.name
, 0) | \
688 vrfPrivFlags
.PROCESSED
690 def _check_vrf_dev_processed_flag(self
, ifaceobj
):
691 if (ifaceobj
.module_flags
.get(self
.name
, 0) & vrfPrivFlags
.PROCESSED
):
695 def _create_vrf_dev(self
, ifaceobj
, vrf_table
):
696 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
697 self
._check
_vrf
_system
_reserved
_names
(ifaceobj
)
699 if self
.vrf_count
== self
.vrf_max_count
:
700 self
.log_error('max vrf count %d hit...not '
701 'creating vrf' % self
.vrf_count
, ifaceobj
)
702 if vrf_table
== 'auto':
703 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
705 self
.log_error('unable to get an auto table id', ifaceobj
)
706 self
.logger
.info('%s: table id auto: selected table id %s\n'
707 %(ifaceobj
.name
, vrf_table
))
709 self
._iproute
2_is
_vrf
_tableid
_inuse
(ifaceobj
, vrf_table
)
710 if ifaceobj
.name
in self
.system_reserved_rt_tables
.keys():
711 self
.log_error('cannot use system reserved %s table ids'
712 %(str(self
.system_reserved_rt_tables
.keys())),
715 if not vrf_table
.isdigit():
716 self
.log_error('vrf-table must be an integer or \'auto\'', ifaceobj
)
718 # XXX: If we decide to not allow vrf id usages out of
719 # the reserved ifupdown range, then uncomment this code.
721 if (int(vrf_table
) < self
.vrf_table_id_start
or
722 int(vrf_table
) > self
.vrf_table_id_end
):
723 self
.log_error('vrf table id %s out of reserved range [%d,%d]'
725 self
.vrf_table_id_start
,
726 self
.vrf_table_id_end
), ifaceobj
)
728 self
.ipcmd
.link_create(ifaceobj
.name
, 'vrf',
729 {'table' : '%s' %vrf_table
})
731 self
.log_error('create failed (%s)\n' % str(e
), ifaceobj
)
732 if vrf_table
!= 'auto':
733 self
._iproute
2_vrf
_table
_entry
_add
(ifaceobj
, vrf_table
)
735 if vrf_table
== 'auto':
736 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
737 if not vrf_table
and not ifupdownflags
.flags
.DRYRUN
:
738 self
.log_error('unable to get vrf table id', ifaceobj
)
740 # if the device exists, check if table id is same
741 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
743 running_table
= vrfdev_attrs
.get('table', None)
744 if vrf_table
!= running_table
:
745 self
.log_error('cannot change vrf table id,running table id'
746 ' %s is different from config id %s'
747 % (running_table
, vrf_table
), ifaceobj
)
750 def _up_vrf_helper(self
, ifaceobj
, vrf_table
):
752 if ifupdownflags
.flags
.PERFMODE
:
755 utils
.exec_command('%s create %s %s %s' %
761 def _up_vrf_dev(self
, ifaceobj
, vrf_table
, add_slaves
=True,
762 ifaceobj_getfunc
=None):
764 # if vrf dev is already processed return. This can happen
765 # if we the slave was configured before.
766 # see self._up_vrf_slave_without_master
767 if self
._check
_vrf
_dev
_processed
_flag
(ifaceobj
):
771 vrf_table
= self
._create
_vrf
_dev
(ifaceobj
, vrf_table
)
773 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
776 self
._add
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
777 self
._up
_vrf
_helper
(ifaceobj
, vrf_table
)
779 self
._add
_vrf
_slaves
(ifaceobj
, ifaceobj_getfunc
)
780 self
._set
_vrf
_dev
_processed
_flag
(ifaceobj
)
782 if not ifaceobj
.link_privflags
& ifaceLinkPrivFlags
.KEEP_LINK_DOWN
:
783 netlink
.link_set_updown(ifaceobj
.name
, "up")
785 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
787 def _kill_ssh_connections(self
, ifacename
):
789 runningaddrsdict
= self
.ipcmd
.get_running_addrs(None, ifacename
)
790 if not runningaddrsdict
:
792 iplist
= [i
.split('/', 1)[0] for i
in runningaddrsdict
.keys()]
797 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
798 #users:(("sshd",pid=2528,fd=3))
799 cmdl
= [utils
.ss_cmd
, '-t', '-p']
800 for line
in utils
.exec_commandl(cmdl
).splitlines():
801 citems
= line
.split()
804 addr
= citems
[3].split('%')[0]
805 elif ':ssh' in citems
[3]:
806 addr
= citems
[3].split(':')[0]
811 proc
.append(citems
[5].split(',')[1].split('=')[1])
816 # outpt of '/usr/bin/pstree -Aps <pid>':
817 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
818 # get the above output to following format
819 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
820 pstree
= list(reversed(utils
.exec_command('%s -Aps %s' %
821 (utils
.pstree_cmd
, os
.getpid())).strip().split('---')))
822 for index
, process
in enumerate(pstree
):
823 # check the parent of SSH process to make sure
824 # we don't kill SSH server or systemd process
825 if 'sshd' in process
and 'sshd' in pstree
[index
+ 1]:
826 pid
= filter(lambda x
: x
.isdigit(), process
)
828 self
.logger
.info("%s: killing active ssh sessions: %s"
829 %(ifacename
, str(proc
)))
831 if ifupdownflags
.flags
.DRYRUN
:
836 os
.kill(int(id), signal
.SIGINT
)
840 # Kill current SSH client
845 self
.logger
.info("fork error : %s [%d]" % (e
.strerror
, e
.errno
))
846 if (forkret
== 0): # The first child.
849 self
.logger
.info("%s: ifreload continuing in the background" %ifacename
)
850 except OSError, (err_no
, err_message
):
851 self
.logger
.info("os.setsid failed: errno=%d: %s" % (err_no
, err_message
))
852 self
.logger
.info("pid=%d pgid=%d" % (os
.getpid(), os
.getpgid(0)))
854 self
.logger
.info("%s: killing our session: %s"
855 %(ifacename
, str(proc
)))
856 os
.kill(int(pid
), signal
.SIGINT
)
861 self
.logger
.info('%s: %s' %(ifacename
, str(e
)))
863 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
865 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
867 self
._iproute
2_vrf
_map
_initialize
()
868 # This is a vrf device
869 self
._up
_vrf
_dev
(ifaceobj
, vrf_table
, True, ifaceobj_getfunc
)
871 vrf
= ifaceobj
.get_attr_value_first('vrf')
873 self
._iproute
2_vrf
_map
_initialize
()
874 # This is a vrf slave
875 self
._up
_vrf
_slave
(ifaceobj
.name
, vrf
, ifaceobj
,
877 elif not ifupdownflags
.flags
.PERFMODE
:
878 # check if we were a slave before
879 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
881 self
._iproute
2_vrf
_map
_initialize
()
882 if self
._is
_vrf
_dev
(master
):
883 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
,
886 self
.log_error(str(e
), ifaceobj
)
888 def _down_vrf_helper(self
, ifaceobj
, vrf_table
):
890 if ifupdownflags
.flags
.PERFMODE
:
893 utils
.exec_command('%s delete %s %s %s' %
899 def _close_sockets(self
, ifaceobj
, ifindex
):
900 if not self
.vrf_close_socks_on_down
:
904 utils
.exec_command('%s -aK \"dev == %s\"'
905 %(utils
.ss_cmd
, ifindex
))
907 self
.logger
.info('%s: closing socks using ss'
908 ' failed (%s)\n' %(ifaceobj
.name
, str(e
)))
911 def _down_vrf_dev(self
, ifaceobj
, vrf_table
, ifaceobj_getfunc
=None):
913 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
916 if vrf_table
== 'auto':
917 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
919 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
921 for s
in running_slaves
:
923 sobj
= ifaceobj_getfunc(s
)
925 self
._handle
_existing
_connections
(sobj
[0]
929 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
932 self
.ipcmd
.addr_flush(s
)
933 netlink
.link_set_updown(s
, "down")
935 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
939 self
._down
_vrf
_helper
(ifaceobj
, vrf_table
)
941 self
.logger
.warn('%s: %s' %(ifaceobj
.name
, str(e
)))
945 self
._del
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
947 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
950 ifindex
= self
.ipcmd
.link_get_ifindex(ifaceobj
.name
)
954 self
.ipcmd
.link_delete(ifaceobj
.name
)
956 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
959 self
._close
_sockets
(ifaceobj
, ifindex
)
962 self
._iproute
2_vrf
_table
_entry
_del
(vrf_table
)
964 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
968 def _down_vrf_slave(self
, ifacename
, ifaceobj
=None, vrfname
=None):
970 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
971 self
.ipcmd
.link_set(ifacename
, 'nomaster')
972 # Down this slave only if it is a slave ifupdown2 manages.
973 # we dont want to down slaves that maybe up'ed by
974 # somebody else. One such example is a macvlan device
975 # which ifupdown2 addressvirtual addon module auto creates
977 netlink
.link_set_updown(ifacename
, "down")
979 self
.logger
.warn('%s: %s' %(ifacename
, str(e
)))
981 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
983 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
985 self
._iproute
2_vrf
_map
_initialize
()
986 self
._down
_vrf
_dev
(ifaceobj
, vrf_table
, ifaceobj_getfunc
)
988 vrf
= ifaceobj
.get_attr_value_first('vrf')
990 self
._iproute
2_vrf
_map
_initialize
()
991 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
, None)
993 self
.log_warn(str(e
))
995 def _query_check_vrf_slave(self
, ifaceobj
, ifaceobjcurr
, vrf
):
997 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
998 if not master
or master
!= vrf
:
999 ifaceobjcurr
.update_config_with_status('vrf', str(master
), 1)
1001 ifaceobjcurr
.update_config_with_status('vrf', master
, 0)
1002 except Exception, e
:
1003 self
.log_error(str(e
), ifaceobjcurr
)
1005 def _query_check_vrf_dev(self
, ifaceobj
, ifaceobjcurr
, vrf_table
):
1007 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
1008 self
.logger
.info('%s: vrf: does not exist' %(ifaceobj
.name
))
1010 if vrf_table
== 'auto':
1011 config_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
1013 config_table
= vrf_table
1014 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
1015 if not vrfdev_attrs
:
1016 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
1018 running_table
= vrfdev_attrs
.get('table')
1019 if not running_table
:
1020 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
1022 if config_table
!= running_table
:
1023 ifaceobjcurr
.update_config_with_status('vrf-table',
1026 ifaceobjcurr
.update_config_with_status('vrf-table',
1028 if not ifupdownflags
.flags
.WITHDEFAULTS
:
1032 utils
.exec_command('%s verify %s %s'
1034 ifaceobj
.name
, config_table
))
1035 ifaceobjcurr
.update_config_with_status('vrf-helper',
1040 except Exception, e
:
1041 ifaceobjcurr
.update_config_with_status('vrf-helper',
1047 except Exception, e
:
1048 self
.log_warn(str(e
))
1050 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
1052 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
1054 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
1055 self
._query
_check
_vrf
_dev
(ifaceobj
, ifaceobjcurr
, vrf_table
)
1057 vrf
= ifaceobj
.get_attr_value_first('vrf')
1059 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
1060 self
._query
_check
_vrf
_slave
(ifaceobj
, ifaceobjcurr
, vrf
)
1061 except Exception, e
:
1062 self
.log_warn(str(e
))
1064 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
1066 kind
= self
.ipcmd
.link_get_kind(ifaceobjrunning
.name
)
1068 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobjrunning
.name
)
1070 running_table
= vrfdev_attrs
.get('table')
1072 ifaceobjrunning
.update_config('vrf-table',
1075 slave_kind
= self
.ipcmd
.link_get_slave_kind(ifaceobjrunning
.name
)
1076 if slave_kind
== 'vrf_slave':
1077 vrf
= self
.ipcmd
.link_get_master(ifaceobjrunning
.name
)
1079 ifaceobjrunning
.update_config('vrf', vrf
)
1080 except Exception, e
:
1081 self
.log_warn(str(e
))
1083 def _query(self
, ifaceobj
, **kwargs
):
1084 if not self
.vrf_helper
:
1086 if (ifaceobj
.link_kind
& ifaceLinkKind
.VRF
):
1087 ifaceobj
.update_config('vrf-helper', '%s %s' %(self
.vrf_helper
,
1090 _run_ops
= {'pre-up' : _up
,
1091 'post-down' : _down
,
1092 'query-running' : _query_running
,
1093 'query-checkcurr' : _query_check
,
1097 """ returns list of ops supported by this module """
1098 return self
._run
_ops
.keys()
1100 def _init_command_handlers(self
):
1102 self
.ipcmd
= self
.bondcmd
= LinkUtils()
1103 if not self
.dhclientcmd
:
1104 self
.dhclientcmd
= dhclient()
1106 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1107 ifaceobj_getfunc
=None, **extra_args
):
1108 """ run bond configuration on the interface object passed as argument
1111 **ifaceobj** (object): iface object
1113 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1117 **query_ifaceobj** (object): query check ifaceobject. This is only
1118 valid when op is 'query-checkcurr'. It is an object same as
1119 ifaceobj, but contains running attribute values and its config
1120 status. The modules can use it to return queried running state
1121 of interfaces. status is success if the running state is same
1122 as user required state in ifaceobj. error otherwise.
1124 op_handler
= self
._run
_ops
.get(operation
)
1127 self
._init
_command
_handlers
()
1128 if operation
== 'query-checkcurr':
1129 op_handler(self
, ifaceobj
, query_ifaceobj
)
1131 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)