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