]> git.proxmox.com Git - mirror_ifupdown2.git/commitdiff
addons: support for new addon module for vrf
authorRoopa Prabhu <roopa@cumulusnetworks.com>
Thu, 28 Jan 2016 22:00:09 +0000 (14:00 -0800)
committerRoopa Prabhu <roopa@cumulusnetworks.com>
Fri, 29 Jan 2016 06:01:59 +0000 (22:01 -0800)
This patch adds initial support for vrf in ifupdown2.

Example interfaces file section:
auto swp1.100
iface swp1.100
    vrf blue

auto blue
iface blue
    vrf-table 10

iproute2 vrf map is generated under:
/etc/iproute2/rt_tables.d/ifupdown2.vrf_map

this patch also adds prelimnary support for 'vrf-table auto'.
But this needs more work.

Signed-off-by: Roopa Prabhu <roopa@cumulusnetworks.com>
addons/vrf.py [new file with mode: 0644]
config/addons.conf
ifupdownaddons/iproute2.py
setup.py

diff --git a/addons/vrf.py b/addons/vrf.py
new file mode 100644 (file)
index 0000000..d087059
--- /dev/null
@@ -0,0 +1,373 @@
+#!/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)
index ebfeb7b4872f415f6b9b8f1017630b09a9b3560e..73fc699e266b216b516b6772afd8fcc2b4f68f7b 100644 (file)
@@ -7,6 +7,7 @@ pre-up,usercmds
 pre-up,bridge
 pre-up,bridgevlan
 pre-up,mstpctl
+pre-up,vrf
 up,dhcp
 up,address
 up,addressvirtual
@@ -22,6 +23,7 @@ down,dhcp
 down,addressvirtual
 down,address
 down,usercmds
+post-down,vrf
 post-down,clagd
 post-down,mstpctl
 post-down,bridgevlan
index a09234c566a43c4c4ff7038625cca7a765ac3ca5..b8f7ae2007cf121705d43ad658f65370acfe1ac5 100644 (file)
@@ -585,6 +585,9 @@ class iproute2(utilsBase):
     def get_vxlandev_attrs(self, ifacename):
         return self._cache_get('link', [ifacename, 'linkinfo'])
 
+    def link_get_linkinfo_attrs(self, ifacename):
+        return self._cache_get('link', [ifacename, 'linkinfo'])
+
     def link_get_mtu(self, ifacename):
         return self._cache_get('link', [ifacename, 'mtu'])
 
@@ -600,13 +603,17 @@ class iproute2(utilsBase):
                                              %ifacename)
         return address
 
-    def link_create(self, ifacename, type, link=None):
+    def link_create(self, ifacename, type, attrs={}):
+        """ generic link_create function """
         if self.link_exists(ifacename):
             return
         cmd = 'link add'
-        if link:
-            cmd += ' link %s' %link
         cmd += ' name %s type %s' %(ifacename, type)
+        if attrs:
+            for k, v in attrs.iteritems():
+                cmd += ' %s' %k
+                if v:
+                    cmd += ' %s' %v
         if self.ipbatch and not self.ipbatch_pause:
             self.add_to_batch(cmd)
         else:
@@ -623,6 +630,17 @@ class iproute2(utilsBase):
             self.exec_command('ip %s' %cmd)
         self._cache_invalidate()
 
+    def link_get_master(self, ifacename):
+        sysfs_master_path = '/sys/class/net/%s/master' %ifacename
+        if os.path.exists(sysfs_master_path):
+            link_path = os.readlink(sysfs_master_path)
+            if link_path:
+                return os.path.basename(link_path)
+            else:
+                return None
+        else:
+            return self._cache_get('link', [ifacename, 'master'])
+
     def bridge_port_vids_add(self, bridgeportname, vids):
         [self.exec_command('bridge vlan add vid %s dev %s'
                           %(v, bridgeportname)) for v in vids]
index 3629a9a34a8453fcc97ce11e12ded665491d5de8..70ba6e21d506789baaebe29b1868f6499e84ea2f 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ setup(name='ifupdown2',
                       'addons/dhcp.py', 'addons/usercmds.py',
                       'addons/ethtool.py',
                       'addons/addressvirtual.py', 'addons/vxlan.py',
-                      'addons/link.py',
+                      'addons/link.py', 'addons/vrf.py',
                       'addons/bridgevlan.py']),
                   ('/etc/network/ifupdown2/', ['config/addons.conf']),
                   ('/var/lib/ifupdown2/policy.d/', []),