--- /dev/null
+#!/usr/bin/python
+#
+# Copyright 2014 Cumulus Networks, Inc. All rights reserved.
+# Author: Roopa Prabhu, roopa@cumulusnetworks.com
+#
+
+import os
+import atexit
+from ifupdown.iface import *
+import ifupdownaddons
+from ifupdownaddons.modulebase import moduleBase
+from ifupdownaddons.bondutil import bondutil
+from ifupdownaddons.iproute2 import iproute2
+
+class vrf(moduleBase):
+ """ ifupdown2 addon module to configure vrfs """
+ _modinfo = { 'mhelp' : 'vrf configuration module',
+ 'attrs' : {
+ 'vrf-table':
+ {'help' : 'vrf device table id. key to ' +
+ 'creating a vrf device',
+ 'example': ['vrf-table-id 1']},
+ 'vrf':
+ {'help' : 'vrf the interface is part of.',
+ 'example': ['vrf blue']}}}
+
+ iproute2_vrf_filename = '/etc/iproute2/rt_tables.d/ifupdown2.vrf_map'
+ iproute2_vrf_filehdr = '# This file is autogenerated by ifupdown2.\n' + \
+ '# It contains the vrf name to table mapping.\n' + \
+ '# Reserved table range 150-200\n'
+ vrf_table_reserved_start = 150
+ vrf_table_reserved_end = 200
+
+ def __init__(self, *args, **kargs):
+ ifupdownaddons.modulebase.moduleBase.__init__(self, *args, **kargs)
+ self.ipcmd = None
+ self.bondcmd = None
+ try:
+ ip_rules = self.exec_command('/sbin/ip rule show').splitlines()
+ self.ip_rule_cache = [' '.join(r.split()) for r in ip_rules]
+ except Exception, e:
+ self.ip_rule_cache = []
+ self.logger.warn('%s' %str(e))
+
+ try:
+ ip_rules = self.exec_command('/sbin/ip -6 rule show').splitlines()
+ self.ip6_rule_cache = [' '.join(r.split()) for r in ip_rules]
+ except Exception, e:
+ self.ip6_rule_cache = []
+ self.logger.warn('%s' %str(e))
+
+ #self.logger.debug("vrf: ip rule cache")
+ #self.logger.info(self.ip_rule_cache)
+
+ #self.logger.info("vrf: ip -6 rule cache")
+ #self.logger.info(self.ip6_rule_cache)
+
+ # XXX: check for vrf reserved overlap in /etc/iproute2/rt_tables
+ self.iproute2_vrf_map = {}
+ # read or create /etc/iproute2/rt_tables.d/ifupdown2.vrf_map
+ if os.path.exists(self.iproute2_vrf_filename):
+ self.vrf_map_fd = open(self.iproute2_vrf_filename, 'a+')
+ lines = self.vrf_map_fd.readlines()
+ for l in lines:
+ l = l.strip()
+ if l[0] == '#':
+ continue
+ try:
+ (table, vrf_name) = l.strip().split()
+ self.iproute2_vrf_map[table] = vrf_name
+ except Exception, e:
+ self.logger.info('vrf: iproute2_vrf_map: unable to parse %s'
+ %l)
+ pass
+ #self.logger.info("vrf: dumping iproute2_vrf_map")
+ #self.logger.info(self.iproute2_vrf_map)
+
+ # purge vrf table entries that are not around
+ iproute2_vrf_map_pruned = {}
+ for t, v in self.iproute2_vrf_map.iteritems():
+ if os.path.exists('/sys/class/net/%s' %v):
+ iproute2_vrf_map_pruned[t] = v
+ else:
+ try:
+ # cleanup rules
+ self._del_vrf_rules(v, t)
+ except Exception:
+ pass
+ self.iproute2_vrf_map = iproute2_vrf_map_pruned
+
+ last_used_vrf_table = self.vrf_table_reserved_start
+ for t in range(self.vrf_table_reserved_start,
+ self.vrf_table_reserved_end):
+ last_used_vrf_table = t
+ if not self.iproute2_vrf_map.get(t):
+ break
+ self.last_used_vrf_table = last_used_vrf_table
+ self.iproute2_write_vrf_map = False
+ atexit.register(self.iproute2_vrf_map_write)
+
+ def iproute2_vrf_map_write(self):
+ if not self.iproute2_write_vrf_map:
+ return
+ self.logger.info('vrf: writing table map to %s'
+ %self.iproute2_vrf_filename)
+ with open(self.iproute2_vrf_filename, 'w') as f:
+ f.write(self.iproute2_vrf_filehdr)
+ for t, v in self.iproute2_vrf_map.iteritems():
+ f.write('%s %s\n' %(t, v))
+
+ def _is_vrf(self, ifaceobj):
+ if ifaceobj.get_attr_value_first('vrf-table'):
+ return True
+ return False
+
+ def get_dependent_ifacenames(self, ifaceobj, ifacenames_all=None):
+ """ Returns list of interfaces dependent on ifaceobj """
+
+ vrf_iface_name = ifaceobj.get_attr_value_first('vrf')
+ if not vrf_iface_name:
+ return None
+ return [vrf_iface_name]
+
+ def get_dependent_ifacenames_running(self, ifaceobj):
+ return None
+
+ def _get_iproute2_vrf_table(self, vrf_dev_name):
+ for t, v in self.iproute2_vrf_map.iteritems():
+ if v == vrf_dev_name:
+ return t
+ return None
+
+ def _get_avail_vrf_table_id(self):
+ for t in range(self.last_used_vrf_table + 1,
+ self.vrf_table_reserved_end):
+ if not self.iproute2_vrf_map.get(t):
+ self.last_used_vrf_table = t
+ return t
+ return None
+
+ def _iproute2_vrf_table_entry_add(self, vrf_dev_name, table_id):
+ self.iproute2_vrf_map[table_id] = vrf_dev_name
+ self.iproute2_write_vrf_map = True
+
+ def _iproute2_vrf_table_entry_del(self, table_id):
+ try:
+ del self.iproute2_vrf_map[table_id]
+ self.iproute2_write_vrf_map = True
+ except Exception, e:
+ self.logger.info('vrf: iproute2 vrf map del failed for %d (%s)'
+ %(table_id, str(e)))
+ pass
+
+ def _up_vrf_slave(self, ifaceobj, vrf):
+ try:
+ self.ipcmd.link_set(ifaceobj.name, 'master', vrf)
+ except Exception, e:
+ self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
+
+ def _del_vrf_rules(self, vrf_dev_name, vrf_table):
+ pref = 200
+ ip_rule_out_format = '%s: from all %s %s lookup %s'
+ ip_rule_cmd = 'ip %s rule del pref %s %s %s table %s'
+
+ rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
+ if rule in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
+ self.exec_command(rule_cmd)
+
+ rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
+ if rule in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
+ self.exec_command(rule_cmd)
+
+ rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
+ if rule in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
+ vrf_table)
+ self.exec_command(rule_cmd)
+
+ rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
+ if rule in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
+ vrf_table)
+ self.exec_command(rule_cmd)
+
+ def _add_vrf_rules(self, vrf_dev_name, vrf_table):
+ pref = 200
+ ip_rule_out_format = '%s: from all %s %s lookup %s'
+ ip_rule_cmd = 'ip %s rule add pref %s %s %s table %s'
+
+ rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
+ if rule not in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('', pref, 'oif', vrf_dev_name, vrf_table)
+ self.exec_command(rule_cmd)
+
+ rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
+ if rule not in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('', pref, 'iif', vrf_dev_name, vrf_table)
+ self.exec_command(rule_cmd)
+
+ rule = ip_rule_out_format %(pref, 'oif', vrf_dev_name, vrf_table)
+ if rule not in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('-6', pref, 'oif', vrf_dev_name,
+ vrf_table)
+ self.exec_command(rule_cmd)
+
+ rule = ip_rule_out_format %(pref, 'iif', vrf_dev_name, vrf_table)
+ if rule not in self.ip_rule_cache:
+ rule_cmd = ip_rule_cmd %('-6', pref, 'iif', vrf_dev_name,
+ vrf_table)
+ self.exec_command(rule_cmd)
+
+
+ def _up_vrf_dev(self, ifaceobj, vrf_table):
+ if vrf_table == 'auto':
+ vrf_table = _get_avail_vrf_table_id(ifaceobj.name)
+
+ try:
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ self.ipcmd.link_create(ifaceobj.name, 'vrf',
+ {'table' : '%s' %vrf_table})
+ self._iproute2_vrf_table_entry_add(ifaceobj.name, vrf_table)
+ self._add_vrf_rules(ifaceobj.name, vrf_table)
+ except Exception, e:
+ self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
+
+ def _up(self, ifaceobj):
+ try:
+ vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+ if vrf_table:
+ self._up_vrf_dev(ifaceobj, vrf_table)
+ else:
+ vrf = ifaceobj.get_attr_value_first('vrf')
+ if vrf:
+ self._up_vrf_slave(ifaceobj, vrf)
+ except Exception, e:
+ self.log_error(str(e))
+
+ def _down_vrf_dev(self, ifaceobj, vrf_table):
+ if vrf_table == 'auto':
+ vrf_table = self._get_iproute2_vrf_table(ifaceobj.name)
+ try:
+ self.ipcmd.link_delete(ifaceobj.name)
+ self._iproute2_vrf_table_entry_del(vrf_table)
+ self._del_vrf_rules(ifaceobj.name, vrf_table)
+ except Exception, e:
+ self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
+
+ def _down_vrf_slave(self, ifaceobj, vrf):
+ try:
+ self.ipcmd.link_set(ifaceobj.name, 'nomaster')
+ except Exception, e:
+ self.logger.warn('%s: %s' %(ifaceobj.name, str(e)))
+
+ def _down(self, ifaceobj):
+ try:
+ vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+ if vrf_table:
+ self._down_vrf_dev(ifaceobj, vrf_table)
+ else:
+ vrf = ifaceobj.get_attr_value_first('vrf')
+ if vrf:
+ self._down_vrf_slave(ifaceobj, vrf)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check_vrf_slave(self, ifaceobj, ifaceobjcurr, vrf):
+ try:
+ master = self.ipcmd.link_get_master(ifacename)
+ if not master or master != vrf:
+ ifaceobjcurr.update_config_with_status('vrf', master, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('vrf', master, 0)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check_vrf_dev(self, ifaceobj, ifaceobjcurr, vrf_table):
+ try:
+ if not self.ipcmd.link_exists(ifaceobj.name):
+ self.logger.info('%s: vrf: does not exist' %(ifaceobj.name))
+ return
+ if vrf_table == 'auto':
+ config_table = self._get_iproute2_vrf_table(ifaceobj.name)
+ else:
+ config_table = vrf_table
+ vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
+ if not vrfdev_attrs:
+ ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
+ return
+ running_table = vrfdev_attrs.get('table')
+ if not running_table:
+ ifaceobjcurr.update_config_with_status('vrf-table', 'None', 1)
+ return
+ if config_table != running_table:
+ ifaceobjcurr.update_config_with_status('vrf-table',
+ running_table, 1)
+ else:
+ ifaceobjcurr.update_config_with_status('vrf-table',
+ running_table, 0)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_check(self, ifaceobj, ifaceobjcurr):
+ try:
+ vrf_table = ifaceobj.get_attr_value_first('vrf-table')
+ if vrf_table:
+ self._query_check_vrf_dev(ifaceobj, ifaceobjcurr, vrf_table)
+ else:
+ vrf = ifaceobj.get_attr_value_first('vrf')
+ if vrf:
+ self._query_check_vrf_slave(ifaceobj, ifaceobjcurr, vrf)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ def _query_running(self, ifaceobjrunning):
+ try:
+ kind = self.ipcmd.link_get_kind(ifaceobjrunning.name)
+ if kind == 'vrf':
+ vrfdev_attrs = self.ipcmd.link_get_linkinfo_attrs(ifaceobj.name)
+ if vrfdev_attrs:
+ running_table = vrfdev_attrs.get('table')
+ if running_table:
+ ifaceobjrunning.update_config('vrf-table',
+ running_table)
+ elif kind == 'vrf_slave':
+ vrf = self.link_get_master(ifaceobjrunning.name)
+ if vrf:
+ ifaceobjrunning.update_config('vrf', vrf)
+ except Exception, e:
+ self.log_warn(str(e))
+
+ _run_ops = {'pre-up' : _up,
+ 'post-down' : _down,
+ 'query-running' : _query_running,
+ 'query-checkcurr' : _query_check}
+
+ def get_ops(self):
+ """ returns list of ops supported by this module """
+ return self._run_ops.keys()
+
+ def _init_command_handlers(self):
+ flags = self.get_flags()
+ if not self.ipcmd:
+ self.ipcmd = iproute2(**flags)
+ if not self.bondcmd:
+ self.bondcmd = bondutil(**flags)
+
+ def run(self, ifaceobj, operation, query_ifaceobj=None, **extra_args):
+ """ run bond configuration on the interface object passed as argument
+
+ Args:
+ **ifaceobj** (object): iface object
+
+ **operation** (str): any of 'pre-up', 'post-down', 'query-checkcurr',
+ 'query-running'
+
+ Kwargs:
+ **query_ifaceobj** (object): query check ifaceobject. This is only
+ valid when op is 'query-checkcurr'. It is an object same as
+ ifaceobj, but contains running attribute values and its config
+ status. The modules can use it to return queried running state
+ of interfaces. status is success if the running state is same
+ as user required state in ifaceobj. error otherwise.
+ """
+ op_handler = self._run_ops.get(operation)
+ if not op_handler:
+ return
+ self._init_command_handlers()
+ if operation == 'query-checkcurr':
+ op_handler(self, ifaceobj, query_ifaceobj)
+ else:
+ op_handler(self, ifaceobj)