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