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