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