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