]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/vrf.py
ifupdownaddons: fix parsing of vlan attributes
[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'
3fcb15fe
N
204 try:
205 if self.vrf_fix_local_table:
206 self.vrf_fix_local_table = False
207 rule = '0: from all lookup local'
208 if rule in self.ip_rule_cache:
209 self.exec_command('ip rule del pref 0')
210 self.exec_command('ip rule add pref 32765 table local')
211 except Exception, e:
212 self.logger.info('%s' %str(e))
8465de90 213
3fcb15fe
N
214 #Example ip rule
215 #200: from all oif blue lookup blue
216 #200: from all iif blue lookup blue
217
218 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
8465de90
RP
219 if rule not in self.ip_rule_cache:
220 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
221 self.exec_command(rule_cmd)
222
3fcb15fe 223 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
8465de90
RP
224 if rule not in self.ip_rule_cache:
225 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
226 self.exec_command(rule_cmd)
227
3fcb15fe 228 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_dev_name)
8465de90 229 if rule not in self.ip_rule_cache:
3fcb15fe 230 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name, vrf_table)
8465de90
RP
231 self.exec_command(rule_cmd)
232
3fcb15fe 233 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_dev_name)
8465de90
RP
234 if rule not in self.ip_rule_cache:
235 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
236 vrf_table)
237 self.exec_command(rule_cmd)
238
768b4ec5
RP
239 def _add_vrf_slaves(self, ifaceobj):
240 running_slaves = self.ipcmd.link_get_lowers(ifaceobj.name)
241 config_slaves = ifaceobj.lowerifaces
242
243 add_slaves = set(config_slaves).difference(set(running_slaves))
244 del_slaves = set(running_slaves).difference(set(config_slaves))
245 if add_slaves:
246 for s in add_slaves:
247 try:
248 self._up_vrf_slave(s, ifaceobj.name)
249 except Exception, e:
250 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
251
252 if del_slaves:
253 for s in del_slaves:
254 try:
255 self._down_vrf_slave(s, ifaceobj.name)
256 except Exception, e:
257 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
258
259 if ifaceobj.link_type == ifaceLinkType.LINK_MASTER:
260 for s in config_slaves:
261 try:
262 rtnetlink_api.rtnl_api.link_set(s, "up")
263 except Exception, e:
264 self.logger.debug('%s: %s: link set up (%s)'
265 %(ifaceobj.name, s, str(e)))
266 pass
8465de90
RP
267
268 def _up_vrf_dev(self, ifaceobj, vrf_table):
269 if vrf_table == 'auto':
270 vrf_table = _get_avail_vrf_table_id(ifaceobj.name)
271
272 try:
273 if not self.ipcmd.link_exists(ifaceobj.name):
274 self.ipcmd.link_create(ifaceobj.name, 'vrf',
275 {'table' : '%s' %vrf_table})
d1d103e1
N
276 else:
277 # if the device exists, check if table id is same
278 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
279 if vrfdev_attrs:
280 running_table = vrfdev_attrs.get('table', None)
281 if vrf_table != running_table:
282 self.log_error("cannot change vrf table id,"
283 " running table id %s is different from config id %s)"
284 %(running_table, vrf_table))
8465de90
RP
285 self._iproute2_vrf_table_entry_add(ifaceobj.name, vrf_table)
286 self._add_vrf_rules(ifaceobj.name, vrf_table)
768b4ec5 287 self._add_vrf_slaves(ifaceobj)
8465de90
RP
288 except Exception, e:
289 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
290
54616d3f
N
291
292 def _up_vrf_default_route(self, ifaceobj, vrf_table):
293 vrf_default_route = ifaceobj.get_attr_value_first('vrf-default-route')
294 if not vrf_default_route:
295 vrf_default_route = policymanager.policymanager_api.get_attr_default(
296 module_name=self.__class__.__name__, attr='vrf-default-route')
297 if not vrf_default_route:
298 return
299 if str(vrf_default_route).lower() == "yes":
300 try:
301 self.exec_command('ip route add table %s unreachable default' %vrf_table)
302 except OSError, e:
303 if e.errno != 17:
304 raise
305 pass
306
8465de90
RP
307 def _up(self, ifaceobj):
308 try:
309 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
310 if vrf_table:
311 self._up_vrf_dev(ifaceobj, vrf_table)
54616d3f 312 self._up_vrf_default_route(ifaceobj, vrf_table)
8465de90
RP
313 else:
314 vrf = ifaceobj.get_attr_value_first('vrf')
315 if vrf:
768b4ec5 316 self._up_vrf_slave(ifaceobj.name, vrf)
8465de90
RP
317 except Exception, e:
318 self.log_error(str(e))
319
320 def _down_vrf_dev(self, ifaceobj, vrf_table):
321 if vrf_table == 'auto':
322 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
323 try:
324 self.ipcmd.link_delete(ifaceobj.name)
768b4ec5
RP
325 except Exception, e:
326 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
327 pass
328
329 try:
8465de90 330 self._iproute2_vrf_table_entry_del(vrf_table)
768b4ec5
RP
331 except Exception, e:
332 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
333 pass
334
335 try:
8465de90
RP
336 self._del_vrf_rules(ifaceobj.name, vrf_table)
337 except Exception, e:
768b4ec5
RP
338 self.logger.info('%s: %s' %(ifaceobj.name, str(e)))
339 pass
8465de90 340
768b4ec5 341 def _down_vrf_slave(self, ifacename, vrf):
8465de90 342 try:
768b4ec5 343 self.ipcmd.link_set(ifacename, 'nomaster')
8465de90 344 except Exception, e:
768b4ec5 345 self.logger.warn('%s: %s' %(ifacename, str(e)))
8465de90
RP
346
347 def _down(self, ifaceobj):
348 try:
349 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
350 if vrf_table:
351 self._down_vrf_dev(ifaceobj, vrf_table)
352 else:
353 vrf = ifaceobj.get_attr_value_first('vrf')
354 if vrf:
768b4ec5 355 self._down_vrf_slave(ifaceobj.name, vrf)
8465de90
RP
356 except Exception, e:
357 self.log_warn(str(e))
358
359 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
360 try:
54616d3f 361 master = self.ipcmd.link_get_master(ifaceobj.name)
8465de90
RP
362 if not master or master != vrf:
363 ifaceobjcurr.update_config_with_status('vrf', master, 1)
364 else:
365 ifaceobjcurr.update_config_with_status('vrf', master, 0)
366 except Exception, e:
367 self.log_warn(str(e))
368
369 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
370 try:
371 if not self.ipcmd.link_exists(ifaceobj.name):
372 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
373 return
374 if vrf_table == 'auto':
375 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
376 else:
377 config_table = vrf_table
378 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
379 if not vrfdev_attrs:
380 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
381 return
382 running_table = vrfdev_attrs.get('table')
383 if not running_table:
384 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
385 return
386 if config_table != running_table:
387 ifaceobjcurr.update_config_with_status('vrf-table',
388 running_table, 1)
389 else:
390 ifaceobjcurr.update_config_with_status('vrf-table',
391 running_table, 0)
392 except Exception, e:
393 self.log_warn(str(e))
394
395 def _query_check(self, ifaceobj, ifaceobjcurr):
396 try:
397 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
398 if vrf_table:
399 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
400 else:
401 vrf = ifaceobj.get_attr_value_first('vrf')
402 if vrf:
403 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
404 except Exception, e:
405 self.log_warn(str(e))
406
407 def _query_running(self, ifaceobjrunning):
408 try:
409 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
410 if kind == 'vrf':
54616d3f 411 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobjrunning.name)
8465de90
RP
412 if vrfdev_attrs:
413 running_table = vrfdev_attrs.get('table')
414 if running_table:
415 ifaceobjrunning.update_config('vrf-table',
416 running_table)
417 elif kind == 'vrf_slave':
54616d3f 418 vrf = self.ipcmd.link_get_master(ifaceobjrunning.name)
8465de90
RP
419 if vrf:
420 ifaceobjrunning.update_config('vrf', vrf)
421 except Exception, e:
422 self.log_warn(str(e))
423
424 _run_ops = {'pre-up' : _up,
425 'post-down' : _down,
426 'query-running' : _query_running,
427 'query-checkcurr' : _query_check}
428
429 def get_ops(self):
430 """ returns list of ops supported by this module """
431 return self._run_ops.keys()
432
433 def _init_command_handlers(self):
434 flags = self.get_flags()
435 if not self.ipcmd:
436 self.ipcmd = iproute2(**flags)
437 if not self.bondcmd:
438 self.bondcmd = bondutil(**flags)
439
440 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
441 """ run bond configuration on the interface object passed as argument
442
443 Args:
444 **ifaceobj** (object): iface object
445
446 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
447 'query-running'
448
449 Kwargs:
450 **query_ifaceobj** (object): query check ifaceobject. This is only
451 valid when op is 'query-checkcurr'. It is an object same as
452 ifaceobj, but contains running attribute values and its config
453 status. The modules can use it to return queried running state
454 of interfaces. status is success if the running state is same
455 as user required state in ifaceobj. error otherwise.
456 """
457 op_handler = self._run_ops.get(operation)
458 if not op_handler:
459 return
460 self._init_command_handlers()
461 if operation == 'query-checkcurr':
462 op_handler(self, ifaceobj, query_ifaceobj)
463 else:
464 op_handler(self, ifaceobj)