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