]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/vrf.py
addons: vrf: fix vrf slave link kind (fixes #39)
[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 self.vrf_close_socks_on_down = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-close-socks-on-down')
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 (%s)' %(l, str(e)))
155 pass
156
157 vrfs = self.ipcmd.link_get_vrfs()
158 running_vrf_map = {}
159 if vrfs:
160 for v, lattrs in vrfs.iteritems():
161 table = lattrs.get('table', None)
162 if table:
163 running_vrf_map[int(table)] = v
164
165 if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
166 self.iproute2_vrf_map = running_vrf_map
167 iproute2_vrf_map_force_rewrite = True
168
169 self.iproute2_vrf_map_fd = None
170 if writetodisk:
171 if iproute2_vrf_map_force_rewrite:
172 # reopen the file and rewrite the map
173 self._iproute2_vrf_map_open(True, False)
174 else:
175 self._iproute2_vrf_map_open(False, True)
176
177 self.iproute2_vrf_map_sync_to_disk = False
178 atexit.register(self._iproute2_vrf_map_sync_to_disk)
179
180 self.logger.info("vrf: dumping iproute2_vrf_map")
181 self.logger.info(self.iproute2_vrf_map)
182
183 last_used_vrf_table = None
184 for t in range(self.vrf_table_id_start,
185 self.vrf_table_id_end):
186 if not self.iproute2_vrf_map.get(t):
187 break
188 last_used_vrf_table = t
189 self.last_used_vrf_table = last_used_vrf_table
190 self._iproute2_vrf_map_initialized = True
191 self.vrf_count = len(self.iproute2_vrf_map)
192
193 def _iproute2_map_warn(self, errstr):
194 if self.warn_on_vrf_map_write_err:
195 if not os.path.exists('/etc/iproute2/rt_tables.d/'):
196 self.logger.info('unable to save iproute2 vrf to table ' +
197 'map (%s)\n' %errstr)
198 self.logger.info('cannot find /etc/iproute2/rt_tables.d.' +
199 ' pls check if your iproute2 version' +
200 ' supports rt_tables.d')
201 else:
202 self.logger.warn('unable to open iproute2 vrf to table ' +
203 'map (%s)\n' %errstr)
204 self.warn_on_vrf_map_write_err = False
205
206 def _iproute2_vrf_map_sync_to_disk(self):
207 if (ifupdownflags.flags.DRYRUN or
208 not self.iproute2_vrf_map_sync_to_disk):
209 return
210 self.logger.info('vrf: syncing table map to %s'
211 %self.iproute2_vrf_filename)
212 try:
213 with open(self.iproute2_vrf_filename, 'w') as f:
214 f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
215 self.vrf_table_id_end))
216 for t, v in self.iproute2_vrf_map.iteritems():
217 f.write('%s %s\n' %(t, v))
218 f.flush()
219 except Exception, e:
220 self._iproute2_map_warn(str(e))
221 pass
222
223 def _iproute2_vrf_map_open(self, sync_vrfs=False, append=False):
224 self.logger.info('vrf: syncing table map to %s'
225 %self.iproute2_vrf_filename)
226 if ifupdownflags.flags.DRYRUN:
227 return
228 fmode = 'a+' if append else 'w'
229 try:
230 self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
231 '%s' %fmode)
232 fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
233 except Exception, e:
234 self._iproute2_map_warn(str(e))
235 return
236
237 if not append:
238 # write file header
239 self.iproute2_vrf_map_fd.write(self.iproute2_vrf_filehdr
240 %(self.vrf_table_id_start,
241 self.vrf_table_id_end))
242 for t, v in self.iproute2_vrf_map.iteritems():
243 self.iproute2_vrf_map_fd.write('%s %s\n' %(t, v))
244 self.iproute2_vrf_map_fd.flush()
245
246 def _is_vrf(self, ifaceobj):
247 if ifaceobj.get_attr_value_first('vrf-table'):
248 return True
249 return False
250
251 def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
252 """ Returns list of interfaces dependent on ifaceobj """
253
254 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
255 if vrf_table:
256 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
257 ifaceobj.link_kind |= ifaceLinkKind.VRF
258 ifaceobj.role |= ifaceRole.MASTER
259 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
260 if not vrf_iface_name:
261 return None
262 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
263 ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
264
265 return [vrf_iface_name]
266
267 def get_upper_ifacenames_running(self, ifaceobj):
268 return None
269
270 def _get_iproute2_vrf_table(self, vrf_dev_name):
271 for t, v in self.iproute2_vrf_map.iteritems():
272 if v == vrf_dev_name:
273 return str(t)
274 return None
275
276 def _get_avail_vrf_table_id(self):
277 if self.last_used_vrf_table == None:
278 table_id_start = self.vrf_table_id_start
279 else:
280 table_id_start = self.last_used_vrf_table + 1
281 for t in range(table_id_start,
282 self.vrf_table_id_end):
283 if not self.iproute2_vrf_map.get(t):
284 self.last_used_vrf_table = t
285 return str(t)
286 return None
287
288 def _iproute2_is_vrf_tableid_inuse(self, vrfifaceobj, table_id):
289 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
290 if old_vrf_name and old_vrf_name != vrfifaceobj.name:
291 self.log_error('table id %s already assigned to vrf dev %s'
292 %(table_id, old_vrf_name), vrfifaceobj)
293
294 def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id):
295 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
296 if not old_vrf_name:
297 self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
298 if self.iproute2_vrf_map_fd:
299 self.iproute2_vrf_map_fd.write('%s %s\n'
300 %(table_id, vrfifaceobj.name))
301 self.iproute2_vrf_map_fd.flush()
302 self.vrf_count += 1
303 return
304 if old_vrf_name != vrfifaceobj.name:
305 self.log_error('table id %d already assigned to vrf dev %s'
306 %(table_id, old_vrf_name))
307
308 def _iproute2_vrf_table_entry_del(self, table_id):
309 try:
310 # with any del of vrf map, we need to force sync to disk
311 self.iproute2_vrf_map_sync_to_disk = True
312 del self.iproute2_vrf_map[int(table_id)]
313 except Exception, e:
314 self.logger.info('vrf: iproute2 vrf map del failed for %s (%s)'
315 %(table_id, str(e)))
316 pass
317
318 def _is_vrf_dev(self, ifacename):
319 # Look at iproute2 map for now.
320 # If it was a master we knew about,
321 # it is definately there
322 if ifacename in self.iproute2_vrf_map.values():
323 return True
324 return False
325
326 def _is_dhcp_slave(self, ifaceobj):
327 if (not ifaceobj.addr_method or
328 (ifaceobj.addr_method != 'dhcp' and
329 ifaceobj.addr_method != 'dhcp6')):
330 return False
331 return True
332
333 def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj,
334 vrf_master_objs):
335 """ If we have a vrf slave that has dhcp configured, bring up the
336 vrf master now. This is needed because vrf has special handling
337 in dhclient hook which requires the vrf master to be present """
338
339 vrf_master = ifaceobj.upperifaces[0]
340 if not vrf_master:
341 self.logger.warn('%s: vrf master not found' %ifacename)
342 return
343 if os.path.exists('/sys/class/net/%s' %vrf_master):
344 self.logger.info('%s: vrf master %s exists returning'
345 %(ifacename, vrf_master))
346 return
347 self.logger.info('%s: bringing up vrf master %s'
348 %(ifacename, vrf_master))
349 for mobj in vrf_master_objs:
350 vrf_table = mobj.get_attr_value_first('vrf-table')
351 if vrf_table:
352 if vrf_table == 'auto':
353 vrf_table = self._get_avail_vrf_table_id()
354 if not vrf_table:
355 self.log_error('%s: unable to get an auto table id'
356 %mobj.name, ifaceobj)
357 self.logger.info('%s: table id auto: selected table id %s\n'
358 %(mobj.name, vrf_table))
359 try:
360 self._up_vrf_dev(mobj, vrf_table, False)
361 except Exception:
362 raise
363 break
364 self._handle_existing_connections(ifaceobj, vrfname)
365 netlink.link_set_master(ifacename, vrfname)
366 return
367
368 def _down_dhcp_slave(self, ifaceobj, vrfname):
369 try:
370 dhclient_cmd_prefix = None
371 if (vrfname and self.vrf_exec_cmd_prefix and
372 self.ipcmd.link_exists(vrfname)):
373 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
374 vrfname)
375 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
376 except:
377 # ignore any dhclient release errors
378 pass
379
380 def _handle_existing_connections(self, ifaceobj, vrfname):
381 if not ifaceobj or ifupdownflags.flags.PERFMODE:
382 return
383 if (self.vrf_mgmt_devname and
384 self.vrf_mgmt_devname == vrfname):
385 self._kill_ssh_connections(ifaceobj.name)
386 if self._is_dhcp_slave(ifaceobj):
387 self._down_dhcp_slave(ifaceobj, vrfname)
388
389 def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
390 ifaceobj_getfunc=None, vrf_exists=False):
391 try:
392 master_exists = True
393 if vrf_exists or self.ipcmd.link_exists(vrfname):
394 uppers = self.ipcmd.link_get_uppers(ifacename)
395 if not uppers or vrfname not in uppers:
396 self._handle_existing_connections(ifaceobj, vrfname)
397 netlink.link_set_master(ifacename, vrfname)
398 elif ifaceobj:
399 vrf_master_objs = ifaceobj_getfunc(vrfname)
400 if not vrf_master_objs:
401 # this is the case where vrf is assigned to an interface
402 # but user has not provided a vrf interface.
403 # people expect you to warn them but go ahead with the
404 # rest of the config on that interface
405 netlink.link_set_updown(ifacename, "up")
406 self.log_error('vrf master ifaceobj %s not found'
407 %vrfname)
408 return
409 if (ifupdownflags.flags.ALL or
410 ifupdownflags.flags.WITH_DEPENDS 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('max vrf count %d hit...not '
608 'creating vrf' % self.vrf_count, ifaceobj)
609 if vrf_table == 'auto':
610 vrf_table = self._get_avail_vrf_table_id()
611 if not vrf_table:
612 self.log_error('unable to get an auto table id', ifaceobj)
613 self.logger.info('%s: table id auto: selected table id %s\n'
614 %(ifaceobj.name, vrf_table))
615 else:
616 self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
617 if ifaceobj.name in self.system_reserved_rt_tables.keys():
618 self.log_error('cannot use system reserved %s table ids'
619 %(str(self.system_reserved_rt_tables.keys())),
620 ifaceobj)
621
622 if not vrf_table.isdigit():
623 self.log_error('vrf-table must be an integer or \'auto\'', ifaceobj)
624
625 # XXX: If we decide to not allow vrf id usages out of
626 # the reserved ifupdown range, then uncomment this code.
627 else:
628 if (int(vrf_table) < self.vrf_table_id_start or
629 int(vrf_table) > self.vrf_table_id_end):
630 self.log_error('vrf table id %s out of reserved range [%d,%d]'
631 %(vrf_table,
632 self.vrf_table_id_start,
633 self.vrf_table_id_end), ifaceobj)
634 try:
635 self.ipcmd.link_create(ifaceobj.name, 'vrf',
636 {'table' : '%s' %vrf_table})
637 except Exception, e:
638 self.log_error('create failed (%s)\n' % str(e), ifaceobj)
639 if vrf_table != 'auto':
640 self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
641 else:
642 if vrf_table == 'auto':
643 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
644 if not vrf_table:
645 self.log_error('unable to get vrf table id', ifaceobj)
646
647 # if the device exists, check if table id is same
648 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
649 if vrfdev_attrs:
650 running_table = vrfdev_attrs.get('table', None)
651 if vrf_table != running_table:
652 self.log_error('cannot change vrf table id,running table id'
653 ' %s is different from config id %s'
654 % (running_table, vrf_table), ifaceobj)
655 return vrf_table
656
657 def _up_vrf_helper(self, ifaceobj, vrf_table):
658 mode = ""
659 if ifupdownflags.flags.PERFMODE:
660 mode = "boot"
661 if self.vrf_helper:
662 utils.exec_command('%s create %s %s %s' %
663 (self.vrf_helper,
664 ifaceobj.name,
665 vrf_table,
666 mode))
667
668 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
669 ifaceobj_getfunc=None):
670
671 # if vrf dev is already processed return. This can happen
672 # if we the slave was configured before.
673 # see self._up_vrf_slave_without_master
674 if self._check_vrf_dev_processed_flag(ifaceobj):
675 return True
676
677 try:
678 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
679 except Exception, e:
680 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
681
682 try:
683 self._add_vrf_rules(ifaceobj.name, vrf_table)
684 self._up_vrf_helper(ifaceobj, vrf_table)
685 if add_slaves:
686 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
687 self._set_vrf_dev_processed_flag(ifaceobj)
688 netlink.link_set_updown(ifaceobj.name, "up")
689 except Exception, e:
690 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
691
692 def _kill_ssh_connections(self, ifacename):
693 try:
694 runningaddrsdict = self.ipcmd.addr_get(ifacename)
695 if not runningaddrsdict:
696 return
697 iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
698 if not iplist:
699 return
700 proc=[]
701 #Example output:
702 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
703 #users:(("sshd",pid=2528,fd=3))
704 cmdl = ['/bin/ss', '-t', '-p']
705 for line in utils.exec_commandl(cmdl).splitlines():
706 citems = line.split()
707 addr = None
708 if '%' in citems[3]:
709 addr = citems[3].split('%')[0]
710 elif ':ssh' in citems[3]:
711 addr = citems[3].split(':')[0]
712 if not addr:
713 continue
714 if addr in iplist:
715 if len(citems) == 6:
716 proc.append(citems[5].split(',')[1].split('=')[1])
717
718 if not proc:
719 return
720 pid = None
721 # outpt of '/usr/bin/pstree -Aps <pid>':
722 # 'systemd(1)---sshd(990)---sshd(16112)---sshd(16126)---bash(16127)---sudo(16756)---ifreload(16761)---pstree(16842)\n'
723 # get the above output to following format
724 # ['systemd(1)', 'sshd(990)', 'sshd(16112)', 'sshd(16126)', 'bash(16127)', 'sudo(16756)', 'ifreload(16761)', 'pstree(16850)']
725 pstree = list(reversed(utils.exec_command('/usr/bin/pstree -Aps %s' %os.getpid()).strip().split('---')))
726 for index, process in enumerate(pstree):
727 # check the parent of SSH process to make sure
728 # we don't kill SSH server or systemd process
729 if 'sshd' in process and 'sshd' in pstree[index + 1]:
730 pid = filter(lambda x: x.isdigit(), process)
731 break
732 self.logger.info("%s: killing active ssh sessions: %s"
733 %(ifacename, str(proc)))
734
735 if ifupdownflags.flags.DRYRUN:
736 return
737 for id in proc:
738 if id != pid:
739 try:
740 os.kill(int(id), signal.SIGINT)
741 except OSError as e:
742 continue
743
744 # Kill current SSH client
745 if pid in proc:
746 try:
747 forkret = os.fork()
748 except OSError, e:
749 self.logger.info("fork error : %s [%d]" % (e.strerror, e.errno))
750 if (forkret == 0): # The first child.
751 try:
752 os.setsid()
753 self.logger.info("%s: ifreload continuing in the background" %ifacename)
754 except OSError, (err_no, err_message):
755 self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
756 self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0)))
757 try:
758 self.logger.info("%s: killing our session: %s"
759 %(ifacename, str(proc)))
760 os.kill(int(pid), signal.SIGINT)
761 return
762 except OSError as e:
763 return
764 except Exception, e:
765 self.logger.info('%s: %s' %(ifacename, str(e)))
766
767 def _up(self, ifaceobj, ifaceobj_getfunc=None):
768 try:
769 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
770 if vrf_table:
771 self._iproute2_vrf_map_initialize()
772 # This is a vrf device
773 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
774 else:
775 vrf = ifaceobj.get_attr_value_first('vrf')
776 if vrf:
777 self._iproute2_vrf_map_initialize()
778 # This is a vrf slave
779 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
780 ifaceobj_getfunc)
781 else:
782 # check if we were a slave before
783 master = self.ipcmd.link_get_master(ifaceobj.name)
784 if master:
785 self._iproute2_vrf_map_initialize()
786 if self._is_vrf_dev(master):
787 self._down_vrf_slave(ifaceobj.name, ifaceobj,
788 master)
789 except Exception, e:
790 self.log_error(str(e), ifaceobj)
791
792 def _down_vrf_helper(self, ifaceobj, vrf_table):
793 mode = ""
794 if ifupdownflags.flags.PERFMODE:
795 mode = "boot"
796 if self.vrf_helper:
797 utils.exec_command('%s delete %s %s %s' %
798 (self.vrf_helper,
799 ifaceobj.name,
800 vrf_table,
801 mode))
802
803 def _close_sockets(self, ifaceobj, ifindex):
804 if not self.vrf_close_socks_on_down:
805 return
806
807 try:
808 utils.exec_command('/bin/ss -aK \"dev == %s\"'
809 %ifindex)
810 except Exception, e:
811 self.logger.info('%s: closing socks using ss'
812 ' failed (%s)\n' %(ifaceobj.name, str(e)))
813 pass
814
815 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
816
817 if not self.ipcmd.link_exists(ifaceobj.name):
818 return
819
820 if vrf_table == 'auto':
821 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
822
823 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
824 if running_slaves:
825 for s in running_slaves:
826 if ifaceobj_getfunc:
827 sobj = ifaceobj_getfunc(s)
828 try:
829 self._handle_existing_connections(sobj[0]
830 if sobj else None,
831 ifaceobj.name)
832 except Exception, e:
833 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
834 pass
835 try:
836 self.ipcmd.addr_flush(s)
837 netlink.link_set_updown(s, "down")
838 except Exception, e:
839 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
840 pass
841
842 try:
843 self._down_vrf_helper(ifaceobj, vrf_table)
844 except Exception, e:
845 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
846 pass
847
848 try:
849 self._del_vrf_rules(ifaceobj.name, vrf_table)
850 except Exception, e:
851 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
852 pass
853
854 ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
855
856 try:
857 self.ipcmd.link_delete(ifaceobj.name)
858 except Exception, e:
859 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
860 pass
861
862 self._close_sockets(ifaceobj, ifindex)
863
864 try:
865 self._iproute2_vrf_table_entry_del(vrf_table)
866 except Exception, e:
867 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
868 pass
869
870
871 def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
872 try:
873 self._handle_existing_connections(ifaceobj, vrfname)
874 netlink.link_set_nomaster(ifacename)
875 # Down this slave only if it is a slave ifupdown2 manages.
876 # we dont want to down slaves that maybe up'ed by
877 # somebody else. One such example is a macvlan device
878 # which ifupdown2 addressvirtual addon module auto creates
879 if ifaceobj:
880 netlink.link_set_updown(ifacename, "down")
881 except Exception, e:
882 self.logger.warn('%s: %s' %(ifacename, str(e)))
883
884 def _down(self, ifaceobj, ifaceobj_getfunc=None):
885 try:
886 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
887 if vrf_table:
888 self._iproute2_vrf_map_initialize()
889 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
890 else:
891 vrf = ifaceobj.get_attr_value_first('vrf')
892 if vrf:
893 self._iproute2_vrf_map_initialize()
894 self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
895 except Exception, e:
896 self.log_warn(str(e))
897
898 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
899 try:
900 master = self.ipcmd.link_get_master(ifaceobj.name)
901 if not master or master != vrf:
902 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
903 else:
904 ifaceobjcurr.update_config_with_status('vrf', master, 0)
905 except Exception, e:
906 self.log_error(str(e), ifaceobjcurr)
907
908 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
909 try:
910 if not self.ipcmd.link_exists(ifaceobj.name):
911 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
912 return
913 if vrf_table == 'auto':
914 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
915 else:
916 config_table = vrf_table
917 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
918 if not vrfdev_attrs:
919 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
920 return
921 running_table = vrfdev_attrs.get('table')
922 if not running_table:
923 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
924 return
925 if config_table != running_table:
926 ifaceobjcurr.update_config_with_status('vrf-table',
927 running_table, 1)
928 else:
929 ifaceobjcurr.update_config_with_status('vrf-table',
930 running_table, 0)
931 if not ifupdownflags.flags.WITHDEFAULTS:
932 return
933 if self.vrf_helper:
934 try:
935 utils.exec_command('%s verify %s %s'
936 %(self.vrf_helper,
937 ifaceobj.name, config_table))
938 ifaceobjcurr.update_config_with_status('vrf-helper',
939 '%s create %s %s'
940 %(self.vrf_helper,
941 ifaceobj.name,
942 config_table), 0)
943 except Exception, e:
944 ifaceobjcurr.update_config_with_status('vrf-helper',
945 '%s create %s %s'
946 %(self.vrf_helper,
947 ifaceobj.name,
948 config_table), 1)
949 pass
950 except Exception, e:
951 self.log_warn(str(e))
952
953 def _query_check(self, ifaceobj, ifaceobjcurr):
954 try:
955 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
956 if vrf_table:
957 self._iproute2_vrf_map_initialize(writetodisk=False)
958 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
959 else:
960 vrf = ifaceobj.get_attr_value_first('vrf')
961 if vrf:
962 self._iproute2_vrf_map_initialize(writetodisk=False)
963 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
964 except Exception, e:
965 self.log_warn(str(e))
966
967 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
968 try:
969 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
970 if kind == 'vrf':
971 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
972 if vrfdev_attrs:
973 running_table = vrfdev_attrs.get('table')
974 if running_table:
975 ifaceobjrunning.update_config('vrf-table',
976 running_table)
977 return
978 slave_kind = self.ipcmd.link_get_slave_kind(ifaceobjrunning.name)
979 if slave_kind == 'vrf_slave':
980 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
981 if vrf:
982 ifaceobjrunning.update_config('vrf', vrf)
983 except Exception, e:
984 self.log_warn(str(e))
985
986 def _query(self, ifaceobj, **kwargs):
987 if not self.vrf_helper:
988 return
989 if (ifaceobj.link_kind & ifaceLinkKind.VRF):
990 ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
991 ifaceobj.name))
992
993 _run_ops = {'pre-up' : _up,
994 'post-down' : _down,
995 'query-running' : _query_running,
996 'query-checkcurr' : _query_check,
997 'query' : _query}
998
999 def get_ops(self):
1000 """ returns list of ops supported by this module """
1001 return self._run_ops.keys()
1002
1003 def _init_command_handlers(self):
1004 if not self.ipcmd:
1005 self.ipcmd = iproute2()
1006 if not self.bondcmd:
1007 self.bondcmd = bondutil()
1008 if not self.dhclientcmd:
1009 self.dhclientcmd = dhclient()
1010
1011 def run(self, ifaceobj, operation, query_ifaceobj=None,
1012 ifaceobj_getfunc=None, **extra_args):
1013 """ run bond configuration on the interface object passed as argument
1014
1015 Args:
1016 **ifaceobj** (object): iface object
1017
1018 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1019 'query-running'
1020
1021 Kwargs:
1022 **query_ifaceobj** (object): query check ifaceobject. This is only
1023 valid when op is 'query-checkcurr'. It is an object same as
1024 ifaceobj, but contains running attribute values and its config
1025 status. The modules can use it to return queried running state
1026 of interfaces. status is success if the running state is same
1027 as user required state in ifaceobj. error otherwise.
1028 """
1029 op_handler = self._run_ops.get(operation)
1030 if not op_handler:
1031 return
1032 self._init_command_handlers()
1033 if operation == 'query-checkcurr':
1034 op_handler(self, ifaceobj, query_ifaceobj)
1035 else:
1036 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)