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 netlink
.link_set_updown(ifacename
, "up")
501 self
.log_error('vrf %s not around, skipping vrf config'
502 %(vrfname), ifaceobj
)
504 self
.log_error('%s: %s' %(ifacename
, str(e
)), ifaceobj
)
506 def _del_vrf_rules(self
, vrf_dev_name
, vrf_table
):
508 ip_rule_out_format
= '%s: from all %s %s lookup %s'
509 ip_rule_cmd
= '%s %s rule del pref %s %s %s table %s'
511 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
512 if rule
in self
.ip_rule_cache
:
513 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
514 '', pref
, 'oif', vrf_dev_name
,
516 utils
.exec_command(rule_cmd
)
518 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
519 if rule
in self
.ip_rule_cache
:
520 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
521 '', pref
, 'iif', vrf_dev_name
,
523 utils
.exec_command(rule_cmd
)
525 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
526 if rule
in self
.ip6_rule_cache
:
527 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
528 '-6', pref
, 'oif', vrf_dev_name
,
530 utils
.exec_command(rule_cmd
)
532 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
533 if rule
in self
.ip6_rule_cache
:
534 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
535 '-6', pref
, 'iif', vrf_dev_name
,
537 utils
.exec_command(rule_cmd
)
539 def _l3mdev_rule(self
, ip_rules
):
540 for rule
in ip_rules
:
541 if not re
.search(r
"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
547 def _rule_cache_fill(self
):
548 ip_rules
= utils
.exec_command('%s rule show'
549 %utils
.ip_cmd
).splitlines()
550 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
551 self
.l3mdev4_rule
= self
._l3mdev
_rule
(self
.ip_rule_cache
)
552 ip_rules
= utils
.exec_command('%s -6 rule show'
553 %utils
.ip_cmd
).splitlines()
554 self
.ip6_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
555 self
.l3mdev6_rule
= self
._l3mdev
_rule
(self
.ip6_rule_cache
)
557 def _add_vrf_rules(self
, vrf_dev_name
, vrf_table
):
559 ip_rule_out_format
= '%s: from all %s %s lookup %s'
560 ip_rule_cmd
= '%s %s rule add pref %s %s %s table %s'
561 if self
.vrf_fix_local_table
:
562 self
.vrf_fix_local_table
= False
563 rule
= '0: from all lookup local'
564 if rule
in self
.ip_rule_cache
:
566 utils
.exec_command('%s rule del pref 0'
568 utils
.exec_command('%s rule add pref 32765 table local'
571 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
573 if rule
in self
.ip6_rule_cache
:
575 utils
.exec_command('%s -6 rule del pref 0'
577 utils
.exec_command('%s -6 rule add pref 32765 table local'
580 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
583 if not self
.l3mdev_checked
:
584 self
._rule
_cache
_fill
()
585 self
.l3mdev_checked
= True
587 #200: from all oif blue lookup blue
588 #200: from all iif blue lookup blue
590 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
591 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
592 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
593 '', pref
, 'oif', vrf_dev_name
,
595 utils
.exec_command(rule_cmd
)
597 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
598 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
599 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
600 '', pref
, 'iif', vrf_dev_name
,
602 utils
.exec_command(rule_cmd
)
604 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
605 if not self
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
606 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
607 '-6', pref
, 'oif', vrf_dev_name
,
609 utils
.exec_command(rule_cmd
)
611 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
612 if not self
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
613 rule_cmd
= ip_rule_cmd
%(utils
.ip_cmd
,
614 '-6', pref
, 'iif', vrf_dev_name
,
616 utils
.exec_command(rule_cmd
)
618 def _is_address_virtual_slaves(self
, vrfobj
, config_vrfslaves
,
620 # Address virtual lines on a vrf slave will create
621 # macvlan devices on the vrf slave and enslave them
622 # to the vrf master. This function checks if the
623 # vrf slave is such a macvlan interface.
624 # XXX: additional possible checks that can be done here
626 # - check if it is also a macvlan device of the
627 # format <vrf_slave>-v<int> created by the
628 # address virtual module
629 vrfslave_lowers
= self
.ipcmd
.link_get_lowers(vrfslave
)
631 if vrfslave_lowers
[0] in config_vrfslaves
:
635 def _add_vrf_slaves(self
, ifaceobj
, ifaceobj_getfunc
=None):
636 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
637 config_slaves
= ifaceobj
.lowerifaces
638 if not config_slaves
and not running_slaves
:
641 if not config_slaves
: config_slaves
= []
642 if not running_slaves
: running_slaves
= []
643 add_slaves
= set(config_slaves
).difference(set(running_slaves
))
644 del_slaves
= set(running_slaves
).difference(set(config_slaves
))
648 if not self
.ipcmd
.link_exists(s
):
652 sobj
= ifaceobj_getfunc(s
)
653 self
._up
_vrf
_slave
(s
, ifaceobj
.name
,
654 sobj
[0] if sobj
else None,
655 ifaceobj_getfunc
, True)
657 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
662 if self
._is
_address
_virtual
_slaves
(ifaceobj
,
667 sobj
= ifaceobj_getfunc(s
)
668 self
._down
_vrf
_slave
(s
, sobj
[0] if sobj
else None,
671 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
673 if ifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
674 for s
in config_slaves
:
676 netlink
.link_set_updown(s
, "up")
678 self
.logger
.debug('%s: %s' % (ifaceobj
.name
, str(e
)))
681 def _set_vrf_dev_processed_flag(self
, ifaceobj
):
682 ifaceobj
.module_flags
[self
.name
] = \
683 ifaceobj
.module_flags
.setdefault(self
.name
, 0) | \
684 vrfPrivFlags
.PROCESSED
686 def _check_vrf_dev_processed_flag(self
, ifaceobj
):
687 if (ifaceobj
.module_flags
.get(self
.name
, 0) & vrfPrivFlags
.PROCESSED
):
691 def _create_vrf_dev(self
, ifaceobj
, vrf_table
):
692 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
693 self
._check
_vrf
_system
_reserved
_names
(ifaceobj
)
695 if self
.vrf_count
== self
.vrf_max_count
:
696 self
.log_error('max vrf count %d hit...not '
697 'creating vrf' % self
.vrf_count
, ifaceobj
)
698 if vrf_table
== 'auto':
699 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
701 self
.log_error('unable to get an auto table id', ifaceobj
)
702 self
.logger
.info('%s: table id auto: selected table id %s\n'
703 %(ifaceobj
.name
, vrf_table
))
705 self
._iproute
2_is
_vrf
_tableid
_inuse
(ifaceobj
, vrf_table
)
706 if ifaceobj
.name
in self
.system_reserved_rt_tables
.keys():
707 self
.log_error('cannot use system reserved %s table ids'
708 %(str(self
.system_reserved_rt_tables
.keys())),
711 if not vrf_table
.isdigit():
712 self
.log_error('vrf-table must be an integer or \'auto\'', ifaceobj
)
714 # XXX: If we decide to not allow vrf id usages out of
715 # the reserved ifupdown range, then uncomment this code.
717 if (int(vrf_table
) < self
.vrf_table_id_start
or
718 int(vrf_table
) > self
.vrf_table_id_end
):
719 self
.log_error('vrf table id %s out of reserved range [%d,%d]'
721 self
.vrf_table_id_start
,
722 self
.vrf_table_id_end
), ifaceobj
)
724 self
.ipcmd
.link_create(ifaceobj
.name
, 'vrf',
725 {'table' : '%s' %vrf_table
})
727 self
.log_error('create failed (%s)\n' % str(e
), ifaceobj
)
728 if vrf_table
!= 'auto':
729 self
._iproute
2_vrf
_table
_entry
_add
(ifaceobj
, vrf_table
)
731 if vrf_table
== 'auto':
732 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
733 if not vrf_table
and not ifupdownflags
.flags
.DRYRUN
:
734 self
.log_error('unable to get vrf table id', ifaceobj
)
736 # if the device exists, check if table id is same
737 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
739 running_table
= vrfdev_attrs
.get('table', None)
740 if vrf_table
!= running_table
:
741 self
.log_error('cannot change vrf table id,running table id'
742 ' %s is different from config id %s'
743 % (running_table
, vrf_table
), ifaceobj
)
746 def _up_vrf_helper(self
, ifaceobj
, vrf_table
):
748 if ifupdownflags
.flags
.PERFMODE
:
751 utils
.exec_command('%s create %s %s %s' %
757 def _up_vrf_dev(self
, ifaceobj
, vrf_table
, add_slaves
=True,
758 ifaceobj_getfunc
=None):
760 # if vrf dev is already processed return. This can happen
761 # if we the slave was configured before.
762 # see self._up_vrf_slave_without_master
763 if self
._check
_vrf
_dev
_processed
_flag
(ifaceobj
):
767 vrf_table
= self
._create
_vrf
_dev
(ifaceobj
, vrf_table
)
769 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
772 self
._add
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
773 self
._up
_vrf
_helper
(ifaceobj
, vrf_table
)
775 self
._add
_vrf
_slaves
(ifaceobj
, ifaceobj_getfunc
)
776 self
._set
_vrf
_dev
_processed
_flag
(ifaceobj
)
777 netlink
.link_set_updown(ifaceobj
.name
, "up")
779 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
781 def _kill_ssh_connections(self
, ifacename
):
783 runningaddrsdict
= self
.ipcmd
.get_running_addrs(None, ifacename
)
784 if not runningaddrsdict
:
786 iplist
= [i
.split('/', 1)[0] for i
in runningaddrsdict
.keys()]
791 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
792 #users:(("sshd",pid=2528,fd=3))
793 cmdl
= [utils
.ss_cmd
, '-t', '-p']
794 for line
in utils
.exec_commandl(cmdl
).splitlines():
795 citems
= line
.split()
798 addr
= citems
[3].split('%')[0]
799 elif ':ssh' in citems
[3]:
800 addr
= citems
[3].split(':')[0]
805 proc
.append(citems
[5].split(',')[1].split('=')[1])
810 # outpt of '/usr/bin/pstree -Aps <pid>':
811 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
812 # get the above output to following format
813 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
814 pstree
= list(reversed(utils
.exec_command('%s -Aps %s' %
815 (utils
.pstree_cmd
, os
.getpid())).strip().split('---')))
816 for index
, process
in enumerate(pstree
):
817 # check the parent of SSH process to make sure
818 # we don't kill SSH server or systemd process
819 if 'sshd' in process
and 'sshd' in pstree
[index
+ 1]:
820 pid
= filter(lambda x
: x
.isdigit(), process
)
822 self
.logger
.info("%s: killing active ssh sessions: %s"
823 %(ifacename
, str(proc
)))
825 if ifupdownflags
.flags
.DRYRUN
:
830 os
.kill(int(id), signal
.SIGINT
)
834 # Kill current SSH client
839 self
.logger
.info("fork error : %s [%d]" % (e
.strerror
, e
.errno
))
840 if (forkret
== 0): # The first child.
843 self
.logger
.info("%s: ifreload continuing in the background" %ifacename
)
844 except OSError, (err_no
, err_message
):
845 self
.logger
.info("os.setsid failed: errno=%d: %s" % (err_no
, err_message
))
846 self
.logger
.info("pid=%d pgid=%d" % (os
.getpid(), os
.getpgid(0)))
848 self
.logger
.info("%s: killing our session: %s"
849 %(ifacename
, str(proc
)))
850 os
.kill(int(pid
), signal
.SIGINT
)
855 self
.logger
.info('%s: %s' %(ifacename
, str(e
)))
857 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
859 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
861 self
._iproute
2_vrf
_map
_initialize
()
862 # This is a vrf device
863 self
._up
_vrf
_dev
(ifaceobj
, vrf_table
, True, ifaceobj_getfunc
)
865 vrf
= ifaceobj
.get_attr_value_first('vrf')
867 self
._iproute
2_vrf
_map
_initialize
()
868 # This is a vrf slave
869 self
._up
_vrf
_slave
(ifaceobj
.name
, vrf
, ifaceobj
,
871 elif not ifupdownflags
.flags
.PERFMODE
:
872 # check if we were a slave before
873 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
875 self
._iproute
2_vrf
_map
_initialize
()
876 if self
._is
_vrf
_dev
(master
):
877 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
,
880 self
.log_error(str(e
), ifaceobj
)
882 def _down_vrf_helper(self
, ifaceobj
, vrf_table
):
884 if ifupdownflags
.flags
.PERFMODE
:
887 utils
.exec_command('%s delete %s %s %s' %
893 def _close_sockets(self
, ifaceobj
, ifindex
):
894 if not self
.vrf_close_socks_on_down
:
898 utils
.exec_command('%s -aK \"dev == %s\"'
899 %(utils
.ss_cmd
, ifindex
))
901 self
.logger
.info('%s: closing socks using ss'
902 ' failed (%s)\n' %(ifaceobj
.name
, str(e
)))
905 def _down_vrf_dev(self
, ifaceobj
, vrf_table
, ifaceobj_getfunc
=None):
907 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
910 if vrf_table
== 'auto':
911 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
913 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
915 for s
in running_slaves
:
917 sobj
= ifaceobj_getfunc(s
)
919 self
._handle
_existing
_connections
(sobj
[0]
923 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
926 self
.ipcmd
.addr_flush(s
)
927 netlink
.link_set_updown(s
, "down")
929 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
933 self
._down
_vrf
_helper
(ifaceobj
, vrf_table
)
935 self
.logger
.warn('%s: %s' %(ifaceobj
.name
, str(e
)))
939 self
._del
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
941 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
944 ifindex
= self
.ipcmd
.link_get_ifindex(ifaceobj
.name
)
948 self
.ipcmd
.link_delete(ifaceobj
.name
)
950 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
953 self
._close
_sockets
(ifaceobj
, ifindex
)
956 self
._iproute
2_vrf
_table
_entry
_del
(vrf_table
)
958 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
962 def _down_vrf_slave(self
, ifacename
, ifaceobj
=None, vrfname
=None):
964 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
965 self
.ipcmd
.link_set(ifacename
, 'nomaster')
966 # Down this slave only if it is a slave ifupdown2 manages.
967 # we dont want to down slaves that maybe up'ed by
968 # somebody else. One such example is a macvlan device
969 # which ifupdown2 addressvirtual addon module auto creates
971 netlink
.link_set_updown(ifacename
, "down")
973 self
.logger
.warn('%s: %s' %(ifacename
, str(e
)))
975 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
977 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
979 self
._iproute
2_vrf
_map
_initialize
()
980 self
._down
_vrf
_dev
(ifaceobj
, vrf_table
, ifaceobj_getfunc
)
982 vrf
= ifaceobj
.get_attr_value_first('vrf')
984 self
._iproute
2_vrf
_map
_initialize
()
985 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
, None)
987 self
.log_warn(str(e
))
989 def _query_check_vrf_slave(self
, ifaceobj
, ifaceobjcurr
, vrf
):
991 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
992 if not master
or master
!= vrf
:
993 ifaceobjcurr
.update_config_with_status('vrf', str(master
), 1)
995 ifaceobjcurr
.update_config_with_status('vrf', master
, 0)
997 self
.log_error(str(e
), ifaceobjcurr
)
999 def _query_check_vrf_dev(self
, ifaceobj
, ifaceobjcurr
, vrf_table
):
1001 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
1002 self
.logger
.info('%s: vrf: does not exist' %(ifaceobj
.name
))
1004 if vrf_table
== 'auto':
1005 config_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
1007 config_table
= vrf_table
1008 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
1009 if not vrfdev_attrs
:
1010 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
1012 running_table
= vrfdev_attrs
.get('table')
1013 if not running_table
:
1014 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
1016 if config_table
!= running_table
:
1017 ifaceobjcurr
.update_config_with_status('vrf-table',
1020 ifaceobjcurr
.update_config_with_status('vrf-table',
1022 if not ifupdownflags
.flags
.WITHDEFAULTS
:
1026 utils
.exec_command('%s verify %s %s'
1028 ifaceobj
.name
, config_table
))
1029 ifaceobjcurr
.update_config_with_status('vrf-helper',
1034 except Exception, e
:
1035 ifaceobjcurr
.update_config_with_status('vrf-helper',
1041 except Exception, e
:
1042 self
.log_warn(str(e
))
1044 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
1046 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
1048 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
1049 self
._query
_check
_vrf
_dev
(ifaceobj
, ifaceobjcurr
, vrf_table
)
1051 vrf
= ifaceobj
.get_attr_value_first('vrf')
1053 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
1054 self
._query
_check
_vrf
_slave
(ifaceobj
, ifaceobjcurr
, vrf
)
1055 except Exception, e
:
1056 self
.log_warn(str(e
))
1058 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
1060 kind
= self
.ipcmd
.link_get_kind(ifaceobjrunning
.name
)
1062 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobjrunning
.name
)
1064 running_table
= vrfdev_attrs
.get('table')
1066 ifaceobjrunning
.update_config('vrf-table',
1069 slave_kind
= self
.ipcmd
.link_get_slave_kind(ifaceobjrunning
.name
)
1070 if slave_kind
== 'vrf_slave':
1071 vrf
= self
.ipcmd
.link_get_master(ifaceobjrunning
.name
)
1073 ifaceobjrunning
.update_config('vrf', vrf
)
1074 except Exception, e
:
1075 self
.log_warn(str(e
))
1077 def _query(self
, ifaceobj
, **kwargs
):
1078 if not self
.vrf_helper
:
1080 if (ifaceobj
.link_kind
& ifaceLinkKind
.VRF
):
1081 ifaceobj
.update_config('vrf-helper', '%s %s' %(self
.vrf_helper
,
1084 _run_ops
= {'pre-up' : _up
,
1085 'post-down' : _down
,
1086 'query-running' : _query_running
,
1087 'query-checkcurr' : _query_check
,
1091 """ returns list of ops supported by this module """
1092 return self
._run
_ops
.keys()
1094 def _init_command_handlers(self
):
1096 self
.ipcmd
= self
.bondcmd
= LinkUtils()
1097 if not self
.dhclientcmd
:
1098 self
.dhclientcmd
= dhclient()
1100 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1101 ifaceobj_getfunc
=None, **extra_args
):
1102 """ run bond configuration on the interface object passed as argument
1105 **ifaceobj** (object): iface object
1107 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1111 **query_ifaceobj** (object): query check ifaceobject. This is only
1112 valid when op is 'query-checkcurr'. It is an object same as
1113 ifaceobj, but contains running attribute values and its config
1114 status. The modules can use it to return queried running state
1115 of interfaces. status is success if the running state is same
1116 as user required state in ifaceobj. error otherwise.
1118 op_handler
= self
._run
_ops
.get(operation
)
1121 self
._init
_command
_handlers
()
1122 if operation
== 'query-checkcurr':
1123 op_handler(self
, ifaceobj
, query_ifaceobj
)
1125 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)