From patchwork Wed Oct 3 12:11:32 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jiri Zupka X-Patchwork-Id: 1541261 Return-Path: X-Original-To: patchwork-kvm@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 3F6D740AC9 for ; Wed, 3 Oct 2012 12:12:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754277Ab2JCMMN (ORCPT ); Wed, 3 Oct 2012 08:12:13 -0400 Received: from mx1.redhat.com ([209.132.183.28]:26787 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753063Ab2JCML7 (ORCPT ); Wed, 3 Oct 2012 08:11:59 -0400 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q93CBbrs000697 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 3 Oct 2012 08:11:37 -0400 Received: from jzupka-pc.local.com (dhcp-26-117.brq.redhat.com [10.34.26.117]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id q93CBXhp002892; Wed, 3 Oct 2012 08:11:34 -0400 From: =?UTF-8?q?Ji=C5=99=C3=AD=20=C5=BDupka?= To: autotest@test.kernel.org, kvm@vger.kernel.org, kvm-autotest@redhat.com, lmr@redhat.com, ldoktor@redhat.com, jzupka@redhat.com Subject: [Autotest][PATCH] Autotest: Add utils for OpenVSwitch patch Date: Wed, 3 Oct 2012 14:11:32 +0200 Message-Id: <1349266292-13041-1-git-send-email-jzupka@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org pull-request https://github.com/autotest/autotest/pull/569 ForAllxx: run object method on every object in list ForAll[a,b,c].print() Signed-off-by: Ji?í Župka --- client/shared/base_utils.py | 81 +++++- client/shared/openvswitch.py | 578 ++++++++++++++++++++++++++++++++++++++++++ client/tests | 2 +- 3 files changed, 646 insertions(+), 15 deletions(-) create mode 100644 client/shared/openvswitch.py diff --git a/client/shared/base_utils.py b/client/shared/base_utils.py index 0734742..573b907 100644 --- a/client/shared/base_utils.py +++ b/client/shared/base_utils.py @@ -1224,6 +1224,56 @@ def system_output_parallel(commands, timeout=None, ignore_status=False, return out +class ForAll(list): + def __getattr__(self, name): + def wrapper(*args, **kargs): + return map(lambda o: o.__getattribute__(name)(*args, **kargs), self) + return wrapper + + +class ForAllP(list): + """ + Parallel version of ForAll + """ + def __getattr__(self, name): + def wrapper(*args, **kargs): + threads = [] + for o in self: + threads.append(InterruptedThread(o.__getattribute__(name), + args=args, kwargs=kargs)) + for t in threads: + t.start() + return map(lambda t: t.join(), threads) + return wrapper + + +class ForAllPSE(list): + """ + Parallel version of and suppress exception. + """ + def __getattr__(self, name): + def wrapper(*args, **kargs): + threads = [] + for o in self: + threads.append(InterruptedThread(o.__getattribute__(name), + args=args, kwargs=kargs)) + for t in threads: + t.start() + + result = [] + for t in threads: + ret = {} + try: + ret["return"] = t.join() + except Exception: + ret["exception"] = sys.exc_info() + ret["args"] = args + ret["kargs"] = kargs + result.append(ret) + return result + return wrapper + + def etraceback(prep, exc_info): """ Enhanced Traceback formats traceback into lines "prep: line\nname: line" @@ -1733,9 +1783,12 @@ def import_site_function(path, module, funcname, dummy, modulefile=None): return import_site_symbol(path, module, funcname, dummy, modulefile) -def _get_pid_path(program_name): - pid_files_dir = GLOBAL_CONFIG.get_config_value("SERVER", 'pid_files_dir', - default="") +def get_pid_path(program_name, pid_files_dir=None): + if pid_files_dir is None: + pid_files_dir = GLOBAL_CONFIG.get_config_value("SERVER", + 'pid_files_dir', + default="") + if not pid_files_dir: base_dir = os.path.dirname(__file__) pid_path = os.path.abspath(os.path.join(base_dir, "..", "..", @@ -1746,25 +1799,25 @@ def _get_pid_path(program_name): return pid_path -def write_pid(program_name): +def write_pid(program_name, pid_files_dir=None): """ Try to drop .pid in the main autotest directory. Args: program_name: prefix for file name """ - pidfile = open(_get_pid_path(program_name), "w") + pidfile = open(get_pid_path(program_name, pid_files_dir), "w") try: pidfile.write("%s\n" % os.getpid()) finally: pidfile.close() -def delete_pid_file_if_exists(program_name): +def delete_pid_file_if_exists(program_name, pid_files_dir=None): """ Tries to remove .pid from the main autotest directory. """ - pidfile_path = _get_pid_path(program_name) + pidfile_path = get_pid_path(program_name, pid_files_dir) try: os.remove(pidfile_path) @@ -1774,18 +1827,18 @@ def delete_pid_file_if_exists(program_name): raise -def get_pid_from_file(program_name): +def get_pid_from_file(program_name, pid_files_dir=None): """ Reads the pid from .pid in the autotest directory. @param program_name the name of the program @return the pid if the file exists, None otherwise. """ - pidfile_path = _get_pid_path(program_name) + pidfile_path = get_pid_path(program_name, pid_files_dir) if not os.path.exists(pidfile_path): return None - pidfile = open(_get_pid_path(program_name), 'r') + pidfile = open(get_pid_path(program_name, pid_files_dir), 'r') try: try: @@ -1808,27 +1861,27 @@ def get_process_name(pid): return get_field(read_file("/proc/%d/stat" % pid), 1)[1:-1] -def program_is_alive(program_name): +def program_is_alive(program_name, pid_files_dir=None): """ Checks if the process is alive and not in Zombie state. @param program_name the name of the program @return True if still alive, False otherwise """ - pid = get_pid_from_file(program_name) + pid = get_pid_from_file(program_name, pid_files_dir) if pid is None: return False return pid_is_alive(pid) -def signal_program(program_name, sig=signal.SIGTERM): +def signal_program(program_name, sig=signal.SIGTERM, pid_files_dir=None): """ Sends a signal to the process listed in .pid @param program_name the name of the program @param sig signal to send """ - pid = get_pid_from_file(program_name) + pid = get_pid_from_file(program_name, pid_files_dir) if pid: signal_pid(pid, sig) diff --git a/client/shared/openvswitch.py b/client/shared/openvswitch.py new file mode 100644 index 0000000..daf4757 --- /dev/null +++ b/client/shared/openvswitch.py @@ -0,0 +1,578 @@ +import logging, re, os, select, signal +try: + import autotest.common as common +except ImportError: + import common +from autotest.client import utils, os_dep +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 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 databimpoty ase. + @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 = 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 + os.environ["PATH"] = (os.path.join(install_prefix, "usr/bin:") + + os.environ["PATH"]) + os.environ["PATH"] = (os.path.join(install_prefix, "usr/sbin:") + + os.environ["PATH"]) + + 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. + """ + version = None + try: + result = utils.run(os_dep.command("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(os_dep.command("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: + if utils.load_module("openvswitch"): + sm.restart("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(os_dep.command("ovsdb-tool"), timeout=10, + args=["create", self.db_path, self.dbschema]) + utils.run(os_dep.command("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(os_dep.command("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 + try: + sm.stop("openvswitch") + except error.CmdError: + pass + utils.load_module("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 b/client/tests index 4ebba12..8f778b1 160000 --- a/client/tests +++ b/client/tests @@ -1 +1 @@ -Subproject commit 4ebba1264bdcddfe738c109e3fde5395278ba31f +Subproject commit 8f778b1a42cdd16ca0bc7fb7f8676cda3c9361cc