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