]> git.proxmox.com Git - mirror_ifupdown2.git/blame - addons/vrf.py
addons: support for new addon module for vrf
[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 *
10import ifupdownaddons
11from ifupdownaddons.modulebase import moduleBase
12from ifupdownaddons.bondutil import bondutil
13from ifupdownaddons.iproute2 import iproute2
14
15class vrf(moduleBase):
16 """ ifupdown2 addon module to configure vrfs """
17 _modinfo = { 'mhelp' : 'vrf configuration module',
18 'attrs' : {
19 'vrf-table':
20 {'help' : 'vrf device table id. key to ' +
21 'creating a vrf device',
22 'example': ['vrf-table-id 1']},
23 'vrf':
24 {'help' : 'vrf the interface is part of.',
25 'example': ['vrf blue']}}}
26
27 iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2.vrf_map'
28 iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
29 '# It contains the vrf name to table mapping.\n' + \
30 '# Reserved table range 150-200\n'
31 vrf_table_reserved_start = 150
32 vrf_table_reserved_end = 200
33
34 def __init__(self, *args, **kargs):
35 ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
36 self.ipcmd = None
37 self.bondcmd = None
38 try:
39 ip_rules = self.exec_command('/sbin/ip rule show').splitlines()
40 self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
41 except Exception, e:
42 self.ip_rule_cache = []
43 self.logger.warn('%s' %str(e))
44
45 try:
46 ip_rules = self.exec_command('/sbin/ip -6 rule show').splitlines()
47 self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
48 except Exception, e:
49 self.ip6_rule_cache = []
50 self.logger.warn('%s' %str(e))
51
52 #self.logger.debug("vrf: ip rule cache")
53 #self.logger.info(self.ip_rule_cache)
54
55 #self.logger.info("vrf: ip -6 rule cache")
56 #self.logger.info(self.ip6_rule_cache)
57
58 # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
59 self.iproute2_vrf_map = {}
60 # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
61 if os.path.exists(self.iproute2_vrf_filename):
62 self.vrf_map_fd = open(self.iproute2_vrf_filename, 'a+')
63 lines = self.vrf_map_fd.readlines()
64 for l in lines:
65 l = l.strip()
66 if l[0] == '#':
67 continue
68 try:
69 (table, vrf_name) = l.strip().split()
70 self.iproute2_vrf_map[table] = vrf_name
71 except Exception, e:
72 self.logger.info('vrf: iproute2_vrf_map: unable to parse %s'
73 %l)
74 pass
75 #self.logger.info("vrf: dumping iproute2_vrf_map")
76 #self.logger.info(self.iproute2_vrf_map)
77
78 # purge vrf table entries that are not around
79 iproute2_vrf_map_pruned = {}
80 for t, v in self.iproute2_vrf_map.iteritems():
81 if os.path.exists('/sys/class/net/%s' %v):
82 iproute2_vrf_map_pruned[t] = v
83 else:
84 try:
85 # cleanup rules
86 self._del_vrf_rules(v, t)
87 except Exception:
88 pass
89 self.iproute2_vrf_map = iproute2_vrf_map_pruned
90
91 last_used_vrf_table = self.vrf_table_reserved_start
92 for t in range(self.vrf_table_reserved_start,
93 self.vrf_table_reserved_end):
94 last_used_vrf_table = t
95 if not self.iproute2_vrf_map.get(t):
96 break
97 self.last_used_vrf_table = last_used_vrf_table
98 self.iproute2_write_vrf_map = False
99 atexit.register(self.iproute2_vrf_map_write)
100
101 def iproute2_vrf_map_write(self):
102 if not self.iproute2_write_vrf_map:
103 return
104 self.logger.info('vrf: writing table map to %s'
105 %self.iproute2_vrf_filename)
106 with open(self.iproute2_vrf_filename, 'w') as f:
107 f.write(self.iproute2_vrf_filehdr)
108 for t, v in self.iproute2_vrf_map.iteritems():
109 f.write('%s %s\n' %(t, v))
110
111 def _is_vrf(self, ifaceobj):
112 if ifaceobj.get_attr_value_first('vrf-table'):
113 return True
114 return False
115
116 def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
117 """ Returns list of interfaces dependent on ifaceobj """
118
119 vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
120 if not vrf_iface_name:
121 return None
122 return [vrf_iface_name]
123
124 def get_dependent_ifacenames_running(self, ifaceobj):
125 return None
126
127 def _get_iproute2_vrf_table(self, vrf_dev_name):
128 for t, v in self.iproute2_vrf_map.iteritems():
129 if v == vrf_dev_name:
130 return t
131 return None
132
133 def _get_avail_vrf_table_id(self):
134 for t in range(self.last_used_vrf_table + 1,
135 self.vrf_table_reserved_end):
136 if not self.iproute2_vrf_map.get(t):
137 self.last_used_vrf_table = t
138 return t
139 return None
140
141 def _iproute2_vrf_table_entry_add(self, vrf_dev_name, table_id):
142 self.iproute2_vrf_map[table_id] = vrf_dev_name
143 self.iproute2_write_vrf_map = True
144
145 def _iproute2_vrf_table_entry_del(self, table_id):
146 try:
147 del self.iproute2_vrf_map[table_id]
148 self.iproute2_write_vrf_map = True
149 except Exception, e:
150 self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)'
151 %(table_id, str(e)))
152 pass
153
154 def _up_vrf_slave(self, ifaceobj, vrf):
155 try:
156 self.ipcmd.link_set(ifaceobj.name, 'master', vrf)
157 except Exception, e:
158 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
159
160 def _del_vrf_rules(self, vrf_dev_name, vrf_table):
161 pref = 200
162 ip_rule_out_format = '%s: from all %s %s lookup %s'
163 ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
164
165 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
166 if rule in self.ip_rule_cache:
167 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
168 self.exec_command(rule_cmd)
169
170 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
171 if rule in self.ip_rule_cache:
172 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
173 self.exec_command(rule_cmd)
174
175 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
176 if rule in self.ip_rule_cache:
177 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
178 vrf_table)
179 self.exec_command(rule_cmd)
180
181 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
182 if rule in self.ip_rule_cache:
183 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
184 vrf_table)
185 self.exec_command(rule_cmd)
186
187 def _add_vrf_rules(self, vrf_dev_name, vrf_table):
188 pref = 200
189 ip_rule_out_format = '%s: from all %s %s lookup %s'
190 ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
191
192 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
193 if rule not in self.ip_rule_cache:
194 rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
195 self.exec_command(rule_cmd)
196
197 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
198 if rule not in self.ip_rule_cache:
199 rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
200 self.exec_command(rule_cmd)
201
202 rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
203 if rule not in self.ip_rule_cache:
204 rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
205 vrf_table)
206 self.exec_command(rule_cmd)
207
208 rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
209 if rule not in self.ip_rule_cache:
210 rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
211 vrf_table)
212 self.exec_command(rule_cmd)
213
214
215 def _up_vrf_dev(self, ifaceobj, vrf_table):
216 if vrf_table == 'auto':
217 vrf_table = _get_avail_vrf_table_id(ifaceobj.name)
218
219 try:
220 if not self.ipcmd.link_exists(ifaceobj.name):
221 self.ipcmd.link_create(ifaceobj.name, 'vrf',
222 {'table' : '%s' %vrf_table})
223 self._iproute2_vrf_table_entry_add(ifaceobj.name, vrf_table)
224 self._add_vrf_rules(ifaceobj.name, vrf_table)
225 except Exception, e:
226 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
227
228 def _up(self, ifaceobj):
229 try:
230 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
231 if vrf_table:
232 self._up_vrf_dev(ifaceobj, vrf_table)
233 else:
234 vrf = ifaceobj.get_attr_value_first('vrf')
235 if vrf:
236 self._up_vrf_slave(ifaceobj, vrf)
237 except Exception, e:
238 self.log_error(str(e))
239
240 def _down_vrf_dev(self, ifaceobj, vrf_table):
241 if vrf_table == 'auto':
242 vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
243 try:
244 self.ipcmd.link_delete(ifaceobj.name)
245 self._iproute2_vrf_table_entry_del(vrf_table)
246 self._del_vrf_rules(ifaceobj.name, vrf_table)
247 except Exception, e:
248 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
249
250 def _down_vrf_slave(self, ifaceobj, vrf):
251 try:
252 self.ipcmd.link_set(ifaceobj.name, 'nomaster')
253 except Exception, e:
254 self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
255
256 def _down(self, ifaceobj):
257 try:
258 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
259 if vrf_table:
260 self._down_vrf_dev(ifaceobj, vrf_table)
261 else:
262 vrf = ifaceobj.get_attr_value_first('vrf')
263 if vrf:
264 self._down_vrf_slave(ifaceobj, vrf)
265 except Exception, e:
266 self.log_warn(str(e))
267
268 def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
269 try:
270 master = self.ipcmd.link_get_master(ifacename)
271 if not master or master != vrf:
272 ifaceobjcurr.update_config_with_status('vrf', master, 1)
273 else:
274 ifaceobjcurr.update_config_with_status('vrf', master, 0)
275 except Exception, e:
276 self.log_warn(str(e))
277
278 def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
279 try:
280 if not self.ipcmd.link_exists(ifaceobj.name):
281 self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
282 return
283 if vrf_table == 'auto':
284 config_table = self._get_iproute2_vrf_table(ifaceobj.name)
285 else:
286 config_table = vrf_table
287 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
288 if not vrfdev_attrs:
289 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
290 return
291 running_table = vrfdev_attrs.get('table')
292 if not running_table:
293 ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
294 return
295 if config_table != running_table:
296 ifaceobjcurr.update_config_with_status('vrf-table',
297 running_table, 1)
298 else:
299 ifaceobjcurr.update_config_with_status('vrf-table',
300 running_table, 0)
301 except Exception, e:
302 self.log_warn(str(e))
303
304 def _query_check(self, ifaceobj, ifaceobjcurr):
305 try:
306 vrf_table = ifaceobj.get_attr_value_first('vrf-table')
307 if vrf_table:
308 self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
309 else:
310 vrf = ifaceobj.get_attr_value_first('vrf')
311 if vrf:
312 self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
313 except Exception, e:
314 self.log_warn(str(e))
315
316 def _query_running(self, ifaceobjrunning):
317 try:
318 kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
319 if kind == 'vrf':
320 vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
321 if vrfdev_attrs:
322 running_table = vrfdev_attrs.get('table')
323 if running_table:
324 ifaceobjrunning.update_config('vrf-table',
325 running_table)
326 elif kind == 'vrf_slave':
327 vrf = self.link_get_master(ifaceobjrunning.name)
328 if vrf:
329 ifaceobjrunning.update_config('vrf', vrf)
330 except Exception, e:
331 self.log_warn(str(e))
332
333 _run_ops = {'pre-up' : _up,
334 'post-down' : _down,
335 'query-running' : _query_running,
336 'query-checkcurr' : _query_check}
337
338 def get_ops(self):
339 """ returns list of ops supported by this module """
340 return self._run_ops.keys()
341
342 def _init_command_handlers(self):
343 flags = self.get_flags()
344 if not self.ipcmd:
345 self.ipcmd = iproute2(**flags)
346 if not self.bondcmd:
347 self.bondcmd = bondutil(**flags)
348
349 def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
350 """ run bond configuration on the interface object passed as argument
351
352 Args:
353 **ifaceobj** (object): iface object
354
355 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
356 'query-running'
357
358 Kwargs:
359 **query_ifaceobj** (object): query check ifaceobject. This is only
360 valid when op is 'query-checkcurr'. It is an object same as
361 ifaceobj, but contains running attribute values and its config
362 status. The modules can use it to return queried running state
363 of interfaces. status is success if the running state is same
364 as user required state in ifaceobj. error otherwise.
365 """
366 op_handler = self._run_ops.get(operation)
367 if not op_handler:
368 return
369 self._init_command_handlers()
370 if operation == 'query-checkcurr':
371 op_handler(self, ifaceobj, query_ifaceobj)
372 else:
373 op_handler(self, ifaceobj)