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