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