]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/vrf.py
ifupdownmain: don't down vrf master in sched callback ops
[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
32f6e6ca 9import errno
5c5a7b93 10import subprocess
8465de90
RP
11import atexit
12from ifupdown.iface import *
54616d3f 13import ifupdown.policymanager as policymanager
8465de90 14import ifupdownaddons
768b4ec5 15import ifupdown.rtnetlink_api as rtnetlink_api
8465de90
RP
16from ifupdownaddons.modulebase import moduleBase
17from ifupdownaddons.bondutil import bondutil
18from ifupdownaddons.iproute2 import iproute2
122ef35b 19from ifupdownaddons.dhclient import dhclient
8465de90 20
8ad5c767
RP
21class vrfPrivFlags:
22 PROCESSED = 0x1
23
8465de90
RP
24class vrf(moduleBase):
25 """ ifupdown2 addon module to configure vrfs """
26 _modinfo = { 'mhelp' : 'vrf configuration module',
27 'attrs' : {
28 'vrf-table':
29 {'help' : 'vrf device table id. key to ' +
30 'creating a vrf device',
31 'example': ['vrf-table-id 1']},
54616d3f
N
32 'vrf-default-route':
33 {'help' : 'vrf device default route ' +
34 'to avoid communication outside the vrf device',
35 'example': ['vrf-default-route yes/no']},
8465de90
RP
36 'vrf':
37 {'help' : 'vrf the interface is part of.',
38 'example': ['vrf blue']}}}
39
a1c23686 40 iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
8465de90
RP
41 iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
42 '# It contains the vrf name to table mapping.\n' + \
6f2890fc
RP
43 '# Reserved table range %s %s\n'
44 VRF_TABLE_START = 1001
45 VRF_TABLE_END = 5000
8465de90
RP
46
47 def __init__(self, *args, **kargs):
48 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
49 self.ipcmd = None
50 self.bondcmd = None
122ef35b 51 self.dhclientcmd = None
8ad5c767 52 self.name = self.__class__.__name__
122ef35b
RP
53 if self.PERFMODE:
54 # if perf mode is set, remove vrf map file.
55 # start afresh. PERFMODE is set at boot
56 if os.path.exists(self.iproute2_vrf_filename):
57 try:
58 self.logger.info('vrf: removing file %s'
59 %self.iproute2_vrf_filename)
60 os.remove(self.iproute2_vrf_filename)
61 except Exception, e:
62 self.logger.debug('vrf: removing file failed (%s)'
63 %str(e))
8465de90
RP
64 try:
65 ip_rules = self.exec_command('/sbin/ip rule show').splitlines()
66 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
67 except Exception, e:
68 self.ip_rule_cache = []
69 self.logger.warn('%s' %str(e))
70
71 try:
72 ip_rules = self.exec_command('/sbin/ip -6 rule show').splitlines()
73 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
74 except Exception, e:
75 self.ip6_rule_cache = []
76 self.logger.warn('%s' %str(e))
77
78 #self.logger.debug("vrf: ip rule cache")
79 #self.logger.info(self.ip_rule_cache)
80
81 #self.logger.info("vrf: ip -6 rule cache")
82 #self.logger.info(self.ip6_rule_cache)
83
84 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
85 self.iproute2_vrf_map = {}
86 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
87 if os.path.exists(self.iproute2_vrf_filename):
88 self.vrf_map_fd = open(self.iproute2_vrf_filename, 'a+')
89 lines = self.vrf_map_fd.readlines()
90 for l in lines:
91 l = l.strip()
92 if l[0] == '#':
93 continue
94 try:
95 (table, vrf_name) = l.strip().split()
96 self.iproute2_vrf_map[table] = vrf_name
97 except Exception, e:
98 self.logger.info('vrf: iproute2_vrf_map: unable to parse %s'
99 %l)
100 pass
101 #self.logger.info("vrf: dumping iproute2_vrf_map")
102 #self.logger.info(self.iproute2_vrf_map)
103
104 # purge vrf table entries that are not around
105 iproute2_vrf_map_pruned = {}
106 for t, v in self.iproute2_vrf_map.iteritems():
107 if os.path.exists('/sys/class/net/%s' %v):
09753350 108 iproute2_vrf_map_pruned[int(t)] = v
8465de90
RP
109 else:
110 try:
111 # cleanup rules
112 self._del_vrf_rules(v, t)
113 except Exception:
114 pass
115 self.iproute2_vrf_map = iproute2_vrf_map_pruned
116
6f2890fc
RP
117 self.vrf_table_id_start = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-start')
118 if not self.vrf_table_id_start:
119 self.vrf_table_id_start = self.VRF_TABLE_START
120 self.vrf_table_id_end = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-table-id-end')
121 if not self.vrf_table_id_end:
122 self.vrf_table_id_end = self.VRF_TABLE_END
4a95c92f 123 self.vrf_max_count = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-max-count')
6f2890fc
RP
124
125 last_used_vrf_table = None
126 for t in range(self.vrf_table_id_start,
127 self.vrf_table_id_end):
8465de90
RP
128 if not self.iproute2_vrf_map.get(t):
129 break
6f2890fc 130 last_used_vrf_table = t
8465de90 131 self.last_used_vrf_table = last_used_vrf_table
09753350 132
8465de90
RP
133 self.iproute2_write_vrf_map = False
134 atexit.register(self.iproute2_vrf_map_write)
3fcb15fe 135 self.vrf_fix_local_table = True
6f2890fc 136 self.vrf_count = 0
83841a51 137 self.vrf_cgroup_create = policymanager.policymanager_api.get_module_globals(module_name=self.__class__.__name__, attr='vrf-cgroup-create')
6f2890fc
RP
138 if not self.vrf_cgroup_create:
139 self.vrf_cgroup_create = False
140 elif self.vrf_cgroup_create == 'yes':
141 self.vrf_cgroup_create = True
142 else:
143 self.vrf_cgroup_create = False
8465de90
RP
144
145 def iproute2_vrf_map_write(self):
146 if not self.iproute2_write_vrf_map:
147 return
148 self.logger.info('vrf: writing table map to %s'
149 %self.iproute2_vrf_filename)
150 with open(self.iproute2_vrf_filename, 'w') as f:
6f2890fc
RP
151 f.write(self.iproute2_vrf_filehdr %(self.vrf_table_id_start,
152 self.vrf_table_id_end))
8465de90
RP
153 for t, v in self.iproute2_vrf_map.iteritems():
154 f.write('%s %s\n' %(t, v))
155
156 def _is_vrf(self, ifaceobj):
157 if ifaceobj.get_attr_value_first('vrf-table'):
158 return True
159 return False
160
768b4ec5 161 def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
8465de90
RP
162 """ Returns list of interfaces dependent on ifaceobj """
163
768b4ec5
RP
164 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
165 if vrf_table:
166 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
167 ifaceobj.link_kind |= ifaceLinkKind.VRF
8465de90
RP
168 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
169 if not vrf_iface_name:
170 return None
768b4ec5 171 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
8ad5c767
RP
172 ifaceobj.link_kind |= ifaceLinkKind.VRF_SLAVE
173
8465de90
RP
174 return [vrf_iface_name]
175
768b4ec5 176 def get_upper_ifacenames_running(self, ifaceobj):
8465de90
RP
177 return None
178
179 def _get_iproute2_vrf_table(self, vrf_dev_name):
180 for t, v in self.iproute2_vrf_map.iteritems():
181 if v == vrf_dev_name:
09753350 182 return str(t)
8465de90
RP
183 return None
184
185 def _get_avail_vrf_table_id(self):
6f2890fc
RP
186 if self.last_used_vrf_table == None:
187 table_id_start = self.vrf_table_id_start
188 else:
189 table_id_start = self.last_used_vrf_table + 1
190 for t in range(table_id_start,
191 self.vrf_table_id_end):
8465de90
RP
192 if not self.iproute2_vrf_map.get(t):
193 self.last_used_vrf_table = t
6f2890fc 194 return str(t)
8465de90
RP
195 return None
196
197 def _iproute2_vrf_table_entry_add(self, vrf_dev_name, table_id):
09753350 198 self.iproute2_vrf_map[int(table_id)] = vrf_dev_name
8465de90
RP
199 self.iproute2_write_vrf_map = True
200
201 def _iproute2_vrf_table_entry_del(self, table_id):
202 try:
09753350 203 del self.iproute2_vrf_map[int(table_id)]
8465de90
RP
204 self.iproute2_write_vrf_map = True
205 except Exception, e:
206 self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)'
207 %(table_id, str(e)))
208 pass
209
aa36221f
RP
210 def _is_vrf_dev(self, ifacename):
211 # Look at iproute2 map for now.
212 # If it was a master we knew about,
213 # it is definately there
214 if ifacename in self.iproute2_vrf_map.values():
215 return True
216 return False
217
122ef35b
RP
218 def _is_dhcp_slave(self, ifaceobj):
219 if (not ifaceobj.addr_method or
220 (ifaceobj.addr_method != 'dhcp' and
221 ifaceobj.addr_method != 'dhcp6')):
222 return False
223 return True
224
8ad5c767
RP
225 def _handle_dhcp_slaves(self, ifacename, vrfname, ifaceobj,
226 ifaceobj_getfunc):
227 """ If we have a vrf slave that has dhcp configured, bring up the
228 vrf master now. This is needed because vrf has special handling
229 in dhclient hook which requires the vrf master to be present """
122ef35b
RP
230 if not self._is_dhcp_slave(ifaceobj):
231 return False
8ad5c767
RP
232 vrf_master = ifaceobj.upperifaces[0]
233 if not vrf_master:
234 self.logger.warn('%s: vrf master not found' %ifacename)
235 return
236 if os.path.exists('/sys/class/net/%s' %vrf_master):
237 self.logger.info('%s: vrf master %s exists returning'
238 %(ifacename, vrf_master))
239 return
240 vrf_master_objs = ifaceobj_getfunc(vrf_master)
241 if not vrf_master_objs:
242 self.logger.warn('%s: vrf master ifaceobj not found' %ifacename)
243 return
244 self.logger.info('%s: bringing up vrf master %s'
245 %(ifacename, vrf_master))
246 for mobj in vrf_master_objs:
247 vrf_table = mobj.get_attr_value_first('vrf-table')
248 if vrf_table:
df53966d
RP
249 if vrf_table == 'auto':
250 vrf_table = self._get_avail_vrf_table_id()
251 if not vrf_table:
252 self.log_error('%s: unable to get an auto table id'
253 %mobj.name)
254 self.logger.info('%s: table id auto: selected table id %s\n'
255 %(mobj.name, vrf_table))
8ad5c767 256 self._up_vrf_dev(mobj, vrf_table, False)
df53966d 257 break
5c5a7b93
N
258 if vrfname == 'mgmt':
259 self._kill_ssh(ifaceobj.name)
122ef35b 260 self._down_dhcp_slave(ifaceobj)
8ad5c767
RP
261 self.ipcmd.link_set(ifacename, 'master', vrfname)
262 return
263
122ef35b
RP
264 def _down_dhcp_slave(self, ifaceobj):
265 try:
266 self.dhclientcmd.release(ifaceobj.name)
267 except:
268 # ignore any dhclient release errors
269 pass
270
8ad5c767 271 def _up_vrf_slave(self, ifacename, vrfname, ifaceobj=None,
09753350 272 ifaceobj_getfunc=None, vrf_exists=False):
8465de90 273 try:
09753350
RP
274 if vrf_exists or self.ipcmd.link_exists(vrfname):
275 upper = self.ipcmd.link_get_upper(ifacename)
4d2c9798 276 if not upper or upper != vrfname:
5c5a7b93
N
277 if ifaceobj and vrfname == 'mgmt':
278 self._kill_ssh(ifaceobj.name)
09753350 279 if ifaceobj and self._is_dhcp_slave(ifaceobj):
122ef35b
RP
280 self._down_dhcp_slave(ifaceobj)
281 self.ipcmd.link_set(ifacename, 'master', vrfname)
8ad5c767
RP
282 elif ifaceobj:
283 self._handle_dhcp_slaves(ifacename, vrfname, ifaceobj,
284 ifaceobj_getfunc)
8465de90 285 except Exception, e:
d54baa22 286 self.log_error('%s: %s' %(ifacename, str(e)))
8465de90
RP
287
288 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
289 pref = 200
290 ip_rule_out_format = '%s: from all %s %s lookup %s'
291 ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
292
4ce47ce4 293 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
8465de90
RP
294 if rule in self.ip_rule_cache:
295 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
296 self.exec_command(rule_cmd)
297
4ce47ce4 298 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
8465de90
RP
299 if rule in self.ip_rule_cache:
300 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
301 self.exec_command(rule_cmd)
302
4ce47ce4 303 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
e6072769 304 if rule in self.ip6_rule_cache:
8465de90
RP
305 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
306 vrf_table)
307 self.exec_command(rule_cmd)
308
4ce47ce4 309 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
e6072769 310 if rule in self.ip6_rule_cache:
8465de90
RP
311 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
312 vrf_table)
313 self.exec_command(rule_cmd)
314
315 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
316 pref = 200
317 ip_rule_out_format = '%s: from all %s %s lookup %s'
318 ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
659097a0
N
319 if self.vrf_fix_local_table:
320 self.vrf_fix_local_table = False
321 rule = '0: from all lookup local'
322 if rule in self.ip_rule_cache:
323 try:
3fcb15fe
N
324 self.exec_command('ip rule del pref 0')
325 self.exec_command('ip rule add pref 32765 table local')
659097a0
N
326 except Exception, e:
327 self.logger.info('%s' %str(e))
328 pass
c61672da
N
329 if rule in self.ip6_rule_cache:
330 try:
331 self.exec_command('ip -6 rule del pref 0')
332 self.exec_command('ip -6 rule add pref 32765 table local')
333 except Exception, e:
334 self.logger.info('%s' %str(e))
335 pass
8465de90 336
3fcb15fe
N
337 #Example ip rule
338 #200: from all oif blue lookup blue
339 #200: from all iif blue lookup blue
340
341 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
8465de90
RP
342 if rule not in self.ip_rule_cache:
343 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
344 self.exec_command(rule_cmd)
345
3fcb15fe 346 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
8465de90
RP
347 if rule not in self.ip_rule_cache:
348 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
349 self.exec_command(rule_cmd)
350
3fcb15fe 351 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
e6072769 352 if rule not in self.ip6_rule_cache:
3fcb15fe 353 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name, vrf_table)
8465de90
RP
354 self.exec_command(rule_cmd)
355
3fcb15fe 356 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
e6072769 357 if rule not in self.ip6_rule_cache:
8465de90
RP
358 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
359 vrf_table)
360 self.exec_command(rule_cmd)
361
4d2c9798 362 def _add_vrf_slaves(self, ifaceobj, ifaceobj_getfunc=None):
768b4ec5
RP
363 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
364 config_slaves = ifaceobj.lowerifaces
b94e4d24 365 if not config_slaves and not running_slaves:
867e11a2 366 return
4d2c9798
RP
367
368 if not config_slaves: config_slaves = []
369 if not running_slaves: running_slaves = []
768b4ec5
RP
370 add_slaves = set(config_slaves).difference(set(running_slaves))
371 del_slaves = set(running_slaves).difference(set(config_slaves))
372 if add_slaves:
373 for s in add_slaves:
374 try:
09753350
RP
375 sobj = None
376 if ifaceobj_getfunc:
377 sobj = ifaceobj_getfunc(s)
378 self._up_vrf_slave(s, ifaceobj.name,
379 sobj[0] if sobj else None,
380 ifaceobj_getfunc, True)
768b4ec5
RP
381 except Exception, e:
382 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
383
384 if del_slaves:
385 for s in del_slaves:
386 try:
aa36221f 387 sobj = None
4d2c9798
RP
388 if ifaceobj_getfunc:
389 sobj = ifaceobj_getfunc(s)
390 # if dhcp slave, release the dhcp lease
5c5a7b93
N
391 if sobj and ifaceobj.name == 'mgmt':
392 self._kill_ssh(sobj[0].name)
aa36221f 393 self._down_vrf_slave(s, sobj[0] if sobj else None)
768b4ec5
RP
394 except Exception, e:
395 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
396
397 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
398 for s in config_slaves:
399 try:
400 rtnetlink_api.rtnl_api.link_set(s, "up")
401 except Exception, e:
402 self.logger.debug('%s: %s: link set up (%s)'
403 %(ifaceobj.name, s, str(e)))
404 pass
8465de90 405
2df6a60f 406 def _create_cgroup(self, ifaceobj):
6f2890fc
RP
407 if not self.vrf_cgroup_create:
408 return
2df6a60f 409 try:
d1e1c43b 410 if not os.path.exists('/sys/fs/cgroup/l3mdev/%s' %ifaceobj.name):
40103cf7 411 self.exec_command('/usr/bin/cgcreate -g l3mdev:%s' %ifaceobj.name)
8ad5c767
RP
412 except Exception, e:
413 self.log_error('%s: cgroup create failed (%s)\n'
414 %(ifaceobj.name, str(e)), ifaceobj)
415 try:
40103cf7 416 self.exec_command('/usr/bin/cgset -r l3mdev.master-device=%s %s'
2df6a60f
RP
417 %(ifaceobj.name, ifaceobj.name))
418 except Exception, e:
8ad5c767 419 self.log_warn('%s: cgset failed (%s)\n'
2df6a60f
RP
420 %(ifaceobj.name, str(e)), ifaceobj)
421
8ad5c767
RP
422 def _set_vrf_dev_processed_flag(self, ifaceobj):
423 ifaceobj.module_flags[self.name] = \
424 ifaceobj.module_flags.setdefault(self.name, 0) | \
425 vrfPrivFlags.PROCESSED
426
427 def _check_vrf_dev_processed_flag(self, ifaceobj):
428 if (ifaceobj.module_flags.get(self.name, 0x0) & vrfPrivFlags.PROCESSED):
429 return True
430 return False
8465de90 431
8ad5c767 432 def _create_vrf_dev(self, ifaceobj, vrf_table):
2df6a60f 433 if not self.ipcmd.link_exists(ifaceobj.name):
6f2890fc
RP
434 if vrf_table == 'auto':
435 vrf_table = self._get_avail_vrf_table_id()
436 if not vrf_table:
437 self.log_error('%s: unable to get an auto table id'
438 %ifaceobj.name)
439 self.logger.info('%s: table id auto: selected table id %s\n'
440 %(ifaceobj.name, vrf_table))
d54baa22
RP
441
442 if not vrf_table.isdigit():
443 self.log_error('%s: vrf-table must be an integer or \'auto\''
444 %(ifaceobj.name), ifaceobj)
445
6f2890fc
RP
446 # XXX: If we decide to not allow vrf id usages out of
447 # the reserved ifupdown range, then uncomment this code.
fd8c6caf
RP
448 else:
449 if (int(vrf_table) < self.vrf_table_id_start or
450 int(vrf_table) > self.vrf_table_id_end):
451 self.log_error('%s: vrf table id %s out of reserved range [%d,%d]'
452 %(ifaceobj.name, vrf_table,
453 self.vrf_table_id_start,
454 self.vrf_table_id_end))
2df6a60f 455 try:
8465de90
RP
456 self.ipcmd.link_create(ifaceobj.name, 'vrf',
457 {'table' : '%s' %vrf_table})
2df6a60f
RP
458 except Exception, e:
459 self.log_error('%s: create failed (%s)\n'
460 %(ifaceobj.name, str(e)))
461 else:
4d2c9798
RP
462 if vrf_table == 'auto':
463 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
464 if not vrf_table:
465 self.log_error('%s: unable to get vrf table id'
466 %ifaceobj.name)
6f2890fc 467
2df6a60f
RP
468 # if the device exists, check if table id is same
469 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
470 if vrfdev_attrs:
471 running_table = vrfdev_attrs.get('table', None)
472 if vrf_table != running_table:
6f2890fc
RP
473 self.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj.name,
474 running_table, vrf_table))
d54baa22
RP
475 if vrf_table != 'auto':
476 self._iproute2_vrf_table_entry_add(ifaceobj.name, vrf_table)
2df6a60f 477
4d2c9798
RP
478 return vrf_table
479
8ad5c767 480 def _add_vrf_default_route(self, ifaceobj, vrf_table):
54616d3f
N
481 vrf_default_route = ifaceobj.get_attr_value_first('vrf-default-route')
482 if not vrf_default_route:
483 vrf_default_route = policymanager.policymanager_api.get_attr_default(
8ad5c767
RP
484 module_name=self.__class__.__name__,
485 attr='vrf-default-route')
54616d3f
N
486 if not vrf_default_route:
487 return
488 if str(vrf_default_route).lower() == "yes":
489 try:
8ad5c767 490 self.exec_command('ip route add table %s unreachable default'
05ca6f01 491 ' metric %d' %(vrf_table, 240))
54616d3f
N
492 except OSError, e:
493 if e.errno != 17:
494 raise
495 pass
496
0ba9abeb 497 try:
05ca6f01
RP
498 self.exec_command('ip -6 route add table %s unreachable '
499 'default metric %d' %(vrf_table, 240))
0ba9abeb
N
500 except OSError, e:
501 if e.errno != 17:
502 raise
503 pass
504
4d2c9798
RP
505 def _up_vrf_dev(self, ifaceobj, vrf_table, add_slaves=True,
506 ifaceobj_getfunc=None):
8ad5c767
RP
507
508 # if vrf dev is already processed return. This can happen
509 # if we had a dhcp slave. See self._handle_dhcp_slaves
510 if self._check_vrf_dev_processed_flag(ifaceobj):
511 return True
512
4d2c9798 513 vrf_table = self._create_vrf_dev(ifaceobj, vrf_table)
8ad5c767 514 try:
8ad5c767
RP
515 self._add_vrf_rules(ifaceobj.name, vrf_table)
516 self._create_cgroup(ifaceobj)
517 if add_slaves:
4d2c9798 518 self._add_vrf_slaves(ifaceobj, ifaceobj_getfunc)
8ad5c767
RP
519 self._add_vrf_default_route(ifaceobj, vrf_table)
520 self._set_vrf_dev_processed_flag(ifaceobj)
521 except Exception, e:
522 self.log_error('%s: %s' %(ifaceobj.name, str(e)))
523
5c5a7b93
N
524 def _kill_ssh(self, ifacename):
525 # Fix this in the next version
526 # runningaddrsdict = self.ipcmd.addr_get(ifacename)
527
60fa9203
RP
528 # XXX: Roopa: it is currently killing ifupdown2.
529 # so, until we fix it, lets not do anything
530 return
531
5c5a7b93
N
532 try:
533 ip=[]
534 ip6=[]
535 proc=[]
536 #Example output:
537 #2: eth0 inet 10.0.1.84/22 brd 10.0.3.255 scope global eth0\
538 #valid_lft forever preferred_lft forever
539 for line in self.ipcmd.addr_show(ifacename=ifacename).splitlines():
540 citems = line.split()
541 if any(word in citems for word in ['inet','inet6']):
542 if 'inet' in citems:
543 ip.append(citems[citems.index('inet')+1].split('/')[0])
544 else:
545 ip6.append(citems[citems.index('inet6')+1].split('/')[0])
aa36221f 546
5c5a7b93
N
547 if not ip and not ip6:
548 return
549
550 #Example output:
551 #ESTAB 0 0 10.0.1.84:ssh 10.0.1.228:45186
552 #users:(("sshd",pid=2528,fd=3))
553 cmdl = ['ss', '-t', '-p']
554 for line in subprocess.check_output(cmdl, stderr=subprocess.STDOUT,
555 shell=False).splitlines():
556 citems = line.split()
557 addr = None
558 if '%' in citems[3]:
559 addr = citems[3].split('%')[0]
560 elif ':ssh' in citems[3]:
561 addr = citems[3].split(':')[0]
562 if not addr:
563 continue
564 if (addr in ip) or (addr in ip6):
565 if len(citems) == 6:
566 proc.append(citems[5].split(',')[1].split('=')[1])
567
568 if not proc:
569 return
570 pid = subprocess.check_output(['ps', '--no-headers',
571 '-fp', str(os.getppid())],
572 stderr=subprocess.STDOUT,
573 shell=False).split()[2]
aa36221f
RP
574 self.logger.info("%s: killing active ssh sessions: %s"
575 %(ifacename, str(proc)))
5c5a7b93
N
576 for id in proc:
577 if id != pid:
578 try:
aa36221f 579 os.kill(int(id), signal.SIGINT)
5c5a7b93
N
580 except OSError as e:
581 continue
582 if pid in proc:
32f6e6ca
N
583 try:
584 os.setsid()
585 except OSError, (err_no, err_message):
586 self.logger.info("os.setsid failed: errno=%d: %s" % (err_no, err_message))
587 self.logger.info("pid=%d pgid=%d" % (os.getpid(), os.getpgid(0)))
5c5a7b93 588 try:
aa36221f
RP
589 self.logger.info("%s: killing our session: %s"
590 %(ifacename, str(proc)))
591 os.kill(int(pid), signal.SIGINT)
5c5a7b93
N
592 return
593 except OSError as e:
594 return
595 except Exception, e:
596 self.logger.info('%s: %s' %(ifacename, str(e)))
597
8ad5c767 598 def _up(self, ifaceobj, ifaceobj_getfunc=None):
8465de90
RP
599 try:
600 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
601 if vrf_table:
09753350 602 # This is a vrf device
6f2890fc
RP
603 if self.vrf_count == self.vrf_max_count:
604 self.log_error('%s: max vrf count %d hit...not '
605 'creating vrf' %(ifaceobj.name,
606 self.vrf_count))
4d2c9798 607 self._up_vrf_dev(ifaceobj, vrf_table, True, ifaceobj_getfunc)
8465de90
RP
608 else:
609 vrf = ifaceobj.get_attr_value_first('vrf')
610 if vrf:
09753350 611 # This is a vrf slave
8ad5c767
RP
612 self._up_vrf_slave(ifaceobj.name, vrf, ifaceobj,
613 ifaceobj_getfunc)
aa36221f
RP
614 else:
615 # check if we were a slave before
616 master = self.ipcmd.link_get_master(ifaceobj.name)
617 if master:
618 if self._is_vrf_dev(master):
619 self._down_vrf_slave(ifaceobj.name, ifaceobj)
8465de90
RP
620 except Exception, e:
621 self.log_error(str(e))
622
2df6a60f
RP
623 def _delete_cgroup(self, ifaceobj):
624 try:
d1e1c43b 625 if os.path.exists('/sys/fs/cgroup/l3mdev/%s' %ifaceobj.name):
3f1811d9 626 self.exec_command('/usr/bin/cgdelete -g l3mdev:%s' %ifaceobj.name)
2df6a60f 627 except Exception, e:
3f1811d9 628 self.log_info('%s: cgroup delete failed (%s)\n'
2df6a60f
RP
629 %(ifaceobj.name, str(e)), ifaceobj)
630
c4be5481 631 def _down_vrf_dev(self, ifaceobj, vrf_table, ifaceobj_getfunc=None):
5c5a7b93 632
8465de90
RP
633 if vrf_table == 'auto':
634 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
aa36221f
RP
635
636 try:
637 self.exec_command('/usr/cumulus/bin/cl-vrf service disable %s' %ifaceobj.name)
638 except Exception, e:
639 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
640 pass
641
8465de90 642 try:
c4be5481
RP
643 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
644 if running_slaves:
645 for s in running_slaves:
646 if ifaceobj_getfunc:
647 sobj = ifaceobj_getfunc(s)
5c5a7b93
N
648 if sobj and self.ipcmd.link_get_master(sobj[0].name) == 'mgmt':
649 self._kill_ssh(sobj[0].name)
c4be5481
RP
650 # if dhcp slave, release the dhcp lease
651 if sobj and self._is_dhcp_slave(sobj[0]):
652 self._down_dhcp_slave(sobj[0])
768b4ec5
RP
653 except Exception, e:
654 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
655 pass
656
657 try:
f1c92482 658 self._del_vrf_rules(ifaceobj.name, vrf_table)
768b4ec5
RP
659 except Exception, e:
660 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
661 pass
662
663 try:
f1c92482
RP
664 self.ipcmd.link_delete(ifaceobj.name)
665 except Exception, e:
666 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
667 pass
668
669 try:
670 self._iproute2_vrf_table_entry_del(vrf_table)
671 self._delete_cgroup(ifaceobj)
8465de90 672 except Exception, e:
768b4ec5
RP
673 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
674 pass
8465de90 675
f1c92482 676
aa36221f 677 def _down_vrf_slave(self, ifacename, ifaceobj=None):
8465de90 678 try:
aa36221f
RP
679 if ifaceobj and self._is_dhcp_slave(ifaceobj):
680 self._down_dhcp_slave(ifaceobj)
768b4ec5 681 self.ipcmd.link_set(ifacename, 'nomaster')
f825610e 682 rtnetlink_api.rtnl_api.link_set(ifacename, "down")
8465de90 683 except Exception, e:
768b4ec5 684 self.logger.warn('%s: %s' %(ifacename, str(e)))
8465de90 685
8ad5c767 686 def _down(self, ifaceobj, ifaceobj_getfunc=None):
8465de90
RP
687 try:
688 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
689 if vrf_table:
c4be5481 690 self._down_vrf_dev(ifaceobj, vrf_table, ifaceobj_getfunc)
8465de90
RP
691 else:
692 vrf = ifaceobj.get_attr_value_first('vrf')
693 if vrf:
aa36221f 694 self._down_vrf_slave(ifaceobj.name, ifaceobj)
8465de90
RP
695 except Exception, e:
696 self.log_warn(str(e))
697
698 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
699 try:
54616d3f 700 master = self.ipcmd.link_get_master(ifaceobj.name)
8465de90 701 if not master or master != vrf:
934c4c49 702 ifaceobjcurr.update_config_with_status('vrf', str(master), 1)
8465de90
RP
703 else:
704 ifaceobjcurr.update_config_with_status('vrf', master, 0)
705 except Exception, e:
706 self.log_warn(str(e))
707
708 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
709 try:
710 if not self.ipcmd.link_exists(ifaceobj.name):
711 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
712 return
713 if vrf_table == 'auto':
714 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
715 else:
716 config_table = vrf_table
717 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
718 if not vrfdev_attrs:
719 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
720 return
721 running_table = vrfdev_attrs.get('table')
722 if not running_table:
723 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
724 return
725 if config_table != running_table:
726 ifaceobjcurr.update_config_with_status('vrf-table',
727 running_table, 1)
728 else:
729 ifaceobjcurr.update_config_with_status('vrf-table',
730 running_table, 0)
731 except Exception, e:
732 self.log_warn(str(e))
733
734 def _query_check(self, ifaceobj, ifaceobjcurr):
735 try:
736 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
737 if vrf_table:
738 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
739 else:
740 vrf = ifaceobj.get_attr_value_first('vrf')
741 if vrf:
742 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
743 except Exception, e:
744 self.log_warn(str(e))
745
8ad5c767 746 def _query_running(self, ifaceobjrunning, ifaceobj_getfunc=None):
8465de90
RP
747 try:
748 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
749 if kind == 'vrf':
54616d3f 750 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
8465de90
RP
751 if vrfdev_attrs:
752 running_table = vrfdev_attrs.get('table')
753 if running_table:
754 ifaceobjrunning.update_config('vrf-table',
755 running_table)
756 elif kind == 'vrf_slave':
54616d3f 757 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
8465de90
RP
758 if vrf:
759 ifaceobjrunning.update_config('vrf', vrf)
760 except Exception, e:
761 self.log_warn(str(e))
762
763 _run_ops = {'pre-up' : _up,
764 'post-down' : _down,
765 'query-running' : _query_running,
766 'query-checkcurr' : _query_check}
767
768 def get_ops(self):
769 """ returns list of ops supported by this module """
770 return self._run_ops.keys()
771
772 def _init_command_handlers(self):
773 flags = self.get_flags()
774 if not self.ipcmd:
775 self.ipcmd = iproute2(**flags)
776 if not self.bondcmd:
777 self.bondcmd = bondutil(**flags)
122ef35b
RP
778 if not self.dhclientcmd:
779 self.dhclientcmd = dhclient(**flags)
8465de90 780
8ad5c767
RP
781 def run(self, ifaceobj, operation, query_ifaceobj=None,
782 ifaceobj_getfunc=None, **extra_args):
8465de90
RP
783 """ run bond configuration on the interface object passed as argument
784
785 Args:
786 **ifaceobj** (object): iface object
787
788 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
789 'query-running'
790
791 Kwargs:
792 **query_ifaceobj** (object): query check ifaceobject. This is only
793 valid when op is 'query-checkcurr'. It is an object same as
794 ifaceobj, but contains running attribute values and its config
795 status. The modules can use it to return queried running state
796 of interfaces. status is success if the running state is same
797 as user required state in ifaceobj. error otherwise.
798 """
799 op_handler = self._run_ops.get(operation)
800 if not op_handler:
801 return
802 self._init_command_handlers()
803 if operation == 'query-checkcurr':
804 op_handler(self, ifaceobj, query_ifaceobj)
805 else:
8ad5c767 806 op_handler(self, ifaceobj, ifaceobj_getfunc=ifaceobj_getfunc)