]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/vrf.py
attribute syntax check using validvals/validrange and keywords
[mirror_ifupdown2.git] / addons / vrf.py
CommitLineData
8465de90
RP
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
5c5a7b93 8import signal
a193d8d1
JF
9import errno
10import fcntl
8465de90 11import atexit
0aa91758 12import re
8465de90 13from ifupdown.iface import *
a193d8d1 14from ifupdown.utils import utils
54616d3f 15import ifupdown.policymanager as policymanager
8465de90 16import ifupdownaddons
2864d6f3 17from ifupdown.netlink import netlink
fc5e1735 18import ifupdown.ifupdownflags as ifupdownflags
8465de90
RP
19from ifupdownaddons.modulebase import moduleBase
20from ifupdownaddons.bondutil import bondutil
21from ifupdownaddons.iproute2 import iproute2
122ef35b 22from ifupdownaddons.dhclient import dhclient
be276272 23from ifupdownaddons.utilsbase import *
8465de90 24
8ad5c767
RP
25class vrfPrivFlags:
26 PROCESSED = 0x1
27
8465de90
RP
28class vrf(moduleBase):
29 """ ifupdown2 addon module to configure vrfs """
30 _modinfo = { 'mhelp' : 'vrf configuration module',
31 'attrs' : {
32 'vrf-table':
fffec8bb
N
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\'',
482b2fab 37 'validvals': ['<auto>', '<number>'],
fffec8bb 38 'example': ['vrf-table auto', 'vrf-table 1001']},
8465de90
RP
39 'vrf':
40 {'help' : 'vrf the interface is part of.',
482b2fab 41 'validvals': ['<text>'],
8465de90
RP
42 'example': ['vrf blue']}}}
43
a1c23686 44 iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
8465de90
RP
45 iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
46 '# It contains the vrf name to table mapping.\n' + \
6f2890fc
RP
47 '# Reserved table range %s %s\n'
48 VRF_TABLE_START = 1001
49 VRF_TABLE_END = 5000
8465de90 50
f6466fcb
RP
51 system_reserved_rt_tables = {'255' : 'local', '254' : 'main',
52 '253' : 'default', '0' : 'unspec'}
53
8465de90
RP
54 def __init__(self, *args, **kargs):
55 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
56 self.ipcmd = None
57 self.bondcmd = None
122ef35b 58 self.dhclientcmd = None
8ad5c767 59 self.name = self.__class__.__name__
fc5e1735 60 if ifupdownflags.flags.PERFMODE:
122ef35b
RP
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))
8465de90 71 try:
a193d8d1 72 ip_rules = utils.exec_command('/sbin/ip rule show').splitlines()
8465de90
RP
73 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
74 except Exception, e:
75 self.ip_rule_cache = []
a193d8d1 76 self.logger.warn('vrf: cache v4: %s' % str(e))
8465de90
RP
77
78 try:
a193d8d1 79 ip_rules = utils.exec_command('/sbin/ip -6 rule show').splitlines()
8465de90
RP
80 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
81 except Exception, e:
82 self.ip6_rule_cache = []
a193d8d1 83 self.logger.warn('vrf: cache v6: %s' % str(e))
8465de90
RP
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
0aa91758
N
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
05ac52f0
RP
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
05ac52f0 115 self.vrf_mgmt_devname = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-mgmt-devname')
25e2386e 116 self.vrf_helper = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-helper')
05ac52f0 117
eb3ce8c8 118 def _iproute2_vrf_map_initialize(self, writetodisk=True):
05ac52f0
RP
119 if self._iproute2_vrf_map_initialized:
120 return
121
8465de90
RP
122 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
123 self.iproute2_vrf_map = {}
05ac52f0 124 iproute2_vrf_map_force_rewrite = False
8465de90
RP
125 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
126 if os.path.exists(self.iproute2_vrf_filename):
a193d8d1
JF
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] == '#':
05ac52f0 132 continue
a193d8d1
JF
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
8465de90 146
05ac52f0
RP
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
4934af35 155 if (not running_vrf_map or (running_vrf_map != self.iproute2_vrf_map)):
05ac52f0
RP
156 self.iproute2_vrf_map = running_vrf_map
157 iproute2_vrf_map_force_rewrite = True
158
159 self.iproute2_vrf_map_fd = None
eb3ce8c8
RP
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)
8465de90 166
05ac52f0
RP
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)
6f2890fc
RP
172
173 last_used_vrf_table = None
174 for t in range(self.vrf_table_id_start,
175 self.vrf_table_id_end):
8465de90
RP
176 if not self.iproute2_vrf_map.get(t):
177 break
6f2890fc 178 last_used_vrf_table = t
8465de90 179 self.last_used_vrf_table = last_used_vrf_table
05ac52f0 180 self._iproute2_vrf_map_initialized = True
c8a3b44e 181 self.vrf_count = len(self.iproute2_vrf_map)
09753350 182
05ac52f0 183 def _iproute2_vrf_map_sync_to_disk(self):
eb3ce8c8
RP
184 if (ifupdownflags.flags.DRYRUN or
185 not self.iproute2_vrf_map_sync_to_disk):
8465de90 186 return
05ac52f0 187 self.logger.info('vrf: syncing table map to %s'
8465de90
RP
188 %self.iproute2_vrf_filename)
189 with open(self.iproute2_vrf_filename, 'w') as f:
6f2890fc
RP
190 f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
191 self.vrf_table_id_end))
8465de90
RP
192 for t, v in self.iproute2_vrf_map.iteritems():
193 f.write('%s %s\n' %(t, v))
05ac52f0
RP
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)
eb3ce8c8
RP
199 if ifupdownflags.flags.DRYRUN:
200 return
05ac52f0
RP
201 fmode = 'a+' if append else 'w'
202 try:
203 self.iproute2_vrf_map_fd = open(self.iproute2_vrf_filename,
204 '%s' %fmode)
a193d8d1 205 fcntl.fcntl(self.iproute2_vrf_map_fd, fcntl.F_SETFD, fcntl.FD_CLOEXEC)
05ac52f0
RP
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()
8465de90
RP
219
220 def _is_vrf(self, ifaceobj):
221 if ifaceobj.get_attr_value_first('vrf-table'):
222 return True
223 return False
224
768b4ec5 225 def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
8465de90
RP
226 """ Returns list of interfaces dependent on ifaceobj """
227
768b4ec5
RP
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
f7551dcb 232 ifaceobj.role |= ifaceRole.MASTER
8465de90
RP
233 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
234 if not vrf_iface_name:
235 return None
768b4ec5 236 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
858a230f 237 ifaceobj.link_privflags |= ifaceLinkPrivFlags.VRF_SLAVE
8ad5c767 238
8465de90
RP
239 return [vrf_iface_name]
240
768b4ec5 241 def get_upper_ifacenames_running(self, ifaceobj):
8465de90
RP
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:
09753350 247 return str(t)
8465de90
RP
248 return None
249
250 def _get_avail_vrf_table_id(self):
6f2890fc
RP
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):
8465de90
RP
257 if not self.iproute2_vrf_map.get(t):
258 self.last_used_vrf_table = t
6f2890fc 259 return str(t)
8465de90
RP
260 return None
261
bf3eda91 262 def _iproute2_is_vrf_tableid_inuse(self, vrfifaceobj, table_id):
25e2386e 263 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
bf3eda91 264 if old_vrf_name and old_vrf_name != vrfifaceobj.name:
25e2386e 265 self.log_error('table id %s already assigned to vrf dev %s'
bf3eda91 266 %(table_id, old_vrf_name), vrfifaceobj)
25e2386e 267
bf3eda91 268 def _iproute2_vrf_table_entry_add(self, vrfifaceobj, table_id):
05ac52f0 269 old_vrf_name = self.iproute2_vrf_map.get(int(table_id))
25e2386e 270 if not old_vrf_name:
bf3eda91 271 self.iproute2_vrf_map[int(table_id)] = vrfifaceobj.name
05ac52f0
RP
272 if self.iproute2_vrf_map_fd:
273 self.iproute2_vrf_map_fd.write('%s %s\n'
bf3eda91 274 %(table_id, vrfifaceobj.name))
05ac52f0 275 self.iproute2_vrf_map_fd.flush()
c8a3b44e 276 self.vrf_count += 1
25e2386e
RP
277 return
278
bf3eda91 279 if old_vrf_name != vrfifaceobj.name:
25e2386e
RP
280 self.log_error('table id %d already assigned to vrf dev %s'
281 %(table_id, old_vrf_name))
8465de90
RP
282
283 def _iproute2_vrf_table_entry_del(self, table_id):
284 try:
05ac52f0
RP
285 # with any del of vrf map, we need to force sync to disk
286 self.iproute2_vrf_map_sync_to_disk = True
09753350 287 del self.iproute2_vrf_map[int(table_id)]
8465de90
RP
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
aa36221f
RP
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
122ef35b
RP
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
0ba04b38
RP
308 def _up_vrf_slave_without_master(self, ifacename, vrfname, ifaceobj,
309 ifaceobj_getfunc):
8ad5c767
RP
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 """
0ba04b38 313
8ad5c767
RP
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:
df53966d
RP
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'
bf3eda91 335 %mobj.name, ifaceobj)
df53966d
RP
336 self.logger.info('%s: table id auto: selected table id %s\n'
337 %(mobj.name, vrf_table))
25e2386e
RP
338 try:
339 self._up_vrf_dev(mobj, vrf_table, False)
340 except Exception:
341 raise
df53966d 342 break
0ba04b38 343 self._handle_existing_connections(ifaceobj, vrfname)
8ad5c767
RP
344 self.ipcmd.link_set(ifacename, 'master', vrfname)
345 return
346
717cee31 347 def _down_dhcp_slave(self, ifaceobj, vrfname):
122ef35b 348 try:
717cee31 349 dhclient_cmd_prefix = None
6369e774
RP
350 if (vrfname and self.vrf_exec_cmd_prefix and
351 self.ipcmd.link_exists(vrfname)):
717cee31
RP
352 dhclient_cmd_prefix = '%s %s' %(self.vrf_exec_cmd_prefix,
353 vrfname)
354 self.dhclientcmd.release(ifaceobj.name, dhclient_cmd_prefix)
122ef35b
RP
355 except:
356 # ignore any dhclient release errors
357 pass
358
0ba04b38 359 def _handle_existing_connections(self, ifaceobj, vrfname):
fc5e1735 360 if not ifaceobj or ifupdownflags.flags.PERFMODE:
0ba04b38
RP
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):
717cee31 366 self._down_dhcp_slave(ifaceobj, vrfname)
0ba04b38 367
8ad5c767 368 def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
09753350 369 ifaceobj_getfunc=None, vrf_exists=False):
8465de90 370 try:
d2b35716 371 master_exists = True
09753350
RP
372 if vrf_exists or self.ipcmd.link_exists(vrfname):
373 upper = self.ipcmd.link_get_upper(ifacename)
4d2c9798 374 if not upper or upper != vrfname:
0ba04b38 375 self._handle_existing_connections(ifaceobj, vrfname)
122ef35b 376 self.ipcmd.link_set(ifacename, 'master', vrfname)
d2b35716 377 elif ifupdownflags.flags.ALL and ifaceobj:
0ba04b38
RP
378 self._up_vrf_slave_without_master(ifacename, vrfname, ifaceobj,
379 ifaceobj_getfunc)
d2b35716
RP
380 else:
381 master_exists = False
382 if master_exists:
c9b929f9 383 netlink.link_set_updown(ifacename, "up")
e0a7f7a4 384 elif ifupdownflags.flags.ALL:
d2b35716 385 self.log_error('vrf %s not around, skipping vrf config'
bf3eda91 386 %(vrfname), ifaceobj)
8465de90 387 except Exception, e:
bf3eda91 388 self.log_error('%s: %s' %(ifacename, str(e)), ifaceobj)
8465de90
RP
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
4ce47ce4 395 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
8465de90 396 if rule in self.ip_rule_cache:
25e2386e
RP
397 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
398 vrf_dev_name)
a193d8d1 399 utils.exec_command(rule_cmd)
8465de90 400
4ce47ce4 401 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
8465de90 402 if rule in self.ip_rule_cache:
25e2386e
RP
403 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
404 vrf_dev_name)
a193d8d1 405 utils.exec_command(rule_cmd)
8465de90 406
4ce47ce4 407 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
e6072769 408 if rule in self.ip6_rule_cache:
8465de90 409 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
25e2386e 410 vrf_dev_name)
a193d8d1 411 utils.exec_command(rule_cmd)
8465de90 412
4ce47ce4 413 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
e6072769 414 if rule in self.ip6_rule_cache:
8465de90 415 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
25e2386e 416 vrf_dev_name)
a193d8d1 417 utils.exec_command(rule_cmd)
8465de90 418
0aa91758
N
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
8465de90
RP
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'
659097a0
N
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:
a193d8d1
JF
444 utils.exec_command('ip rule del pref 0')
445 utils.exec_command('ip rule add pref 32765 table local')
659097a0 446 except Exception, e:
a193d8d1 447 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
659097a0 448 pass
c61672da
N
449 if rule in self.ip6_rule_cache:
450 try:
a193d8d1
JF
451 utils.exec_command('ip -6 rule del pref 0')
452 utils.exec_command('ip -6 rule add pref 32765 table local')
c61672da 453 except Exception, e:
a193d8d1 454 self.logger.info('%s: %s' % (vrf_dev_name, str(e)))
c61672da 455 pass
8465de90 456
0aa91758
N
457 if not self.l3mdev_checked:
458 self._rule_cache_fill()
459 self.l3mdev_checked = True
3fcb15fe
N
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)
0aa91758 465 if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
25e2386e
RP
466 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name,
467 vrf_dev_name)
a193d8d1 468 utils.exec_command(rule_cmd)
8465de90 469
3fcb15fe 470 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
0aa91758 471 if not self.l3mdev4_rule and rule not in self.ip_rule_cache:
25e2386e
RP
472 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name,
473 vrf_dev_name)
a193d8d1 474 utils.exec_command(rule_cmd)
8465de90 475
3fcb15fe 476 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
0aa91758 477 if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
25e2386e
RP
478 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
479 vrf_dev_name)
a193d8d1 480 utils.exec_command(rule_cmd)
8465de90 481
3fcb15fe 482 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
0aa91758 483 if not self.l3mdev6_rule and rule not in self.ip6_rule_cache:
8465de90 484 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
25e2386e 485 vrf_dev_name)
a193d8d1 486 utils.exec_command(rule_cmd)
8465de90 487
1b284018
RP
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
4d2c9798 505 def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
768b4ec5
RP
506 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
507 config_slaves = ifaceobj.lowerifaces
b94e4d24 508 if not config_slaves and not running_slaves:
867e11a2 509 return
4d2c9798
RP
510
511 if not config_slaves: config_slaves = []
512 if not running_slaves: running_slaves = []
768b4ec5
RP
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:
d2b35716
RP
518 if not self.ipcmd.link_exists(s):
519 continue
09753350
RP
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)
768b4ec5
RP
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:
1b284018
RP
532 if self._is_address_virtual_slaves(ifaceobj,
533 config_slaves, s):
534 continue
aa36221f 535 sobj = None
4d2c9798
RP
536 if ifaceobj_getfunc:
537 sobj = ifaceobj_getfunc(s)
0ba04b38
RP
538 self._down_vrf_slave(s, sobj[0] if sobj else None,
539 ifaceobj.name)
768b4ec5
RP
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:
2864d6f3 546 netlink.link_set_updown(s, "up")
768b4ec5 547 except Exception, e:
2864d6f3 548 self.logger.debug('%s: %s' % (ifaceobj.name, str(e)))
768b4ec5 549 pass
8465de90 550
8ad5c767
RP
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):
25e2386e 557 if (ifaceobj.module_flags.get(self.name, 0) & vrfPrivFlags.PROCESSED):
8ad5c767
RP
558 return True
559 return False
8465de90 560
8ad5c767 561 def _create_vrf_dev(self, ifaceobj, vrf_table):
2df6a60f 562 if not self.ipcmd.link_exists(ifaceobj.name):
f6466fcb
RP
563 if ifaceobj.name in self.system_reserved_rt_tables.values():
564 self.log_error('cannot use system reserved %s vrf names'
bf3eda91
RP
565 %(str(self.system_reserved_rt_tables.values())),
566 ifaceobj)
c8a3b44e
RP
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,
bf3eda91 570 self.vrf_count), ifaceobj)
6f2890fc
RP
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'
bf3eda91 575 %ifaceobj.name, ifaceobj)
6f2890fc
RP
576 self.logger.info('%s: table id auto: selected table id %s\n'
577 %(ifaceobj.name, vrf_table))
25e2386e 578 else:
bf3eda91 579 self._iproute2_is_vrf_tableid_inuse(ifaceobj, vrf_table)
f6466fcb
RP
580 if ifaceobj.name in self.system_reserved_rt_tables.keys():
581 self.log_error('cannot use system reserved %s table ids'
bf3eda91
RP
582 %(str(self.system_reserved_rt_tables.keys())),
583 ifaceobj)
d54baa22
RP
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
6f2890fc
RP
589 # XXX: If we decide to not allow vrf id usages out of
590 # the reserved ifupdown range, then uncomment this code.
fd8c6caf
RP
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,
bf3eda91 597 self.vrf_table_id_end), ifaceobj)
2df6a60f 598 try:
8465de90
RP
599 self.ipcmd.link_create(ifaceobj.name, 'vrf',
600 {'table' : '%s' %vrf_table})
2df6a60f
RP
601 except Exception, e:
602 self.log_error('%s: create failed (%s)\n'
bf3eda91 603 %(ifaceobj.name, str(e)), ifaceobj)
25e2386e 604 if vrf_table != 'auto':
bf3eda91 605 self._iproute2_vrf_table_entry_add(ifaceobj, vrf_table)
2df6a60f 606 else:
4d2c9798
RP
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'
bf3eda91 611 %ifaceobj.name, ifaceobj)
6f2890fc 612
2df6a60f
RP
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:
6f2890fc 618 self.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj.name,
bf3eda91
RP
619 running_table, vrf_table),
620 ifaceobj)
4d2c9798
RP
621 return vrf_table
622
25e2386e 623 def _up_vrf_helper(self, ifaceobj, vrf_table):
c4e05f9f
RP
624 mode = ""
625 if ifupdownflags.flags.PERFMODE:
626 mode = "boot"
25e2386e 627 if self.vrf_helper:
a193d8d1
JF
628 utils.exec_command('%s create %s %s %s' %
629 (self.vrf_helper,
630 ifaceobj.name,
631 vrf_table,
632 mode))
25e2386e 633
4d2c9798
RP
634 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
635 ifaceobj_getfunc=None):
8ad5c767
RP
636
637 # if vrf dev is already processed return. This can happen
0ba04b38
RP
638 # if we the slave was configured before.
639 # see self._up_vrf_slave_without_master
8ad5c767
RP
640 if self._check_vrf_dev_processed_flag(ifaceobj):
641 return True
642
25e2386e
RP
643 try:
644 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
645 except Exception, e:
bf3eda91 646 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
25e2386e 647
8ad5c767 648 try:
8ad5c767 649 self._add_vrf_rules(ifaceobj.name, vrf_table)
6369e774 650 self._up_vrf_helper(ifaceobj, vrf_table)
8ad5c767 651 if add_slaves:
4d2c9798 652 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
8ad5c767 653 self._set_vrf_dev_processed_flag(ifaceobj)
2864d6f3 654 netlink.link_set_updown(ifaceobj.name, "up")
8ad5c767 655 except Exception, e:
bf3eda91 656 self.log_error('%s: %s' %(ifaceobj.name, str(e)), ifaceobj)
8ad5c767 657
0ba04b38 658 def _kill_ssh_connections(self, ifacename):
5c5a7b93 659 try:
0ba04b38
RP
660 runningaddrsdict = self.ipcmd.addr_get(ifacename)
661 if not runningaddrsdict:
5c5a7b93 662 return
0ba04b38
RP
663 iplist = [i.split('/', 1)[0] for i in runningaddrsdict.keys()]
664 if not iplist:
665 return
666 proc=[]
5c5a7b93
N
667 #Example output:
668 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
669 #users:(("sshd",pid=2528,fd=3))
0ba04b38 670 cmdl = ['/bin/ss', '-t', '-p']
a193d8d1 671 for line in utils.exec_commandl(cmdl).splitlines():
5c5a7b93
N
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
0ba04b38 680 if addr in iplist:
5c5a7b93
N
681 if len(citems) == 6:
682 proc.append(citems[5].split(',')[1].split('=')[1])
683
684 if not proc:
685 return
be276272
N
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
aa36221f
RP
698 self.logger.info("%s: killing active ssh sessions: %s"
699 %(ifacename, str(proc)))
cbdde74e
RP
700
701 if ifupdownflags.flags.DRYRUN:
702 return
5c5a7b93
N
703 for id in proc:
704 if id != pid:
705 try:
aa36221f 706 os.kill(int(id), signal.SIGINT)
5c5a7b93
N
707 except OSError as e:
708 continue
0ba04b38
RP
709
710 # Kill current SSH client
5c5a7b93 711 if pid in proc:
32f6e6ca 712 try:
0ba04b38
RP
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)))
5c5a7b93 723 try:
aa36221f
RP
724 self.logger.info("%s: killing our session: %s"
725 %(ifacename, str(proc)))
726 os.kill(int(pid), signal.SIGINT)
5c5a7b93
N
727 return
728 except OSError as e:
729 return
730 except Exception, e:
731 self.logger.info('%s: %s' %(ifacename, str(e)))
732
8ad5c767 733 def _up(self, ifaceobj, ifaceobj_getfunc=None):
8465de90
RP
734 try:
735 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
736 if vrf_table:
05ac52f0 737 self._iproute2_vrf_map_initialize()
09753350 738 # This is a vrf device
4d2c9798 739 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
8465de90
RP
740 else:
741 vrf = ifaceobj.get_attr_value_first('vrf')
742 if vrf:
05ac52f0 743 self._iproute2_vrf_map_initialize()
09753350 744 # This is a vrf slave
8ad5c767
RP
745 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
746 ifaceobj_getfunc)
aa36221f
RP
747 else:
748 # check if we were a slave before
749 master = self.ipcmd.link_get_master(ifaceobj.name)
750 if master:
717cee31 751 self._iproute2_vrf_map_initialize()
aa36221f 752 if self._is_vrf_dev(master):
0ba04b38
RP
753 self._down_vrf_slave(ifaceobj.name, ifaceobj,
754 master)
8465de90 755 except Exception, e:
bf3eda91 756 self.log_error(str(e), ifaceobj)
8465de90 757
25e2386e 758 def _down_vrf_helper(self, ifaceobj, vrf_table):
c4e05f9f
RP
759 mode = ""
760 if ifupdownflags.flags.PERFMODE:
761 mode = "boot"
25e2386e 762 if self.vrf_helper:
a193d8d1
JF
763 utils.exec_command('%s delete %s %s %s' %
764 (self.vrf_helper,
765 ifaceobj.name,
766 vrf_table,
767 mode))
25e2386e 768
c4be5481 769 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
5c5a7b93 770
8465de90
RP
771 if vrf_table == 'auto':
772 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
aa36221f 773
2365b3c9
RP
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,
0ba04b38 782 ifaceobj.name)
2365b3c9
RP
783 except Exception, e:
784 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
785 pass
786 try:
787 self.ipcmd.addr_flush(s)
2864d6f3 788 netlink.link_set_updown(s, "down")
2365b3c9
RP
789 except Exception, e:
790 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
791 pass
768b4ec5 792
40804f1a
RP
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
25e2386e 798
768b4ec5 799 try:
f1c92482 800 self._del_vrf_rules(ifaceobj.name, vrf_table)
768b4ec5
RP
801 except Exception, e:
802 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
803 pass
804
805 try:
f1c92482
RP
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)
8465de90 813 except Exception, e:
768b4ec5
RP
814 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
815 pass
8465de90 816
f1c92482 817
0ba04b38 818 def _down_vrf_slave(self, ifacename, ifaceobj=None, vrfname=None):
8465de90 819 try:
0ba04b38 820 self._handle_existing_connections(ifaceobj, vrfname)
768b4ec5 821 self.ipcmd.link_set(ifacename, 'nomaster')
2864d6f3 822 netlink.link_set_updown(ifacename, "down")
8465de90 823 except Exception, e:
768b4ec5 824 self.logger.warn('%s: %s' %(ifacename, str(e)))
8465de90 825
8ad5c767 826 def _down(self, ifaceobj, ifaceobj_getfunc=None):
8465de90
RP
827 try:
828 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
829 if vrf_table:
05ac52f0 830 self._iproute2_vrf_map_initialize()
c4be5481 831 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
8465de90
RP
832 else:
833 vrf = ifaceobj.get_attr_value_first('vrf')
834 if vrf:
05ac52f0 835 self._iproute2_vrf_map_initialize()
0ba04b38 836 self._down_vrf_slave(ifaceobj.name, ifaceobj, None)
8465de90
RP
837 except Exception, e:
838 self.log_warn(str(e))
839
840 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
841 try:
54616d3f 842 master = self.ipcmd.link_get_master(ifaceobj.name)
8465de90 843 if not master or master != vrf:
586535e8 844 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
8465de90
RP
845 else:
846 ifaceobjcurr.update_config_with_status('vrf', master, 0)
847 except Exception, e:
bf3eda91 848 self.log_error(str(e), ifaceobjcurr)
8465de90 849
6e16e5ae 850 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
8465de90
RP
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)
6e16e5ae 873 if not ifupdownflags.flags.WITHDEFAULTS:
669b422a
RP
874 return
875 if self.vrf_helper:
6e16e5ae 876 try:
a193d8d1 877 utils.exec_command('%s verify %s %s'
6e16e5ae
N
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
8465de90
RP
892 except Exception, e:
893 self.log_warn(str(e))
894
6e16e5ae 895 def _query_check(self, ifaceobj, ifaceobjcurr):
8465de90
RP
896 try:
897 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
898 if vrf_table:
eb3ce8c8 899 self._iproute2_vrf_map_initialize(writetodisk=False)
6e16e5ae 900 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
8465de90
RP
901 else:
902 vrf = ifaceobj.get_attr_value_first('vrf')
903 if vrf:
eb3ce8c8 904 self._iproute2_vrf_map_initialize(writetodisk=False)
8465de90
RP
905 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
906 except Exception, e:
907 self.log_warn(str(e))
908
8ad5c767 909 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
8465de90
RP
910 try:
911 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
912 if kind == 'vrf':
54616d3f 913 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
8465de90
RP
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':
54616d3f 920 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
8465de90
RP
921 if vrf:
922 ifaceobjrunning.update_config('vrf', vrf)
923 except Exception, e:
924 self.log_warn(str(e))
925
baa909c6
N
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
8465de90
RP
933 _run_ops = {'pre-up' : _up,
934 'post-down' : _down,
935 'query-running' : _query_running,
baa909c6
N
936 'query-checkcurr' : _query_check,
937 'query' : _query}
8465de90
RP
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):
8465de90 944 if not self.ipcmd:
fc5e1735 945 self.ipcmd = iproute2()
8465de90 946 if not self.bondcmd:
fc5e1735 947 self.bondcmd = bondutil()
122ef35b 948 if not self.dhclientcmd:
fc5e1735 949 self.dhclientcmd = dhclient()
8465de90 950
8ad5c767
RP
951 def run(self, ifaceobj, operation, query_ifaceobj=None,
952 ifaceobj_getfunc=None, **extra_args):
8465de90
RP
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':
6e16e5ae 974 op_handler(self, ifaceobj, query_ifaceobj)
8465de90 975 else:
8ad5c767 976 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)