Change in vdsm[master]: WIP: Add iproute2 configurator

wudxw at linux.vnet.ibm.com wudxw at linux.vnet.ibm.com
Mon Jun 3 07:41:39 UTC 2013


Mark Wu has uploaded a new change for review.

Change subject: WIP: Add iproute2 configurator
......................................................................

WIP: Add iproute2 configurator

It's sent out to get early feadback. Still there's many improvements
be done, including move duplicate code with ifcfg to the base configurator
class after refactoring ifcfg accordingly.

Change-Id: I762cfa30f78c5a46507b86f53e98bcf79dfc5844
Signed-off-by: Mark Wu <wudxw at linux.vnet.ibm.com>
---
M configure.ac
M lib/vdsm/constants.py.in
M lib/vdsm/netinfo.py
M vdsm.spec.in
M vdsm/netconf/Makefile.am
A vdsm/netconf/iproute2.py
M vdsm/neterrors.py
7 files changed, 229 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/vdsm refs/changes/01/15301/1

diff --git a/configure.ac b/configure.ac
index 265e3ab..377bc5b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -144,6 +144,7 @@
 AC_PATH_PROG([CHOWN_PATH], [chown], [/bin/chown])
 AC_PATH_PROG([CP_PATH], [cp], [/bin/cp])
 AC_PATH_PROG([DD_PATH], [dd], [/bin/dd])
+AC_PATH_PROG([DHCLIENT_PATH], [dhclient], [/sbin/dhclient])
 AC_PATH_PROG([DMIDECODE_PATH], [dmidecode], [/usr/sbin/dmidecode])
 AC_PATH_PROG([DMSETUP_PATH], [dmsetup], [/sbin/dmsetup])
 AC_PATH_PROG([ECHO_PATH], [echo], [/bin/echo])
@@ -154,6 +155,7 @@
 AC_PATH_PROG([HWCLOCK_PATH], [hwclock], [/usr/sbin/hwclock])
 AC_PATH_PROG([IFDOWN_PATH], [ifdown], [/sbin/ifdown])
 AC_PATH_PROG([IFUP_PATH], [ifup], [/sbin/ifup])
+AC_PATH_PROG([IPROUTE_PATH], [ip], [/sbin/ip])
 AC_PATH_PROG([IONICE_PATH], [ionice], [/usr/bin/ionice])
 AC_PATH_PROG([ISCSIADM_PATH], [iscsiadm], [/sbin/iscsiadm])
 AC_PATH_PROG([KILL_PATH], [kill], [/bin/kill])
diff --git a/lib/vdsm/constants.py.in b/lib/vdsm/constants.py.in
index 022affd..c983772 100644
--- a/lib/vdsm/constants.py.in
+++ b/lib/vdsm/constants.py.in
@@ -84,6 +84,7 @@
 EXT_CP = '@CP_PATH@'
 
 EXT_DD = '@DD_PATH@'
+EXT_DHCLIENT = '@DHCLIENT_PATH@'
 EXT_DMIDECODE = '@DMIDECODE_PATH@'
 EXT_DMSETUP = '@DMSETUP_PATH@'
 
@@ -93,6 +94,7 @@
 
 EXT_IFDOWN = '@IFDOWN_PATH@'
 EXT_IFUP = '@IFUP_PATH@'
+EXT_IPROUTE = '@IPROUTE_PATH@'
 EXT_IONICE = '@IONICE_PATH@'
 EXT_ISCSIADM = '@ISCSIADM_PATH@'
 EXT_TC = '@TC_PATH@'
diff --git a/lib/vdsm/netinfo.py b/lib/vdsm/netinfo.py
index c5b9860..127b962 100644
--- a/lib/vdsm/netinfo.py
+++ b/lib/vdsm/netinfo.py
@@ -43,6 +43,7 @@
 PROC_NET_VLAN = '/proc/net/vlan/'
 BONDING_MASTERS = '/sys/class/net/bonding_masters'
 BONDING_SLAVES = '/sys/class/net/%s/bonding/slaves'
+BONDING_OPTION = '/sys/class/net/{bond}/bonding/{option}'
 
 LIBVIRT_NET_PREFIX = 'vdsm-'
 DUMMY_BRIDGE = ';vdsmdummy;'
@@ -203,6 +204,10 @@
         struct.pack("!I", int('1' * prefix + '0' * (32 - prefix), 2)))
 
 
+def netmask2prefix(netmask):
+    return bin(struct.unpack('>I', socket.inet_aton(netmask))[0]).count('1')
+
+
 def getnetmask(dev):
     dev_info_list = ethtool.get_interfaces_info(dev.encode('utf8'))
     netmask = dev_info_list[0].ipv4_netmask
diff --git a/vdsm.spec.in b/vdsm.spec.in
index 039278a..f47714d 100644
--- a/vdsm.spec.in
+++ b/vdsm.spec.in
@@ -892,6 +892,7 @@
 %{_datadir}/%{vdsm_name}/momIF.py*
 %{_datadir}/%{vdsm_name}/netconf/__init__.py*
 %{_datadir}/%{vdsm_name}/netconf/ifcfg.py*
+%{_datadir}/%{vdsm_name}/netconf/iproute2.py*
 %{_datadir}/%{vdsm_name}/netconf/libvirtCfg.py*
 %{_datadir}/%{vdsm_name}/neterrors.py*
 %{_datadir}/%{vdsm_name}/netmodels.py*
diff --git a/vdsm/netconf/Makefile.am b/vdsm/netconf/Makefile.am
index 57be550..14d7b46 100644
--- a/vdsm/netconf/Makefile.am
+++ b/vdsm/netconf/Makefile.am
@@ -24,4 +24,5 @@
 dist_vdsmnetconf_PYTHON = \
 	__init__.py \
 	ifcfg.py \
+	iproute2.py \
 	libvirtCfg.py
diff --git a/vdsm/netconf/iproute2.py b/vdsm/netconf/iproute2.py
new file mode 100644
index 0000000..deb989d
--- /dev/null
+++ b/vdsm/netconf/iproute2.py
@@ -0,0 +1,217 @@
+# Copyright (C) 2013, IBM Corporation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# Refer to the README and COPYING files for full details of the license
+#
+
+import os
+import threading
+from contextlib import contextmanager
+
+from neterrors import ConfigNetworkError
+from storage.misc import execCmd
+from vdsm import constants
+from vdsm import netinfo
+from vdsm import utils
+from netconf import Configurator
+import neterrors as ne
+
+
+class Iproute2(Configurator):
+
+    def configureBridge(self, bridge, **opts):
+        # Todo: check the stp option.
+        execCmd(['brctl', 'addbr', bridge.name])
+        self.configWriter.ifdown(bridge)
+        if bridge.port:
+            bridge.port.configure(bridge=bridge.name, **opts)
+            execCmd(['brctl', 'addif', bridge.name, bridge.port.name])
+        self.configWriter.configureIp(bridge)
+        self.configWriter.configureMtu(bridge)
+        self.configWriter.ifup(bridge)
+
+    def configureVlan(self, vlan, bridge=None, **opts):
+        vlan.device.configure(vlan=vlan.name, **opts)
+        self.configWriter.addVlan(vlan)
+        if vlan.ip:
+            self.configWriter.configureIp(vlan)
+        self.configWriter.configureMtu(vlan)
+        self.configWriter.ifup(vlan)
+
+    def configureBond(self, bond, bridge=None, **opts):
+        try:
+            with open(netinfo.BONDING_MASTERS, 'w') as f:
+                f.write('+%s' % bond.name)
+        except IOError as e:
+            if e.errno == os.errno.EEXIST:
+                self.configWriter.ifdown(bond)
+            else:
+                raise
+
+        for option in bond.options.split():
+            key, value = option.split('=')
+            with open(netinfo.BONDING_OPTION.format(bond=bond.name,
+                                                    option=key), 'w') as f:
+                f.write(value)
+        for slave in bond.slaves:
+            self.configWriter.ifdown(slave)
+            with open(netinfo.BONDING_SLAVES % bond.name, 'w') as f:
+                f.write('+%s' % slave.name)
+            slave.configure(**opts)
+
+        if bond.ip:
+            self.configWriter.configureIp(bond)
+        self.configWriter.configureMtu(bond)
+        self.configWriter.ifup(bond)
+
+    def configureNic(self, nic, **opts):
+        if nic.ip:
+            self.configWriter.configureIp(nic)
+        self.configWriter.configureMtu(nic)
+        self.configWriter.ifup(nic)
+
+    def removeBridge(self, bridge):
+        self.configWriter.ifdown(bridge)
+        execCmd([constants.EXT_BRCTL, 'delbr', bridge.name])
+        if bridge.port:
+            bridge.port.remove()
+
+    def removeVlan(self, vlan):
+        vlan.mtu = netinfo.getMtu(vlan.name)
+        self.configWriter.ifdown(vlan)
+        self.configWriter.removeLink(vlan)
+        vlan.device.remove()
+
+    @contextmanager
+    def _removeIface(self, iface):
+        _netinfo = netinfo.NetInfo()
+        self.configWriter.ifdown(iface)
+        multiple_usage = _netinfo.ifaceHasMultiUsers(iface.name)
+        yield not multiple_usage
+        if multiple_usage:
+            self._setNewMtu(iface, _netinfo=_netinfo)
+            if iface.master is None:
+                self.configWriter.removeIp(iface)
+        if not hasattr(iface, 'destroy'):
+            self.configWriter.ifup(iface)
+
+    def removeBond(self, bonding):
+        with self._removeIface(bonding) as to_be_removed:
+            if to_be_removed:
+                if hasattr(bonding, 'destroy'):
+                    with open(netinfo.BONDING_MASTERS, 'w') as f:
+                        f.write('-%s' % bonding.name)
+                    for slave in bonding.slaves:
+                        slave.remove()
+                else:
+                    self.configWriter.configureMtu(bonding,
+                                                   netinfo.DEFAULT_MTU)
+                    self.configWriter.removeIp(bonding)
+
+    def removeNic(self, nic):
+        with self._removeIface(nic) as to_be_removed:
+            if to_be_removed:
+                self.configWriter.configureMtu(nic, netinfo.DEFAULT_MTU)
+                self.configWriter.removeIp(nic)
+
+    def editBonding(self, bond, newAttrs, _netinfo):
+        self.configWriter.ifdown(bond)
+        for slave in bond.slaves:
+            slave.remove()
+        bond.updateSlaves(newAttrs['nics'], _netinfo)
+        bond.options = newAttrs.get('options', None)
+        bridge = _netinfo.getBridgedNetworkForIface(bond.name)
+        self.configureBond(bond, bridge)
+
+
+class DhcpClient():
+    PID_FILE = '/var/run/dhclient-%s.pid'
+    LEASE_DIR = '/var/lib/dhclient/'
+    LEASE_FILE = LEASE_DIR + 'dhclient-%s.lease'
+
+    def __init__(self, iface):
+        self.iface = iface
+        self.pidFile = self.PID_FILE % self.iface
+        if not os.path.exists(self.LEASE_DIR):
+            execCmd(['mkdir', self.LEASE_DIR])
+        self.leaseFile = (self.LEASE_FILE % self.iface)
+
+    def _dhclient(self):
+        rc, out, err = execCmd([constants.EXT_DHCLIENT, '-1', '-pf',
+                                self.pidFile, '-lf', self.leaseFile,
+                                self.iface])
+        return rc, out, err
+
+    def start(self, async):
+        if async:
+            t = threading.Thread(target=self._dhclient, name='vdsm-dhclient-%s'
+                                 % self.iface)
+            t.daemon = True
+            t.start()
+        else:
+            rc, out, err = self._dhclient()
+            return rc
+
+    def shutdown(self):
+        if os.path.exists(self.pidFile):
+            pid = int(file(self.pidFile).readline().strip())
+            os.kill(pid)
+            utils.rmFile(self.pidFile)
+
+
+class ConfigWriter(object):
+    def _runIpCmd(self, args):
+        rc, out, err = execCmd([constants.EXT_IPROUTE] + args)
+        if rc != 0:
+            raise ConfigNetworkError(ne.ERR_FAILED_IFUP,
+                                     out if out else '')
+
+    def configureIp(self, netObj):
+        ipaddr, netmask, gateway, _, _ = netObj.getIpConfig()
+        if ipaddr:
+            self._runIpCmd(['addr', 'add', 'dev', netObj.name, '%s/%s' %
+                           (ipaddr, netinfo.netmask2prefix(netmask))])
+            if gateway:
+                self._runIpCmd(['route', 'add', 'default', 'via', gateway])
+
+    def removeIp(self, netObj):
+        self._runIpCmd(['address', 'flush', 'dev', netObj.name])
+
+    def configureMtu(self, netObj, mtu=None):
+        if netObj.mtu:
+            self._runIpCmd(['link', 'set', 'dev', netObj.name, 'mtu',
+                           mtu if mtu else str(netObj.mtu)])
+
+    def ifup(self, netObj):
+        self._runIpCmd(['link', 'set', 'dev', netObj.name, 'up'])
+        _, _, _, bootproto, async = netObj.getIpConfig()
+        if bootproto == 'dhcp':
+            dhclient = DhcpClient(netObj.name)
+            dhclient.start(async)
+
+    def ifdown(self, netObj):
+        iface = netObj.name
+        self._runIpCmd(['link', 'set', 'dev', iface, 'down'])
+        self.removeIp(netObj)
+        dhclient = DhcpClient(iface)
+        dhclient.shutdown()
+
+    def addVlan(self, vlan):
+        self._runIpCmd(['link', 'add', 'link', vlan.device.name, 'name',
+                        vlan.name, 'type', 'vlan', 'id', vlan.tag])
+
+    def removeLink(self, netObj):
+        self._runIpCmd(['link', 'del', 'dev', netObj.name])
diff --git a/vdsm/neterrors.py b/vdsm/neterrors.py
index 034414c..5b611ac 100644
--- a/vdsm/neterrors.py
+++ b/vdsm/neterrors.py
@@ -28,6 +28,7 @@
 ERR_BAD_BRIDGE = 27
 ERR_USED_BRIDGE = 28
 ERR_FAILED_IFUP = 29
+ERR_FAILED_IP = 30
 ERR_LOST_CONNECTION = 10    # noConPeer
 
 


--
To view, visit http://gerrit.ovirt.org/15301
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I762cfa30f78c5a46507b86f53e98bcf79dfc5844
Gerrit-PatchSet: 1
Gerrit-Project: vdsm
Gerrit-Branch: master
Gerrit-Owner: Mark Wu <wudxw at linux.vnet.ibm.com>


More information about the vdsm-patches mailing list