]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/vrf.py
vrf: adding default route to ipv6 table
[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
8import atexit
9from ifupdown.iface import *
54616d3f 10import ifupdown.policymanager as policymanager
8465de90 11import ifupdownaddons
768b4ec5 12import ifupdown.rtnetlink_api as rtnetlink_api
8465de90
RP
13from ifupdownaddons.modulebase import moduleBase
14from ifupdownaddons.bondutil import bondutil
15from ifupdownaddons.iproute2 import iproute2
16
17class vrf(moduleBase):
18 """ ifupdown2 addon module to configure vrfs """
19 _modinfo = { 'mhelp' : 'vrf configuration module',
20 'attrs' : {
21 'vrf-table':
22 {'help' : 'vrf device table id. key to ' +
23 'creating a vrf device',
24 'example': ['vrf-table-id 1']},
54616d3f
N
25 'vrf-default-route':
26 {'help' : 'vrf device default route ' +
27 'to avoid communication outside the vrf device',
28 'example': ['vrf-default-route yes/no']},
8465de90
RP
29 'vrf':
30 {'help' : 'vrf the interface is part of.',
31 'example': ['vrf blue']}}}
32
a1c23686 33 iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
8465de90
RP
34 iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
35 '# It contains the vrf name to table mapping.\n' + \
36 '# Reserved table range 150-200\n'
37 vrf_table_reserved_start = 150
38 vrf_table_reserved_end = 200
39
40 def __init__(self, *args, **kargs):
41 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
42 self.ipcmd = None
43 self.bondcmd = None
44 try:
45 ip_rules = self.exec_command('/sbin/ip rule show').splitlines()
46 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
47 except Exception, e:
48 self.ip_rule_cache = []
49 self.logger.warn('%s' %str(e))
50
51 try:
52 ip_rules = self.exec_command('/sbin/ip -6 rule show').splitlines()
53 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
54 except Exception, e:
55 self.ip6_rule_cache = []
56 self.logger.warn('%s' %str(e))
57
58 #self.logger.debug("vrf: ip rule cache")
59 #self.logger.info(self.ip_rule_cache)
60
61 #self.logger.info("vrf: ip -6 rule cache")
62 #self.logger.info(self.ip6_rule_cache)
63
64 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
65 self.iproute2_vrf_map = {}
66 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
67 if os.path.exists(self.iproute2_vrf_filename):
68 self.vrf_map_fd = open(self.iproute2_vrf_filename, 'a+')
69 lines = self.vrf_map_fd.readlines()
70 for l in lines:
71 l = l.strip()
72 if l[0] == '#':
73 continue
74 try:
75 (table, vrf_name) = l.strip().split()
76 self.iproute2_vrf_map[table] = vrf_name
77 except Exception, e:
78 self.logger.info('vrf: iproute2_vrf_map: unable to parse %s'
79 %l)
80 pass
81 #self.logger.info("vrf: dumping iproute2_vrf_map")
82 #self.logger.info(self.iproute2_vrf_map)
83
84 # purge vrf table entries that are not around
85 iproute2_vrf_map_pruned = {}
86 for t, v in self.iproute2_vrf_map.iteritems():
87 if os.path.exists('/sys/class/net/%s' %v):
88 iproute2_vrf_map_pruned[t] = v
89 else:
90 try:
91 # cleanup rules
92 self._del_vrf_rules(v, t)
93 except Exception:
94 pass
95 self.iproute2_vrf_map = iproute2_vrf_map_pruned
96
97 last_used_vrf_table = self.vrf_table_reserved_start
98 for t in range(self.vrf_table_reserved_start,
99 self.vrf_table_reserved_end):
100 last_used_vrf_table = t
101 if not self.iproute2_vrf_map.get(t):
102 break
103 self.last_used_vrf_table = last_used_vrf_table
104 self.iproute2_write_vrf_map = False
105 atexit.register(self.iproute2_vrf_map_write)
3fcb15fe 106 self.vrf_fix_local_table = True
8465de90
RP
107
108 def iproute2_vrf_map_write(self):
109 if not self.iproute2_write_vrf_map:
110 return
111 self.logger.info('vrf: writing table map to %s'
112 %self.iproute2_vrf_filename)
113 with open(self.iproute2_vrf_filename, 'w') as f:
114 f.write(self.iproute2_vrf_filehdr)
115 for t, v in self.iproute2_vrf_map.iteritems():
116 f.write('%s %s\n' %(t, v))
117
118 def _is_vrf(self, ifaceobj):
119 if ifaceobj.get_attr_value_first('vrf-table'):
120 return True
121 return False
122
768b4ec5 123 def get_upper_ifacenames(self, ifaceobj, ifacenames_all=None):
8465de90
RP
124 """ Returns list of interfaces dependent on ifaceobj """
125
768b4ec5
RP
126 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
127 if vrf_table:
128 ifaceobj.link_type = ifaceLinkType.LINK_MASTER
129 ifaceobj.link_kind |= ifaceLinkKind.VRF
8465de90
RP
130 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
131 if not vrf_iface_name:
132 return None
768b4ec5 133 ifaceobj.link_type = ifaceLinkType.LINK_SLAVE
8465de90
RP
134 return [vrf_iface_name]
135
768b4ec5 136 def get_upper_ifacenames_running(self, ifaceobj):
8465de90
RP
137 return None
138
139 def _get_iproute2_vrf_table(self, vrf_dev_name):
140 for t, v in self.iproute2_vrf_map.iteritems():
141 if v == vrf_dev_name:
142 return t
143 return None
144
145 def _get_avail_vrf_table_id(self):
146 for t in range(self.last_used_vrf_table + 1,
147 self.vrf_table_reserved_end):
148 if not self.iproute2_vrf_map.get(t):
149 self.last_used_vrf_table = t
150 return t
151 return None
152
153 def _iproute2_vrf_table_entry_add(self, vrf_dev_name, table_id):
154 self.iproute2_vrf_map[table_id] = vrf_dev_name
155 self.iproute2_write_vrf_map = True
156
157 def _iproute2_vrf_table_entry_del(self, table_id):
158 try:
159 del self.iproute2_vrf_map[table_id]
160 self.iproute2_write_vrf_map = True
161 except Exception, e:
162 self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)'
163 %(table_id, str(e)))
164 pass
165
768b4ec5 166 def _up_vrf_slave(self, ifacename, vrfname):
8465de90 167 try:
768b4ec5
RP
168 if self.ipcmd.link_exists(vrfname):
169 self.ipcmd.link_set(ifacename, 'master', vrfname)
8465de90 170 except Exception, e:
768b4ec5 171 self.logger.warn('%s: %s' %(ifacename, str(e)))
8465de90
RP
172
173 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
174 pref = 200
175 ip_rule_out_format = '%s: from all %s %s lookup %s'
176 ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
177
178 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
179 if rule in self.ip_rule_cache:
180 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
181 self.exec_command(rule_cmd)
182
183 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
184 if rule in self.ip_rule_cache:
185 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
186 self.exec_command(rule_cmd)
187
188 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
189 if rule in self.ip_rule_cache:
190 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
191 vrf_table)
192 self.exec_command(rule_cmd)
193
194 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
195 if rule in self.ip_rule_cache:
196 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
197 vrf_table)
198 self.exec_command(rule_cmd)
199
200 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
201 pref = 200
202 ip_rule_out_format = '%s: from all %s %s lookup %s'
203 ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
659097a0
N
204 if self.vrf_fix_local_table:
205 self.vrf_fix_local_table = False
206 rule = '0: from all lookup local'
207 if rule in self.ip_rule_cache:
208 try:
3fcb15fe
N
209 self.exec_command('ip rule del pref 0')
210 self.exec_command('ip rule add pref 32765 table local')
659097a0
N
211 except Exception, e:
212 self.logger.info('%s' %str(e))
213 pass
8465de90 214
3fcb15fe
N
215 #Example ip rule
216 #200: from all oif blue lookup blue
217 #200: from all iif blue lookup blue
218
219 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
8465de90
RP
220 if rule not in self.ip_rule_cache:
221 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
222 self.exec_command(rule_cmd)
223
3fcb15fe 224 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
8465de90
RP
225 if rule not in self.ip_rule_cache:
226 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
227 self.exec_command(rule_cmd)
228
3fcb15fe 229 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
8465de90 230 if rule not in self.ip_rule_cache:
3fcb15fe 231 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name, vrf_table)
8465de90
RP
232 self.exec_command(rule_cmd)
233
3fcb15fe 234 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
8465de90
RP
235 if rule not in self.ip_rule_cache:
236 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
237 vrf_table)
238 self.exec_command(rule_cmd)
239
768b4ec5
RP
240 def _add_vrf_slaves(self, ifaceobj):
241 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
242 config_slaves = ifaceobj.lowerifaces
b94e4d24 243 if not config_slaves and not running_slaves:
867e11a2 244 return
768b4ec5
RP
245 add_slaves = set(config_slaves).difference(set(running_slaves))
246 del_slaves = set(running_slaves).difference(set(config_slaves))
247 if add_slaves:
248 for s in add_slaves:
249 try:
250 self._up_vrf_slave(s, ifaceobj.name)
251 except Exception, e:
252 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
253
254 if del_slaves:
255 for s in del_slaves:
256 try:
257 self._down_vrf_slave(s, ifaceobj.name)
258 except Exception, e:
259 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
260
261 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
262 for s in config_slaves:
263 try:
264 rtnetlink_api.rtnl_api.link_set(s, "up")
265 except Exception, e:
266 self.logger.debug('%s: %s: link set up (%s)'
267 %(ifaceobj.name, s, str(e)))
268 pass
8465de90 269
2df6a60f
RP
270 def _create_cgroup(self, ifaceobj):
271 try:
d1e1c43b
RP
272 if not os.path.exists('/sys/fs/cgroup/l3mdev/%s' %ifaceobj.name):
273 self.exec_command('cgcreate -g l3mdev:%s' %ifaceobj.name)
2df6a60f
RP
274 self.exec_command('cgset -r l3mdev.master-device=%s %s'
275 %(ifaceobj.name, ifaceobj.name))
276 except Exception, e:
277 self.log_warn('%s: cgroup create failed (%s)\n'
278 %(ifaceobj.name, str(e)), ifaceobj)
279
8465de90
RP
280 def _up_vrf_dev(self, ifaceobj, vrf_table):
281 if vrf_table == 'auto':
282 vrf_table = _get_avail_vrf_table_id(ifaceobj.name)
283
2df6a60f
RP
284 if not self.ipcmd.link_exists(ifaceobj.name):
285 try:
8465de90
RP
286 self.ipcmd.link_create(ifaceobj.name, 'vrf',
287 {'table' : '%s' %vrf_table})
2df6a60f
RP
288 except Exception, e:
289 self.log_error('%s: create failed (%s)\n'
290 %(ifaceobj.name, str(e)))
291 else:
292 # if the device exists, check if table id is same
293 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
294 if vrfdev_attrs:
295 running_table = vrfdev_attrs.get('table', None)
296 if vrf_table != running_table:
297 self.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(running_table, vrf_table))
298
299 try:
8465de90
RP
300 self._iproute2_vrf_table_entry_add(ifaceobj.name, vrf_table)
301 self._add_vrf_rules(ifaceobj.name, vrf_table)
768b4ec5 302 self._add_vrf_slaves(ifaceobj)
2df6a60f 303 self._create_cgroup(ifaceobj)
8465de90 304 except Exception, e:
2df6a60f 305 self.log_error('%s: %s' %(ifaceobj.name, str(e)))
54616d3f
N
306
307 def _up_vrf_default_route(self, ifaceobj, vrf_table):
308 vrf_default_route = ifaceobj.get_attr_value_first('vrf-default-route')
309 if not vrf_default_route:
310 vrf_default_route = policymanager.policymanager_api.get_attr_default(
311 module_name=self.__class__.__name__, attr='vrf-default-route')
312 if not vrf_default_route:
313 return
314 if str(vrf_default_route).lower() == "yes":
315 try:
316 self.exec_command('ip route add table %s unreachable default' %vrf_table)
317 except OSError, e:
318 if e.errno != 17:
319 raise
320 pass
321
0ba9abeb
N
322 try:
323 self.exec_command('ip -6 route add table %s unreachable default' %vrf_table)
324 except OSError, e:
325 if e.errno != 17:
326 raise
327 pass
328
8465de90
RP
329 def _up(self, ifaceobj):
330 try:
331 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
332 if vrf_table:
333 self._up_vrf_dev(ifaceobj, vrf_table)
54616d3f 334 self._up_vrf_default_route(ifaceobj, vrf_table)
8465de90
RP
335 else:
336 vrf = ifaceobj.get_attr_value_first('vrf')
337 if vrf:
768b4ec5 338 self._up_vrf_slave(ifaceobj.name, vrf)
8465de90
RP
339 except Exception, e:
340 self.log_error(str(e))
341
2df6a60f
RP
342 def _delete_cgroup(self, ifaceobj):
343 try:
d1e1c43b
RP
344 if os.path.exists('/sys/fs/cgroup/l3mdev/%s' %ifaceobj.name):
345 self.exec_command('cgdelete -g l3mdev:%s' %ifaceobj.name)
2df6a60f 346 except Exception, e:
d1e1c43b 347 self.log_warn('%s: cgroup delete failed (%s)\n'
2df6a60f
RP
348 %(ifaceobj.name, str(e)), ifaceobj)
349
8465de90
RP
350 def _down_vrf_dev(self, ifaceobj, vrf_table):
351 if vrf_table == 'auto':
352 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
353 try:
354 self.ipcmd.link_delete(ifaceobj.name)
768b4ec5
RP
355 except Exception, e:
356 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
357 pass
358
359 try:
8465de90 360 self._iproute2_vrf_table_entry_del(vrf_table)
2df6a60f 361 self._delete_cgroup(ifaceobj)
768b4ec5
RP
362 except Exception, e:
363 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
364 pass
365
366 try:
8465de90
RP
367 self._del_vrf_rules(ifaceobj.name, vrf_table)
368 except Exception, e:
768b4ec5
RP
369 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
370 pass
8465de90 371
768b4ec5 372 def _down_vrf_slave(self, ifacename, vrf):
8465de90 373 try:
768b4ec5 374 self.ipcmd.link_set(ifacename, 'nomaster')
8465de90 375 except Exception, e:
768b4ec5 376 self.logger.warn('%s: %s' %(ifacename, str(e)))
8465de90
RP
377
378 def _down(self, ifaceobj):
379 try:
380 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
381 if vrf_table:
382 self._down_vrf_dev(ifaceobj, vrf_table)
383 else:
384 vrf = ifaceobj.get_attr_value_first('vrf')
385 if vrf:
768b4ec5 386 self._down_vrf_slave(ifaceobj.name, vrf)
8465de90
RP
387 except Exception, e:
388 self.log_warn(str(e))
389
390 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
391 try:
54616d3f 392 master = self.ipcmd.link_get_master(ifaceobj.name)
8465de90
RP
393 if not master or master != vrf:
394 ifaceobjcurr.update_config_with_status('vrf', master, 1)
395 else:
396 ifaceobjcurr.update_config_with_status('vrf', master, 0)
397 except Exception, e:
398 self.log_warn(str(e))
399
400 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
401 try:
402 if not self.ipcmd.link_exists(ifaceobj.name):
403 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
404 return
405 if vrf_table == 'auto':
406 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
407 else:
408 config_table = vrf_table
409 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
410 if not vrfdev_attrs:
411 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
412 return
413 running_table = vrfdev_attrs.get('table')
414 if not running_table:
415 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
416 return
417 if config_table != running_table:
418 ifaceobjcurr.update_config_with_status('vrf-table',
419 running_table, 1)
420 else:
421 ifaceobjcurr.update_config_with_status('vrf-table',
422 running_table, 0)
423 except Exception, e:
424 self.log_warn(str(e))
425
426 def _query_check(self, ifaceobj, ifaceobjcurr):
427 try:
428 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
429 if vrf_table:
430 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
431 else:
432 vrf = ifaceobj.get_attr_value_first('vrf')
433 if vrf:
434 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
435 except Exception, e:
436 self.log_warn(str(e))
437
438 def _query_running(self, ifaceobjrunning):
439 try:
440 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
441 if kind == 'vrf':
54616d3f 442 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
8465de90
RP
443 if vrfdev_attrs:
444 running_table = vrfdev_attrs.get('table')
445 if running_table:
446 ifaceobjrunning.update_config('vrf-table',
447 running_table)
448 elif kind == 'vrf_slave':
54616d3f 449 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
8465de90
RP
450 if vrf:
451 ifaceobjrunning.update_config('vrf', vrf)
452 except Exception, e:
453 self.log_warn(str(e))
454
455 _run_ops = {'pre-up' : _up,
456 'post-down' : _down,
457 'query-running' : _query_running,
458 'query-checkcurr' : _query_check}
459
460 def get_ops(self):
461 """ returns list of ops supported by this module """
462 return self._run_ops.keys()
463
464 def _init_command_handlers(self):
465 flags = self.get_flags()
466 if not self.ipcmd:
467 self.ipcmd = iproute2(**flags)
468 if not self.bondcmd:
469 self.bondcmd = bondutil(**flags)
470
471 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
472 """ run bond configuration on the interface object passed as argument
473
474 Args:
475 **ifaceobj** (object): iface object
476
477 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
478 'query-running'
479
480 Kwargs:
481 **query_ifaceobj** (object): query check ifaceobject. This is only
482 valid when op is 'query-checkcurr'. It is an object same as
483 ifaceobj, but contains running attribute values and its config
484 status. The modules can use it to return queried running state
485 of interfaces. status is success if the running state is same
486 as user required state in ifaceobj. error otherwise.
487 """
488 op_handler = self._run_ops.get(operation)
489 if not op_handler:
490 return
491 self._init_command_handlers()
492 if operation == 'query-checkcurr':
493 op_handler(self, ifaceobj, query_ifaceobj)
494 else:
495 op_handler(self, ifaceobj)