[Autotest][PATCH 2/5] virt: Adds OpenVSwitch support to virt tests.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



When autotest tries add tap to bridge then test recognize if
test is bridge is standard linux or OpenVSwitch.

And adds some utils for bridge manipulation.

Signed-off-by: Jiří Župka <jzupka@xxxxxxxxxx>
---
 client/shared/openvswitch.py             |  583 ++++++++++++++++++++++++++++++
 client/tests/virt/virttest/utils_misc.py |  473 +++++++++++++++++++++++-
 2 files changed, 1042 insertions(+), 14 deletions(-)
 create mode 100644 client/shared/openvswitch.py

diff --git a/client/shared/openvswitch.py b/client/shared/openvswitch.py
new file mode 100644
index 0000000..98dc5e9
--- /dev/null
+++ b/client/shared/openvswitch.py
@@ -0,0 +1,583 @@
+import logging, re, os, select, signal
+try:
+    import autotest.common as common
+except ImportError:
+    import common
+from autotest.client import utils, package
+from autotest.client.shared import error
+from autotest.client.shared.base_utils import VersionableClass
+
+
+class ServiceManagerInterface(VersionableClass):
+    def __new__(cls, *args, **kargs):
+        ServiceManagerInterface.master_class = ServiceManagerInterface
+        return super(ServiceManagerInterface, cls).__new__(cls, *args, **kargs)
+
+    @classmethod
+    def get_version(cls):
+        """
+        Get version of ServiceManager.
+        @return: Version of ServiceManager.
+        """
+        return open("/proc/1/comm", "r").read().strip()
+
+    def stop(self, service_name):
+        raise NotImplementedError("Method 'stop' must be"
+                                  " implemented in child class")
+
+
+    def start(self, service_name):
+        raise NotImplementedError("Method 'start' must be"
+                                  " implemented in child class")
+
+
+    def restart(self, service_name):
+        raise NotImplementedError("Method 'restart' must be"
+                                  " implemented in child class")
+
+
+    def status(self, service_name):
+        raise NotImplementedError("Method 'status' must be"
+                                  " implemented in child class")
+
+
+class ServiceManagerSystemD(ServiceManagerInterface, VersionableClass):
+    @classmethod
+    def is_right_version(cls, version):
+        if version == "systemd":
+                return True
+        return False
+
+    def stop(self, service_name):
+        utils.run("systemctl stop %s.service" % (service_name))
+
+
+    def start(self, service_name):
+        utils.run("systemctl start %s.service" % (service_name))
+
+
+    def restart(self, service_name):
+        utils.run("systemctl restart %s.service" % (service_name))
+
+
+    def status(self, service_name):
+        utils.run("systemctl show %s.service" % (service_name))
+
+
+class ServiceManagerSysvinit(ServiceManagerInterface, VersionableClass):
+    @classmethod
+    def is_right_version(cls, version):
+        if version == "init":
+                return True
+        return False
+
+
+    def stop(self, service_name):
+        utils.run("/etc/init.d/%s stop" % (service_name))
+
+
+    def start(self, service_name):
+        utils.run("/etc/init.d/%s start" % (service_name))
+
+
+    def restart(self, service_name):
+        utils.run("/etc/init.d/%s restart" % (service_name))
+
+
+class ServiceManager(ServiceManagerInterface):
+    pass
+
+
+class PathComplete(object):
+    def __init__(self, bindir):
+        self.bindir = bindir
+
+
+    def __call__(self, path):
+        return os.path.join(self.bindir, path)
+
+
+class OpenVSwitchControl(object):
+    """
+    Class select the best matches control class for installed version
+    of OpenVSwitch.
+
+    OpenVSwtich parameters are described in man ovs-vswitchd.conf.db
+    """
+    def __new__(cls, db_path=None, db_socket=None, db_pidfile=None,
+                 ovs_pidfile=None, dbschema=None, install_prefix=None):
+        """
+        Makes initialization of OpenVSwitch.
+
+        @param tmpdir: Tmp directory for save openvswitch test files.
+        @param db_path: Path of OVS database.
+        @param db_socket: Path of OVS db socket.
+        @param db_pidfile: Path of OVS db ovsdb-server pid.
+        @param ovs_pidfile: Path of OVS ovs-vswitchd pid.
+        @param install_prefix: Path where is openvswitch installed.
+        """
+        # if path is None set default path.
+        if not install_prefix:
+            install_prefix = "/"
+        if not db_path:
+            db_path = os.path.join(install_prefix,
+                                   "/etc/openvswitch/conf.db")
+        if not db_socket:
+            db_socket = os.path.join(install_prefix,
+                                     "/var/run/openvswitch/db.sock")
+        if not db_pidfile:
+            db_pidfile = os.path.join(install_prefix,
+                                      "/var/run/openvswitch/ovsdb-server.pid")
+        if not ovs_pidfile:
+            ovs_pidfile = os.path.join(install_prefix,
+                                       "/var/run/openvswitch/ovs-vswitchd.pid")
+        if not dbschema:
+            dbschema = os.path.join(install_prefix,
+                                    "/usr/share/openvswitch/vswitch.ovsschema")
+
+        OpenVSwitchControl.install_prefix = PathComplete(install_prefix)
+
+        OpenVSwitchControl.db_path = db_path
+        OpenVSwitchControl.db_socket = db_socket
+        OpenVSwitchControl.db_pidfile = db_pidfile
+        OpenVSwitchControl.ovs_pidfile = ovs_pidfile
+
+        OpenVSwitchControl.dbschema = install_prefix, dbschema
+        OpenVSwitchControl.bin = PathComplete(os.path.join(install_prefix,
+                                                           "usr/bin"))
+        OpenVSwitchControl.sbin = PathComplete(os.path.join(install_prefix,
+                                                            "usr/sbin"))
+
+        return super(OpenVSwitchControl, cls).__new__(cls)
+
+
+    @staticmethod
+    def convert_version_to_int(version):
+        """
+        @param version: (int) Converted from version string 1.4.0 => int 140
+        """
+        if (isinstance(version, int)):
+            return version
+        try:
+            int_ver = int(version.replace(".", ""))
+        except:
+            raise error.AutotestError("Wrong version format '%s'" % (version))
+        return int_ver
+
+
+    @classmethod
+    def get_version(cls):
+        """
+        Get version of installed OpenVSwtich.
+
+        @return: Version of OpenVSwtich.
+        """
+        sbin_path = OpenVSwitchControl.sbin
+        version = None
+        try:
+            result = utils.run(sbin_path("ovs-vswitchd"),
+                               args=["--version"])
+            pattern = "ovs-vswitchd \(Open vSwitch\) (.+)"
+            version = re.search(pattern, result.stdout).group(1)
+        except error.CmdError:
+            logging.debug("OpenVSwitch is not available in system.")
+        return version
+
+
+    def status(self):
+        raise NotImplementedError()
+
+
+    def add_br(self, br_name):
+        raise NotImplementedError()
+
+
+    def del_br(self, br_name):
+        raise NotImplementedError()
+
+
+    def br_exist(self, br_name):
+        raise NotImplementedError()
+
+
+    def list_br(self):
+        raise NotImplementedError()
+
+
+    def add_port(self, br_name, port_name):
+        raise NotImplementedError()
+
+
+    def del_port(self, br_name, port_name):
+        raise NotImplementedError()
+
+
+    def add_port_tag(self, port_name, tag):
+        raise NotImplementedError()
+
+
+    def add_port_trunk(self, port_name, trunk):
+        raise NotImplementedError()
+
+
+    def set_vlanmode(self, port_name, vlan_mode):
+        raise NotImplementedError()
+
+
+    def check_port_in_br(self, br_name, port_name):
+        raise NotImplementedError()
+
+
+class OpenVSwitchControlCli(OpenVSwitchControl, VersionableClass):
+    """
+    Class select the best matches control class for installed version
+    of OpenVSwitch.
+    """
+    def __new__(cls, db_path=None, db_socket=None, db_pidfile=None,
+                ovs_pidfile=None, dbschema=None, install_prefix=None):
+        OpenVSwitchControlCli.master_class = OpenVSwitchControlCli
+        return super(OpenVSwitchControlCli, cls).__new__(cls, db_path,
+                                                         db_socket,
+                                                         db_pidfile,
+                                                         ovs_pidfile,
+                                                         dbschema,
+                                                         install_prefix)
+
+
+class OpenVSwitchControlDB(OpenVSwitchControl, VersionableClass):
+    """
+    Class select the best matches control class for installed version
+    of OpenVSwitch.
+    """
+
+    def __new__(cls, db_path=None, db_socket=None, db_pidfile=None,
+                 ovs_pidfile=None, dbschema=None, install_prefix=None):
+        OpenVSwitchControlDB.master_class = OpenVSwitchControlDB
+        return super(OpenVSwitchControlDB, cls).__new__(cls, db_path,
+                                                        db_socket,
+                                                        db_pidfile,
+                                                        ovs_pidfile,
+                                                        dbschema,
+                                                        install_prefix)
+
+
+class OpenVSwitchControlDB_140(OpenVSwitchControlDB, VersionableClass):
+    """
+    Don't use this class directly. This class is automatically selected by
+    OpenVSwitchControl.
+    """
+    @classmethod
+    def is_right_version(cls, version):
+        """
+        Check condition for select control class.
+
+        @param version: version of OpenVSwtich
+        """
+        if version is not None:
+            int_ver = cls.convert_version_to_int(version)
+            if int_ver <= 140:
+                return True
+        return False
+
+    #TODO: implement database manipulation methods.
+
+
+class OpenVSwitchControlCli_140(OpenVSwitchControlCli, VersionableClass):
+    """
+    Don't use this class directly. This class is automatically selected by
+    OpenVSwitchControl.
+    """
+    @classmethod
+    def is_right_version(cls, version):
+        """
+        Check condition for select control class.
+
+        @param version: version of OpenVSwtich
+        """
+        if version is not None:
+            int_ver = cls.convert_version_to_int(version)
+            if int_ver <= 140:
+                return True
+        return False
+
+
+    def ovs_vsctl(self, parmas, ignore_status=False):
+        return utils.run(self.bin("ovs-vsctl"), timeout=10,
+                         ignore_status=ignore_status, verbose=False,
+                         args=["--db=unix:%s" % (self.db_socket)] + parmas)
+
+
+    def status(self):
+        return self.ovs_vsctl(["show"]).stdout
+
+
+    def add_br(self, br_name):
+        self.ovs_vsctl(["add-br", br_name])
+
+
+    def add_fake_br(self, br_name, parent, vlan):
+        self.ovs_vsctl(["add-br", br_name, parent, vlan])
+
+
+    def del_br(self, br_name):
+        try:
+            self.ovs_vsctl(["del-br", br_name])
+        except error.CmdError, e:
+            logging.debug(e.result_obj)
+            raise
+
+
+    def br_exist(self, br_name):
+        try:
+            self.ovs_vsctl(["br-exists", br_name])
+        except error.CmdError, e:
+            if e.result_obj.exit_status == 2:
+                return False
+            else:
+                raise
+        return True
+
+
+    def list_br(self):
+        return self.ovs_vsctl(["list-br"]).stdout.splitlines()
+
+
+    def add_port(self, br_name, port_name):
+        self.ovs_vsctl(["add-port", br_name, port_name])
+
+
+    def del_port(self, br_name, port_name):
+        self.ovs_vsctl(["del-port", br_name, port_name])
+
+
+    def add_port_tag(self, port_name, tag):
+        self.ovs_vsctl(["set", "Port", port_name, "tag=%s" % tag])
+
+
+    def add_port_trunk(self, port_name, trunk):
+        """
+        @param trunk: list of vlans id.
+        """
+        trunk = map(lambda x: str(x), trunk)
+        trunk = "[" + ",".join(trunk) + "]"
+        self.ovs_vsctl(["set", "Port", port_name, "trunk=%s" % trunk])
+
+
+    def set_vlanmode(self, port_name, vlan_mode):
+        self.ovs_vsctl(["set", "Port", port_name, "vlan-mode=%s" % vlan_mode])
+
+
+    def list_ports(self, br_name):
+        return self.ovs_vsctl(["list-ports", br_name]).stdout.splitlines()
+
+
+    def port_to_br(self, port_name):
+        """
+        Return bridge which contain port.
+
+        @param port_name: Name of port.
+        @return: Bridge name or None if there is no bridge which contain port.
+        """
+        bridge = None
+        try:
+            bridge = self.ovs_vsctl(["port-to-br", port_name]).stdout
+        except error.CmdError, e:
+            if e.result_obj.exit_status == 1:
+                pass
+        return bridge
+
+
+class OpenVSwitchSystem(OpenVSwitchControlCli, OpenVSwitchControlDB):
+    """
+    OpenVSwtich class.
+    """
+    def __new__(cls, db_path=None, db_socket=None, db_pidfile=None,
+                 ovs_pidfile=None, dbschema=None, install_prefix=None):
+        return super(OpenVSwitchSystem, cls).__new__(cls, db_path, db_socket,
+                                                db_pidfile, ovs_pidfile,
+                                                dbschema, install_prefix)
+
+
+    def __init__(self, db_path=None, db_socket=None, db_pidfile=None,
+                 ovs_pidfile=None, dbschema=None, install_prefix=None):
+        """
+        Makes initialization of OpenVSwitch.
+
+        @param db_path: Path of OVS database.
+        @param db_socket: Path of OVS db socket.
+        @param db_pidfile: Path of OVS db ovsdb-server pid.
+        @param ovs_pidfile: Path of OVS ovs-vswitchd pid.
+        @param install_prefix: Path where is openvswitch installed.
+        """
+        super(OpenVSwitchSystem, self).__init__(self, db_path, db_socket,
+                                                db_pidfile, ovs_pidfile,
+                                                dbschema, install_prefix)
+
+        self.cleanup = False
+        self.pid_files_path = None
+
+
+    def is_installed(self):
+        """
+        Check if OpenVSwitch is already installed in system on default places.
+
+        @return: Version of OpenVSwtich.
+        """
+        if self.get_version():
+            return True
+        else:
+            return False
+
+
+    def check_db_daemon(self):
+        """
+        Check if OVS daemon is started correctly.
+        """
+        working = utils.program_is_alive("ovsdb-server", self.pid_files_path)
+        if not working:
+            logging.error("OpenVSwitch database daemon with PID in file %s"
+                          " not working.", self.db_pidfile)
+        return working
+
+
+    def check_switch_daemon(self):
+        """
+        Check if OVS daemon is started correctly.
+        """
+        working = utils.program_is_alive("ovs-vswitchd", self.pid_files_path)
+        if not working:
+            logging.error("OpenVSwitch switch daemon with PID in file %s"
+                          " not working.", self.ovs_pidfile)
+        return working
+
+
+    def check_db_file(self):
+        """
+        Check if db_file exists.
+        """
+        exists = os.path.exists(self.db_path)
+        if not exists:
+            logging.error("OpenVSwitch database file %s not exists.",
+                           self.db_path)
+        return exists
+
+
+    def check_db_socket(self):
+        """
+        Check if db socket exists.
+        """
+        exists = os.path.exists(self.db_socket)
+        if not exists:
+            logging.error("OpenVSwitch database socket file %s not exists.",
+                          self.db_socket)
+        return exists
+
+
+    def check(self):
+        return (self.check_db_daemon() and self.check_switch_daemon() and
+                self.check_db_file() and self.check_db_socket())
+
+
+    def init_system(self):
+        """
+        Create new dbfile without any configuration.
+        """
+        sm = ServiceManager()
+        try:
+            sm.start("openvswitch")
+        except error.CmdError:
+            logging.error("Service OpenVSwitch is probably not"
+                          " installed in system.")
+            raise
+        self.pid_files_path = "/var/run/openvswitch/"
+
+
+    def clean(self):
+        """
+        Empty cleanup function
+        """
+        pass
+
+
+class OpenVSwitch(OpenVSwitchSystem):
+    """
+    OpenVSwtich class.
+    """
+    def __new__(cls, tmpdir, db_path=None, db_socket=None, db_pidfile=None,
+                 ovs_pidfile=None, dbschema=None, install_prefix=None):
+        return super(OpenVSwitch, cls).__new__(cls, db_path, db_socket,
+                                                db_pidfile, ovs_pidfile,
+                                                dbschema, install_prefix)
+
+
+    def __init__(self, tmpdir, db_path=None, db_socket=None, db_pidfile=None,
+                 ovs_pidfile=None, dbschema=None, install_prefix=None):
+        """
+        Makes initialization of OpenVSwitch.
+
+        @param tmpdir: Tmp directory for save openvswitch test files.
+        @param db_path: Path of OVS database.
+        @param db_socket: Path of OVS db socket.
+        @param db_pidfile: Path of OVS db ovsdb-server pid.
+        @param ovs_pidfile: Path of OVS ovs-vswitchd pid.
+        @param install_prefix: Path where is openvswitch installed.
+        """
+        super(OpenVSwitch, self).__init__(db_path, db_socket, db_pidfile,
+                                        ovs_pidfile, dbschema, install_prefix)
+        self.tmpdir = "/%s/openvswitch" % (tmpdir)
+        try:
+            os.mkdir(self.tmpdir)
+        except OSError, e:
+            if e.errno != 17:
+                raise
+
+
+    def init_db(self):
+        utils.run(self.bin("ovsdb-tool"), timeout=10,
+                  args=["create", self.db_path, self.dbschema])
+        utils.run(self.sbin("ovsdb-server"), timeout=10,
+                  args=["--remote=punix:%s" % (self.db_socket),
+                        "--remote=db:Open_vSwitch,manager_options",
+                        "--pidfile=%s" % (self.db_pidfile),
+                        "--detach"])
+        self.ovs_vsctl(["--no-wait", "init"])
+
+
+    def start_ovs_vswitchd(self):
+        utils.run(self.sbin("ovs-vswitchd"), timeout=10,
+                  args=["--detach",
+                        "--pidfile=%s" % (self.ovs_pidfile),
+                        "unix:%s" % (self.db_socket)])
+
+
+    def init_new(self):
+        """
+        Create new dbfile without any configuration.
+        """
+        self.db_path = os.path.join(self.tmpdir, "conf.db")
+        self.db_socket = os.path.join(self.tmpdir, "db.sock")
+        self.db_pidfile = utils.get_pid_path("ovsdb-server")
+        self.ovs_pidfile = utils.get_pid_path("ovs-vswitchd")
+        self.dbschema = "/usr/share/openvswitch/vswitch.ovsschema"
+
+        self.cleanup = True
+        sm = ServiceManager()
+        #Stop system openvswitch
+        sm.stop("openvswitch")
+        self.clean()
+        if (os.path.exists(self.db_path)):
+            os.remove(self.db_path)
+
+        self.init_db()
+        self.start_ovs_vswitchd()
+
+
+    def clean(self):
+        logging.debug("Killall ovsdb-server")
+        utils.signal_program("ovsdb-server")
+        if (utils.program_is_alive("ovsdb-server")):
+            utils.signal_program("ovsdb-server", signal.SIGKILL)
+        logging.debug("Killall ovs-vswitchd")
+        utils.signal_program("ovs-vswitchd")
+        if (utils.program_is_alive("ovs-vswitchd")):
+            utils.signal_program("ovs-vswitchd", signal.SIGKILL)
diff --git a/client/tests/virt/virttest/utils_misc.py b/client/tests/virt/virttest/utils_misc.py
index c2f81dc..e416551 100644
--- a/client/tests/virt/virttest/utils_misc.py
+++ b/client/tests/virt/virttest/utils_misc.py
@@ -8,9 +8,10 @@ import time, string, random, socket, os, signal, re, logging, commands, cPickle
 import fcntl, shelve, ConfigParser, sys, UserDict, inspect, tarfile
 import struct, shutil, glob, HTMLParser, urllib, traceback, platform
 from autotest.client import utils, os_dep
-from autotest.client.shared import error, logging_config
+from autotest.client.shared import error, logging_config, openvswitch
 from autotest.client.shared import logging_manager, git
 
+
 try:
     import koji
     KOJI_INSTALLED = True
@@ -25,6 +26,7 @@ if ARCH == "ppc64":
     SIOCSIFFLAGS   = 0x8914
     SIOCGIFINDEX   = 0x8933
     SIOCBRADDIF    = 0x89a2
+    SIOCBRDELIF    = 0x89a3
     # From linux/include/linux/if_tun.h
     TUNSETIFF      = 0x800454ca
     TUNGETIFF      = 0x400454d2
@@ -38,9 +40,10 @@ else:
     # From include/linux/sockios.h
     SIOCSIFHWADDR = 0x8924
     SIOCGIFHWADDR = 0x8927
-    SIOCSIFFLAGS = 0x8914
-    SIOCGIFINDEX = 0x8933
-    SIOCBRADDIF = 0x89a2
+    SIOCSIFFLAGS  = 0x8914
+    SIOCGIFINDEX  = 0x8933
+    SIOCBRADDIF   = 0x89a2
+    SIOCBRDELIF   = 0x89a3
     # From linux/include/linux/if_tun.h
     TUNSETIFF = 0x400454ca
     TUNGETIFF = 0x800454d2
@@ -52,6 +55,110 @@ else:
     IFF_UP = 0x1
 
 
+class Bridge(object):
+    def get_structure(self):
+        """
+        Get bridge list.
+        """
+        br_i = re.compile("^(\S+).*?(\S+)$", re.MULTILINE)
+        nbr_i = re.compile("^\s+(\S+)$", re.MULTILINE)
+        out_line = utils.run("brctl show", verbose=False).stdout.splitlines()
+        result = dict()
+        bridge = None
+        iface = None
+        for line in out_line[1:]:
+            try:
+                (tmpbr, iface) = br_i.findall(line)[0]
+                bridge = tmpbr
+                result[bridge] = []
+            except IndexError:
+                iface = nbr_i.findall(line)[0]
+
+            if iface:  # add interface to bridge
+                result[bridge].append(iface)
+
+        return result
+
+
+    def list_br(self):
+        return self.get_structure().keys()
+
+
+    def port_to_br(self, port_name):
+        """
+        Return bridge which contain port.
+
+        @param port_name: Name of port.
+        @return: Bridge name or None if there is no bridge which contain port.
+        """
+        bridge = None
+        for (br, ifaces) in self.get_structure().iteritems():
+            if port_name in ifaces:
+                bridge = br
+        return bridge
+
+
+    def _br_ioctl(self, io_cmd, brname, ifname):
+        ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
+        index = if_nametoindex(ifname)
+        if index == 0:
+            raise TAPNotExistError(ifname)
+        ifr = struct.pack("16si", brname, index)
+        _ = fcntl.ioctl(ctrl_sock, io_cmd, ifr)
+        ctrl_sock.close()
+
+
+    def add_port(self, brname, ifname):
+        """
+        Add a device to bridge
+
+        @param ifname: Name of TAP device
+        @param brname: Name of the bridge
+        """
+        try:
+            self._br_ioctl(SIOCBRADDIF, brname, ifname)
+        except IOError, details:
+            raise BRAddIfError(ifname, brname, details)
+
+
+    def del_port(self, brname, ifname):
+        """
+        Remove a TAP device from bridge
+
+        @param ifname: Name of TAP device
+        @param brname: Name of the bridge
+        """
+        try:
+            self._br_ioctl(SIOCBRDELIF, brname, ifname)
+        except IOError, details:
+            raise BRDelIfError(ifname, brname, details)
+
+
+def __init_openvswitch(func):
+    """
+    Decorator used for late init of __ovs variable.
+    """
+    def wrap_init(*args, **kargs):
+        global __ovs
+        if __ovs is None:
+            try:
+                __ovs = openvswitch.OpenVSwitchSystem()
+                __ovs.init_system()
+                if (not __ovs.check()):
+                    raise Exception("Check of OpenVSwitch failed.")
+            except Exception, e:
+                logging.debug("System not support OpenVSwitch:")
+                logging.debug(e)
+
+        return func(*args, **kargs)
+    return wrap_init
+
+
+#Global variable for OpenVSwitch
+__ovs = None
+__bridge = Bridge()
+
+
 def lock_file(filename, mode=fcntl.LOCK_EX):
     f = open(filename, "w")
     fcntl.lockf(f, mode)
@@ -132,6 +239,74 @@ class BRAddIfError(NetError):
                 (self.ifname, self.brname, self.details))
 
 
+class BRDelIfError(NetError):
+    def __init__(self, ifname, brname, details):
+        NetError.__init__(self, ifname, brname, details)
+        self.ifname = ifname
+        self.brname = brname
+        self.details = details
+
+    def __str__(self):
+        return ("Can not del if %s to bridge %s: %s" %
+                (self.ifname, self.brname, self.details))
+
+
+class IfNotInBridgeError(NetError):
+    def __init__(self, ifname, details):
+        NetError.__init__(self, ifname, details)
+        self.ifname = ifname
+        self.details = details
+
+    def __str__(self):
+        return ("If %s in any bridge: %s" %
+                (self.ifname, self.details))
+
+
+class BRNotExistError(NetError):
+    def __init__(self, brname, details):
+        NetError.__init__(self, brname, details)
+        self.brname = brname
+        self.details = details
+
+    def __str__(self):
+        return ("Bridge %s not exists: %s" % (self.brname, self.details))
+
+
+class IfChangeBrError(NetError):
+    def __init__(self, ifname, old_brname, new_brname, details):
+        NetError.__init__(self, ifname, old_brname, new_brname, details)
+        self.ifname = ifname
+        self.new_brname = new_brname
+        self.old_brname = old_brname
+        self.details = details
+
+    def __str__(self):
+        return ("Can not change if %s from bridge %s to bridge %s: %s" %
+                (self.ifname, self.new_brname, self.oldbrname, self.details))
+
+
+class IfChangeAddrError(NetError):
+    def __init__(self, ifname, ipaddr, details):
+        NetError.__init__(self, ifname, ipaddr, details)
+        self.ifname = ifname
+        self.ipaddr = ipaddr
+        self.details = details
+
+    def __str__(self):
+        return ("Can not change if %s from bridge %s to bridge %s: %s" %
+                (self.ifname, self.ipaddr, self.details))
+
+
+class BRIpError(NetError):
+    def __init__(self, brname):
+        NetError.__init__(self, brname)
+        self.brname = brname
+
+    def __str__(self):
+        return ("Bridge %s doesn't have assigned any ip address. It is"
+                " impossible to start dnsmasq for this bridge." % (self.brname))
+
+
 class HwAddrSetError(NetError):
     def __init__(self, ifname, mac):
         NetError.__init__(self, ifname, mac)
@@ -151,10 +326,22 @@ class HwAddrGetError(NetError):
         return "Can not get mac of interface %s" % self.ifname
 
 
+class VlanError(NetError):
+    def __init__(self, ifname, details):
+        NetError.__init__(self, ifname, details)
+        self.ifname = ifname
+        self.details = details
+
+    def __str__(self):
+        return ("Vlan error on interface %s: %s" %
+                (self.ifname, self.details))
+
+
 class PropCanKeyError(KeyError, AttributeError):
     def __init__(self, key, slots):
         self.key = key
         self.slots = slots
+
     def __str__(self):
         return "Unsupported key name %s (supported keys: %s)" % (
                     str(self.key), str(self.slots))
@@ -3823,33 +4010,291 @@ def open_tap(devname, ifname, vnet_hdr=True):
     return tapfd
 
 
-def add_to_bridge(ifname, brname):
+def is_virtual_network_dev(dev_name):
+    """
+    @param dev_name: Device name.
+
+    @return: True if dev_name is in virtual/net dir, else false.
+    """
+    if dev_name in os.listdir("/sys/devices/virtual/net/"):
+        return True
+    else:
+        return False
+
+
+def find_dnsmasq_listen_address():
+    """
+    Search all dnsmasq listen addresses.
+
+    @param bridge_name: Name of bridge.
+    @param bridge_ip: Bridge ip.
+    @return: List of ip where dnsmasq is listening.
+    """
+    cmd = "ps -Af | grep dnsmasq"
+    result = utils.run(cmd).stdout
+    return re.findall("--listen-address (.+?) ", result, re.MULTILINE)
+
+
+def local_runner(cmd, timeout=None):
+    return utils.run(cmd, verbose=False, timeout=timeout).stdout
+
+
+def local_runner_status(cmd, timeout=None):
+    return utils.run(cmd, verbose=False, timeout=timeout).exit_status
+
+
+def get_net_if(runner=None):
+    """
+    @param output: Output form ip link command.
+    @return: List of netowork interface
+    """
+    if runner is None:
+        runner = local_runner
+    cmd = "ip link"
+    result = runner(cmd)
+    return re.findall("^\d+: (\S+?)[@:].*$", result, re.MULTILINE)
+
+
+def get_net_if_addrs(if_name, runner=None):
+    """
+    Get network device ip addresses. ioctl not used because there is
+    incompatibility with ipv6.
+
+    @param if_name: Name of interface.
+    @return: List ip addresses of network interface.
+    """
+    if runner is None:
+        runner = local_runner
+    cmd = "ip addr show %s" % (if_name)
+    result = runner(cmd)
+    return {"ipv4": re.findall("inet (.+?)/..?", result, re.MULTILINE),
+            "ipv6": re.findall("inet6 (.+?)/...?", result, re.MULTILINE),
+            "mac": re.findall("link/ether (.+?) ", result, re.MULTILINE)}
+
+
+def get_net_if_and_addrs(runner=None):
+    """
+    @return: Dict of interfaces and their addresses {"ifname": addrs}.
+    """
+    ret = {}
+    ifs = get_net_if(runner)
+    for iface in ifs:
+        ret[iface] = get_net_if_addrs(iface, runner)
+    return ret
+
+
+def set_net_if_ip(if_name, ip_addr):
+    """
+    Get network device ip addresses. ioctl not used because there is
+    incompatibility with ipv6.
+
+    @param if_name: Name of interface.
+    @param ip_addr: Interface ip addr in format "ip_address/mask".
+    @raise: IfChangeAddrError.
+    """
+    cmd = "ip addr add %s dev %s" % (ip_addr, if_name)
+    try:
+        utils.run(cmd, verbose=False)
+    except error.CmdError, e:
+        raise IfChangeAddrError(if_name, ip_addr, e)
+
+
+def ipv6_from_mac_addr(mac_addr):
+    """
+    @return: Ipv6 address for communication in link range.
+    """
+    mp = mac_addr.split(":")
+    mp[0] = ("%x") % (int(mp[0], 16) ^ 0x2)
+    return "fe80::%s%s:%sff:fe%s:%s%s" % tuple(mp)
+
+
+def check_add_dnsmasq_to_br(br_name, tmpdir):
+    """
+    Add dnsmasq for bridge. dnsmasq could be added only if bridge
+    have assigned ip address.
+
+    @param bridge_name: Name of bridge.
+    @param bridge_ip: Bridge ip.
+    @param tmpdir: Tmp dir for save pid file and ip range file.
+    """
+    br_ips = get_net_if_addrs(br_name)["ipv4"]
+    if not br_ips:
+        raise BRIpError(br_name)
+    dnsmasq_listen = find_dnsmasq_listen_address()
+    dhcp_ip_start = br_ips[0].split(".")
+    dhcp_ip_start[3] = "128"
+    dhcp_ip_start = ".".join(dhcp_ip_start)
+
+    dhcp_ip_end = br_ips[0].split(".")
+    dhcp_ip_end[3] = "254"
+    dhcp_ip_end = ".".join(dhcp_ip_end)
+
+    pidfile = ("%s-dnsmasq.pid") % (br_ips[0])
+    leases = ("%s.leases") % (br_ips[0])
+
+    if not (set(br_ips) & set(dnsmasq_listen)):
+        logging.debug("There is no dnsmasq on br %s."
+                      "Starting new one." % (br_name))
+        utils.run("/usr/sbin/dnsmasq --strict-order --bind-interfaces"
+                  " --pid-file=%s --conf-file= --except-interface lo"
+                  " --listen-address %s --dhcp-range %s,%s --dhcp-leasefile=%s"
+                  " --dhcp-lease-max=127 --dhcp-no-override" %
+                  (os.path.join(tmpdir, pidfile), br_ips[0], dhcp_ip_start,
+                   dhcp_ip_end, (os.path.join(tmpdir, leases))))
+    return pidfile
+
+
+@__init_openvswitch
+def find_bridge_manager(br_name, ovs=None):
+    """
+    Finds bridge which contain interface iface_name.
+
+    @param iface_name: Name of interface.
+    @return: (br_manager) which contain bridge or None.
+    """
+    if ovs is None:
+        ovs = __ovs
+    # find ifname in standard linux bridge.
+    if br_name in __bridge.list_br():
+        return __bridge
+    elif br_name in ovs.list_br():
+        return ovs
+    else:
+        return None
+
+
+@__init_openvswitch
+def find_current_bridge(iface_name, ovs=None):
+    """
+    Finds bridge which contain interface iface_name.
+
+    @param iface_name: Name of interface.
+    @return: (br_manager, Bridge) which contain iface_name or None.
+    """
+    if ovs is None:
+        ovs = __ovs
+    # find ifname in standard linux bridge.
+    master = __bridge
+    bridge = master.port_to_br(iface_name)
+    if bridge is None:
+        master = ovs
+        bridge = master.port_to_br(iface_name)
+
+    if bridge is None:
+        master = None
+
+    return (master, bridge)
+
+
+@__init_openvswitch
+def change_iface_bridge(ifname, new_bridge, ovs=None):
+    """
+    Change bridge on which is port added.
+
+    @param ifname: Iface name or Iface struct.
+    @param new_bridge: Name of new bridge.
+    """
+    if ovs is None:
+        ovs = __ovs
+    br_manager_new = find_bridge_manager(new_bridge, ovs)
+    if br_manager_new is None:
+        raise BRNotExistError(new_bridge, "")
+
+    if type(ifname) is str:
+        (br_manager_old, br_old) = find_current_bridge(ifname, ovs)
+        if not br_manager_old is None:
+            br_manager_old.del_port(br_old, ifname)
+        br_manager_new.add_port(new_bridge, ifname)
+    elif issubclass(type(ifname), VirtIface):
+        br_manager_old = find_bridge_manager(ifname.netdst, ovs)
+        if not br_manager_old is None:
+            br_manager_old.del_port(ifname.netdst, ifname.ifname)
+        br_manager_new.add_port(new_bridge, ifname.ifname)
+        ifname.netdst = new_bridge
+    else:
+        raise error.AutotestError("Network interface %s is wrong type %s." %
+                                  (ifname, new_bridge))
+
+
+@__init_openvswitch
+def add_to_bridge(ifname, brname, ovs=None):
     """
     Add a TAP device to bridge
 
     @param ifname: Name of TAP device
     @param brname: Name of the bridge
+    @param ovs: OpenVSwitch object.
+    """
+    if ovs is None:
+        ovs = __ovs
+
+    _ifname = None
+    if type(ifname) is str:
+        _ifname = ifname
+    elif issubclass(type(ifname), VirtIface):
+        _ifname = ifname.ifname
+
+    if brname in __bridge.list_br():
+        #Try add port to standard bridge or openvswitch in compatible mode.
+        __bridge.add_port(brname, _ifname)
+        return
+
+    #Try add port to OpenVSwitch bridge.
+    if brname in ovs.list_br():
+        ovs.add_port(brname, ifname)
+
+
+@__init_openvswitch
+def del_from_bridge(ifname, brname, ovs=None):
+    """
+    Del a TAP device to bridge
+
+    @param ifname: Name of TAP device
+    @param brname: Name of the bridge
+    @param ovs: OpenVSwitch object.
+    """
+    if ovs is None:
+        ovs = __ovs
+
+    _ifname = None
+    if type(ifname) is str:
+        _ifname = ifname
+    elif issubclass(type(ifname), VirtIface):
+        _ifname = ifname.ifname
+
+    if brname in __bridge.list_br():
+        #Try add port to standard bridge or openvswitch in compatible mode.
+        __bridge.del_port(brname, _ifname)
+        return
+
+    #Try add port to OpenVSwitch bridge.
+    if brname in ovs.list_br():
+        ovs.del_port(brname, _ifname)
+
+
+def bring_up_ifname(ifname):
+    """
+    Bring up an interface
+
+    @param ifname: Name of the interface
     """
     ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
-    index = if_nametoindex(ifname)
-    if index == 0:
-        raise TAPNotExistError(ifname)
-    ifr = struct.pack("16si", brname, index)
+    ifr = struct.pack("16sh", ifname, IFF_UP)
     try:
-        fcntl.ioctl(ctrl_sock, SIOCBRADDIF, ifr)
-    except IOError, details:
-        raise BRAddIfError(ifname, brname, details)
+        fcntl.ioctl(ctrl_sock, SIOCSIFFLAGS, ifr)
+    except IOError:
+        raise TAPBringUpError(ifname)
     ctrl_sock.close()
 
 
-def bring_up_ifname(ifname):
+def bring_down_ifname(ifname):
     """
     Bring up an interface
 
     @param ifname: Name of the interface
     """
     ctrl_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
-    ifr = struct.pack("16sh", ifname, IFF_UP)
+    ifr = struct.pack("16sh", ifname, 0)
     try:
         fcntl.ioctl(ctrl_sock, SIOCSIFFLAGS, ifr)
     except IOError:
-- 
1.7.7.6

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux