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'
158 vrfs
= self
.ipcmd
.link_get_vrfs()
161 for v
, lattrs
in vrfs
.iteritems():
162 table
= lattrs
.get('table', None)
164 running_vrf_map
[int(table
)] = v
166 if (not running_vrf_map
or (running_vrf_map
!= self
.iproute2_vrf_map
)):
167 self
.iproute2_vrf_map
= running_vrf_map
168 iproute2_vrf_map_force_rewrite
= True
170 self
.iproute2_vrf_map_fd
= None
172 if iproute2_vrf_map_force_rewrite
:
173 # reopen the file and rewrite the map
174 self
._iproute
2_vrf
_map
_open
(True, False)
176 self
._iproute
2_vrf
_map
_open
(False, True)
178 self
.iproute2_vrf_map_sync_to_disk
= False
179 atexit
.register(self
._iproute
2_vrf
_map
_sync
_to
_disk
)
181 self
.logger
.info("vrf: dumping iproute2_vrf_map")
182 self
.logger
.info(self
.iproute2_vrf_map
)
184 last_used_vrf_table
= None
185 for t
in range(self
.vrf_table_id_start
,
186 self
.vrf_table_id_end
):
187 if not self
.iproute2_vrf_map
.get(t
):
189 last_used_vrf_table
= t
190 self
.last_used_vrf_table
= last_used_vrf_table
191 self
._iproute
2_vrf
_map
_initialized
= True
192 self
.vrf_count
= len(self
.iproute2_vrf_map
)
194 def _iproute2_map_warn(self
, errstr
):
195 if self
.warn_on_vrf_map_write_err
:
196 if not os
.path
.exists('/etc/iproute2/rt_tables.d/'):
197 self
.logger
.info('unable to save iproute2 vrf to table ' +
198 'map (%s)\n' %errstr
)
199 self
.logger
.info('cannot find /etc/iproute2/rt_tables.d.' +
200 ' pls check if your iproute2 version' +
201 ' supports rt_tables.d')
203 self
.logger
.warn('unable to open iproute2 vrf to table ' +
204 'map (%s)\n' %errstr
)
205 self
.warn_on_vrf_map_write_err
= False
207 def _iproute2_vrf_map_sync_to_disk(self
):
208 if (ifupdownflags
.flags
.DRYRUN
or
209 not self
.iproute2_vrf_map_sync_to_disk
):
211 self
.logger
.info('vrf: syncing table map to %s'
212 %self
.iproute2_vrf_filename
)
214 with
open(self
.iproute2_vrf_filename
, 'w') as f
:
215 f
.write(self
.iproute2_vrf_filehdr
%(self
.vrf_table_id_start
,
216 self
.vrf_table_id_end
))
217 for t
, v
in self
.iproute2_vrf_map
.iteritems():
218 f
.write('%s %s\n' %(t
, v
))
221 self
._iproute
2_map
_warn
(str(e
))
224 def _iproute2_vrf_map_open(self
, sync_vrfs
=False, append
=False):
225 self
.logger
.info('vrf: syncing table map to %s'
226 %self
.iproute2_vrf_filename
)
227 if ifupdownflags
.flags
.DRYRUN
:
229 fmode
= 'a+' if append
else 'w'
231 self
.iproute2_vrf_map_fd
= open(self
.iproute2_vrf_filename
,
233 fcntl
.fcntl(self
.iproute2_vrf_map_fd
, fcntl
.F_SETFD
, fcntl
.FD_CLOEXEC
)
235 self
._iproute
2_map
_warn
(str(e
))
240 self
.iproute2_vrf_map_fd
.write(self
.iproute2_vrf_filehdr
241 %(self
.vrf_table_id_start
,
242 self
.vrf_table_id_end
))
243 for t
, v
in self
.iproute2_vrf_map
.iteritems():
244 self
.iproute2_vrf_map_fd
.write('%s %s\n' %(t
, v
))
245 self
.iproute2_vrf_map_fd
.flush()
247 def _is_vrf(self
, ifaceobj
):
248 if ifaceobj
.get_attr_value_first('vrf-table'):
252 def get_upper_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
253 """ Returns list of interfaces dependent on ifaceobj """
255 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
257 ifaceobj
.link_type
= ifaceLinkType
.LINK_MASTER
258 ifaceobj
.link_kind |
= ifaceLinkKind
.VRF
259 ifaceobj
.role |
= ifaceRole
.MASTER
260 vrf_iface_name
= ifaceobj
.get_attr_value_first('vrf')
261 if not vrf_iface_name
:
263 ifaceobj
.link_type
= ifaceLinkType
.LINK_SLAVE
264 ifaceobj
.link_privflags |
= ifaceLinkPrivFlags
.VRF_SLAVE
266 return [vrf_iface_name
]
268 def get_upper_ifacenames_running(self
, ifaceobj
):
271 def _get_iproute2_vrf_table(self
, vrf_dev_name
):
272 for t
, v
in self
.iproute2_vrf_map
.iteritems():
273 if v
== vrf_dev_name
:
277 def _get_avail_vrf_table_id(self
):
278 if self
.last_used_vrf_table
== None:
279 table_id_start
= self
.vrf_table_id_start
281 table_id_start
= self
.last_used_vrf_table
+ 1
282 for t
in range(table_id_start
,
283 self
.vrf_table_id_end
):
284 if not self
.iproute2_vrf_map
.get(t
):
285 self
.last_used_vrf_table
= t
289 def _iproute2_is_vrf_tableid_inuse(self
, vrfifaceobj
, table_id
):
290 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
291 if old_vrf_name
and old_vrf_name
!= vrfifaceobj
.name
:
292 self
.log_error('table id %s already assigned to vrf dev %s'
293 %(table_id
, old_vrf_name
), vrfifaceobj
)
295 def _iproute2_vrf_table_entry_add(self
, vrfifaceobj
, table_id
):
296 old_vrf_name
= self
.iproute2_vrf_map
.get(int(table_id
))
298 self
.iproute2_vrf_map
[int(table_id
)] = vrfifaceobj
.name
299 if self
.iproute2_vrf_map_fd
:
300 self
.iproute2_vrf_map_fd
.write('%s %s\n'
301 %(table_id
, vrfifaceobj
.name
))
302 self
.iproute2_vrf_map_fd
.flush()
305 if old_vrf_name
!= vrfifaceobj
.name
:
306 self
.log_error('table id %d already assigned to vrf dev %s'
307 %(table_id
, old_vrf_name
))
309 def _iproute2_vrf_table_entry_del(self
, table_id
):
311 # with any del of vrf map, we need to force sync to disk
312 self
.iproute2_vrf_map_sync_to_disk
= True
313 del self
.iproute2_vrf_map
[int(table_id
)]
315 self
.logger
.info('vrf: iproute2 vrf map del failed for %d (%s)'
319 def _is_vrf_dev(self
, ifacename
):
320 # Look at iproute2 map for now.
321 # If it was a master we knew about,
322 # it is definately there
323 if ifacename
in self
.iproute2_vrf_map
.values():
327 def _is_dhcp_slave(self
, ifaceobj
):
328 if (not ifaceobj
.addr_method
or
329 (ifaceobj
.addr_method
!= 'dhcp' and
330 ifaceobj
.addr_method
!= 'dhcp6')):
334 def _up_vrf_slave_without_master(self
, ifacename
, vrfname
, ifaceobj
,
336 """ If we have a vrf slave that has dhcp configured, bring up the
337 vrf master now. This is needed because vrf has special handling
338 in dhclient hook which requires the vrf master to be present """
340 vrf_master
= ifaceobj
.upperifaces
[0]
342 self
.logger
.warn('%s: vrf master not found' %ifacename
)
344 if os
.path
.exists('/sys/class/net/%s' %vrf_master
):
345 self
.logger
.info('%s: vrf master %s exists returning'
346 %(ifacename
, vrf_master
))
348 self
.logger
.info('%s: bringing up vrf master %s'
349 %(ifacename
, vrf_master
))
350 for mobj
in vrf_master_objs
:
351 vrf_table
= mobj
.get_attr_value_first('vrf-table')
353 if vrf_table
== 'auto':
354 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
356 self
.log_error('%s: unable to get an auto table id'
357 %mobj
.name
, ifaceobj
)
358 self
.logger
.info('%s: table id auto: selected table id %s\n'
359 %(mobj
.name
, vrf_table
))
361 self
._up
_vrf
_dev
(mobj
, vrf_table
, False)
365 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
366 self
.ipcmd
.link_set(ifacename
, 'master', vrfname
)
369 def _down_dhcp_slave(self
, ifaceobj
, vrfname
):
371 dhclient_cmd_prefix
= None
372 if (vrfname
and self
.vrf_exec_cmd_prefix
and
373 self
.ipcmd
.link_exists(vrfname
)):
374 dhclient_cmd_prefix
= '%s %s' %(self
.vrf_exec_cmd_prefix
,
376 self
.dhclientcmd
.release(ifaceobj
.name
, dhclient_cmd_prefix
)
378 # ignore any dhclient release errors
381 def _handle_existing_connections(self
, ifaceobj
, vrfname
):
382 if not ifaceobj
or ifupdownflags
.flags
.PERFMODE
:
384 if (self
.vrf_mgmt_devname
and
385 self
.vrf_mgmt_devname
== vrfname
):
386 self
._kill
_ssh
_connections
(ifaceobj
.name
)
387 if self
._is
_dhcp
_slave
(ifaceobj
):
388 self
._down
_dhcp
_slave
(ifaceobj
, vrfname
)
390 def _up_vrf_slave(self
, ifacename
, vrfname
, ifaceobj
=None,
391 ifaceobj_getfunc
=None, vrf_exists
=False):
394 if vrf_exists
or self
.ipcmd
.link_exists(vrfname
):
395 upper
= self
.ipcmd
.link_get_upper(ifacename
)
396 if not upper
or upper
!= vrfname
:
397 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
398 self
.ipcmd
.link_set(ifacename
, 'master', vrfname
)
400 vrf_master_objs
= ifaceobj_getfunc(vrfname
)
401 if not vrf_master_objs
:
402 # this is the case where vrf is assigned to an interface
403 # but user has not provided a vrf interface.
404 # people expect you to warn them but go ahead with the
405 # rest of the config on that interface
406 netlink
.link_set_updown(ifacename
, "up")
407 self
.log_error('vrf master ifaceobj %s not found'
410 if (ifupdownflags
.flags
.ALL
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('%s: max vrf count %d hit...not '
608 'creating vrf' %(ifaceobj
.name
,
609 self
.vrf_count
), ifaceobj
)
610 if vrf_table
== 'auto':
611 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
613 self
.log_error('%s: unable to get an auto table id'
614 %ifaceobj
.name
, ifaceobj
)
615 self
.logger
.info('%s: table id auto: selected table id %s\n'
616 %(ifaceobj
.name
, vrf_table
))
618 self
._iproute
2_is
_vrf
_tableid
_inuse
(ifaceobj
, vrf_table
)
619 if ifaceobj
.name
in self
.system_reserved_rt_tables
.keys():
620 self
.log_error('cannot use system reserved %s table ids'
621 %(str(self
.system_reserved_rt_tables
.keys())),
624 if not vrf_table
.isdigit():
625 self
.log_error('%s: vrf-table must be an integer or \'auto\''
626 %(ifaceobj
.name
), ifaceobj
)
628 # XXX: If we decide to not allow vrf id usages out of
629 # the reserved ifupdown range, then uncomment this code.
631 if (int(vrf_table
) < self
.vrf_table_id_start
or
632 int(vrf_table
) > self
.vrf_table_id_end
):
633 self
.log_error('%s: vrf table id %s out of reserved range [%d,%d]'
634 %(ifaceobj
.name
, vrf_table
,
635 self
.vrf_table_id_start
,
636 self
.vrf_table_id_end
), ifaceobj
)
638 self
.ipcmd
.link_create(ifaceobj
.name
, 'vrf',
639 {'table' : '%s' %vrf_table
})
641 self
.log_error('%s: create failed (%s)\n'
642 %(ifaceobj
.name
, str(e
)), ifaceobj
)
643 if vrf_table
!= 'auto':
644 self
._iproute
2_vrf
_table
_entry
_add
(ifaceobj
, vrf_table
)
646 if vrf_table
== 'auto':
647 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
649 self
.log_error('%s: unable to get vrf table id'
650 %ifaceobj
.name
, ifaceobj
)
652 # if the device exists, check if table id is same
653 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
655 running_table
= vrfdev_attrs
.get('table', None)
656 if vrf_table
!= running_table
:
657 self
.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj
.name
,
658 running_table
, vrf_table
),
662 def _up_vrf_helper(self
, ifaceobj
, vrf_table
):
664 if ifupdownflags
.flags
.PERFMODE
:
667 utils
.exec_command('%s create %s %s %s' %
673 def _up_vrf_dev(self
, ifaceobj
, vrf_table
, add_slaves
=True,
674 ifaceobj_getfunc
=None):
676 # if vrf dev is already processed return. This can happen
677 # if we the slave was configured before.
678 # see self._up_vrf_slave_without_master
679 if self
._check
_vrf
_dev
_processed
_flag
(ifaceobj
):
683 vrf_table
= self
._create
_vrf
_dev
(ifaceobj
, vrf_table
)
685 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
688 self
._add
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
689 self
._up
_vrf
_helper
(ifaceobj
, vrf_table
)
691 self
._add
_vrf
_slaves
(ifaceobj
, ifaceobj_getfunc
)
692 self
._set
_vrf
_dev
_processed
_flag
(ifaceobj
)
693 netlink
.link_set_updown(ifaceobj
.name
, "up")
695 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)), ifaceobj
)
697 def _kill_ssh_connections(self
, ifacename
):
699 runningaddrsdict
= self
.ipcmd
.addr_get(ifacename
)
700 if not runningaddrsdict
:
702 iplist
= [i
.split('/', 1)[0] for i
in runningaddrsdict
.keys()]
707 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
708 #users:(("sshd",pid=2528,fd=3))
709 cmdl
= ['/bin/ss', '-t', '-p']
710 for line
in utils
.exec_commandl(cmdl
).splitlines():
711 citems
= line
.split()
714 addr
= citems
[3].split('%')[0]
715 elif ':ssh' in citems
[3]:
716 addr
= citems
[3].split(':')[0]
721 proc
.append(citems
[5].split(',')[1].split('=')[1])
726 # outpt of '/usr/bin/pstree -Aps <pid>':
727 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
728 # get the above output to following format
729 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
730 pstree
= list(reversed(utils
.exec_command('/usr/bin/pstree -Aps %s' %os.getpid()).strip().split('---')))
731 for index
, process
in enumerate(pstree
):
732 # check the parent of SSH process to make sure
733 # we don't kill SSH server or systemd process
734 if 'sshd' in process
and 'sshd' in pstree
[index
+ 1]:
735 pid
= filter(lambda x
: x
.isdigit(), process
)
737 self
.logger
.info("%s: killing active ssh sessions: %s"
738 %(ifacename
, str(proc
)))
740 if ifupdownflags
.flags
.DRYRUN
:
745 os
.kill(int(id), signal
.SIGINT
)
749 # Kill current SSH client
754 self
.logger
.info("fork error : %s [%d]" % (e
.strerror
, e
.errno
))
755 if (forkret
== 0): # The first child.
758 self
.logger
.info("%s: ifreload continuing in the background" %ifacename
)
759 except OSError, (err_no
, err_message
):
760 self
.logger
.info("os.setsid failed: errno=%d: %s" % (err_no
, err_message
))
761 self
.logger
.info("pid=%d pgid=%d" % (os
.getpid(), os
.getpgid(0)))
763 self
.logger
.info("%s: killing our session: %s"
764 %(ifacename
, str(proc
)))
765 os
.kill(int(pid
), signal
.SIGINT
)
770 self
.logger
.info('%s: %s' %(ifacename
, str(e
)))
772 def _up(self
, ifaceobj
, ifaceobj_getfunc
=None):
774 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
776 self
._iproute
2_vrf
_map
_initialize
()
777 # This is a vrf device
778 self
._up
_vrf
_dev
(ifaceobj
, vrf_table
, True, ifaceobj_getfunc
)
780 vrf
= ifaceobj
.get_attr_value_first('vrf')
782 self
._iproute
2_vrf
_map
_initialize
()
783 # This is a vrf slave
784 self
._up
_vrf
_slave
(ifaceobj
.name
, vrf
, ifaceobj
,
787 # check if we were a slave before
788 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
790 self
._iproute
2_vrf
_map
_initialize
()
791 if self
._is
_vrf
_dev
(master
):
792 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
,
795 self
.log_error(str(e
), ifaceobj
)
797 def _down_vrf_helper(self
, ifaceobj
, vrf_table
):
799 if ifupdownflags
.flags
.PERFMODE
:
802 utils
.exec_command('%s delete %s %s %s' %
808 def _close_sockets(self
, ifaceobj
, ifindex
):
809 if not self
.vrf_close_socks_on_down
:
813 utils
.exec_command('/bin/ss -aK \"dev == %s\"'
816 self
.logger
.info('%s: closing socks using ss'
817 ' failed (%s)\n' %(ifaceobj
.name
, str(e
)))
820 def _down_vrf_dev(self
, ifaceobj
, vrf_table
, ifaceobj_getfunc
=None):
822 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
825 if vrf_table
== 'auto':
826 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
828 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
830 for s
in running_slaves
:
832 sobj
= ifaceobj_getfunc(s
)
834 self
._handle
_existing
_connections
(sobj
[0]
838 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
841 self
.ipcmd
.addr_flush(s
)
842 netlink
.link_set_updown(s
, "down")
844 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
848 self
._down
_vrf
_helper
(ifaceobj
, vrf_table
)
850 self
.logger
.warn('%s: %s' %(ifaceobj
.name
, str(e
)))
854 self
._del
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
856 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
859 ifindex
= self
.ipcmd
.link_get_ifindex(ifaceobj
.name
)
862 self
.ipcmd
.link_delete(ifaceobj
.name
)
864 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
867 self
._close
_sockets
(ifaceobj
, ifindex
)
870 self
._iproute
2_vrf
_table
_entry
_del
(vrf_table
)
872 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
876 def _down_vrf_slave(self
, ifacename
, ifaceobj
=None, vrfname
=None):
878 self
._handle
_existing
_connections
(ifaceobj
, vrfname
)
879 self
.ipcmd
.link_set(ifacename
, 'nomaster')
880 # Down this slave only if it is a slave ifupdown2 manages.
881 # we dont want to down slaves that maybe up'ed by
882 # somebody else. One such example is a macvlan device
883 # which ifupdown2 addressvirtual addon module auto creates
885 netlink
.link_set_updown(ifacename
, "down")
887 self
.logger
.warn('%s: %s' %(ifacename
, str(e
)))
889 def _down(self
, ifaceobj
, ifaceobj_getfunc
=None):
891 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
893 self
._iproute
2_vrf
_map
_initialize
()
894 self
._down
_vrf
_dev
(ifaceobj
, vrf_table
, ifaceobj_getfunc
)
896 vrf
= ifaceobj
.get_attr_value_first('vrf')
898 self
._iproute
2_vrf
_map
_initialize
()
899 self
._down
_vrf
_slave
(ifaceobj
.name
, ifaceobj
, None)
901 self
.log_warn(str(e
))
903 def _query_check_vrf_slave(self
, ifaceobj
, ifaceobjcurr
, vrf
):
905 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
906 if not master
or master
!= vrf
:
907 ifaceobjcurr
.update_config_with_status('vrf', str(master
), 1)
909 ifaceobjcurr
.update_config_with_status('vrf', master
, 0)
911 self
.log_error(str(e
), ifaceobjcurr
)
913 def _query_check_vrf_dev(self
, ifaceobj
, ifaceobjcurr
, vrf_table
):
915 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
916 self
.logger
.info('%s: vrf: does not exist' %(ifaceobj
.name
))
918 if vrf_table
== 'auto':
919 config_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
921 config_table
= vrf_table
922 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
924 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
926 running_table
= vrfdev_attrs
.get('table')
927 if not running_table
:
928 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
930 if config_table
!= running_table
:
931 ifaceobjcurr
.update_config_with_status('vrf-table',
934 ifaceobjcurr
.update_config_with_status('vrf-table',
936 if not ifupdownflags
.flags
.WITHDEFAULTS
:
940 utils
.exec_command('%s verify %s %s'
942 ifaceobj
.name
, config_table
))
943 ifaceobjcurr
.update_config_with_status('vrf-helper',
949 ifaceobjcurr
.update_config_with_status('vrf-helper',
956 self
.log_warn(str(e
))
958 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
960 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
962 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
963 self
._query
_check
_vrf
_dev
(ifaceobj
, ifaceobjcurr
, vrf_table
)
965 vrf
= ifaceobj
.get_attr_value_first('vrf')
967 self
._iproute
2_vrf
_map
_initialize
(writetodisk
=False)
968 self
._query
_check
_vrf
_slave
(ifaceobj
, ifaceobjcurr
, vrf
)
970 self
.log_warn(str(e
))
972 def _query_running(self
, ifaceobjrunning
, ifaceobj_getfunc
=None):
974 kind
= self
.ipcmd
.link_get_kind(ifaceobjrunning
.name
)
976 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobjrunning
.name
)
978 running_table
= vrfdev_attrs
.get('table')
980 ifaceobjrunning
.update_config('vrf-table',
982 elif kind
== 'vrf_slave':
983 vrf
= self
.ipcmd
.link_get_master(ifaceobjrunning
.name
)
985 ifaceobjrunning
.update_config('vrf', vrf
)
987 self
.log_warn(str(e
))
989 def _query(self
, ifaceobj
, **kwargs
):
990 if not self
.vrf_helper
:
992 if (ifaceobj
.link_kind
& ifaceLinkKind
.VRF
):
993 ifaceobj
.update_config('vrf-helper', '%s %s' %(self
.vrf_helper
,
996 _run_ops
= {'pre-up' : _up
,
998 'query-running' : _query_running
,
999 'query-checkcurr' : _query_check
,
1003 """ returns list of ops supported by this module """
1004 return self
._run
_ops
.keys()
1006 def _init_command_handlers(self
):
1008 self
.ipcmd
= iproute2()
1009 if not self
.bondcmd
:
1010 self
.bondcmd
= bondutil()
1011 if not self
.dhclientcmd
:
1012 self
.dhclientcmd
= dhclient()
1014 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None,
1015 ifaceobj_getfunc
=None, **extra_args
):
1016 """ run bond configuration on the interface object passed as argument
1019 **ifaceobj** (object): iface object
1021 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1025 **query_ifaceobj** (object): query check ifaceobject. This is only
1026 valid when op is 'query-checkcurr'. It is an object same as
1027 ifaceobj, but contains running attribute values and its config
1028 status. The modules can use it to return queried running state
1029 of interfaces. status is success if the running state is same
1030 as user required state in ifaceobj. error otherwise.
1032 op_handler
= self
._run
_ops
.get(operation
)
1035 self
._init
_command
_handlers
()
1036 if operation
== 'query-checkcurr':
1037 op_handler(self
, ifaceobj
, query_ifaceobj
)
1039 op_handler(self
, ifaceobj
, ifaceobj_getfunc
=ifaceobj_getfunc
)