]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/vrf.py
addons: vrf: be more friendly to older iproute2 versions
[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
306 if old_vrf_name != vrfifaceobj.name:
307 self.log_error('table id %d already assigned to vrf dev %s'
308 %(table_id, old_vrf_name))
309
310 def _iproute2_vrf_table_entry_del(self, table_id):
311 try:
312 # with any del of vrf map, we need to force sync to disk
313 self.iproute2_vrf_map_sync_to_disk = True
314 del self.iproute2_vrf_map[int(table_id)]
315 except Exception, e:
316 self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)'
317 %(table_id, str(e)))
318 pass
319
320 def _is_vrf_dev(self, ifacename):
321 # Look at iproute2 map for now.
322 # If it was a master we knew about,
323 # it is definately there
324 if ifacename in self.iproute2_vrf_map.values():
325 return True
326 return False
327
328 def _is_dhcp_slave(self, ifaceobj):
329 if (not ifaceobj.addr_method or
330 (ifaceobj.addr_method != 'dhcp' and
331 ifaceobj.addr_method != 'dhcp6')):
332 return False
333 return True
334
335 def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj,
336 vrf_master_objs):
337 """ If we have a vrf slave that has dhcp configured, bring up the
338 vrf master now. This is needed because vrf has special handling
339 in dhclient hook which requires the vrf master to be present """
340
341 vrf_master = ifaceobj.upperifaces[0]
342 if not vrf_master:
343 self.logger.warn('%s: vrf master not found' %ifacename)
344 return
345 if os.path.exists('/sys/class/net/%s' %vrf_master):
346 self.logger.info('%s: vrf master %s exists returning'
347 %(ifacename, vrf_master))
348 return
349 self.logger.info('%s: bringing up vrf master %s'
350 %(ifacename, vrf_master))
351 for mobj in vrf_master_objs:
352 vrf_table = mobj.get_attr_value_first('vrf-table')
353 if vrf_table:
354 if vrf_table == 'auto':
355 vrf_table = self._get_avail_vrf_table_id()
356 if not vrf_table:
357 self.log_error('%s: unable to get an auto table id'
358 %mobj.name, ifaceobj)
359 self.logger.info('%s: table id auto: selected table id %s\n'
360 %(mobj.name, vrf_table))
361 try:
362 self._up_vrf_dev(mobj, vrf_table, False)
363 except Exception:
364 raise
365 break
366 self._handle_existing_connections(ifaceobj, vrfname)
367 self.ipcmd.link_set(ifacename, 'master', vrfname)
368 return
369
370 def _down_dhcp_slave(self, ifaceobj, vrfname):
371 try:
372 dhclient_cmd_prefix = None
373 if (vrfname and self.vrf_exec_cmd_prefix and
374 self.ipcmd.link_exists(vrfname)):
375 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
376 vrfname)
377 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
378 except:
379 # ignore any dhclient release errors
380 pass
381
382 def _handle_existing_connections(self, ifaceobj, vrfname):
383 if not ifaceobj or ifupdownflags.flags.PERFMODE:
384 return
385 if (self.vrf_mgmt_devname and
386 self.vrf_mgmt_devname == vrfname):
387 self._kill_ssh_connections(ifaceobj.name)
388 if self._is_dhcp_slave(ifaceobj):
389 self._down_dhcp_slave(ifaceobj, vrfname)
390
391 def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
392 ifaceobj_getfunc=None, vrf_exists=False):
393 try:
394 master_exists = True
395 if vrf_exists or self.ipcmd.link_exists(vrfname):
396 upper = self.ipcmd.link_get_upper(ifacename)
397 if not upper or upper != vrfname:
398 self._handle_existing_connections(ifaceobj, vrfname)
399 self.ipcmd.link_set(ifacename, 'master', vrfname)
400 elif ifaceobj:
401 vrf_master_objs = ifaceobj_getfunc(vrfname)
402 if not vrf_master_objs:
403 # this is the case where vrf is assigned to an interface
404 # but user has not provided a vrf interface.
405 # people expect you to warn them but go ahead with the
406 # rest of the config on that interface
407 netlink.link_set_updown(ifacename, "up")
408 self.log_error('vrf master ifaceobj %s not found'
409 %vrfname)
410 return
411 if (ifupdownflags.flags.ALL or
412 (ifupdownflags.flags.CLASS and
413 ifaceobj.classes and vrf_master_objs[0].classes and
414 Set(ifaceobj.classes).intersection(vrf_master_objs[0].classes))):
415 self._up_vrf_slave_without_master(ifacename, vrfname,
416 ifaceobj,
417 vrf_master_objs)
418 else:
419 master_exists = False
420 else:
421 master_exists = False
422 if master_exists:
423 netlink.link_set_updown(ifacename, "up")
424 else:
425 self.log_error('vrf %s not around, skipping vrf config'
426 %(vrfname), ifaceobj)
427 except Exception, e:
428 self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj)
429
430 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
431 pref = 200
432 ip_rule_out_format = '%s: from all %s %s lookup %s'
433 ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
434
435 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
436 if rule in self.ip_rule_cache:
437 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
438 vrf_dev_name)
439 utils.exec_command(rule_cmd)
440
441 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
442 if rule in self.ip_rule_cache:
443 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
444 vrf_dev_name)
445 utils.exec_command(rule_cmd)
446
447 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
448 if rule in self.ip6_rule_cache:
449 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
450 vrf_dev_name)
451 utils.exec_command(rule_cmd)
452
453 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
454 if rule in self.ip6_rule_cache:
455 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
456 vrf_dev_name)
457 utils.exec_command(rule_cmd)
458
459 def _l3mdev_rule(self, ip_rules):
460 for rule in ip_rules:
461 if not re.search(r"\d.*from\s+all\s+lookup\s+\W?l3mdev-table\W?",
462 rule):
463 continue
464 return True
465 return False
466
467 def _rule_cache_fill(self):
468 ip_rules = utils.exec_command('/sbin/ip rule show').splitlines()
469 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
470 self.l3mdev4_rule = self._l3mdev_rule(self.ip_rule_cache)
471 ip_rules = utils.exec_command('/sbin/ip -6 rule show').splitlines()
472 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
473 self.l3mdev6_rule = self._l3mdev_rule(self.ip6_rule_cache)
474
475 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
476 pref = 200
477 ip_rule_out_format = '%s: from all %s %s lookup %s'
478 ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
479 if self.vrf_fix_local_table:
480 self.vrf_fix_local_table = False
481 rule = '0: from all lookup local'
482 if rule in self.ip_rule_cache:
483 try:
484 utils.exec_command('ip rule del pref 0')
485 utils.exec_command('ip rule add pref 32765 table local')
486 except Exception, e:
487 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
488 pass
489 if rule in self.ip6_rule_cache:
490 try:
491 utils.exec_command('ip -6 rule del pref 0')
492 utils.exec_command('ip -6 rule add pref 32765 table local')
493 except Exception, e:
494 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
495 pass
496
497 if not self.l3mdev_checked:
498 self._rule_cache_fill()
499 self.l3mdev_checked = True
500 #Example ip rule
501 #200: from all oif blue lookup blue
502 #200: from all iif blue lookup blue
503
504 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
505 if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
506 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
507 vrf_dev_name)
508 utils.exec_command(rule_cmd)
509
510 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
511 if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
512 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
513 vrf_dev_name)
514 utils.exec_command(rule_cmd)
515
516 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
517 if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
518 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
519 vrf_dev_name)
520 utils.exec_command(rule_cmd)
521
522 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
523 if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
524 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
525 vrf_dev_name)
526 utils.exec_command(rule_cmd)
527
528 def _is_address_virtual_slaves(self, vrfobj, config_vrfslaves,
529 vrfslave):
530 # Address virtual lines on a vrf slave will create
531 # macvlan devices on the vrf slave and enslave them
532 # to the vrf master. This function checks if the
533 # vrf slave is such a macvlan interface.
534 # XXX: additional possible checks that can be done here
535 # are:
536 # - check if it is also a macvlan device of the
537 # format <vrf_slave>-v<int> created by the
538 # address virtual module
539 vrfslave_lowers = self.ipcmd.link_get_lowers(vrfslave)
540 if vrfslave_lowers:
541 if vrfslave_lowers[0] in config_vrfslaves:
542 return True
543 return False
544
545 def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
546 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
547 config_slaves = ifaceobj.lowerifaces
548 if not config_slaves and not running_slaves:
549 return
550
551 if not config_slaves: config_slaves = []
552 if not running_slaves: running_slaves = []
553 add_slaves = set(config_slaves).difference(set(running_slaves))
554 del_slaves = set(running_slaves).difference(set(config_slaves))
555 if add_slaves:
556 for s in add_slaves:
557 try:
558 if not self.ipcmd.link_exists(s):
559 continue
560 sobj = None
561 if ifaceobj_getfunc:
562 sobj = ifaceobj_getfunc(s)
563 self._up_vrf_slave(s, ifaceobj.name,
564 sobj[0] if sobj else None,
565 ifaceobj_getfunc, True)
566 except Exception, e:
567 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
568
569 if del_slaves:
570 for s in del_slaves:
571 try:
572 if self._is_address_virtual_slaves(ifaceobj,
573 config_slaves, s):
574 continue
575 sobj = None
576 if ifaceobj_getfunc:
577 sobj = ifaceobj_getfunc(s)
578 self._down_vrf_slave(s, sobj[0] if sobj else None,
579 ifaceobj.name)
580 except Exception, e:
581 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
582
583 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
584 for s in config_slaves:
585 try:
586 netlink.link_set_updown(s, "up")
587 except Exception, e:
588 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
589 pass
590
591 def _set_vrf_dev_processed_flag(self, ifaceobj):
592 ifaceobj.module_flags[self.name] = \
593 ifaceobj.module_flags.setdefault(self.name, 0) | \
594 vrfPrivFlags.PROCESSED
595
596 def _check_vrf_dev_processed_flag(self, ifaceobj):
597 if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
598 return True
599 return False
600
601 def _create_vrf_dev(self, ifaceobj, vrf_table):
602 if not self.ipcmd.link_exists(ifaceobj.name):
603 if ifaceobj.name in self.system_reserved_rt_tables.values():
604 self.log_error('cannot use system reserved %s vrf names'
605 %(str(self.system_reserved_rt_tables.values())),
606 ifaceobj)
607 if self.vrf_count == self.vrf_max_count:
608 self.log_error('%s: max vrf count %d hit...not '
609 'creating vrf' %(ifaceobj.name,
610 self.vrf_count), ifaceobj)
611 if vrf_table == 'auto':
612 vrf_table = self._get_avail_vrf_table_id()
613 if not vrf_table:
614 self.log_error('%s: unable to get an auto table id'
615 %ifaceobj.name, ifaceobj)
616 self.logger.info('%s: table id auto: selected table id %s\n'
617 %(ifaceobj.name, vrf_table))
618 else:
619 self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
620 if ifaceobj.name in self.system_reserved_rt_tables.keys():
621 self.log_error('cannot use system reserved %s table ids'
622 %(str(self.system_reserved_rt_tables.keys())),
623 ifaceobj)
624
625 if not vrf_table.isdigit():
626 self.log_error('%s: vrf-table must be an integer or \'auto\''
627 %(ifaceobj.name), ifaceobj)
628
629 # XXX: If we decide to not allow vrf id usages out of
630 # the reserved ifupdown range, then uncomment this code.
631 else:
632 if (int(vrf_table) < self.vrf_table_id_start or
633 int(vrf_table) > self.vrf_table_id_end):
634 self.log_error('%s: vrf table id %s out of reserved range [%d,%d]'
635 %(ifaceobj.name, vrf_table,
636 self.vrf_table_id_start,
637 self.vrf_table_id_end), ifaceobj)
638 try:
639 self.ipcmd.link_create(ifaceobj.name, 'vrf',
640 {'table' : '%s' %vrf_table})
641 except Exception, e:
642 self.log_error('%s: create failed (%s)\n'
643 %(ifaceobj.name, str(e)), ifaceobj)
644 if vrf_table != 'auto':
645 self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
646 else:
647 if vrf_table == 'auto':
648 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
649 if not vrf_table:
650 self.log_error('%s: unable to get vrf table id'
651 %ifaceobj.name, ifaceobj)
652
653 # if the device exists, check if table id is same
654 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
655 if vrfdev_attrs:
656 running_table = vrfdev_attrs.get('table', None)
657 if vrf_table != running_table:
658 self.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj.name,
659 running_table, vrf_table),
660 ifaceobj)
661 return vrf_table
662
663 def _up_vrf_helper(self, ifaceobj, vrf_table):
664 mode = ""
665 if ifupdownflags.flags.PERFMODE:
666 mode = "boot"
667 if self.vrf_helper:
668 utils.exec_command('%s create %s %s %s' %
669 (self.vrf_helper,
670 ifaceobj.name,
671 vrf_table,
672 mode))
673
674 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
675 ifaceobj_getfunc=None):
676
677 # if vrf dev is already processed return. This can happen
678 # if we the slave was configured before.
679 # see self._up_vrf_slave_without_master
680 if self._check_vrf_dev_processed_flag(ifaceobj):
681 return True
682
683 try:
684 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
685 except Exception, e:
686 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
687
688 try:
689 self._add_vrf_rules(ifaceobj.name, vrf_table)
690 self._up_vrf_helper(ifaceobj, vrf_table)
691 if add_slaves:
692 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
693 self._set_vrf_dev_processed_flag(ifaceobj)
694 netlink.link_set_updown(ifaceobj.name, "up")
695 except Exception, e:
696 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
697
698 def _kill_ssh_connections(self, ifacename):
699 try:
700 runningaddrsdict = self.ipcmd.addr_get(ifacename)
701 if not runningaddrsdict:
702 return
703 iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
704 if not iplist:
705 return
706 proc=[]
707 #Example output:
708 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
709 #users:(("sshd",pid=2528,fd=3))
710 cmdl = ['/bin/ss', '-t', '-p']
711 for line in utils.exec_commandl(cmdl).splitlines():
712 citems = line.split()
713 addr = None
714 if '%' in citems[3]:
715 addr = citems[3].split('%')[0]
716 elif ':ssh' in citems[3]:
717 addr = citems[3].split(':')[0]
718 if not addr:
719 continue
720 if addr in iplist:
721 if len(citems) == 6:
722 proc.append(citems[5].split(',')[1].split('=')[1])
723
724 if not proc:
725 return
726 pid = None
727 # outpt of '/usr/bin/pstree -Aps <pid>':
728 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
729 # get the above output to following format
730 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
731 pstree = list(reversed(utils.exec_command('/usr/bin/pstree -Aps %s' %os.getpid()).strip().split('---')))
732 for index, process in enumerate(pstree):
733 # check the parent of SSH process to make sure
734 # we don't kill SSH server or systemd process
735 if 'sshd' in process and 'sshd' in pstree[index + 1]:
736 pid = filter(lambda x: x.isdigit(), process)
737 break
738 self.logger.info("%s: killing active ssh sessions: %s"
739 %(ifacename, str(proc)))
740
741 if ifupdownflags.flags.DRYRUN:
742 return
743 for id in proc:
744 if id != pid:
745 try:
746 os.kill(int(id), signal.SIGINT)
747 except OSError as e:
748 continue
749
750 # Kill current SSH client
751 if pid in proc:
752 try:
753 forkret = os.fork()
754 except OSError, e:
755 self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
756 if (forkret == 0): # The first child.
757 try:
758 os.setsid()
759 self.logger.info("%s: ifreload continuing in the background" %ifacename)
760 except OSError, (err_no, err_message):
761 self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
762 self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0)))
763 try:
764 self.logger.info("%s: killing our session: %s"
765 %(ifacename, str(proc)))
766 os.kill(int(pid), signal.SIGINT)
767 return
768 except OSError as e:
769 return
770 except Exception, e:
771 self.logger.info('%s: %s' %(ifacename, str(e)))
772
773 def _up(self, ifaceobj, ifaceobj_getfunc=None):
774 try:
775 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
776 if vrf_table:
777 self._iproute2_vrf_map_initialize()
778 # This is a vrf device
779 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
780 else:
781 vrf = ifaceobj.get_attr_value_first('vrf')
782 if vrf:
783 self._iproute2_vrf_map_initialize()
784 # This is a vrf slave
785 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
786 ifaceobj_getfunc)
787 else:
788 # check if we were a slave before
789 master = self.ipcmd.link_get_master(ifaceobj.name)
790 if master:
791 self._iproute2_vrf_map_initialize()
792 if self._is_vrf_dev(master):
793 self._down_vrf_slave(ifaceobj.name, ifaceobj,
794 master)
795 except Exception, e:
796 self.log_error(str(e), ifaceobj)
797
798 def _down_vrf_helper(self, ifaceobj, vrf_table):
799 mode = ""
800 if ifupdownflags.flags.PERFMODE:
801 mode = "boot"
802 if self.vrf_helper:
803 utils.exec_command('%s delete %s %s %s' %
804 (self.vrf_helper,
805 ifaceobj.name,
806 vrf_table,
807 mode))
808
809 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
810
811 if vrf_table == 'auto':
812 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
813
814 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
815 if running_slaves:
816 for s in running_slaves:
817 if ifaceobj_getfunc:
818 sobj = ifaceobj_getfunc(s)
819 try:
820 self._handle_existing_connections(sobj[0]
821 if sobj else None,
822 ifaceobj.name)
823 except Exception, e:
824 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
825 pass
826 try:
827 self.ipcmd.addr_flush(s)
828 netlink.link_set_updown(s, "down")
829 except Exception, e:
830 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
831 pass
832
833 try:
834 self._down_vrf_helper(ifaceobj, vrf_table)
835 except Exception, e:
836 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
837 pass
838
839 try:
840 self._del_vrf_rules(ifaceobj.name, vrf_table)
841 except Exception, e:
842 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
843 pass
844
845 try:
846 self.ipcmd.link_delete(ifaceobj.name)
847 except Exception, e:
848 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
849 pass
850
851 try:
852 self._iproute2_vrf_table_entry_del(vrf_table)
853 except Exception, e:
854 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
855 pass
856
857
858 def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
859 try:
860 self._handle_existing_connections(ifaceobj, vrfname)
861 self.ipcmd.link_set(ifacename, 'nomaster')
862 netlink.link_set_updown(ifacename, "down")
863 except Exception, e:
864 self.logger.warn('%s: %s' %(ifacename, str(e)))
865
866 def _down(self, ifaceobj, ifaceobj_getfunc=None):
867 try:
868 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
869 if vrf_table:
870 self._iproute2_vrf_map_initialize()
871 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
872 else:
873 vrf = ifaceobj.get_attr_value_first('vrf')
874 if vrf:
875 self._iproute2_vrf_map_initialize()
876 self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
877 except Exception, e:
878 self.log_warn(str(e))
879
880 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
881 try:
882 master = self.ipcmd.link_get_master(ifaceobj.name)
883 if not master or master != vrf:
884 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
885 else:
886 ifaceobjcurr.update_config_with_status('vrf', master, 0)
887 except Exception, e:
888 self.log_error(str(e), ifaceobjcurr)
889
890 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
891 try:
892 if not self.ipcmd.link_exists(ifaceobj.name):
893 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
894 return
895 if vrf_table == 'auto':
896 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
897 else:
898 config_table = vrf_table
899 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
900 if not vrfdev_attrs:
901 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
902 return
903 running_table = vrfdev_attrs.get('table')
904 if not running_table:
905 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
906 return
907 if config_table != running_table:
908 ifaceobjcurr.update_config_with_status('vrf-table',
909 running_table, 1)
910 else:
911 ifaceobjcurr.update_config_with_status('vrf-table',
912 running_table, 0)
913 if not ifupdownflags.flags.WITHDEFAULTS:
914 return
915 if self.vrf_helper:
916 try:
917 utils.exec_command('%s verify %s %s'
918 %(self.vrf_helper,
919 ifaceobj.name, config_table))
920 ifaceobjcurr.update_config_with_status('vrf-helper',
921 '%s create %s %s'
922 %(self.vrf_helper,
923 ifaceobj.name,
924 config_table), 0)
925 except Exception, e:
926 ifaceobjcurr.update_config_with_status('vrf-helper',
927 '%s create %s %s'
928 %(self.vrf_helper,
929 ifaceobj.name,
930 config_table), 1)
931 pass
932 except Exception, e:
933 self.log_warn(str(e))
934
935 def _query_check(self, ifaceobj, ifaceobjcurr):
936 try:
937 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
938 if vrf_table:
939 self._iproute2_vrf_map_initialize(writetodisk=False)
940 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
941 else:
942 vrf = ifaceobj.get_attr_value_first('vrf')
943 if vrf:
944 self._iproute2_vrf_map_initialize(writetodisk=False)
945 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
946 except Exception, e:
947 self.log_warn(str(e))
948
949 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
950 try:
951 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
952 if kind == 'vrf':
953 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
954 if vrfdev_attrs:
955 running_table = vrfdev_attrs.get('table')
956 if running_table:
957 ifaceobjrunning.update_config('vrf-table',
958 running_table)
959 elif kind == 'vrf_slave':
960 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
961 if vrf:
962 ifaceobjrunning.update_config('vrf', vrf)
963 except Exception, e:
964 self.log_warn(str(e))
965
966 def _query(self, ifaceobj, **kwargs):
967 if not self.vrf_helper:
968 return
969 if (ifaceobj.link_kind & ifaceLinkKind.VRF):
970 ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
971 ifaceobj.name))
972
973 _run_ops = {'pre-up' : _up,
974 'post-down' : _down,
975 'query-running' : _query_running,
976 'query-checkcurr' : _query_check,
977 'query' : _query}
978
979 def get_ops(self):
980 """ returns list of ops supported by this module """
981 return self._run_ops.keys()
982
983 def _init_command_handlers(self):
984 if not self.ipcmd:
985 self.ipcmd = iproute2()
986 if not self.bondcmd:
987 self.bondcmd = bondutil()
988 if not self.dhclientcmd:
989 self.dhclientcmd = dhclient()
990
991 def run(self, ifaceobj, operation, query_ifaceobj=None,
992 ifaceobj_getfunc=None, **extra_args):
993 """ run bond configuration on the interface object passed as argument
994
995 Args:
996 **ifaceobj** (object): iface object
997
998 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
999 'query-running'
1000
1001 Kwargs:
1002 **query_ifaceobj** (object): query check ifaceobject. This is only
1003 valid when op is 'query-checkcurr'. It is an object same as
1004 ifaceobj, but contains running attribute values and its config
1005 status. The modules can use it to return queried running state
1006 of interfaces. status is success if the running state is same
1007 as user required state in ifaceobj. error otherwise.
1008 """
1009 op_handler = self._run_ops.get(operation)
1010 if not op_handler:
1011 return
1012 self._init_command_handlers()
1013 if operation == 'query-checkcurr':
1014 op_handler(self, ifaceobj, query_ifaceobj)
1015 else:
1016 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)