]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/vrf.py
Merge pull request #3 in CRMC/ifupdown2 from dev to release/cl-stable
[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 if old_vrf_name != vrfifaceobj.name:
264 self.log_error('table id %d already assigned to vrf dev %s'
265 %(table_id, old_vrf_name))
266
267 def _iproute2_vrf_table_entry_del(self, table_id):
268 try:
269 # with any del of vrf map, we need to force sync to disk
270 self.iproute2_vrf_map_sync_to_disk = True
271 del self.iproute2_vrf_map[int(table_id)]
272 except Exception, e:
273 self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)'
274 %(table_id, str(e)))
275 pass
276
277 def _is_vrf_dev(self, ifacename):
278 # Look at iproute2 map for now.
279 # If it was a master we knew about,
280 # it is definately there
281 if ifacename in self.iproute2_vrf_map.values():
282 return True
283 return False
284
285 def _is_dhcp_slave(self, ifaceobj):
286 if (not ifaceobj.addr_method or
287 (ifaceobj.addr_method != 'dhcp' and
288 ifaceobj.addr_method != 'dhcp6')):
289 return False
290 return True
291
292 def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj,
293 ifaceobj_getfunc):
294 """ If we have a vrf slave that has dhcp configured, bring up the
295 vrf master now. This is needed because vrf has special handling
296 in dhclient hook which requires the vrf master to be present """
297
298 vrf_master = ifaceobj.upperifaces[0]
299 if not vrf_master:
300 self.logger.warn('%s: vrf master not found' %ifacename)
301 return
302 if os.path.exists('/sys/class/net/%s' %vrf_master):
303 self.logger.info('%s: vrf master %s exists returning'
304 %(ifacename, vrf_master))
305 return
306 vrf_master_objs = ifaceobj_getfunc(vrf_master)
307 if not vrf_master_objs:
308 self.logger.warn('%s: vrf master ifaceobj not found' %ifacename)
309 return
310 self.logger.info('%s: bringing up vrf master %s'
311 %(ifacename, vrf_master))
312 for mobj in vrf_master_objs:
313 vrf_table = mobj.get_attr_value_first('vrf-table')
314 if vrf_table:
315 if vrf_table == 'auto':
316 vrf_table = self._get_avail_vrf_table_id()
317 if not vrf_table:
318 self.log_error('%s: unable to get an auto table id'
319 %mobj.name, ifaceobj)
320 self.logger.info('%s: table id auto: selected table id %s\n'
321 %(mobj.name, vrf_table))
322 try:
323 self._up_vrf_dev(mobj, vrf_table, False)
324 except Exception:
325 raise
326 break
327 self._handle_existing_connections(ifaceobj, vrfname)
328 self.ipcmd.link_set(ifacename, 'master', vrfname)
329 return
330
331 def _down_dhcp_slave(self, ifaceobj, vrfname):
332 try:
333 dhclient_cmd_prefix = None
334 if (vrfname and self.vrf_exec_cmd_prefix and
335 self.ipcmd.link_exists(vrfname)):
336 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
337 vrfname)
338 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
339 except:
340 # ignore any dhclient release errors
341 pass
342
343 def _handle_existing_connections(self, ifaceobj, vrfname):
344 if not ifaceobj or ifupdownflags.flags.PERFMODE:
345 return
346 if (self.vrf_mgmt_devname and
347 self.vrf_mgmt_devname == vrfname):
348 self._kill_ssh_connections(ifaceobj.name)
349 if self._is_dhcp_slave(ifaceobj):
350 self._down_dhcp_slave(ifaceobj, vrfname)
351
352 def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
353 ifaceobj_getfunc=None, vrf_exists=False):
354 try:
355 master_exists = True
356 if vrf_exists or self.ipcmd.link_exists(vrfname):
357 upper = self.ipcmd.link_get_upper(ifacename)
358 if not upper or upper != vrfname:
359 self._handle_existing_connections(ifaceobj, vrfname)
360 self.ipcmd.link_set(ifacename, 'master', vrfname)
361 elif ifupdownflags.flags.ALL and ifaceobj:
362 self._up_vrf_slave_without_master(ifacename, vrfname, ifaceobj,
363 ifaceobj_getfunc)
364 else:
365 master_exists = False
366 if master_exists:
367 rtnetlink_api.rtnl_api.link_set(ifacename, "up")
368 elif ifupdownflags.flags.ALL:
369 self.log_error('vrf %s not around, skipping vrf config'
370 %(vrfname), ifaceobj)
371 except Exception, e:
372 self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj)
373
374 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
375 pref = 200
376 ip_rule_out_format = '%s: from all %s %s lookup %s'
377 ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
378
379 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
380 if rule in self.ip_rule_cache:
381 rule_cmd = ip_rule_cmd %('', 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.ip_rule_cache:
387 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
388 vrf_dev_name)
389 self.exec_command(rule_cmd)
390
391 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
392 if rule in self.ip6_rule_cache:
393 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
394 vrf_dev_name)
395 self.exec_command(rule_cmd)
396
397 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
398 if rule in self.ip6_rule_cache:
399 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
400 vrf_dev_name)
401 self.exec_command(rule_cmd)
402
403 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
404 pref = 200
405 ip_rule_out_format = '%s: from all %s %s lookup %s'
406 ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
407 if self.vrf_fix_local_table:
408 self.vrf_fix_local_table = False
409 rule = '0: from all lookup local'
410 if rule in self.ip_rule_cache:
411 try:
412 self.exec_command('ip rule del pref 0')
413 self.exec_command('ip rule add pref 32765 table local')
414 except Exception, e:
415 self.logger.info('%s' %str(e))
416 pass
417 if rule in self.ip6_rule_cache:
418 try:
419 self.exec_command('ip -6 rule del pref 0')
420 self.exec_command('ip -6 rule add pref 32765 table local')
421 except Exception, e:
422 self.logger.info('%s' %str(e))
423 pass
424
425 #Example ip rule
426 #200: from all oif blue lookup blue
427 #200: from all iif blue lookup blue
428
429 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
430 if rule not in self.ip_rule_cache:
431 rule_cmd = ip_rule_cmd %('', 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.ip_rule_cache:
437 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
438 vrf_dev_name)
439 self.exec_command(rule_cmd)
440
441 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
442 if rule not in self.ip6_rule_cache:
443 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
444 vrf_dev_name)
445 self.exec_command(rule_cmd)
446
447 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
448 if rule not in self.ip6_rule_cache:
449 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
450 vrf_dev_name)
451 self.exec_command(rule_cmd)
452
453 def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
454 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
455 config_slaves = ifaceobj.lowerifaces
456 if not config_slaves and not running_slaves:
457 return
458
459 if not config_slaves: config_slaves = []
460 if not running_slaves: running_slaves = []
461 add_slaves = set(config_slaves).difference(set(running_slaves))
462 del_slaves = set(running_slaves).difference(set(config_slaves))
463 if add_slaves:
464 for s in add_slaves:
465 try:
466 if not self.ipcmd.link_exists(s):
467 continue
468 sobj = None
469 if ifaceobj_getfunc:
470 sobj = ifaceobj_getfunc(s)
471 self._up_vrf_slave(s, ifaceobj.name,
472 sobj[0] if sobj else None,
473 ifaceobj_getfunc, True)
474 except Exception, e:
475 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
476
477 if del_slaves:
478 for s in del_slaves:
479 try:
480 sobj = None
481 if ifaceobj_getfunc:
482 sobj = ifaceobj_getfunc(s)
483 self._down_vrf_slave(s, sobj[0] if sobj else None,
484 ifaceobj.name)
485 except Exception, e:
486 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
487
488 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
489 for s in config_slaves:
490 try:
491 rtnetlink_api.rtnl_api.link_set(s, "up")
492 except Exception, e:
493 self.logger.debug('%s: %s: link set up (%s)'
494 %(ifaceobj.name, s, str(e)))
495 pass
496
497 def _set_vrf_dev_processed_flag(self, ifaceobj):
498 ifaceobj.module_flags[self.name] = \
499 ifaceobj.module_flags.setdefault(self.name, 0) | \
500 vrfPrivFlags.PROCESSED
501
502 def _check_vrf_dev_processed_flag(self, ifaceobj):
503 if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
504 return True
505 return False
506
507 def _create_vrf_dev(self, ifaceobj, vrf_table):
508 if not self.ipcmd.link_exists(ifaceobj.name):
509 if ifaceobj.name in self.system_reserved_rt_tables.values():
510 self.log_error('cannot use system reserved %s vrf names'
511 %(str(self.system_reserved_rt_tables.values())),
512 ifaceobj)
513 if self.vrf_count == self.vrf_max_count:
514 self.log_error('%s: max vrf count %d hit...not '
515 'creating vrf' %(ifaceobj.name,
516 self.vrf_count), ifaceobj)
517 if vrf_table == 'auto':
518 vrf_table = self._get_avail_vrf_table_id()
519 if not vrf_table:
520 self.log_error('%s: unable to get an auto table id'
521 %ifaceobj.name, ifaceobj)
522 self.logger.info('%s: table id auto: selected table id %s\n'
523 %(ifaceobj.name, vrf_table))
524 else:
525 self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
526 if ifaceobj.name in self.system_reserved_rt_tables.keys():
527 self.log_error('cannot use system reserved %s table ids'
528 %(str(self.system_reserved_rt_tables.keys())),
529 ifaceobj)
530
531 if not vrf_table.isdigit():
532 self.log_error('%s: vrf-table must be an integer or \'auto\''
533 %(ifaceobj.name), ifaceobj)
534
535 # XXX: If we decide to not allow vrf id usages out of
536 # the reserved ifupdown range, then uncomment this code.
537 else:
538 if (int(vrf_table) < self.vrf_table_id_start or
539 int(vrf_table) > self.vrf_table_id_end):
540 self.log_error('%s: vrf table id %s out of reserved range [%d,%d]'
541 %(ifaceobj.name, vrf_table,
542 self.vrf_table_id_start,
543 self.vrf_table_id_end), ifaceobj)
544 try:
545 self.ipcmd.link_create(ifaceobj.name, 'vrf',
546 {'table' : '%s' %vrf_table})
547 except Exception, e:
548 self.log_error('%s: create failed (%s)\n'
549 %(ifaceobj.name, str(e)), ifaceobj)
550 if vrf_table != 'auto':
551 self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
552 else:
553 if vrf_table == 'auto':
554 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
555 if not vrf_table:
556 self.log_error('%s: unable to get vrf table id'
557 %ifaceobj.name, ifaceobj)
558
559 # if the device exists, check if table id is same
560 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
561 if vrfdev_attrs:
562 running_table = vrfdev_attrs.get('table', None)
563 if vrf_table != running_table:
564 self.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj.name,
565 running_table, vrf_table),
566 ifaceobj)
567 return vrf_table
568
569 def _up_vrf_helper(self, ifaceobj, vrf_table):
570 mode = ""
571 if ifupdownflags.flags.PERFMODE:
572 mode = "boot"
573 if self.vrf_helper:
574 self.exec_command('%s create %s %s %s' %(self.vrf_helper,
575 ifaceobj.name, vrf_table, mode))
576
577 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
578 ifaceobj_getfunc=None):
579
580 # if vrf dev is already processed return. This can happen
581 # if we the slave was configured before.
582 # see self._up_vrf_slave_without_master
583 if self._check_vrf_dev_processed_flag(ifaceobj):
584 return True
585
586 try:
587 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
588 except Exception, e:
589 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
590
591 try:
592 self._add_vrf_rules(ifaceobj.name, vrf_table)
593 self._up_vrf_helper(ifaceobj, vrf_table)
594 if add_slaves:
595 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
596 self._set_vrf_dev_processed_flag(ifaceobj)
597 rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
598 except Exception, e:
599 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
600
601 def _kill_ssh_connections(self, ifacename):
602 try:
603 runningaddrsdict = self.ipcmd.addr_get(ifacename)
604 if not runningaddrsdict:
605 return
606 iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
607 if not iplist:
608 return
609 proc=[]
610 #Example output:
611 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
612 #users:(("sshd",pid=2528,fd=3))
613 cmdl = ['/bin/ss', '-t', '-p']
614 for line in subprocess.check_output(cmdl, stderr=subprocess.STDOUT,
615 shell=False).splitlines():
616 citems = line.split()
617 addr = None
618 if '%' in citems[3]:
619 addr = citems[3].split('%')[0]
620 elif ':ssh' in citems[3]:
621 addr = citems[3].split(':')[0]
622 if not addr:
623 continue
624 if addr in iplist:
625 if len(citems) == 6:
626 proc.append(citems[5].split(',')[1].split('=')[1])
627
628 if not proc:
629 return
630 pid = subprocess.check_output(['/bin/ps', '--no-headers',
631 '-fp', str(os.getppid())],
632 stderr=subprocess.STDOUT,
633 shell=False).split()[2]
634 self.logger.info("%s: killing active ssh sessions: %s"
635 %(ifacename, str(proc)))
636
637 if ifupdownflags.flags.DRYRUN:
638 return
639 for id in proc:
640 if id != pid:
641 try:
642 os.kill(int(id), signal.SIGINT)
643 except OSError as e:
644 continue
645
646 # Kill current SSH client
647 if pid in proc:
648 try:
649 forkret = os.fork()
650 except OSError, e:
651 self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
652 if (forkret == 0): # The first child.
653 try:
654 os.setsid()
655 self.logger.info("%s: ifreload continuing in the background" %ifacename)
656 except OSError, (err_no, err_message):
657 self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
658 self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0)))
659 try:
660 self.logger.info("%s: killing our session: %s"
661 %(ifacename, str(proc)))
662 os.kill(int(pid), signal.SIGINT)
663 return
664 except OSError as e:
665 return
666 except Exception, e:
667 self.logger.info('%s: %s' %(ifacename, str(e)))
668
669 def _up(self, ifaceobj, ifaceobj_getfunc=None):
670 try:
671 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
672 if vrf_table:
673 self._iproute2_vrf_map_initialize()
674 # This is a vrf device
675 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
676 else:
677 vrf = ifaceobj.get_attr_value_first('vrf')
678 if vrf:
679 self._iproute2_vrf_map_initialize()
680 # This is a vrf slave
681 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
682 ifaceobj_getfunc)
683 else:
684 # check if we were a slave before
685 master = self.ipcmd.link_get_master(ifaceobj.name)
686 if master:
687 self._iproute2_vrf_map_initialize()
688 if self._is_vrf_dev(master):
689 self._down_vrf_slave(ifaceobj.name, ifaceobj,
690 master)
691 except Exception, e:
692 self.log_error(str(e), ifaceobj)
693
694 def _down_vrf_helper(self, ifaceobj, vrf_table):
695 mode = ""
696 if ifupdownflags.flags.PERFMODE:
697 mode = "boot"
698 if self.vrf_helper:
699 self.exec_command('%s delete %s %s %s' %(self.vrf_helper,
700 ifaceobj.name, vrf_table, mode))
701
702 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
703
704 if vrf_table == 'auto':
705 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
706
707 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
708 if running_slaves:
709 for s in running_slaves:
710 if ifaceobj_getfunc:
711 sobj = ifaceobj_getfunc(s)
712 try:
713 self._handle_existing_connections(sobj[0]
714 if sobj else None,
715 ifaceobj.name)
716 except Exception, e:
717 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
718 pass
719 try:
720 self.ipcmd.addr_flush(s)
721 rtnetlink_api.rtnl_api.link_set(s, "down")
722 except Exception, e:
723 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
724 pass
725
726 self._down_vrf_helper(ifaceobj, vrf_table)
727
728 try:
729 self._del_vrf_rules(ifaceobj.name, vrf_table)
730 except Exception, e:
731 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
732 pass
733
734 try:
735 self.ipcmd.link_delete(ifaceobj.name)
736 except Exception, e:
737 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
738 pass
739
740 try:
741 self._iproute2_vrf_table_entry_del(vrf_table)
742 except Exception, e:
743 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
744 pass
745
746
747 def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
748 try:
749 self._handle_existing_connections(ifaceobj, vrfname)
750 self.ipcmd.link_set(ifacename, 'nomaster')
751 rtnetlink_api.rtnl_api.link_set(ifacename, "down")
752 except Exception, e:
753 self.logger.warn('%s: %s' %(ifacename, str(e)))
754
755 def _down(self, ifaceobj, ifaceobj_getfunc=None):
756 try:
757 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
758 if vrf_table:
759 self._iproute2_vrf_map_initialize()
760 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
761 else:
762 vrf = ifaceobj.get_attr_value_first('vrf')
763 if vrf:
764 self._iproute2_vrf_map_initialize()
765 self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
766 except Exception, e:
767 self.log_warn(str(e))
768
769 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
770 try:
771 master = self.ipcmd.link_get_master(ifaceobj.name)
772 if not master or master != vrf:
773 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
774 else:
775 ifaceobjcurr.update_config_with_status('vrf', master, 0)
776 except Exception, e:
777 self.log_error(str(e), ifaceobjcurr)
778
779 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
780 try:
781 if not self.ipcmd.link_exists(ifaceobj.name):
782 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
783 return
784 if vrf_table == 'auto':
785 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
786 else:
787 config_table = vrf_table
788 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
789 if not vrfdev_attrs:
790 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
791 return
792 running_table = vrfdev_attrs.get('table')
793 if not running_table:
794 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
795 return
796 if config_table != running_table:
797 ifaceobjcurr.update_config_with_status('vrf-table',
798 running_table, 1)
799 else:
800 ifaceobjcurr.update_config_with_status('vrf-table',
801 running_table, 0)
802 if not ifupdownflags.flags.WITHDEFAULTS:
803 return
804 if self.vrf_helper:
805 try:
806 self.exec_command('%s verify %s %s'
807 %(self.vrf_helper,
808 ifaceobj.name, config_table))
809 ifaceobjcurr.update_config_with_status('vrf-helper',
810 '%s create %s %s'
811 %(self.vrf_helper,
812 ifaceobj.name,
813 config_table), 0)
814 except Exception, e:
815 ifaceobjcurr.update_config_with_status('vrf-helper',
816 '%s create %s %s'
817 %(self.vrf_helper,
818 ifaceobj.name,
819 config_table), 1)
820 pass
821 except Exception, e:
822 self.log_warn(str(e))
823
824 def _query_check(self, ifaceobj, ifaceobjcurr):
825 try:
826 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
827 if vrf_table:
828 self._iproute2_vrf_map_initialize(writetodisk=False)
829 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
830 else:
831 vrf = ifaceobj.get_attr_value_first('vrf')
832 if vrf:
833 self._iproute2_vrf_map_initialize(writetodisk=False)
834 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
835 except Exception, e:
836 self.log_warn(str(e))
837
838 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
839 try:
840 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
841 if kind == 'vrf':
842 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
843 if vrfdev_attrs:
844 running_table = vrfdev_attrs.get('table')
845 if running_table:
846 ifaceobjrunning.update_config('vrf-table',
847 running_table)
848 elif kind == 'vrf_slave':
849 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
850 if vrf:
851 ifaceobjrunning.update_config('vrf', vrf)
852 except Exception, e:
853 self.log_warn(str(e))
854
855 def _query(self, ifaceobj, **kwargs):
856 if not self.vrf_helper:
857 return
858 if (ifaceobj.link_kind & ifaceLinkKind.VRF):
859 ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
860 ifaceobj.name))
861
862 _run_ops = {'pre-up' : _up,
863 'post-down' : _down,
864 'query-running' : _query_running,
865 'query-checkcurr' : _query_check,
866 'query' : _query}
867
868 def get_ops(self):
869 """ returns list of ops supported by this module """
870 return self._run_ops.keys()
871
872 def _init_command_handlers(self):
873 if not self.ipcmd:
874 self.ipcmd = iproute2()
875 if not self.bondcmd:
876 self.bondcmd = bondutil()
877 if not self.dhclientcmd:
878 self.dhclientcmd = dhclient()
879
880 def run(self, ifaceobj, operation, query_ifaceobj=None,
881 ifaceobj_getfunc=None, **extra_args):
882 """ run bond configuration on the interface object passed as argument
883
884 Args:
885 **ifaceobj** (object): iface object
886
887 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
888 'query-running'
889
890 Kwargs:
891 **query_ifaceobj** (object): query check ifaceobject. This is only
892 valid when op is 'query-checkcurr'. It is an object same as
893 ifaceobj, but contains running attribute values and its config
894 status. The modules can use it to return queried running state
895 of interfaces. status is success if the running state is same
896 as user required state in ifaceobj. error otherwise.
897 """
898 op_handler = self._run_ops.get(operation)
899 if not op_handler:
900 return
901 self._init_command_handlers()
902 if operation == 'query-checkcurr':
903 op_handler(self, ifaceobj, query_ifaceobj)
904 else:
905 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)