]>
git.proxmox.com Git - mirror_ifupdown2.git/blob - addons/vrf.py
3 # Copyright 2014 Cumulus Networks, Inc. All rights reserved.
4 # Author: Roopa Prabhu, roopa@cumulusnetworks.com
9 from ifupdown
.iface
import *
10 import ifupdown
.policymanager
as policymanager
12 import ifupdown
.rtnetlink_api
as rtnetlink_api
13 from ifupdownaddons
.modulebase
import moduleBase
14 from ifupdownaddons
.bondutil
import bondutil
15 from ifupdownaddons
.iproute2
import iproute2
17 class vrf(moduleBase
):
18 """ ifupdown2 addon module to configure vrfs """
19 _modinfo
= { 'mhelp' : 'vrf configuration module',
22 {'help' : 'vrf device table id. key to ' +
23 'creating a vrf device',
24 'example': ['vrf-table-id 1']},
26 {'help' : 'vrf device default route ' +
27 'to avoid communication outside the vrf device',
28 'example': ['vrf-default-route yes/no']},
30 {'help' : 'vrf the interface is part of.',
31 'example': ['vrf blue']}}}
33 iproute2_vrf_filename
= '/etc/iproute2/rt_tables.d/ifupdown2_vrf_map.conf'
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 %s %s\n'
37 VRF_TABLE_START
= 1001
40 def __init__(self
, *args
, **kargs
):
41 ifupdownaddons
.modulebase
.moduleBase
.__init
__(self
, *args
, **kargs
)
45 ip_rules
= self
.exec_command('/sbin/ip rule show').splitlines()
46 self
.ip_rule_cache
= [' '.join(r
.split()) for r
in ip_rules
]
48 self
.ip_rule_cache
= []
49 self
.logger
.warn('%s' %str
(e
))
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
]
55 self
.ip6_rule_cache
= []
56 self
.logger
.warn('%s' %str
(e
))
58 #self.logger.debug("vrf: ip rule cache")
59 #self.logger.info(self.ip_rule_cache)
61 #self.logger.info("vrf: ip -6 rule cache")
62 #self.logger.info(self.ip6_rule_cache)
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()
75 (table
, vrf_name
) = l
.strip().split()
76 self
.iproute2_vrf_map
[table
] = vrf_name
78 self
.logger
.info('vrf: iproute2_vrf_map: unable to parse %s'
81 #self.logger.info("vrf: dumping iproute2_vrf_map")
82 #self.logger.info(self.iproute2_vrf_map)
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
92 self
._del
_vrf
_rules
(v
, t
)
95 self
.iproute2_vrf_map
= iproute2_vrf_map_pruned
97 self
.vrf_table_id_start
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-start')
98 if not self
.vrf_table_id_start
:
99 self
.vrf_table_id_start
= self
.VRF_TABLE_START
100 self
.vrf_table_id_end
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-table-id-end')
101 if not self
.vrf_table_id_end
:
102 self
.vrf_table_id_end
= self
.VRF_TABLE_END
103 self
.vrf_max_count
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-max-count')
105 last_used_vrf_table
= None
106 for t
in range(self
.vrf_table_id_start
,
107 self
.vrf_table_id_end
):
108 if not self
.iproute2_vrf_map
.get(t
):
110 last_used_vrf_table
= t
111 self
.last_used_vrf_table
= last_used_vrf_table
112 self
.iproute2_write_vrf_map
= False
113 atexit
.register(self
.iproute2_vrf_map_write
)
114 self
.vrf_fix_local_table
= True
116 self
.vrf_cgroup_create
= policymanager
.policymanager_api
.get_module_globals(module_name
=self
.__class
__.__name
__, attr
='vrf-cgroup-create')
117 if not self
.vrf_cgroup_create
:
118 self
.vrf_cgroup_create
= False
119 elif self
.vrf_cgroup_create
== 'yes':
120 self
.vrf_cgroup_create
= True
122 self
.vrf_cgroup_create
= False
124 def iproute2_vrf_map_write(self
):
125 if not self
.iproute2_write_vrf_map
:
127 self
.logger
.info('vrf: writing table map to %s'
128 %self
.iproute2_vrf_filename
)
129 with
open(self
.iproute2_vrf_filename
, 'w') as f
:
130 f
.write(self
.iproute2_vrf_filehdr
%(self
.vrf_table_id_start
,
131 self
.vrf_table_id_end
))
132 for t
, v
in self
.iproute2_vrf_map
.iteritems():
133 f
.write('%s %s\n' %(t
, v
))
135 def _is_vrf(self
, ifaceobj
):
136 if ifaceobj
.get_attr_value_first('vrf-table'):
140 def get_upper_ifacenames(self
, ifaceobj
, ifacenames_all
=None):
141 """ Returns list of interfaces dependent on ifaceobj """
143 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
145 ifaceobj
.link_type
= ifaceLinkType
.LINK_MASTER
146 ifaceobj
.link_kind |
= ifaceLinkKind
.VRF
147 vrf_iface_name
= ifaceobj
.get_attr_value_first('vrf')
148 if not vrf_iface_name
:
150 ifaceobj
.link_type
= ifaceLinkType
.LINK_SLAVE
151 return [vrf_iface_name
]
153 def get_upper_ifacenames_running(self
, ifaceobj
):
156 def _get_iproute2_vrf_table(self
, vrf_dev_name
):
157 for t
, v
in self
.iproute2_vrf_map
.iteritems():
158 if v
== vrf_dev_name
:
162 def _get_avail_vrf_table_id(self
):
163 if self
.last_used_vrf_table
== None:
164 table_id_start
= self
.vrf_table_id_start
166 table_id_start
= self
.last_used_vrf_table
+ 1
167 for t
in range(table_id_start
,
168 self
.vrf_table_id_end
):
169 if not self
.iproute2_vrf_map
.get(t
):
170 self
.last_used_vrf_table
= t
174 def _iproute2_vrf_table_entry_add(self
, vrf_dev_name
, table_id
):
175 self
.iproute2_vrf_map
[table_id
] = vrf_dev_name
176 self
.iproute2_write_vrf_map
= True
178 def _iproute2_vrf_table_entry_del(self
, table_id
):
180 del self
.iproute2_vrf_map
[table_id
]
181 self
.iproute2_write_vrf_map
= True
183 self
.logger
.info('vrf: iproute2 vrf map del failed for %d (%s)'
187 def _up_vrf_slave(self
, ifacename
, vrfname
):
189 if self
.ipcmd
.link_exists(vrfname
):
190 self
.ipcmd
.link_set(ifacename
, 'master', vrfname
)
192 self
.logger
.warn('%s: %s' %(ifacename
, str(e
)))
194 def _del_vrf_rules(self
, vrf_dev_name
, vrf_table
):
196 ip_rule_out_format
= '%s: from all %s %s lookup %s'
197 ip_rule_cmd
= 'ip %s rule del pref %s %s %s table %s'
199 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
200 if rule
in self
.ip_rule_cache
:
201 rule_cmd
= ip_rule_cmd
%('', pref
, 'oif', vrf_dev_name
, vrf_table
)
202 self
.exec_command(rule_cmd
)
204 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
205 if rule
in self
.ip_rule_cache
:
206 rule_cmd
= ip_rule_cmd
%('', pref
, 'iif', vrf_dev_name
, vrf_table
)
207 self
.exec_command(rule_cmd
)
209 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
210 if rule
in self
.ip_rule_cache
:
211 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'oif', vrf_dev_name
,
213 self
.exec_command(rule_cmd
)
215 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
216 if rule
in self
.ip_rule_cache
:
217 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'iif', vrf_dev_name
,
219 self
.exec_command(rule_cmd
)
221 def _add_vrf_rules(self
, vrf_dev_name
, vrf_table
):
223 ip_rule_out_format
= '%s: from all %s %s lookup %s'
224 ip_rule_cmd
= 'ip %s rule add pref %s %s %s table %s'
225 if self
.vrf_fix_local_table
:
226 self
.vrf_fix_local_table
= False
227 rule
= '0: from all lookup local'
228 if rule
in self
.ip_rule_cache
:
230 self
.exec_command('ip rule del pref 0')
231 self
.exec_command('ip rule add pref 32765 table local')
233 self
.logger
.info('%s' %str
(e
))
237 #200: from all oif blue lookup blue
238 #200: from all iif blue lookup blue
240 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
241 if rule
not in self
.ip_rule_cache
:
242 rule_cmd
= ip_rule_cmd
%('', pref
, 'oif', vrf_dev_name
, vrf_table
)
243 self
.exec_command(rule_cmd
)
245 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
246 if rule
not in self
.ip_rule_cache
:
247 rule_cmd
= ip_rule_cmd
%('', pref
, 'iif', vrf_dev_name
, vrf_table
)
248 self
.exec_command(rule_cmd
)
250 rule
= ip_rule_out_format
%(pref
, 'oif', vrf_dev_name
, vrf_dev_name
)
251 if rule
not in self
.ip_rule_cache
:
252 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'oif', vrf_dev_name
, vrf_table
)
253 self
.exec_command(rule_cmd
)
255 rule
= ip_rule_out_format
%(pref
, 'iif', vrf_dev_name
, vrf_dev_name
)
256 if rule
not in self
.ip_rule_cache
:
257 rule_cmd
= ip_rule_cmd
%('-6', pref
, 'iif', vrf_dev_name
,
259 self
.exec_command(rule_cmd
)
261 def _add_vrf_slaves(self
, ifaceobj
):
262 running_slaves
= self
.ipcmd
.link_get_lowers(ifaceobj
.name
)
263 config_slaves
= ifaceobj
.lowerifaces
264 if not config_slaves
and not running_slaves
:
266 add_slaves
= set(config_slaves
).difference(set(running_slaves
))
267 del_slaves
= set(running_slaves
).difference(set(config_slaves
))
271 self
._up
_vrf
_slave
(s
, ifaceobj
.name
)
273 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
278 self
._down
_vrf
_slave
(s
, ifaceobj
.name
)
280 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
282 if ifaceobj
.link_type
== ifaceLinkType
.LINK_MASTER
:
283 for s
in config_slaves
:
285 rtnetlink_api
.rtnl_api
.link_set(s
, "up")
287 self
.logger
.debug('%s: %s: link set up (%s)'
288 %(ifaceobj
.name
, s
, str(e
)))
291 def _create_cgroup(self
, ifaceobj
):
292 if not self
.vrf_cgroup_create
:
295 if not os
.path
.exists('/sys/fs/cgroup/l3mdev/%s' %ifaceobj
.name
):
296 self
.exec_command('/usr/bin/cgcreate -g l3mdev:%s' %ifaceobj
.name
)
297 self
.exec_command('/usr/bin/cgset -r l3mdev.master-device=%s %s'
298 %(ifaceobj
.name
, ifaceobj
.name
))
300 self
.log_warn('%s: cgroup create failed (%s)\n'
301 %(ifaceobj
.name
, str(e
)), ifaceobj
)
303 def _up_vrf_dev(self
, ifaceobj
, vrf_table
):
305 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
306 if vrf_table
== 'auto':
307 vrf_table
= self
._get
_avail
_vrf
_table
_id
()
309 self
.log_error('%s: unable to get an auto table id'
311 self
.logger
.info('%s: table id auto: selected table id %s\n'
312 %(ifaceobj
.name
, vrf_table
))
313 # XXX: If we decide to not allow vrf id usages out of
314 # the reserved ifupdown range, then uncomment this code.
316 # if (int(vrf_table) < self.vrf_table_id_start or
317 # int(vrf_table) > self.vrf_table_id_end):
318 # self.log_error('%s: vrf table id %s out of reserved range [%d,%d]'
319 # %(ifaceobj.name, vrf_table,
320 # self.vrf_table_id_start,
321 # self.vrf_table_id_end))
323 self
.ipcmd
.link_create(ifaceobj
.name
, 'vrf',
324 {'table' : '%s' %vrf_table
})
326 self
.log_error('%s: create failed (%s)\n'
327 %(ifaceobj
.name
, str(e
)))
329 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
331 self
.log_error('%s: unable to get vrf table id'
334 # if the device exists, check if table id is same
335 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
337 running_table
= vrfdev_attrs
.get('table', None)
338 if vrf_table
!= running_table
:
339 self
.log_error('%s: cannot change vrf table id,running table id %s is different from config id %s' %(ifaceobj
.name
,
340 running_table
, vrf_table
))
343 self
._iproute
2_vrf
_table
_entry
_add
(ifaceobj
.name
, vrf_table
)
344 self
._add
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
345 self
._add
_vrf
_slaves
(ifaceobj
)
346 self
._create
_cgroup
(ifaceobj
)
348 self
.log_error('%s: %s' %(ifaceobj
.name
, str(e
)))
350 def _up_vrf_default_route(self
, ifaceobj
, vrf_table
):
351 vrf_default_route
= ifaceobj
.get_attr_value_first('vrf-default-route')
352 if not vrf_default_route
:
353 vrf_default_route
= policymanager
.policymanager_api
.get_attr_default(
354 module_name
=self
.__class
__.__name
__, attr
='vrf-default-route')
355 if not vrf_default_route
:
357 if str(vrf_default_route
).lower() == "yes":
359 self
.exec_command('ip route add table %s unreachable default' %vrf_table
)
366 self
.exec_command('ip -6 route add table %s unreachable default' %vrf_table
)
372 def _up(self
, ifaceobj
):
374 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
376 if self
.vrf_count
== self
.vrf_max_count
:
377 self
.log_error('%s: max vrf count %d hit...not '
378 'creating vrf' %(ifaceobj
.name
,
380 self
._up
_vrf
_dev
(ifaceobj
, vrf_table
)
381 self
._up
_vrf
_default
_route
(ifaceobj
, vrf_table
)
383 vrf
= ifaceobj
.get_attr_value_first('vrf')
385 self
._up
_vrf
_slave
(ifaceobj
.name
, vrf
)
387 self
.log_error(str(e
))
389 def _delete_cgroup(self
, ifaceobj
):
391 if os
.path
.exists('/sys/fs/cgroup/l3mdev/%s' %ifaceobj
.name
):
392 self
.exec_command('cgdelete -g l3mdev:%s' %ifaceobj
.name
)
394 self
.log_warn('%s: cgroup delete failed (%s)\n'
395 %(ifaceobj
.name
, str(e
)), ifaceobj
)
397 def _down_vrf_dev(self
, ifaceobj
, vrf_table
):
398 if vrf_table
== 'auto':
399 vrf_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
401 self
.ipcmd
.link_delete(ifaceobj
.name
)
403 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
407 self
._iproute
2_vrf
_table
_entry
_del
(vrf_table
)
408 self
._delete
_cgroup
(ifaceobj
)
410 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
414 self
._del
_vrf
_rules
(ifaceobj
.name
, vrf_table
)
416 self
.logger
.info('%s: %s' %(ifaceobj
.name
, str(e
)))
419 def _down_vrf_slave(self
, ifacename
, vrf
):
421 self
.ipcmd
.link_set(ifacename
, 'nomaster')
423 self
.logger
.warn('%s: %s' %(ifacename
, str(e
)))
425 def _down(self
, ifaceobj
):
427 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
429 self
._down
_vrf
_dev
(ifaceobj
, vrf_table
)
431 vrf
= ifaceobj
.get_attr_value_first('vrf')
433 self
._down
_vrf
_slave
(ifaceobj
.name
, vrf
)
435 self
.log_warn(str(e
))
437 def _query_check_vrf_slave(self
, ifaceobj
, ifaceobjcurr
, vrf
):
439 master
= self
.ipcmd
.link_get_master(ifaceobj
.name
)
440 if not master
or master
!= vrf
:
441 ifaceobjcurr
.update_config_with_status('vrf', master
, 1)
443 ifaceobjcurr
.update_config_with_status('vrf', master
, 0)
445 self
.log_warn(str(e
))
447 def _query_check_vrf_dev(self
, ifaceobj
, ifaceobjcurr
, vrf_table
):
449 if not self
.ipcmd
.link_exists(ifaceobj
.name
):
450 self
.logger
.info('%s: vrf: does not exist' %(ifaceobj
.name
))
452 if vrf_table
== 'auto':
453 config_table
= self
._get
_iproute
2_vrf
_table
(ifaceobj
.name
)
455 config_table
= vrf_table
456 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobj
.name
)
458 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
460 running_table
= vrfdev_attrs
.get('table')
461 if not running_table
:
462 ifaceobjcurr
.update_config_with_status('vrf-table', 'None', 1)
464 if config_table
!= running_table
:
465 ifaceobjcurr
.update_config_with_status('vrf-table',
468 ifaceobjcurr
.update_config_with_status('vrf-table',
471 self
.log_warn(str(e
))
473 def _query_check(self
, ifaceobj
, ifaceobjcurr
):
475 vrf_table
= ifaceobj
.get_attr_value_first('vrf-table')
477 self
._query
_check
_vrf
_dev
(ifaceobj
, ifaceobjcurr
, vrf_table
)
479 vrf
= ifaceobj
.get_attr_value_first('vrf')
481 self
._query
_check
_vrf
_slave
(ifaceobj
, ifaceobjcurr
, vrf
)
483 self
.log_warn(str(e
))
485 def _query_running(self
, ifaceobjrunning
):
487 kind
= self
.ipcmd
.link_get_kind(ifaceobjrunning
.name
)
489 vrfdev_attrs
= self
.ipcmd
.link_get_linkinfo_attrs(ifaceobjrunning
.name
)
491 running_table
= vrfdev_attrs
.get('table')
493 ifaceobjrunning
.update_config('vrf-table',
495 elif kind
== 'vrf_slave':
496 vrf
= self
.ipcmd
.link_get_master(ifaceobjrunning
.name
)
498 ifaceobjrunning
.update_config('vrf', vrf
)
500 self
.log_warn(str(e
))
502 _run_ops
= {'pre-up' : _up
,
504 'query-running' : _query_running
,
505 'query-checkcurr' : _query_check
}
508 """ returns list of ops supported by this module """
509 return self
._run
_ops
.keys()
511 def _init_command_handlers(self
):
512 flags
= self
.get_flags()
514 self
.ipcmd
= iproute2(**flags
)
516 self
.bondcmd
= bondutil(**flags
)
518 def run(self
, ifaceobj
, operation
, query_ifaceobj
=None, **extra_args
):
519 """ run bond configuration on the interface object passed as argument
522 **ifaceobj** (object): iface object
524 **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
528 **query_ifaceobj** (object): query check ifaceobject. This is only
529 valid when op is 'query-checkcurr'. It is an object same as
530 ifaceobj, but contains running attribute values and its config
531 status. The modules can use it to return queried running state
532 of interfaces. status is success if the running state is same
533 as user required state in ifaceobj. error otherwise.
535 op_handler
= self
._run
_ops
.get(operation
)
538 self
._init
_command
_handlers
()
539 if operation
== 'query-checkcurr':
540 op_handler(self
, ifaceobj
, query_ifaceobj
)
542 op_handler(self
, ifaceobj
)