]> git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/vrf.py
Merge branch '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 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'
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 _close_sockets(self, ifaceobj, ifindex):
809 if not self.vrf_close_socks_on_down:
810 return
811
812 try:
813 utils.exec_command('/bin/ss -aK \"dev == %s\"'
814 %ifindex)
815 except Exception, e:
816 self.logger.info('%s: closing socks using ss'
817 ' failed (%s)\n' %(ifaceobj.name, str(e)))
818 pass
819
820 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
821
822 if not self.ipcmd.link_exists(ifaceobj.name):
823 return
824
825 if vrf_table == 'auto':
826 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
827
828 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
829 if running_slaves:
830 for s in running_slaves:
831 if ifaceobj_getfunc:
832 sobj = ifaceobj_getfunc(s)
833 try:
834 self._handle_existing_connections(sobj[0]
835 if sobj else None,
836 ifaceobj.name)
837 except Exception, e:
838 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
839 pass
840 try:
841 self.ipcmd.addr_flush(s)
842 netlink.link_set_updown(s, "down")
843 except Exception, e:
844 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
845 pass
846
847 try:
848 self._down_vrf_helper(ifaceobj, vrf_table)
849 except Exception, e:
850 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
851 pass
852
853 try:
854 self._del_vrf_rules(ifaceobj.name, vrf_table)
855 except Exception, e:
856 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
857 pass
858
859 ifindex = self.ipcmd.link_get_ifindex(ifaceobj.name)
860
861 try:
862 self.ipcmd.link_delete(ifaceobj.name)
863 except Exception, e:
864 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
865 pass
866
867 self._close_sockets(ifaceobj, ifindex)
868
869 try:
870 self._iproute2_vrf_table_entry_del(vrf_table)
871 except Exception, e:
872 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
873 pass
874
875
876 def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
877 try:
878 self._handle_existing_connections(ifaceobj, vrfname)
879 self.ipcmd.link_set(ifacename, 'nomaster')
880 # Down this slave only if it is a slave ifupdown2 manages.
881 # we dont want to down slaves that maybe up'ed by
882 # somebody else. One such example is a macvlan device
883 # which ifupdown2 addressvirtual addon module auto creates
884 if ifaceobj:
885 netlink.link_set_updown(ifacename, "down")
886 except Exception, e:
887 self.logger.warn('%s: %s' %(ifacename, str(e)))
888
889 def _down(self, ifaceobj, ifaceobj_getfunc=None):
890 try:
891 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
892 if vrf_table:
893 self._iproute2_vrf_map_initialize()
894 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
895 else:
896 vrf = ifaceobj.get_attr_value_first('vrf')
897 if vrf:
898 self._iproute2_vrf_map_initialize()
899 self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
900 except Exception, e:
901 self.log_warn(str(e))
902
903 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
904 try:
905 master = self.ipcmd.link_get_master(ifaceobj.name)
906 if not master or master != vrf:
907 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
908 else:
909 ifaceobjcurr.update_config_with_status('vrf', master, 0)
910 except Exception, e:
911 self.log_error(str(e), ifaceobjcurr)
912
913 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
914 try:
915 if not self.ipcmd.link_exists(ifaceobj.name):
916 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
917 return
918 if vrf_table == 'auto':
919 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
920 else:
921 config_table = vrf_table
922 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
923 if not vrfdev_attrs:
924 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
925 return
926 running_table = vrfdev_attrs.get('table')
927 if not running_table:
928 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
929 return
930 if config_table != running_table:
931 ifaceobjcurr.update_config_with_status('vrf-table',
932 running_table, 1)
933 else:
934 ifaceobjcurr.update_config_with_status('vrf-table',
935 running_table, 0)
936 if not ifupdownflags.flags.WITHDEFAULTS:
937 return
938 if self.vrf_helper:
939 try:
940 utils.exec_command('%s verify %s %s'
941 %(self.vrf_helper,
942 ifaceobj.name, config_table))
943 ifaceobjcurr.update_config_with_status('vrf-helper',
944 '%s create %s %s'
945 %(self.vrf_helper,
946 ifaceobj.name,
947 config_table), 0)
948 except Exception, e:
949 ifaceobjcurr.update_config_with_status('vrf-helper',
950 '%s create %s %s'
951 %(self.vrf_helper,
952 ifaceobj.name,
953 config_table), 1)
954 pass
955 except Exception, e:
956 self.log_warn(str(e))
957
958 def _query_check(self, ifaceobj, ifaceobjcurr):
959 try:
960 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
961 if vrf_table:
962 self._iproute2_vrf_map_initialize(writetodisk=False)
963 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
964 else:
965 vrf = ifaceobj.get_attr_value_first('vrf')
966 if vrf:
967 self._iproute2_vrf_map_initialize(writetodisk=False)
968 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
969 except Exception, e:
970 self.log_warn(str(e))
971
972 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
973 try:
974 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
975 if kind == 'vrf':
976 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
977 if vrfdev_attrs:
978 running_table = vrfdev_attrs.get('table')
979 if running_table:
980 ifaceobjrunning.update_config('vrf-table',
981 running_table)
982 elif kind == 'vrf_slave':
983 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
984 if vrf:
985 ifaceobjrunning.update_config('vrf', vrf)
986 except Exception, e:
987 self.log_warn(str(e))
988
989 def _query(self, ifaceobj, **kwargs):
990 if not self.vrf_helper:
991 return
992 if (ifaceobj.link_kind & ifaceLinkKind.VRF):
993 ifaceobj.update_config('vrf-helper', '%s %s' %(self.vrf_helper,
994 ifaceobj.name))
995
996 _run_ops = {'pre-up' : _up,
997 'post-down' : _down,
998 'query-running' : _query_running,
999 'query-checkcurr' : _query_check,
1000 'query' : _query}
1001
1002 def get_ops(self):
1003 """ returns list of ops supported by this module """
1004 return self._run_ops.keys()
1005
1006 def _init_command_handlers(self):
1007 if not self.ipcmd:
1008 self.ipcmd = iproute2()
1009 if not self.bondcmd:
1010 self.bondcmd = bondutil()
1011 if not self.dhclientcmd:
1012 self.dhclientcmd = dhclient()
1013
1014 def run(self, ifaceobj, operation, query_ifaceobj=None,
1015 ifaceobj_getfunc=None, **extra_args):
1016 """ run bond configuration on the interface object passed as argument
1017
1018 Args:
1019 **ifaceobj** (object): iface object
1020
1021 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
1022 'query-running'
1023
1024 Kwargs:
1025 **query_ifaceobj** (object): query check ifaceobject. This is only
1026 valid when op is 'query-checkcurr'. It is an object same as
1027 ifaceobj, but contains running attribute values and its config
1028 status. The modules can use it to return queried running state
1029 of interfaces. status is success if the running state is same
1030 as user required state in ifaceobj. error otherwise.
1031 """
1032 op_handler = self._run_ops.get(operation)
1033 if not op_handler:
1034 return
1035 self._init_command_handlers()
1036 if operation == 'query-checkcurr':
1037 op_handler(self, ifaceobj, query_ifaceobj)
1038 else:
1039 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)