]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/vrf.py
Merge branch 'dev' into 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 table id. key to ' +
31 'creating a vrf device',
32 'example': ['vrf-table-id 1']},
33 'vrf-default-route':
34 {'help' : 'vrf device default route ' +
35 'to avoid communication outside the vrf device',
36 'example': ['vrf-default-route yes/no']},
37 'vrf':
38 {'help' : 'vrf the interface is part of.',
39 'example': ['vrf blue']}}}
40
41 iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
42 iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
43 '# It contains the vrf name to table mapping.\n' + \
44 '# Reserved table range %s %s\n'
45 VRF_TABLE_START = 1001
46 VRF_TABLE_END = 5000
47
48 def __init__(self, *args, **kargs):
49 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
50 self.ipcmd = None
51 self.bondcmd = None
52 self.dhclientcmd = None
53 self.name = self.__class__.__name__
54 if ifupdownflags.flags.PERFMODE:
55 # if perf mode is set, remove vrf map file.
56 # start afresh. PERFMODE is set at boot
57 if os.path.exists(self.iproute2_vrf_filename):
58 try:
59 self.logger.info('vrf: removing file %s'
60 %self.iproute2_vrf_filename)
61 os.remove(self.iproute2_vrf_filename)
62 except Exception, e:
63 self.logger.debug('vrf: removing file failed (%s)'
64 %str(e))
65 try:
66 ip_rules = self.exec_command('/sbin/ip rule show').splitlines()
67 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
68 except Exception, e:
69 self.ip_rule_cache = []
70 self.logger.warn('%s' %str(e))
71
72 try:
73 ip_rules = self.exec_command('/sbin/ip -6 rule show').splitlines()
74 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
75 except Exception, e:
76 self.ip6_rule_cache = []
77 self.logger.warn('%s' %str(e))
78
79 #self.logger.debug("vrf: ip rule cache")
80 #self.logger.info(self.ip_rule_cache)
81
82 #self.logger.info("vrf: ip -6 rule cache")
83 #self.logger.info(self.ip6_rule_cache)
84
85 self._iproute2_vrf_map_initialized = False
86 self.iproute2_vrf_map = {}
87 self.iproute2_vrf_map_fd = None
88 self.iproute2_vrf_map_sync_to_disk = False
89
90 self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
91 if not self.vrf_table_id_start:
92 self.vrf_table_id_start = self.VRF_TABLE_START
93 self.vrf_table_id_end = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-end')
94 if not self.vrf_table_id_end:
95 self.vrf_table_id_end = self.VRF_TABLE_END
96 self.vrf_max_count = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-max-count')
97
98 self.vrf_fix_local_table = True
99 self.vrf_count = 0
100 self.vrf_cgroup_create = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-cgroup-create')
101
102 if not self.vrf_cgroup_create:
103 self.vrf_cgroup_create = False
104 elif self.vrf_cgroup_create == 'yes':
105 self.vrf_cgroup_create = True
106 else:
107 self.vrf_cgroup_create = False
108 self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
109 self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper')
110
111 def _iproute2_vrf_map_initialize(self):
112 if self._iproute2_vrf_map_initialized:
113 return
114
115 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
116 self.iproute2_vrf_map = {}
117 iproute2_vrf_map_force_rewrite = False
118 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
119 if os.path.exists(self.iproute2_vrf_filename):
120 vrf_map_fd = open(self.iproute2_vrf_filename, 'r+')
121 lines = vrf_map_fd.readlines()
122 for l in lines:
123 l = l.strip()
124 if l[0] == '#':
125 continue
126 try:
127 (table, vrf_name) = l.strip().split()
128 if self.iproute2_vrf_map.get(int(table)):
129 # looks like the existing file has
130 # duplicate entries, force rewrite of the
131 # file
132 iproute2_vrf_map_force_rewrite = True
133 continue
134 self.iproute2_vrf_map[int(table)] = vrf_name
135 except Exception, e:
136 self.logger.info('vrf: iproute2_vrf_map: unable to parse %s'
137 %l)
138 pass
139
140 vrfs = self.ipcmd.link_get_vrfs()
141 running_vrf_map = {}
142 if vrfs:
143 for v, lattrs in vrfs.iteritems():
144 table = lattrs.get('table', None)
145 if table:
146 running_vrf_map[int(table)] = v
147
148 if running_vrf_map and (running_vrf_map != self.iproute2_vrf_map):
149 self.iproute2_vrf_map = running_vrf_map
150 iproute2_vrf_map_force_rewrite = True
151
152 self.iproute2_vrf_map_fd = None
153 if iproute2_vrf_map_force_rewrite:
154 # reopen the file and rewrite the map
155 self._iproute2_vrf_map_open(True, False)
156 else:
157 self._iproute2_vrf_map_open(False, True)
158
159 self.iproute2_vrf_map_sync_to_disk = False
160 atexit.register(self._iproute2_vrf_map_sync_to_disk)
161
162 self.logger.info("vrf: dumping iproute2_vrf_map")
163 self.logger.info(self.iproute2_vrf_map)
164
165 last_used_vrf_table = None
166 for t in range(self.vrf_table_id_start,
167 self.vrf_table_id_end):
168 if not self.iproute2_vrf_map.get(t):
169 break
170 last_used_vrf_table = t
171 self.last_used_vrf_table = last_used_vrf_table
172 self._iproute2_vrf_map_initialized = True
173
174 def _iproute2_vrf_map_sync_to_disk(self):
175 if not self.iproute2_vrf_map_sync_to_disk:
176 return
177 self.logger.info('vrf: syncing table map to %s'
178 %self.iproute2_vrf_filename)
179 with open(self.iproute2_vrf_filename, 'w') as f:
180 f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
181 self.vrf_table_id_end))
182 for t, v in self.iproute2_vrf_map.iteritems():
183 f.write('%s %s\n' %(t, v))
184 f.flush()
185
186 def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False):
187 self.logger.info('vrf: syncing table map to %s'
188 %self.iproute2_vrf_filename)
189 fmode = 'a+' if append else 'w'
190 try:
191 self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
192 '%s' %fmode)
193 except Exception, e:
194 self.log_warn('vrf: error opening %s (%s)'
195 %(self.iproute2_vrf_filename, str(e)))
196 return
197
198 if not append:
199 # write file header
200 self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
201 %(self.vrf_table_id_start,
202 self.vrf_table_id_end))
203 for t, v in self.iproute2_vrf_map.iteritems():
204 self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
205 self.iproute2_vrf_map_fd.flush()
206
207 def _is_vrf(self, ifaceobj):
208 if ifaceobj.get_attr_value_first('vrf-table'):
209 return True
210 return False
211
212 def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
213 """ Returns list of interfaces dependent on ifaceobj """
214
215 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
216 if vrf_table:
217 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
218 ifaceobj.link_kind |= ifaceLinkKind.VRF
219 ifaceobj.role |= ifaceRole.MASTER
220 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
221 if not vrf_iface_name:
222 return None
223 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
224 ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
225
226 return [vrf_iface_name]
227
228 def get_upper_ifacenames_running(self, ifaceobj):
229 return None
230
231 def _get_iproute2_vrf_table(self, vrf_dev_name):
232 for t, v in self.iproute2_vrf_map.iteritems():
233 if v == vrf_dev_name:
234 return str(t)
235 return None
236
237 def _get_avail_vrf_table_id(self):
238 if self.last_used_vrf_table == None:
239 table_id_start = self.vrf_table_id_start
240 else:
241 table_id_start = self.last_used_vrf_table + 1
242 for t in range(table_id_start,
243 self.vrf_table_id_end):
244 if not self.iproute2_vrf_map.get(t):
245 self.last_used_vrf_table = t
246 return str(t)
247 return None
248
249 def _iproute2_is_vrf_tableid_inuse(self, vrf_dev_name, table_id):
250 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
251 if old_vrf_name and old_vrf_name != vrf_dev_name:
252 self.log_error('table id %s already assigned to vrf dev %s'
253 %(table_id, old_vrf_name))
254
255 def _iproute2_vrf_table_entry_add(self, vrf_dev_name, table_id):
256 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
257 if not old_vrf_name:
258 self.iproute2_vrf_map[int(table_id)] = vrf_dev_name
259 if self.iproute2_vrf_map_fd:
260 self.iproute2_vrf_map_fd.write('%s %s\n'
261 %(table_id, vrf_dev_name))
262 self.iproute2_vrf_map_fd.flush()
263 return
264 if old_vrf_name != vrf_dev_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)
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 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 ifaceobj:
362 self._up_vrf_slave_without_master(ifacename, vrfname, ifaceobj,
363 ifaceobj_getfunc)
364 rtnetlink_api.rtnl_api.link_set(ifacename, "up")
365 except Exception, e:
366 self.log_error('%s: %s' %(ifacename, str(e)))
367
368 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
369 pref = 200
370 ip_rule_out_format = '%s: from all %s %s lookup %s'
371 ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
372
373 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
374 if rule in self.ip_rule_cache:
375 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
376 vrf_dev_name)
377 self.exec_command(rule_cmd)
378
379 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
380 if rule in self.ip_rule_cache:
381 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
382 vrf_dev_name)
383 self.exec_command(rule_cmd)
384
385 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
386 if rule in self.ip6_rule_cache:
387 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
388 vrf_dev_name)
389 self.exec_command(rule_cmd)
390
391 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
392 if rule in self.ip6_rule_cache:
393 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
394 vrf_dev_name)
395 self.exec_command(rule_cmd)
396
397 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
398 pref = 200
399 ip_rule_out_format = '%s: from all %s %s lookup %s'
400 ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
401 if self.vrf_fix_local_table:
402 self.vrf_fix_local_table = False
403 rule = '0: from all lookup local'
404 if rule in self.ip_rule_cache:
405 try:
406 self.exec_command('ip rule del pref 0')
407 self.exec_command('ip rule add pref 32765 table local')
408 except Exception, e:
409 self.logger.info('%s' %str(e))
410 pass
411 if rule in self.ip6_rule_cache:
412 try:
413 self.exec_command('ip -6 rule del pref 0')
414 self.exec_command('ip -6 rule add pref 32765 table local')
415 except Exception, e:
416 self.logger.info('%s' %str(e))
417 pass
418
419 #Example ip rule
420 #200: from all oif blue lookup blue
421 #200: from all iif blue lookup blue
422
423 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
424 if rule not in self.ip_rule_cache:
425 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
426 vrf_dev_name)
427 self.exec_command(rule_cmd)
428
429 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
430 if rule not in self.ip_rule_cache:
431 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
432 vrf_dev_name)
433 self.exec_command(rule_cmd)
434
435 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
436 if rule not in self.ip6_rule_cache:
437 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
438 vrf_dev_name)
439 self.exec_command(rule_cmd)
440
441 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
442 if rule not in self.ip6_rule_cache:
443 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
444 vrf_dev_name)
445 self.exec_command(rule_cmd)
446
447 def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
448 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
449 config_slaves = ifaceobj.lowerifaces
450 if not config_slaves and not running_slaves:
451 return
452
453 if not config_slaves: config_slaves = []
454 if not running_slaves: running_slaves = []
455 add_slaves = set(config_slaves).difference(set(running_slaves))
456 del_slaves = set(running_slaves).difference(set(config_slaves))
457 if add_slaves:
458 for s in add_slaves:
459 try:
460 sobj = None
461 if ifaceobj_getfunc:
462 sobj = ifaceobj_getfunc(s)
463 self._up_vrf_slave(s, ifaceobj.name,
464 sobj[0] if sobj else None,
465 ifaceobj_getfunc, True)
466 except Exception, e:
467 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
468
469 if del_slaves:
470 for s in del_slaves:
471 try:
472 sobj = None
473 if ifaceobj_getfunc:
474 sobj = ifaceobj_getfunc(s)
475 self._down_vrf_slave(s, sobj[0] if sobj else None,
476 ifaceobj.name)
477 except Exception, e:
478 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
479
480 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
481 for s in config_slaves:
482 try:
483 rtnetlink_api.rtnl_api.link_set(s, "up")
484 except Exception, e:
485 self.logger.debug('%s: %s: link set up (%s)'
486 %(ifaceobj.name, s, str(e)))
487 pass
488
489 def _set_vrf_dev_processed_flag(self, ifaceobj):
490 ifaceobj.module_flags[self.name] = \
491 ifaceobj.module_flags.setdefault(self.name, 0) | \
492 vrfPrivFlags.PROCESSED
493
494 def _check_vrf_dev_processed_flag(self, ifaceobj):
495 if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
496 return True
497 return False
498
499 def _create_vrf_dev(self, ifaceobj, vrf_table):
500 if not self.ipcmd.link_exists(ifaceobj.name):
501 if vrf_table == 'auto':
502 vrf_table = self._get_avail_vrf_table_id()
503 if not vrf_table:
504 self.log_error('%s: unable to get an auto table id'
505 %ifaceobj.name)
506 self.logger.info('%s: table id auto: selected table id %s\n'
507 %(ifaceobj.name, vrf_table))
508 else:
509 self._iproute2_is_vrf_tableid_inuse(ifaceobj.name, vrf_table)
510
511 if not vrf_table.isdigit():
512 self.log_error('%s: vrf-table must be an integer or \'auto\''
513 %(ifaceobj.name), ifaceobj)
514
515 # XXX: If we decide to not allow vrf id usages out of
516 # the reserved ifupdown range, then uncomment this code.
517 else:
518 if (int(vrf_table) < self.vrf_table_id_start or
519 int(vrf_table) > self.vrf_table_id_end):
520 self.log_error('%s: vrf table id %s out of reserved range [%d,%d]'
521 %(ifaceobj.name, vrf_table,
522 self.vrf_table_id_start,
523 self.vrf_table_id_end))
524 try:
525 self.ipcmd.link_create(ifaceobj.name, 'vrf',
526 {'table' : '%s' %vrf_table})
527 except Exception, e:
528 self.log_error('%s: create failed (%s)\n'
529 %(ifaceobj.name, str(e)))
530 if vrf_table != 'auto':
531 self._iproute2_vrf_table_entry_add(ifaceobj.name, vrf_table)
532 else:
533 if vrf_table == 'auto':
534 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
535 if not vrf_table:
536 self.log_error('%s: unable to get vrf table id'
537 %ifaceobj.name)
538
539 # if the device exists, check if table id is same
540 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
541 if vrfdev_attrs:
542 running_table = vrfdev_attrs.get('table', None)
543 if vrf_table != running_table:
544 self.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj.name,
545 running_table, vrf_table))
546 return vrf_table
547
548 def _up_vrf_helper(self, ifaceobj, vrf_table):
549 if self.vrf_helper:
550 self.exec_command('%s create %s %s' %(self.vrf_helper,
551 ifaceobj.name, vrf_table))
552
553 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
554 ifaceobj_getfunc=None):
555
556 # if vrf dev is already processed return. This can happen
557 # if we the slave was configured before.
558 # see self._up_vrf_slave_without_master
559 if self._check_vrf_dev_processed_flag(ifaceobj):
560 return True
561
562 try:
563 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
564 except Exception, e:
565 self.log_error('%s: %s' %(ifaceobj.name, str(e)))
566
567 try:
568 self._add_vrf_rules(ifaceobj.name, vrf_table)
569 self._up_vrf_helper(ifaceobj, vrf_table)
570 if add_slaves:
571 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
572 self._set_vrf_dev_processed_flag(ifaceobj)
573 rtnetlink_api.rtnl_api.link_set(ifaceobj.name, "up")
574 except Exception, e:
575 self.log_error('%s: %s' %(ifaceobj.name, str(e)))
576
577 def _kill_ssh_connections(self, ifacename):
578 try:
579 runningaddrsdict = self.ipcmd.addr_get(ifacename)
580 if not runningaddrsdict:
581 return
582 iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
583 if not iplist:
584 return
585 proc=[]
586 #Example output:
587 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
588 #users:(("sshd",pid=2528,fd=3))
589 cmdl = ['/bin/ss', '-t', '-p']
590 for line in subprocess.check_output(cmdl, stderr=subprocess.STDOUT,
591 shell=False).splitlines():
592 citems = line.split()
593 addr = None
594 if '%' in citems[3]:
595 addr = citems[3].split('%')[0]
596 elif ':ssh' in citems[3]:
597 addr = citems[3].split(':')[0]
598 if not addr:
599 continue
600 if addr in iplist:
601 if len(citems) == 6:
602 proc.append(citems[5].split(',')[1].split('=')[1])
603
604 if not proc:
605 return
606 pid = subprocess.check_output(['/bin/ps', '--no-headers',
607 '-fp', str(os.getppid())],
608 stderr=subprocess.STDOUT,
609 shell=False).split()[2]
610 self.logger.info("%s: killing active ssh sessions: %s"
611 %(ifacename, str(proc)))
612 for id in proc:
613 if id != pid:
614 try:
615 os.kill(int(id), signal.SIGINT)
616 except OSError as e:
617 continue
618
619 # Kill current SSH client
620 if pid in proc:
621 try:
622 forkret = os.fork()
623 except OSError, e:
624 self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
625 if (forkret == 0): # The first child.
626 try:
627 os.setsid()
628 self.logger.info("%s: ifreload continuing in the background" %ifacename)
629 except OSError, (err_no, err_message):
630 self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
631 self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0)))
632 try:
633 self.logger.info("%s: killing our session: %s"
634 %(ifacename, str(proc)))
635 os.kill(int(pid), signal.SIGINT)
636 return
637 except OSError as e:
638 return
639 except Exception, e:
640 self.logger.info('%s: %s' %(ifacename, str(e)))
641
642 def _up(self, ifaceobj, ifaceobj_getfunc=None):
643 try:
644 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
645 if vrf_table:
646 self._iproute2_vrf_map_initialize()
647 # This is a vrf device
648 if self.vrf_count == self.vrf_max_count:
649 self.log_error('%s: max vrf count %d hit...not '
650 'creating vrf' %(ifaceobj.name,
651 self.vrf_count))
652 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
653 else:
654 vrf = ifaceobj.get_attr_value_first('vrf')
655 if vrf:
656 self._iproute2_vrf_map_initialize()
657 # This is a vrf slave
658 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
659 ifaceobj_getfunc)
660 else:
661 # check if we were a slave before
662 master = self.ipcmd.link_get_master(ifaceobj.name)
663 if master:
664 self._iproute2_vrf_map_initialize()
665 if self._is_vrf_dev(master):
666 self._down_vrf_slave(ifaceobj.name, ifaceobj,
667 master)
668 except Exception, e:
669 self.log_error(str(e))
670
671 def _down_vrf_helper(self, ifaceobj, vrf_table):
672 if self.vrf_helper:
673 self.exec_command('%s delete %s %s' %(self.vrf_helper,
674 ifaceobj.name, vrf_table))
675
676 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
677
678 if vrf_table == 'auto':
679 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
680
681 try:
682 self.exec_command('/usr/bin/vrf service disable %s' %ifaceobj.name)
683 except Exception, e:
684 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
685 pass
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 except Exception, e:
776 self.log_warn(str(e))
777
778 def _query_check(self, ifaceobj, ifaceobjcurr):
779 try:
780 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
781 if vrf_table:
782 self._iproute2_vrf_map_initialize()
783 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
784 else:
785 vrf = ifaceobj.get_attr_value_first('vrf')
786 if vrf:
787 self._iproute2_vrf_map_initialize()
788 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
789 except Exception, e:
790 self.log_warn(str(e))
791
792 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
793 try:
794 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
795 if kind == 'vrf':
796 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
797 if vrfdev_attrs:
798 running_table = vrfdev_attrs.get('table')
799 if running_table:
800 ifaceobjrunning.update_config('vrf-table',
801 running_table)
802 elif kind == 'vrf_slave':
803 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
804 if vrf:
805 ifaceobjrunning.update_config('vrf', vrf)
806 except Exception, e:
807 self.log_warn(str(e))
808
809 _run_ops = {'pre-up' : _up,
810 'post-down' : _down,
811 'query-running' : _query_running,
812 'query-checkcurr' : _query_check}
813
814 def get_ops(self):
815 """ returns list of ops supported by this module """
816 return self._run_ops.keys()
817
818 def _init_command_handlers(self):
819 if not self.ipcmd:
820 self.ipcmd = iproute2()
821 if not self.bondcmd:
822 self.bondcmd = bondutil()
823 if not self.dhclientcmd:
824 self.dhclientcmd = dhclient()
825
826 def run(self, ifaceobj, operation, query_ifaceobj=None,
827 ifaceobj_getfunc=None, **extra_args):
828 """ run bond configuration on the interface object passed as argument
829
830 Args:
831 **ifaceobj** (object): iface object
832
833 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
834 'query-running'
835
836 Kwargs:
837 **query_ifaceobj** (object): query check ifaceobject. This is only
838 valid when op is 'query-checkcurr'. It is an object same as
839 ifaceobj, but contains running attribute values and its config
840 status. The modules can use it to return queried running state
841 of interfaces. status is success if the running state is same
842 as user required state in ifaceobj. error otherwise.
843 """
844 op_handler = self._run_ops.get(operation)
845 if not op_handler:
846 return
847 self._init_command_handlers()
848 if operation == 'query-checkcurr':
849 op_handler(self, ifaceobj, query_ifaceobj)
850 else:
851 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)