]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/vrf.py
addons: vrf: Fix to kill current ssh on 'sudo ifreload -a' to enable mgmt VRF
[mirror_ifupdown2.git] / addons / vrf.py
1 #!/usr/bin/python
2 #
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
5 #
6
7 import os
8 import signal
9 import errno
10 import fcntl
11 import atexit
12 from ifupdown.iface import *
13 from ifupdown.utils import utils
14 import ifupdown.policymanager as policymanager
15 import ifupdownaddons
16 import ifupdown.rtnetlink_api as rtnetlink_api
17 import ifupdown.ifupdownflags as ifupdownflags
18 from ifupdownaddons.modulebase import moduleBase
19 from ifupdownaddons.bondutil import bondutil
20 from ifupdownaddons.iproute2 import iproute2
21 from ifupdownaddons.dhclient import dhclient
22 from ifupdownaddons.utilsbase import *
23
24 class vrfPrivFlags:
25 PROCESSED = 0x1
26
27 class vrf(moduleBase):
28 """ ifupdown2 addon module to configure vrfs """
29 _modinfo = { 'mhelp' : 'vrf configuration module',
30 'attrs' : {
31 'vrf-table':
32 {'help' : 'vrf device routing table id. key to ' +
33 'creating a vrf device. ' +
34 'Table id is either \'auto\' or '+
35 '\'valid routing table id\'',
36 'example': ['vrf-table auto', 'vrf-table 1001']},
37 'vrf':
38 {'help' : 'vrf the interface is part of.',
39 'example': ['vrf blue']}}}
40
41 iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
42 iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
43 '# It contains the vrf name to table mapping.\n' + \
44 '# Reserved table range %s %s\n'
45 VRF_TABLE_START = 1001
46 VRF_TABLE_END = 5000
47
48 system_reserved_rt_tables = {'255' : 'local', '254' : 'main',
49 '253' : 'default', '0' : 'unspec'}
50
51 def __init__(self, *args, **kargs):
52 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
53 self.ipcmd = None
54 self.bondcmd = None
55 self.dhclientcmd = None
56 self.name = self.__class__.__name__
57 if ifupdownflags.flags.PERFMODE:
58 # if perf mode is set, remove vrf map file.
59 # start afresh. PERFMODE is set at boot
60 if os.path.exists(self.iproute2_vrf_filename):
61 try:
62 self.logger.info('vrf: removing file %s'
63 %self.iproute2_vrf_filename)
64 os.remove(self.iproute2_vrf_filename)
65 except Exception, e:
66 self.logger.debug('vrf: removing file failed (%s)'
67 %str(e))
68 try:
69 ip_rules = utils.exec_command('/sbin/ip rule show').splitlines()
70 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
71 except Exception, e:
72 self.ip_rule_cache = []
73 self.logger.warn('vrf: cache v4: %s' % str(e))
74
75 try:
76 ip_rules = utils.exec_command('/sbin/ip -6 rule show').splitlines()
77 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
78 except Exception, e:
79 self.ip6_rule_cache = []
80 self.logger.warn('vrf: cache v6: %s' % str(e))
81
82 #self.logger.debug("vrf: ip rule cache")
83 #self.logger.info(self.ip_rule_cache)
84
85 #self.logger.info("vrf: ip -6 rule cache")
86 #self.logger.info(self.ip6_rule_cache)
87
88 self._iproute2_vrf_map_initialized = False
89 self.iproute2_vrf_map = {}
90 self.iproute2_vrf_map_fd = None
91 self.iproute2_vrf_map_sync_to_disk = False
92
93 self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
94 if not self.vrf_table_id_start:
95 self.vrf_table_id_start = self.VRF_TABLE_START
96 self.vrf_table_id_end = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-end')
97 if not self.vrf_table_id_end:
98 self.vrf_table_id_end = self.VRF_TABLE_END
99 self.vrf_max_count = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-max-count')
100
101 self.vrf_fix_local_table = True
102 self.vrf_count = 0
103 self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
104 self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper')
105
106 def _iproute2_vrf_map_initialize(self, writetodisk=True):
107 if self._iproute2_vrf_map_initialized:
108 return
109
110 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
111 self.iproute2_vrf_map = {}
112 iproute2_vrf_map_force_rewrite = False
113 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
114 if os.path.exists(self.iproute2_vrf_filename):
115 with open(self.iproute2_vrf_filename, 'r+') as vrf_map_fd:
116 lines = vrf_map_fd.readlines()
117 for l in lines:
118 l = l.strip()
119 if l[0] == '#':
120 continue
121 try:
122 (table, vrf_name) = l.strip().split()
123 if self.iproute2_vrf_map.get(int(table)):
124 # looks like the existing file has
125 # duplicate entries, force rewrite of the
126 # file
127 iproute2_vrf_map_force_rewrite = True
128 continue
129 self.iproute2_vrf_map[int(table)] = vrf_name
130 except Exception, e:
131 self.logger.info('vrf: iproute2_vrf_map: unable to parse %s'
132 %l)
133 pass
134
135 vrfs = self.ipcmd.link_get_vrfs()
136 running_vrf_map = {}
137 if vrfs:
138 for v, lattrs in vrfs.iteritems():
139 table = lattrs.get('table', None)
140 if table:
141 running_vrf_map[int(table)] = v
142
143 if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
144 self.iproute2_vrf_map = running_vrf_map
145 iproute2_vrf_map_force_rewrite = True
146
147 self.iproute2_vrf_map_fd = None
148 if writetodisk:
149 if iproute2_vrf_map_force_rewrite:
150 # reopen the file and rewrite the map
151 self._iproute2_vrf_map_open(True, False)
152 else:
153 self._iproute2_vrf_map_open(False, True)
154
155 self.iproute2_vrf_map_sync_to_disk = False
156 atexit.register(self._iproute2_vrf_map_sync_to_disk)
157
158 self.logger.info("vrf: dumping iproute2_vrf_map")
159 self.logger.info(self.iproute2_vrf_map)
160
161 last_used_vrf_table = None
162 for t in range(self.vrf_table_id_start,
163 self.vrf_table_id_end):
164 if not self.iproute2_vrf_map.get(t):
165 break
166 last_used_vrf_table = t
167 self.last_used_vrf_table = last_used_vrf_table
168 self._iproute2_vrf_map_initialized = True
169 self.vrf_count = len(self.iproute2_vrf_map)
170
171 def _iproute2_vrf_map_sync_to_disk(self):
172 if (ifupdownflags.flags.DRYRUN or
173 not self.iproute2_vrf_map_sync_to_disk):
174 return
175 self.logger.info('vrf: syncing table map to %s'
176 %self.iproute2_vrf_filename)
177 with open(self.iproute2_vrf_filename, 'w') as f:
178 f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
179 self.vrf_table_id_end))
180 for t, v in self.iproute2_vrf_map.iteritems():
181 f.write('%s %s\n' %(t, v))
182 f.flush()
183
184 def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False):
185 self.logger.info('vrf: syncing table map to %s'
186 %self.iproute2_vrf_filename)
187 if ifupdownflags.flags.DRYRUN:
188 return
189 fmode = 'a+' if append else 'w'
190 try:
191 self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
192 '%s' %fmode)
193 fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
194 except Exception, e:
195 self.log_warn('vrf: error opening %s (%s)'
196 %(self.iproute2_vrf_filename, str(e)))
197 return
198
199 if not append:
200 # write file header
201 self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
202 %(self.vrf_table_id_start,
203 self.vrf_table_id_end))
204 for t, v in self.iproute2_vrf_map.iteritems():
205 self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
206 self.iproute2_vrf_map_fd.flush()
207
208 def _is_vrf(self, ifaceobj):
209 if ifaceobj.get_attr_value_first('vrf-table'):
210 return True
211 return False
212
213 def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
214 """ Returns list of interfaces dependent on ifaceobj """
215
216 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
217 if vrf_table:
218 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
219 ifaceobj.link_kind |= ifaceLinkKind.VRF
220 ifaceobj.role |= ifaceRole.MASTER
221 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
222 if not vrf_iface_name:
223 return None
224 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
225 ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
226
227 return [vrf_iface_name]
228
229 def get_upper_ifacenames_running(self, ifaceobj):
230 return None
231
232 def _get_iproute2_vrf_table(self, vrf_dev_name):
233 for t, v in self.iproute2_vrf_map.iteritems():
234 if v == vrf_dev_name:
235 return str(t)
236 return None
237
238 def _get_avail_vrf_table_id(self):
239 if self.last_used_vrf_table == None:
240 table_id_start = self.vrf_table_id_start
241 else:
242 table_id_start = self.last_used_vrf_table + 1
243 for t in range(table_id_start,
244 self.vrf_table_id_end):
245 if not self.iproute2_vrf_map.get(t):
246 self.last_used_vrf_table = t
247 return str(t)
248 return None
249
250 def _iproute2_is_vrf_tableid_inuse(self, vrfifaceobj, table_id):
251 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
252 if old_vrf_name and old_vrf_name != vrfifaceobj.name:
253 self.log_error('table id %s already assigned to vrf dev %s'
254 %(table_id, old_vrf_name), vrfifaceobj)
255
256 def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id):
257 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
258 if not old_vrf_name:
259 self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
260 if self.iproute2_vrf_map_fd:
261 self.iproute2_vrf_map_fd.write('%s %s\n'
262 %(table_id, vrfifaceobj.name))
263 self.iproute2_vrf_map_fd.flush()
264 self.vrf_count += 1
265 return
266
267 if old_vrf_name != vrfifaceobj.name:
268 self.log_error('table id %d already assigned to vrf dev %s'
269 %(table_id, old_vrf_name))
270
271 def _iproute2_vrf_table_entry_del(self, table_id):
272 try:
273 # with any del of vrf map, we need to force sync to disk
274 self.iproute2_vrf_map_sync_to_disk = True
275 del self.iproute2_vrf_map[int(table_id)]
276 except Exception, e:
277 self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)'
278 %(table_id, str(e)))
279 pass
280
281 def _is_vrf_dev(self, ifacename):
282 # Look at iproute2 map for now.
283 # If it was a master we knew about,
284 # it is definately there
285 if ifacename in self.iproute2_vrf_map.values():
286 return True
287 return False
288
289 def _is_dhcp_slave(self, ifaceobj):
290 if (not ifaceobj.addr_method or
291 (ifaceobj.addr_method != 'dhcp' and
292 ifaceobj.addr_method != 'dhcp6')):
293 return False
294 return True
295
296 def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj,
297 ifaceobj_getfunc):
298 """ If we have a vrf slave that has dhcp configured, bring up the
299 vrf master now. This is needed because vrf has special handling
300 in dhclient hook which requires the vrf master to be present """
301
302 vrf_master = ifaceobj.upperifaces[0]
303 if not vrf_master:
304 self.logger.warn('%s: vrf master not found' %ifacename)
305 return
306 if os.path.exists('/sys/class/net/%s' %vrf_master):
307 self.logger.info('%s: vrf master %s exists returning'
308 %(ifacename, vrf_master))
309 return
310 vrf_master_objs = ifaceobj_getfunc(vrf_master)
311 if not vrf_master_objs:
312 self.logger.warn('%s: vrf master ifaceobj not found' %ifacename)
313 return
314 self.logger.info('%s: bringing up vrf master %s'
315 %(ifacename, vrf_master))
316 for mobj in vrf_master_objs:
317 vrf_table = mobj.get_attr_value_first('vrf-table')
318 if vrf_table:
319 if vrf_table == 'auto':
320 vrf_table = self._get_avail_vrf_table_id()
321 if not vrf_table:
322 self.log_error('%s: unable to get an auto table id'
323 %mobj.name, ifaceobj)
324 self.logger.info('%s: table id auto: selected table id %s\n'
325 %(mobj.name, vrf_table))
326 try:
327 self._up_vrf_dev(mobj, vrf_table, False)
328 except Exception:
329 raise
330 break
331 self._handle_existing_connections(ifaceobj, vrfname)
332 self.ipcmd.link_set(ifacename, 'master', vrfname)
333 return
334
335 def _down_dhcp_slave(self, ifaceobj, vrfname):
336 try:
337 dhclient_cmd_prefix = None
338 if (vrfname and self.vrf_exec_cmd_prefix and
339 self.ipcmd.link_exists(vrfname)):
340 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
341 vrfname)
342 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
343 except:
344 # ignore any dhclient release errors
345 pass
346
347 def _handle_existing_connections(self, ifaceobj, vrfname):
348 if not ifaceobj or ifupdownflags.flags.PERFMODE:
349 return
350 if (self.vrf_mgmt_devname and
351 self.vrf_mgmt_devname == vrfname):
352 self._kill_ssh_connections(ifaceobj.name)
353 if self._is_dhcp_slave(ifaceobj):
354 self._down_dhcp_slave(ifaceobj, vrfname)
355
356 def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
357 ifaceobj_getfunc=None, vrf_exists=False):
358 try:
359 master_exists = True
360 if vrf_exists or self.ipcmd.link_exists(vrfname):
361 upper = self.ipcmd.link_get_upper(ifacename)
362 if not upper or upper != vrfname:
363 self._handle_existing_connections(ifaceobj, vrfname)
364 self.ipcmd.link_set(ifacename, 'master', vrfname)
365 elif ifupdownflags.flags.ALL and ifaceobj:
366 self._up_vrf_slave_without_master(ifacename, vrfname, ifaceobj,
367 ifaceobj_getfunc)
368 else:
369 master_exists = False
370 if master_exists:
371 rtnetlink_api.rtnl_api.link_set(ifacename, "up")
372 elif ifupdownflags.flags.ALL:
373 self.log_error('vrf %s not around, skipping vrf config'
374 %(vrfname), ifaceobj)
375 except Exception, e:
376 self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj)
377
378 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
379 pref = 200
380 ip_rule_out_format = '%s: from all %s %s lookup %s'
381 ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
382
383 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
384 if rule in self.ip_rule_cache:
385 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
386 vrf_dev_name)
387 utils.exec_command(rule_cmd)
388
389 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
390 if rule in self.ip_rule_cache:
391 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
392 vrf_dev_name)
393 utils.exec_command(rule_cmd)
394
395 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
396 if rule in self.ip6_rule_cache:
397 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
398 vrf_dev_name)
399 utils.exec_command(rule_cmd)
400
401 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
402 if rule in self.ip6_rule_cache:
403 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
404 vrf_dev_name)
405 utils.exec_command(rule_cmd)
406
407 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
408 pref = 200
409 ip_rule_out_format = '%s: from all %s %s lookup %s'
410 ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
411 if self.vrf_fix_local_table:
412 self.vrf_fix_local_table = False
413 rule = '0: from all lookup local'
414 if rule in self.ip_rule_cache:
415 try:
416 utils.exec_command('ip rule del pref 0')
417 utils.exec_command('ip rule add pref 32765 table local')
418 except Exception, e:
419 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
420 pass
421 if rule in self.ip6_rule_cache:
422 try:
423 utils.exec_command('ip -6 rule del pref 0')
424 utils.exec_command('ip -6 rule add pref 32765 table local')
425 except Exception, e:
426 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
427 pass
428
429 #Example ip rule
430 #200: from all oif blue lookup blue
431 #200: from all iif blue lookup blue
432
433 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
434 if rule not in self.ip_rule_cache:
435 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
436 vrf_dev_name)
437 utils.exec_command(rule_cmd)
438
439 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
440 if rule not in self.ip_rule_cache:
441 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
442 vrf_dev_name)
443 utils.exec_command(rule_cmd)
444
445 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
446 if rule not in self.ip6_rule_cache:
447 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
448 vrf_dev_name)
449 utils.exec_command(rule_cmd)
450
451 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
452 if rule not in self.ip6_rule_cache:
453 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
454 vrf_dev_name)
455 utils.exec_command(rule_cmd)
456
457 def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
458 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
459 config_slaves = ifaceobj.lowerifaces
460 if not config_slaves and not running_slaves:
461 return
462
463 if not config_slaves: config_slaves = []
464 if not running_slaves: running_slaves = []
465 add_slaves = set(config_slaves).difference(set(running_slaves))
466 del_slaves = set(running_slaves).difference(set(config_slaves))
467 if add_slaves:
468 for s in add_slaves:
469 try:
470 if not self.ipcmd.link_exists(s):
471 continue
472 sobj = None
473 if ifaceobj_getfunc:
474 sobj = ifaceobj_getfunc(s)
475 self._up_vrf_slave(s, ifaceobj.name,
476 sobj[0] if sobj else None,
477 ifaceobj_getfunc, True)
478 except Exception, e:
479 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
480
481 if del_slaves:
482 for s in del_slaves:
483 try:
484 sobj = None
485 if ifaceobj_getfunc:
486 sobj = ifaceobj_getfunc(s)
487 self._down_vrf_slave(s, sobj[0] if sobj else None,
488 ifaceobj.name)
489 except Exception, e:
490 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
491
492 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
493 for s in config_slaves:
494 try:
495 rtnetlink_api.rtnl_api.link_set(s, "up")
496 except Exception, e:
497 self.logger.debug('%s: %s: link set up (%s)'
498 %(ifaceobj.name, s, str(e)))
499 pass
500
501 def _set_vrf_dev_processed_flag(self, ifaceobj):
502 ifaceobj.module_flags[self.name] = \
503 ifaceobj.module_flags.setdefault(self.name, 0) | \
504 vrfPrivFlags.PROCESSED
505
506 def _check_vrf_dev_processed_flag(self, ifaceobj):
507 if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
508 return True
509 return False
510
511 def _create_vrf_dev(self, ifaceobj, vrf_table):
512 if not self.ipcmd.link_exists(ifaceobj.name):
513 if ifaceobj.name in self.system_reserved_rt_tables.values():
514 self.log_error('cannot use system reserved %s vrf names'
515 %(str(self.system_reserved_rt_tables.values())),
516 ifaceobj)
517 if self.vrf_count == self.vrf_max_count:
518 self.log_error('%s: max vrf count %d hit...not '
519 'creating vrf' %(ifaceobj.name,
520 self.vrf_count), ifaceobj)
521 if vrf_table == 'auto':
522 vrf_table = self._get_avail_vrf_table_id()
523 if not vrf_table:
524 self.log_error('%s: unable to get an auto table id'
525 %ifaceobj.name, ifaceobj)
526 self.logger.info('%s: table id auto: selected table id %s\n'
527 %(ifaceobj.name, vrf_table))
528 else:
529 self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
530 if ifaceobj.name in self.system_reserved_rt_tables.keys():
531 self.log_error('cannot use system reserved %s table ids'
532 %(str(self.system_reserved_rt_tables.keys())),
533 ifaceobj)
534
535 if not vrf_table.isdigit():
536 self.log_error('%s: vrf-table must be an integer or \'auto\''
537 %(ifaceobj.name), ifaceobj)
538
539 # XXX: If we decide to not allow vrf id usages out of
540 # the reserved ifupdown range, then uncomment this code.
541 else:
542 if (int(vrf_table) < self.vrf_table_id_start or
543 int(vrf_table) > self.vrf_table_id_end):
544 self.log_error('%s: vrf table id %s out of reserved range [%d,%d]'
545 %(ifaceobj.name, vrf_table,
546 self.vrf_table_id_start,
547 self.vrf_table_id_end), ifaceobj)
548 try:
549 self.ipcmd.link_create(ifaceobj.name, 'vrf',
550 {'table' : '%s' %vrf_table})
551 except Exception, e:
552 self.log_error('%s: create failed (%s)\n'
553 %(ifaceobj.name, str(e)), ifaceobj)
554 if vrf_table != 'auto':
555 self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
556 else:
557 if vrf_table == 'auto':
558 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
559 if not vrf_table:
560 self.log_error('%s: unable to get vrf table id'
561 %ifaceobj.name, ifaceobj)
562
563 # if the device exists, check if table id is same
564 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
565 if vrfdev_attrs:
566 running_table = vrfdev_attrs.get('table', None)
567 if vrf_table != running_table:
568 self.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj.name,
569 running_table, vrf_table),
570 ifaceobj)
571 return vrf_table
572
573 def _up_vrf_helper(self, ifaceobj, vrf_table):
574 mode = ""
575 if ifupdownflags.flags.PERFMODE:
576 mode = "boot"
577 if self.vrf_helper:
578 utils.exec_command('%s create %s %s %s' %
579 (self.vrf_helper,
580 ifaceobj.name,
581 vrf_table,
582 mode))
583
584 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
585 ifaceobj_getfunc=None):
586
587 # if vrf dev is already processed return. This can happen
588 # if we the slave was configured before.
589 # see self._up_vrf_slave_without_master
590 if self._check_vrf_dev_processed_flag(ifaceobj):
591 return True
592
593 try:
594 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
595 except Exception, e:
596 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
597
598 try:
599 self._add_vrf_rules(ifaceobj.name, vrf_table)
600 self._up_vrf_helper(ifaceobj, vrf_table)
601 if add_slaves:
602 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
603 self._set_vrf_dev_processed_flag(ifaceobj)
604 rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
605 except Exception, e:
606 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
607
608 def _kill_ssh_connections(self, ifacename):
609 try:
610 runningaddrsdict = self.ipcmd.addr_get(ifacename)
611 if not runningaddrsdict:
612 return
613 iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
614 if not iplist:
615 return
616 proc=[]
617 #Example output:
618 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
619 #users:(("sshd",pid=2528,fd=3))
620 cmdl = ['/bin/ss', '-t', '-p']
621 for line in utils.exec_commandl(cmdl).splitlines():
622 citems = line.split()
623 addr = None
624 if '%' in citems[3]:
625 addr = citems[3].split('%')[0]
626 elif ':ssh' in citems[3]:
627 addr = citems[3].split(':')[0]
628 if not addr:
629 continue
630 if addr in iplist:
631 if len(citems) == 6:
632 proc.append(citems[5].split(',')[1].split('=')[1])
633
634 if not proc:
635 return
636 pid = None
637 # outpt of '/usr/bin/pstree -Aps <pid>':
638 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
639 # get the above output to following format
640 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
641 pstree = list(reversed(utils.exec_command('/usr/bin/pstree -Aps %s' %os.getpid()).strip().split('---')))
642 for index, process in enumerate(pstree):
643 # check the parent of SSH process to make sure
644 # we don't kill SSH server or systemd process
645 if 'sshd' in process and 'sshd' in pstree[index + 1]:
646 pid = filter(lambda x: x.isdigit(), process)
647 break
648 self.logger.info("%s: killing active ssh sessions: %s"
649 %(ifacename, str(proc)))
650
651 if ifupdownflags.flags.DRYRUN:
652 return
653 for id in proc:
654 if id != pid:
655 try:
656 os.kill(int(id), signal.SIGINT)
657 except OSError as e:
658 continue
659
660 # Kill current SSH client
661 if pid in proc:
662 try:
663 forkret = os.fork()
664 except OSError, e:
665 self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
666 if (forkret == 0): # The first child.
667 try:
668 os.setsid()
669 self.logger.info("%s: ifreload continuing in the background" %ifacename)
670 except OSError, (err_no, err_message):
671 self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
672 self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0)))
673 try:
674 self.logger.info("%s: killing our session: %s"
675 %(ifacename, str(proc)))
676 os.kill(int(pid), signal.SIGINT)
677 return
678 except OSError as e:
679 return
680 except Exception, e:
681 self.logger.info('%s: %s' %(ifacename, str(e)))
682
683 def _up(self, ifaceobj, ifaceobj_getfunc=None):
684 try:
685 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
686 if vrf_table:
687 self._iproute2_vrf_map_initialize()
688 # This is a vrf device
689 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
690 else:
691 vrf = ifaceobj.get_attr_value_first('vrf')
692 if vrf:
693 self._iproute2_vrf_map_initialize()
694 # This is a vrf slave
695 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
696 ifaceobj_getfunc)
697 else:
698 # check if we were a slave before
699 master = self.ipcmd.link_get_master(ifaceobj.name)
700 if master:
701 self._iproute2_vrf_map_initialize()
702 if self._is_vrf_dev(master):
703 self._down_vrf_slave(ifaceobj.name, ifaceobj,
704 master)
705 except Exception, e:
706 self.log_error(str(e), ifaceobj)
707
708 def _down_vrf_helper(self, ifaceobj, vrf_table):
709 mode = ""
710 if ifupdownflags.flags.PERFMODE:
711 mode = "boot"
712 if self.vrf_helper:
713 utils.exec_command('%s delete %s %s %s' %
714 (self.vrf_helper,
715 ifaceobj.name,
716 vrf_table,
717 mode))
718
719 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
720
721 if vrf_table == 'auto':
722 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
723
724 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
725 if running_slaves:
726 for s in running_slaves:
727 if ifaceobj_getfunc:
728 sobj = ifaceobj_getfunc(s)
729 try:
730 self._handle_existing_connections(sobj[0]
731 if sobj else None,
732 ifaceobj.name)
733 except Exception, e:
734 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
735 pass
736 try:
737 self.ipcmd.addr_flush(s)
738 rtnetlink_api.rtnl_api.link_set(s, "down")
739 except Exception, e:
740 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
741 pass
742
743 try:
744 self._down_vrf_helper(ifaceobj, vrf_table)
745 except Exception, e:
746 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
747 pass
748
749 try:
750 self._del_vrf_rules(ifaceobj.name, vrf_table)
751 except Exception, e:
752 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
753 pass
754
755 try:
756 self.ipcmd.link_delete(ifaceobj.name)
757 except Exception, e:
758 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
759 pass
760
761 try:
762 self._iproute2_vrf_table_entry_del(vrf_table)
763 except Exception, e:
764 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
765 pass
766
767
768 def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
769 try:
770 self._handle_existing_connections(ifaceobj, vrfname)
771 self.ipcmd.link_set(ifacename, 'nomaster')
772 rtnetlink_api.rtnl_api.link_set(ifacename, "down")
773 except Exception, e:
774 self.logger.warn('%s: %s' %(ifacename, str(e)))
775
776 def _down(self, ifaceobj, ifaceobj_getfunc=None):
777 try:
778 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
779 if vrf_table:
780 self._iproute2_vrf_map_initialize()
781 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
782 else:
783 vrf = ifaceobj.get_attr_value_first('vrf')
784 if vrf:
785 self._iproute2_vrf_map_initialize()
786 self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
787 except Exception, e:
788 self.log_warn(str(e))
789
790 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
791 try:
792 master = self.ipcmd.link_get_master(ifaceobj.name)
793 if not master or master != vrf:
794 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
795 else:
796 ifaceobjcurr.update_config_with_status('vrf', master, 0)
797 except Exception, e:
798 self.log_error(str(e), ifaceobjcurr)
799
800 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
801 try:
802 if not self.ipcmd.link_exists(ifaceobj.name):
803 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
804 return
805 if vrf_table == 'auto':
806 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
807 else:
808 config_table = vrf_table
809 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
810 if not vrfdev_attrs:
811 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
812 return
813 running_table = vrfdev_attrs.get('table')
814 if not running_table:
815 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
816 return
817 if config_table != running_table:
818 ifaceobjcurr.update_config_with_status('vrf-table',
819 running_table, 1)
820 else:
821 ifaceobjcurr.update_config_with_status('vrf-table',
822 running_table, 0)
823 if not ifupdownflags.flags.WITHDEFAULTS:
824 return
825 if self.vrf_helper:
826 try:
827 utils.exec_command('%s verify %s %s'
828 %(self.vrf_helper,
829 ifaceobj.name, config_table))
830 ifaceobjcurr.update_config_with_status('vrf-helper',
831 '%s create %s %s'
832 %(self.vrf_helper,
833 ifaceobj.name,
834 config_table), 0)
835 except Exception, e:
836 ifaceobjcurr.update_config_with_status('vrf-helper',
837 '%s create %s %s'
838 %(self.vrf_helper,
839 ifaceobj.name,
840 config_table), 1)
841 pass
842 except Exception, e:
843 self.log_warn(str(e))
844
845 def _query_check(self, ifaceobj, ifaceobjcurr):
846 try:
847 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
848 if vrf_table:
849 self._iproute2_vrf_map_initialize(writetodisk=False)
850 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
851 else:
852 vrf = ifaceobj.get_attr_value_first('vrf')
853 if vrf:
854 self._iproute2_vrf_map_initialize(writetodisk=False)
855 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
856 except Exception, e:
857 self.log_warn(str(e))
858
859 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
860 try:
861 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
862 if kind == 'vrf':
863 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
864 if vrfdev_attrs:
865 running_table = vrfdev_attrs.get('table')
866 if running_table:
867 ifaceobjrunning.update_config('vrf-table',
868 running_table)
869 elif kind == 'vrf_slave':
870 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
871 if vrf:
872 ifaceobjrunning.update_config('vrf', vrf)
873 except Exception, e:
874 self.log_warn(str(e))
875
876 def _query(self, ifaceobj, **kwargs):
877 if not self.vrf_helper:
878 return
879 if (ifaceobj.link_kind & ifaceLinkKind.VRF):
880 ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
881 ifaceobj.name))
882
883 _run_ops = {'pre-up' : _up,
884 'post-down' : _down,
885 'query-running' : _query_running,
886 'query-checkcurr' : _query_check,
887 'query' : _query}
888
889 def get_ops(self):
890 """ returns list of ops supported by this module """
891 return self._run_ops.keys()
892
893 def _init_command_handlers(self):
894 if not self.ipcmd:
895 self.ipcmd = iproute2()
896 if not self.bondcmd:
897 self.bondcmd = bondutil()
898 if not self.dhclientcmd:
899 self.dhclientcmd = dhclient()
900
901 def run(self, ifaceobj, operation, query_ifaceobj=None,
902 ifaceobj_getfunc=None, **extra_args):
903 """ run bond configuration on the interface object passed as argument
904
905 Args:
906 **ifaceobj** (object): iface object
907
908 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
909 'query-running'
910
911 Kwargs:
912 **query_ifaceobj** (object): query check ifaceobject. This is only
913 valid when op is 'query-checkcurr'. It is an object same as
914 ifaceobj, but contains running attribute values and its config
915 status. The modules can use it to return queried running state
916 of interfaces. status is success if the running state is same
917 as user required state in ifaceobj. error otherwise.
918 """
919 op_handler = self._run_ops.get(operation)
920 if not op_handler:
921 return
922 self._init_command_handlers()
923 if operation == 'query-checkcurr':
924 op_handler(self, ifaceobj, query_ifaceobj)
925 else:
926 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)