3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
14 from ifupdown
.iface
import *
15 from ifupdown
.utils
import utils
16 import ifupdown
.policymanager
as policymanager
18 from ifupdown
.netlink
import netlink
19 import ifupdown
.ifupdownflags
as ifupdownflags
20 from ifupdownaddons
.modulebase
import moduleBase
21 from ifupdownaddons
.bondutil
import bondutil
22 from ifupdownaddons
.iproute2
import iproute2
23 from ifupdownaddons
.dhclient
import dhclient
24 from ifupdownaddons
.utilsbase
import *
29 class vrf(moduleBase
):
30 """ ifupdown2 addon module to configure vrfs """
31 _modinfo
= { 'mhelp' : 'vrf configuration module',
34 {'help' : 'vrf device routing table id. key to ' +
35 'creating a vrf device. ' +
36 'Table id is either \'auto\' or '+
37 '\'valid routing table id\'',
38 'validvals': ['auto', '<number>'],
39 'example': ['vrf-table auto', 'vrf-table 1001']},
41 {'help' : 'vrf the interface is part of.',
42 'validvals': ['<text>'],
43 'example': ['vrf blue']}}}
45 iproute2_vrf_filename
= '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
46 iproute2_vrf_filehdr
= '# This file is autogenerated by ifupdown2.\n' + \
47 '# It contains the vrf name to table mapping.\n' + \
48 '# Reserved table range %s %s\n'
49 VRF_TABLE_START
= 1001
52 system_reserved_rt_tables
= {'255' : 'local', '254' : 'main',
53 '253' : 'default', '0' : 'unspec'}
55 def __init__(self
, *args
, **kargs
):
56 ifupdownaddons
.modulebase
.moduleBase
.__init
__(self
, *args
, **kargs
)
59 self
.dhclientcmd
= None
60 self
.name
= self
.__class
__.__name
__
61 self
.vrf_mgmt_devname
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-mgmt-devname')
63 if (ifupdownflags
.flags
.PERFMODE
and
64 not (self
.vrf_mgmt_devname
and os
.path
.exists('/sys/class/net/%s'
65 %self
.vrf_mgmt_devname
))):
66 # if perf mode is set (PERFMODE is set at boot), and this is the first
67 # time we are calling ifup at boot (check for mgmt vrf existance at
68 # boot, make sure this is really the first invocation at boot.
69 # ifup is called with PERFMODE at boot multiple times (once for mgmt vrf
70 # and the second time with all auto interfaces). We want to delete
71 # the map file only the first time. This is to avoid accidently
72 # deleting map file with a valid mgmt vrf entry
73 if os
.path
.exists(self
.iproute2_vrf_filename
):
75 self
.logger
.info('vrf: removing file %s'
76 %self
.iproute2_vrf_filename
)
77 os
.remove(self
.iproute2_vrf_filename
)
79 self
.logger
.debug('vrf: removing file failed (%s)'
82 ip_rules
= utils
.exec_command('/sbin/ip rule show').splitlines()
83 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
85 self
.ip_rule_cache
= []
86 self
.logger
.warn('vrf: cache v4: %s' % str(e
))
89 ip_rules
= utils
.exec_command('/sbin/ip -6 rule show').splitlines()
90 self
.ip6_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
92 self
.ip6_rule_cache
= []
93 self
.logger
.warn('vrf: cache v6: %s' % str(e
))
95 #self.logger.debug("vrf: ip rule cache")
96 #self.logger.info(self.ip_rule_cache)
98 #self.logger.info("vrf: ip -6 rule cache")
99 #self.logger.info(self.ip6_rule_cache)
101 self
.l3mdev_checked
= False
102 self
.l3mdev4_rule
= False
103 if self
._l3mdev
_rule
(self
.ip_rule_cache
):
104 self
.l3mdev4_rule
= True
105 self
.l3mdev_checked
= True
106 self
.l3mdev6_rule
= False
107 if self
._l3mdev
_rule
(self
.ip6_rule_cache
):
108 self
.l3mdev6_rule
= True
109 self
.l3mdev_checked
= True
110 self
._iproute
2_vrf
_map
_initialized
= False
111 self
.iproute2_vrf_map
= {}
112 self
.iproute2_vrf_map_fd
= None
113 self
.iproute2_vrf_map_sync_to_disk
= False
115 self
.vrf_table_id_start
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-start')
116 if not self
.vrf_table_id_start
:
117 self
.vrf_table_id_start
= self
.VRF_TABLE_START
118 self
.vrf_table_id_end
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-end')
119 if not self
.vrf_table_id_end
:
120 self
.vrf_table_id_end
= self
.VRF_TABLE_END
121 self
.vrf_max_count
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-max-count')
123 self
.vrf_fix_local_table
= True
125 self
.vrf_helper
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-helper')
126 self
.vrf_close_socks_on_down
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-close-socks-on-down')
127 self
.warn_on_vrf_map_write_err
= True
129 def _iproute2_vrf_map_initialize(self
, writetodisk
=True):
130 if self
._iproute
2_vrf
_map
_initialized
:
133 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
134 self
.iproute2_vrf_map
= {}
135 iproute2_vrf_map_force_rewrite
= False
136 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
137 if os
.path
.exists(self
.iproute2_vrf_filename
):
138 with
open(self
.iproute2_vrf_filename
, 'r+') as vrf_map_fd
:
139 lines
= vrf_map_fd
.readlines()
145 (table
, vrf_name
) = l
.strip().split()
146 if self
.iproute2_vrf_map
.get(int(table
)):
147 # looks like the existing file has
148 # duplicate entries, force rewrite of the
150 iproute2_vrf_map_force_rewrite
= True
152 self
.iproute2_vrf_map
[int(table
)] = vrf_name
154 self
.logger
.info('vrf: iproute2_vrf_map: unable to parse %s (%s)' %(l
, str(e
)))
157 vrfs
= self
.ipcmd
.link_get_vrfs()
160 for v
, lattrs
in vrfs
.iteritems():
161 table
= lattrs
.get('table', None)
163 running_vrf_map
[int(table
)] = v
165 if (not running_vrf_map
or (running_vrf_map
!= self
.iproute2_vrf_map
)):
166 self
.iproute2_vrf_map
= running_vrf_map
167 iproute2_vrf_map_force_rewrite
= True
169 self
.iproute2_vrf_map_fd
= None
171 if iproute2_vrf_map_force_rewrite
:
172 # reopen the file and rewrite the map
173 self
._iproute
2_vrf
_map
_open
(True, False)
175 self
._iproute
2_vrf
_map
_open
(False, True)
177 self
.iproute2_vrf_map_sync_to_disk
= False
178 atexit
.register(self
._iproute
2_vrf
_map
_sync
_to
_disk
)
180 self
.logger
.info("vrf: dumping iproute2_vrf_map")
181 self
.logger
.info(self
.iproute2_vrf_map
)
183 last_used_vrf_table
= None
184 for t
in range(self
.vrf_table_id_start
,
185 self
.vrf_table_id_end
):
186 if not self
.iproute2_vrf_map
.get(t
):
188 last_used_vrf_table
= t
189 self
.last_used_vrf_table
= last_used_vrf_table
190 self
._iproute
2_vrf
_map
_initialized
= True
191 self
.vrf_count
= len(self
.iproute2_vrf_map
)
193 def _iproute2_map_warn(self
, errstr
):
194 if self
.warn_on_vrf_map_write_err
:
195 if not os
.path
.exists('/etc/iproute2/rt_tables.d/'):
196 self
.logger
.info('unable to save iproute2 vrf to table ' +
197 'map (%s)\n' %errstr
)
198 self
.logger
.info('cannot find /etc/iproute2/rt_tables.d.' +
199 ' pls check if your iproute2 version' +
200 ' supports rt_tables.d')
202 self
.logger
.warn('unable to open iproute2 vrf to table ' +
203 'map (%s)\n' %errstr
)
204 self
.warn_on_vrf_map_write_err
= False
206 def _iproute2_vrf_map_sync_to_disk(self
):
207 if (ifupdownflags
.flags
.DRYRUN
or
208 not self
.iproute2_vrf_map_sync_to_disk
):
210 self
.logger
.info('vrf: syncing table map to %s'
211 %self
.iproute2_vrf_filename
)
213 with
open(self
.iproute2_vrf_filename
, 'w') as f
:
214 f
.write(self
.iproute2_vrf_filehdr
%(self
.vrf_table_id_start
,
215 self
.vrf_table_id_end
))
216 for t
, v
in self
.iproute2_vrf_map
.iteritems():
217 f
.write('%s %s\n' %(t
, v
))
220 self
._iproute
2_map
_warn
(str(e
))
223 def _iproute2_vrf_map_open(self
, sync_vrfs
=False, append
=False):
224 self
.logger
.info('vrf: syncing table map to %s'
225 %self
.iproute2_vrf_filename
)
226 if ifupdownflags
.flags
.DRYRUN
:
228 fmode
= 'a+' if append
else 'w'
230 self
.iproute2_vrf_map_fd
= open(self
.iproute2_vrf_filename
,
232 fcntl
.fcntl(self
.iproute2_vrf_map_fd
, fcntl
.F_SETFD
, fcntl
.FD_CLOEXEC
)
234 self
._iproute
2_map
_warn
(str(e
))
239 self
.iproute2_vrf_map_fd
.write(self
.iproute2_vrf_filehdr
240 %(self
.vrf_table_id_start
,
241 self
.vrf_table_id_end
))
242 for t
, v
in self
.iproute2_vrf_map
.iteritems():
243 self
.iproute2_vrf_map_fd
.write('%s %s\n' %(t
, v
))
244 self
.iproute2_vrf_map_fd
.flush()
246 def _is_vrf(self
, ifaceobj
):
247 if ifaceobj
.get_attr_value_first('vrf-table'):
251 def get_upper_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
252 """ Returns list of interfaces dependent on ifaceobj """
254 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
256 ifaceobj
.link_type
= ifaceLinkType
.LINK_MASTER
257 ifaceobj
.link_kind |
= ifaceLinkKind
.VRF
258 ifaceobj
.role |
= ifaceRole
.MASTER
259 vrf_iface_name
= ifaceobj
.get_attr_value_first('vrf')
260 if not vrf_iface_name
:
262 ifaceobj
.link_type
= ifaceLinkType
.LINK_SLAVE
263 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.VRF_SLAVE
265 return [vrf_iface_name
]
267 def get_upper_ifacenames_running(self
, ifaceobj
):
270 def _get_iproute2_vrf_table(self
, vrf_dev_name
):
271 for t
, v
in self
.iproute2_vrf_map
.iteritems():
272 if v
== vrf_dev_name
:
276 def _get_avail_vrf_table_id(self
):
277 if self
.last_used_vrf_table
== None:
278 table_id_start
= self
.vrf_table_id_start
280 table_id_start
= self
.last_used_vrf_table
+ 1
281 for t
in range(table_id_start
,
282 self
.vrf_table_id_end
):
283 if not self
.iproute2_vrf_map
.get(t
):
284 self
.last_used_vrf_table
= t
288 def _iproute2_is_vrf_tableid_inuse(self
, vrfifaceobj
, table_id
):
289 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
290 if old_vrf_name
and old_vrf_name
!= vrfifaceobj
.name
:
291 self
.log_error('table id %s already assigned to vrf dev %s'
292 %(table_id
, old_vrf_name
), vrfifaceobj
)
294 def _iproute2_vrf_table_entry_add(self
, vrfifaceobj
, table_id
):
295 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
297 self
.iproute2_vrf_map
[int(table_id
)] = vrfifaceobj
.name
298 if self
.iproute2_vrf_map_fd
:
299 self
.iproute2_vrf_map_fd
.write('%s %s\n'
300 %(table_id
, vrfifaceobj
.name
))
301 self
.iproute2_vrf_map_fd
.flush()
304 if old_vrf_name
!= vrfifaceobj
.name
:
305 self
.log_error('table id %d already assigned to vrf dev %s'
306 %(table_id
, old_vrf_name
))
308 def _iproute2_vrf_table_entry_del(self
, table_id
):
310 # with any del of vrf map, we need to force sync to disk
311 self
.iproute2_vrf_map_sync_to_disk
= True
312 del self
.iproute2_vrf_map
[int(table_id
)]
314 self
.logger
.info('vrf: iproute2 vrf map del failed for %s (%s)'
318 def _is_vrf_dev(self
, ifacename
):
319 # Look at iproute2 map for now.
320 # If it was a master we knew about,
321 # it is definately there
322 if ifacename
in self
.iproute2_vrf_map
.values():
326 def _is_dhcp_slave(self
, ifaceobj
):
327 if (not ifaceobj
.addr_method
or
328 (ifaceobj
.addr_method
!= 'dhcp' and
329 ifaceobj
.addr_method
!= 'dhcp6')):
333 def _up_vrf_slave_without_master(self
, ifacename
, vrfname
, ifaceobj
,
335 """ If we have a vrf slave that has dhcp configured, bring up the
336 vrf master now. This is needed because vrf has special handling
337 in dhclient hook which requires the vrf master to be present """
339 vrf_master
= ifaceobj
.upperifaces
[0]
341 self
.logger
.warn('%s: vrf master not found' %ifacename
)
343 if os
.path
.exists('/sys/class/net/%s' %vrf_master
):
344 self
.logger
.info('%s: vrf master %s exists returning'
345 %(ifacename
, vrf_master
))
347 self
.logger
.info('%s: bringing up vrf master %s'
348 %(ifacename
, vrf_master
))
349 for mobj
in vrf_master_objs
:
350 vrf_table
= mobj
.get_attr_value_first('vrf-table')
352 if vrf_table
== 'auto':
353 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
355 self
.log_error('%s: unable to get an auto table id'
356 %mobj
.name
, ifaceobj
)
357 self
.logger
.info('%s: table id auto: selected table id %s\n'
358 %(mobj
.name
, vrf_table
))
360 self
._up
_vrf
_dev
(mobj
, vrf_table
, False)
364 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
365 netlink
.link_set_master(ifacename
, vrfname
)
368 def _down_dhcp_slave(self
, ifaceobj
, vrfname
):
370 dhclient_cmd_prefix
= None
371 if (vrfname
and self
.vrf_exec_cmd_prefix
and
372 self
.ipcmd
.link_exists(vrfname
)):
373 dhclient_cmd_prefix
= '%s %s' %(self
.vrf_exec_cmd_prefix
,
375 self
.dhclientcmd
.release(ifaceobj
.name
, dhclient_cmd_prefix
)
377 # ignore any dhclient release errors
380 def _handle_existing_connections(self
, ifaceobj
, vrfname
):
381 if not ifaceobj
or ifupdownflags
.flags
.PERFMODE
:
383 if (self
.vrf_mgmt_devname
and
384 self
.vrf_mgmt_devname
== vrfname
):
385 self
._kill
_ssh
_connections
(ifaceobj
.name
)
386 if self
._is
_dhcp
_slave
(ifaceobj
):
387 self
._down
_dhcp
_slave
(ifaceobj
, vrfname
)
389 def _up_vrf_slave(self
, ifacename
, vrfname
, ifaceobj
=None,
390 ifaceobj_getfunc
=None, vrf_exists
=False):
393 if vrf_exists
or self
.ipcmd
.link_exists(vrfname
):
394 uppers
= self
.ipcmd
.link_get_uppers(ifacename
)
395 if not uppers
or vrfname
not in uppers
:
396 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
397 netlink
.link_set_master(ifacename
, vrfname
)
399 vrf_master_objs
= ifaceobj_getfunc(vrfname
)
400 if not vrf_master_objs
:
401 # this is the case where vrf is assigned to an interface
402 # but user has not provided a vrf interface.
403 # people expect you to warn them but go ahead with the
404 # rest of the config on that interface
405 netlink
.link_set_updown(ifacename
, "up")
406 self
.log_error('vrf master ifaceobj %s not found'
409 if (ifupdownflags
.flags
.ALL
or
410 ifupdownflags
.flags
.WITH_DEPENDS
or
411 (ifupdownflags
.flags
.CLASS
and
412 ifaceobj
.classes
and vrf_master_objs
[0].classes
and
413 Set(ifaceobj
.classes
).intersection(vrf_master_objs
[0].classes
))):
414 self
._up
_vrf
_slave
_without
_master
(ifacename
, vrfname
,
418 master_exists
= False
420 master_exists
= False
422 netlink
.link_set_updown(ifacename
, "up")
424 self
.log_error('vrf %s not around, skipping vrf config'
425 %(vrfname), ifaceobj
)
427 self
.log_error('%s: %s' %(ifacename
, str(e
)), ifaceobj
)
429 def _del_vrf_rules(self
, vrf_dev_name
, vrf_table
):
431 ip_rule_out_format
= '%s: from all %s %s lookup %s'
432 ip_rule_cmd
= 'ip %s rule del pref %s %s %s table %s'
434 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
435 if rule
in self
.ip_rule_cache
:
436 rule_cmd
= ip_rule_cmd
%('', pref
, 'oif', vrf_dev_name
,
438 utils
.exec_command(rule_cmd
)
440 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
441 if rule
in self
.ip_rule_cache
:
442 rule_cmd
= ip_rule_cmd
%('', pref
, 'iif', vrf_dev_name
,
444 utils
.exec_command(rule_cmd
)
446 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
447 if rule
in self
.ip6_rule_cache
:
448 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'oif', vrf_dev_name
,
450 utils
.exec_command(rule_cmd
)
452 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
453 if rule
in self
.ip6_rule_cache
:
454 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'iif', vrf_dev_name
,
456 utils
.exec_command(rule_cmd
)
458 def _l3mdev_rule(self
, ip_rules
):
459 for rule
in ip_rules
:
460 if not re
.search(r
"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
466 def _rule_cache_fill(self
):
467 ip_rules
= utils
.exec_command('/sbin/ip rule show').splitlines()
468 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
469 self
.l3mdev4_rule
= self
._l3mdev
_rule
(self
.ip_rule_cache
)
470 ip_rules
= utils
.exec_command('/sbin/ip -6 rule show').splitlines()
471 self
.ip6_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
472 self
.l3mdev6_rule
= self
._l3mdev
_rule
(self
.ip6_rule_cache
)
474 def _add_vrf_rules(self
, vrf_dev_name
, vrf_table
):
476 ip_rule_out_format
= '%s: from all %s %s lookup %s'
477 ip_rule_cmd
= 'ip %s rule add pref %s %s %s table %s'
478 if self
.vrf_fix_local_table
:
479 self
.vrf_fix_local_table
= False
480 rule
= '0: from all lookup local'
481 if rule
in self
.ip_rule_cache
:
483 utils
.exec_command('ip rule del pref 0')
484 utils
.exec_command('ip rule add pref 32765 table local')
486 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
488 if rule
in self
.ip6_rule_cache
:
490 utils
.exec_command('ip -6 rule del pref 0')
491 utils
.exec_command('ip -6 rule add pref 32765 table local')
493 self
.logger
.info('%s: %s' % (vrf_dev_name
, str(e
)))
496 if not self
.l3mdev_checked
:
497 self
._rule
_cache
_fill
()
498 self
.l3mdev_checked
= True
500 #200: from all oif blue lookup blue
501 #200: from all iif blue lookup blue
503 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
504 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
505 rule_cmd
= ip_rule_cmd
%('', pref
, 'oif', vrf_dev_name
,
507 utils
.exec_command(rule_cmd
)
509 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
510 if not self
.l3mdev4_rule
and rule
not in self
.ip_rule_cache
:
511 rule_cmd
= ip_rule_cmd
%('', pref
, 'iif', vrf_dev_name
,
513 utils
.exec_command(rule_cmd
)
515 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
516 if not self
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
517 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'oif', vrf_dev_name
,
519 utils
.exec_command(rule_cmd
)
521 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
522 if not self
.l3mdev6_rule
and rule
not in self
.ip6_rule_cache
:
523 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'iif', vrf_dev_name
,
525 utils
.exec_command(rule_cmd
)
527 def _is_address_virtual_slaves(self
, vrfobj
, config_vrfslaves
,
529 # Address virtual lines on a vrf slave will create
530 # macvlan devices on the vrf slave and enslave them
531 # to the vrf master. This function checks if the
532 # vrf slave is such a macvlan interface.
533 # XXX: additional possible checks that can be done here
535 # - check if it is also a macvlan device of the
536 # format <vrf_slave>-v<int> created by the
537 # address virtual module
538 vrfslave_lowers
= self
.ipcmd
.link_get_lowers(vrfslave
)
540 if vrfslave_lowers
[0] in config_vrfslaves
:
544 def _add_vrf_slaves(self
, ifaceobj
, ifaceobj_getfunc
=None):
545 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
546 config_slaves
= ifaceobj
.lowerifaces
547 if not config_slaves
and not running_slaves
:
550 if not config_slaves
: config_slaves
= []
551 if not running_slaves
: running_slaves
= []
552 add_slaves
= set(config_slaves
).difference(set(running_slaves
))
553 del_slaves
= set(running_slaves
).difference(set(config_slaves
))
557 if not self
.ipcmd
.link_exists(s
):
561 sobj
= ifaceobj_getfunc(s
)
562 self
._up
_vrf
_slave
(s
, ifaceobj
.name
,
563 sobj
[0] if sobj
else None,
564 ifaceobj_getfunc
, True)
566 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
571 if self
._is
_address
_virtual
_slaves
(ifaceobj
,
576 sobj
= ifaceobj_getfunc(s
)
577 self
._down
_vrf
_slave
(s
, sobj
[0] if sobj
else None,
580 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
582 if ifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
583 for s
in config_slaves
:
585 netlink
.link_set_updown(s
, "up")
587 self
.logger
.debug('%s: %s' % (ifaceobj
.name
, str(e
)))
590 def _set_vrf_dev_processed_flag(self
, ifaceobj
):
591 ifaceobj
.module_flags
[self
.name
] = \
592 ifaceobj
.module_flags
.setdefault(self
.name
, 0) | \
593 vrfPrivFlags
.PROCESSED
595 def _check_vrf_dev_processed_flag(self
, ifaceobj
):
596 if (ifaceobj
.module_flags
.get(self
.name
, 0) & vrfPrivFlags
.PROCESSED
):
600 def _create_vrf_dev(self
, ifaceobj
, vrf_table
):
601 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
602 if ifaceobj
.name
in self
.system_reserved_rt_tables
.values():
603 self
.log_error('cannot use system reserved %s vrf names'
604 %(str(self
.system_reserved_rt_tables
.values())),
606 if self
.vrf_count
== self
.vrf_max_count
:
607 self
.log_error('max vrf count %d hit...not '
608 'creating vrf' % self
.vrf_count
, ifaceobj
)
609 if vrf_table
== 'auto':
610 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
612 self
.log_error('unable to get an auto table id', ifaceobj
)
613 self
.logger
.info('%s: table id auto: selected table id %s\n'
614 %(ifaceobj
.name
, vrf_table
))
616 self
._iproute
2_is
_vrf
_tableid
_inuse
(ifaceobj
, vrf_table
)
617 if ifaceobj
.name
in self
.system_reserved_rt_tables
.keys():
618 self
.log_error('cannot use system reserved %s table ids'
619 %(str(self
.system_reserved_rt_tables
.keys())),
622 if not vrf_table
.isdigit():
623 self
.log_error('vrf-table must be an integer or \'auto\'', ifaceobj
)
625 # XXX: If we decide to not allow vrf id usages out of
626 # the reserved ifupdown range, then uncomment this code.
628 if (int(vrf_table
) < self
.vrf_table_id_start
or
629 int(vrf_table
) > self
.vrf_table_id_end
):
630 self
.log_error('vrf table id %s out of reserved range [%d,%d]'
632 self
.vrf_table_id_start
,
633 self
.vrf_table_id_end
), ifaceobj
)
635 self
.ipcmd
.link_create(ifaceobj
.name
, 'vrf',
636 {'table' : '%s' %vrf_table
})
638 self
.log_error('create failed (%s)\n' % str(e
), ifaceobj
)
639 if vrf_table
!= 'auto':
640 self
._iproute
2_vrf
_table
_entry
_add
(ifaceobj
, vrf_table
)
642 if vrf_table
== 'auto':
643 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
645 self
.log_error('unable to get vrf table id', ifaceobj
)
647 # if the device exists, check if table id is same
648 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
650 running_table
= vrfdev_attrs
.get('table', None)
651 if vrf_table
!= running_table
:
652 self
.log_error('cannot change vrf table id,running table id'
653 ' %s is different from config id %s'
654 % (running_table
, vrf_table
), ifaceobj
)
657 def _up_vrf_helper(self
, ifaceobj
, vrf_table
):
659 if ifupdownflags
.flags
.PERFMODE
:
662 utils
.exec_command('%s create %s %s %s' %
668 def _up_vrf_dev(self
, ifaceobj
, vrf_table
, add_slaves
=True,
669 ifaceobj_getfunc
=None):
671 # if vrf dev is already processed return. This can happen
672 # if we the slave was configured before.
673 # see self._up_vrf_slave_without_master
674 if self
._check
_vrf
_dev
_processed
_flag
(ifaceobj
):
678 vrf_table
= self
._create
_vrf
_dev
(ifaceobj
, vrf_table
)
680 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
683 self
._add
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
684 self
._up
_vrf
_helper
(ifaceobj
, vrf_table
)
686 self
._add
_vrf
_slaves
(ifaceobj
, ifaceobj_getfunc
)
687 self
._set
_vrf
_dev
_processed
_flag
(ifaceobj
)
688 netlink
.link_set_updown(ifaceobj
.name
, "up")
690 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
692 def _kill_ssh_connections(self
, ifacename
):
694 runningaddrsdict
= self
.ipcmd
.addr_get(ifacename
)
695 if not runningaddrsdict
:
697 iplist
= [i
.split('/', 1)[0] for i
in runningaddrsdict
.keys()]
702 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
703 #users:(("sshd",pid=2528,fd=3))
704 cmdl
= ['/bin/ss', '-t', '-p']
705 for line
in utils
.exec_commandl(cmdl
).splitlines():
706 citems
= line
.split()
709 addr
= citems
[3].split('%')[0]
710 elif ':ssh' in citems
[3]:
711 addr
= citems
[3].split(':')[0]
716 proc
.append(citems
[5].split(',')[1].split('=')[1])
721 # outpt of '/usr/bin/pstree -Aps <pid>':
722 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
723 # get the above output to following format
724 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
725 pstree
= list(reversed(utils
.exec_command('/usr/bin/pstree -Aps %s' %os.getpid()).strip().split('---')))
726 for index
, process
in enumerate(pstree
):
727 # check the parent of SSH process to make sure
728 # we don't kill SSH server or systemd process
729 if 'sshd' in process
and 'sshd' in pstree
[index
+ 1]:
730 pid
= filter(lambda x
: x
.isdigit(), process
)
732 self
.logger
.info("%s: killing active ssh sessions: %s"
733 %(ifacename
, str(proc
)))
735 if ifupdownflags
.flags
.DRYRUN
:
740 os
.kill(int(id), signal
.SIGINT
)
744 # Kill current SSH client
749 self
.logger
.info("fork error : %s [%d]" % (e
.strerror
, e
.errno
))
750 if (forkret
== 0): # The first child.
753 self
.logger
.info("%s: ifreload continuing in the background" %ifacename
)
754 except OSError, (err_no
, err_message
):
755 self
.logger
.info("os.setsid failed: errno=%d: %s" % (err_no
, err_message
))
756 self
.logger
.info("pid=%d pgid=%d" % (os
.getpid(), os
.getpgid(0)))
758 self
.logger
.info("%s: killing our session: %s"
759 %(ifacename
, str(proc
)))
760 os
.kill(int(pid
), signal
.SIGINT
)
765 self
.logger
.info('%s: %s' %(ifacename
, str(e
)))
767 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
769 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
771 self
._iproute
2_vrf
_map
_initialize
()
772 # This is a vrf device
773 self
._up
_vrf
_dev
(ifaceobj
, vrf_table
, True, ifaceobj_getfunc
)
775 vrf
= ifaceobj
.get_attr_value_first('vrf')
777 self
._iproute
2_vrf
_map
_initialize
()
778 # This is a vrf slave
779 self
._up
_vrf
_slave
(ifaceobj
.name
, vrf
, ifaceobj
,
782 # check if we were a slave before
783 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
785 self
._iproute
2_vrf
_map
_initialize
()
786 if self
._is
_vrf
_dev
(master
):
787 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
,
790 self
.log_error(str(e
), ifaceobj
)
792 def _down_vrf_helper(self
, ifaceobj
, vrf_table
):
794 if ifupdownflags
.flags
.PERFMODE
:
797 utils
.exec_command('%s delete %s %s %s' %
803 def _close_sockets(self
, ifaceobj
, ifindex
):
804 if not self
.vrf_close_socks_on_down
:
808 utils
.exec_command('/bin/ss -aK \"dev == %s\"'
811 self
.logger
.info('%s: closing socks using ss'
812 ' failed (%s)\n' %(ifaceobj
.name
, str(e
)))
815 def _down_vrf_dev(self
, ifaceobj
, vrf_table
, ifaceobj_getfunc
=None):
817 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
820 if vrf_table
== 'auto':
821 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
823 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
825 for s
in running_slaves
:
827 sobj
= ifaceobj_getfunc(s
)
829 self
._handle
_existing
_connections
(sobj
[0]
833 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
836 self
.ipcmd
.addr_flush(s
)
837 netlink
.link_set_updown(s
, "down")
839 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
843 self
._down
_vrf
_helper
(ifaceobj
, vrf_table
)
845 self
.logger
.warn('%s: %s' %(ifaceobj
.name
, str(e
)))
849 self
._del
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
851 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
854 ifindex
= self
.ipcmd
.link_get_ifindex(ifaceobj
.name
)
857 self
.ipcmd
.link_delete(ifaceobj
.name
)
859 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
862 self
._close
_sockets
(ifaceobj
, ifindex
)
865 self
._iproute
2_vrf
_table
_entry
_del
(vrf_table
)
867 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
871 def _down_vrf_slave(self
, ifacename
, ifaceobj
=None, vrfname
=None):
873 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
874 netlink
.link_set_nomaster(ifacename
)
875 # Down this slave only if it is a slave ifupdown2 manages.
876 # we dont want to down slaves that maybe up'ed by
877 # somebody else. One such example is a macvlan device
878 # which ifupdown2 addressvirtual addon module auto creates
880 netlink
.link_set_updown(ifacename
, "down")
882 self
.logger
.warn('%s: %s' %(ifacename
, str(e
)))
884 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
886 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
888 self
._iproute
2_vrf
_map
_initialize
()
889 self
._down
_vrf
_dev
(ifaceobj
, vrf_table
, ifaceobj_getfunc
)
891 vrf
= ifaceobj
.get_attr_value_first('vrf')
893 self
._iproute
2_vrf
_map
_initialize
()
894 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
, None)
896 self
.log_warn(str(e
))
898 def _query_check_vrf_slave(self
, ifaceobj
, ifaceobjcurr
, vrf
):
900 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
901 if not master
or master
!= vrf
:
902 ifaceobjcurr
.update_config_with_status('vrf', str(master
), 1)
904 ifaceobjcurr
.update_config_with_status('vrf', master
, 0)
906 self
.log_error(str(e
), ifaceobjcurr
)
908 def _query_check_vrf_dev(self
, ifaceobj
, ifaceobjcurr
, vrf_table
):
910 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
911 self
.logger
.info('%s: vrf: does not exist' %(ifaceobj
.name
))
913 if vrf_table
== 'auto':
914 config_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
916 config_table
= vrf_table
917 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
919 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
921 running_table
= vrfdev_attrs
.get('table')
922 if not running_table
:
923 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
925 if config_table
!= running_table
:
926 ifaceobjcurr
.update_config_with_status('vrf-table',
929 ifaceobjcurr
.update_config_with_status('vrf-table',
931 if not ifupdownflags
.flags
.WITHDEFAULTS
:
935 utils
.exec_command('%s verify %s %s'
937 ifaceobj
.name
, config_table
))
938 ifaceobjcurr
.update_config_with_status('vrf-helper',
944 ifaceobjcurr
.update_config_with_status('vrf-helper',
951 self
.log_warn(str(e
))
953 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
955 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
957 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
958 self
._query
_check
_vrf
_dev
(ifaceobj
, ifaceobjcurr
, vrf_table
)
960 vrf
= ifaceobj
.get_attr_value_first('vrf')
962 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
963 self
._query
_check
_vrf
_slave
(ifaceobj
, ifaceobjcurr
, vrf
)
965 self
.log_warn(str(e
))
967 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
969 kind
= self
.ipcmd
.link_get_kind(ifaceobjrunning
.name
)
971 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobjrunning
.name
)
973 running_table
= vrfdev_attrs
.get('table')
975 ifaceobjrunning
.update_config('vrf-table',
978 slave_kind
= self
.ipcmd
.link_get_slave_kind(ifaceobjrunning
.name
)
979 if slave_kind
== 'vrf_slave':
980 vrf
= self
.ipcmd
.link_get_master(ifaceobjrunning
.name
)
982 ifaceobjrunning
.update_config('vrf', vrf
)
984 self
.log_warn(str(e
))
986 def _query(self
, ifaceobj
, **kwargs
):
987 if not self
.vrf_helper
:
989 if (ifaceobj
.link_kind
& ifaceLinkKind
.VRF
):
990 ifaceobj
.update_config('vrf-helper', '%s %s' %(self
.vrf_helper
,
993 _run_ops
= {'pre-up' : _up
,
995 'query-running' : _query_running
,
996 'query-checkcurr' : _query_check
,
1000 """ returns list of ops supported by this module """
1001 return self
._run
_ops
.keys()
1003 def _init_command_handlers(self
):
1005 self
.ipcmd
= iproute2()
1006 if not self
.bondcmd
:
1007 self
.bondcmd
= bondutil()
1008 if not self
.dhclientcmd
:
1009 self
.dhclientcmd
= dhclient()
1011 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1012 ifaceobj_getfunc
=None, **extra_args
):
1013 """ run bond configuration on the interface object passed as argument
1016 **ifaceobj** (object): iface object
1018 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1022 **query_ifaceobj** (object): query check ifaceobject. This is only
1023 valid when op is 'query-checkcurr'. It is an object same as
1024 ifaceobj, but contains running attribute values and its config
1025 status. The modules can use it to return queried running state
1026 of interfaces. status is success if the running state is same
1027 as user required state in ifaceobj. error otherwise.
1029 op_handler
= self
._run
_ops
.get(operation
)
1032 self
._init
_command
_handlers
()
1033 if operation
== 'query-checkcurr':
1034 op_handler(self
, ifaceobj
, query_ifaceobj
)
1036 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)