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