Refactored KVM install test. Recently the source directory layout for KVM was changed (now the toplevel is the userspace (qemu) code with a kvm directory) and that was breaking the install test. While I was working on the install test, I refactored the test: * Moved functions that fall into the 'more general utility functions' to kvm_utils; * Made a simple function to detect kvm source code directory layout, to handle automatically build differences. * Made module code removal more robust using as a base the work made by Mike Burns * Apply a patch to fix a build issue on current release tarballs. * Made the build process to use all processors available on the machine, reducing the time needed to build the userspace programs and the kernel modules. --- client/tests/kvm_runtest_2/kvm_install.py | 431 ++++++++++++++--------------- client/tests/kvm_runtest_2/kvm_utils.py | 96 +++++++ 2 files changed, 303 insertions(+), 224 deletions(-) diff --git a/client/tests/kvm_runtest_2/kvm_install.py b/client/tests/kvm_runtest_2/kvm_install.py index 8be5a93..c2c79f7 100755 --- a/client/tests/kvm_runtest_2/kvm_install.py +++ b/client/tests/kvm_runtest_2/kvm_install.py @@ -4,7 +4,7 @@ import sys import urllib import re import signal -from datetime import * +import datetime from autotest_lib.client.bin import utils, test from autotest_lib.client.common_lib import error @@ -14,19 +14,85 @@ import kvm_utils def run_kvm_install(test, params, env): - install_mode = params.get("mode") - kvm_log.info("Selected installation mode: %s" % install_mode) + """ + Installs KVM using the selected install mode. Most install methods will + take kvm source code, build it and install it to a given location. - srcdir = params.get("srcdir", test.srcdir) - if not os.path.exists(srcdir): - os.mkdir(srcdir) + @param test: kvm_runtest_2 test object + @param params: Dictionary with test parameters + @param env: Test environment + """ + known_install_modes = ['noinstall', 'release', 'snapshot', 'git', + 'localtar', 'localsrc'] - # do not install - if install_mode == "noinstall": + install_mode = params.get("mode") + srcdir = params.get("srcdir", test.srcdir) + params["srcdir"] = srcdir + + if install_mode not in known_install_modes: + raise error.TestError("Invalid installation mode" % install_mode) + + if not install_mode == 'noinstall': + kvm_log.info("Selected installation mode: %s" % install_mode) + build_dir = __get_kvm(params) + __build_kvm(build_dir) + __install_kvm(build_dir, install_mode, test.bindir) + __load_kvm_modules(build_dir) + kvm_log.info("Done building and installing KVM") + else: kvm_log.info("Skipping installation") - # install from git - elif install_mode == "git": + +def __get_kvm(params): + """ + Get KVM and returns a source code directory path. + + @param params: Dictionary with all parameters passed to the + install test. + """ + install_mode = params.get("mode") + srcdir = params.get("srcdir") + if not os.path.isdir(srcdir): + os.makedirs(srcdir) + + if install_mode in ['release', 'snapshot', 'localtar']: + if install_mode == 'release': + release_tag = params.get("release_tag") + release_dir = params.get("release_dir") + kvm_log.info("Installing KVM from release tarball") + if not release_tag: + release_tag = kvm_utils.get_latest_kvm_release_tag(release_dir) + tarball = os.path.join(release_dir, "kvm-%s.tar.gz" % release_tag) + kvm_log.info("Retrieving release kvm-%s" % release_tag) + tarball = utils.unmap_url("/", tarball, "/tmp") + elif install_mode == 'snapshot': + kvm_log.info("Installing KVM from snapshot") + snapshot_date = params.get("snapshot_date") + snapshot_dir = params.get("snapshot_dir") + if not snapshot_date: + # Take yesterday's snapshot + d = (datetime.date.today() - + datetime.timedelta(1)).strftime("%Y%m%d") + else: + d = snapshot_date + tarball = os.path.join(snapshot_dir, "kvm-snapshot-%s.tar.gz" % d) + kvm_log.info("Retrieving kvm-snapshot-%s" % d) + tarball = utils.unmap_url("/", tarball, "/tmp") + elif install_mode == 'localtar': + tarball = params.get("tarball") + if not tarball: + raise error.TestError("KVM Tarball install specified but no" + " tarball provided on control file.") + kvm_log.info("Installing KVM from a local tarball") + kvm_log.info("Using tarball %s") + tarball = utils.unmap_url("/", params.get("tarball"), "/tmp") + os.chdir(srcdir) + return os.path.join(srcdir, utils.extract_tarball(tarball)) + + elif install_mode == 'localsrc': + return params.get('srcdir') + + elif install_mode == 'git': repo = params.get("git_repo") user_repo = params.get("user_git_repo") branch = params.get("git_branch", "master") @@ -36,248 +102,165 @@ def run_kvm_install(test, params, env): if not repo: message = "KVM git repository path not specified" kvm_log.error(message) - raise error.TestError, message + raise error.TestError(message) if not user_repo: message = "KVM user git repository path not specified" kvm_log.error(message) - raise error.TestError, message - __install_kvm_from_git(test, srcdir, repo, user_repo, branch, tag, - user_tag, lbranch) - - # install from release - elif install_mode == "release": - release_dir = params.get("release_dir") - release_tag = params.get("release_tag") - if not release_dir: - message = "Release dir not specified" - kvm_log.error(message) - raise error.TestError, message - __install_kvm_release(test, srcdir, release_dir, release_tag) - - # install from snapshot - elif install_mode == "snapshot": - snapshot_dir = params.get("snapshot_dir") - snapshot_date = params.get("snapshot_date") - if not snapshot_dir: - message = "Snapshot dir not specified" - kvm_log.error(message) - raise error.TestError, message - __install_kvm_from_snapshot(test, srcdir, snapshot_dir, snapshot_date) - - # install from tarball - elif install_mode == "localtar": - tarball = params.get("tarball") - if not tarball: - message = "Local tarball filename not specified" - kvm_log.error(message) - raise error.TestError, message - __install_kvm_from_local_tarball(test, srcdir, tarball) - - # install from local sources - elif install_mode == "localsrc": - __install_kvm(test, srcdir) + raise error.TestError(message) - # invalid installation mode - else: - message = "Invalid installation mode: '%s'" % install_mode - kvm_log.error(message) - raise error.TestError, message + local_git_srcdir = os.path.join(srcdir, "kvm") + if not os.path.exists(local_git_srcdir): + os.mkdir(local_git_srcdir) + local_user_git_srcdir = os.path.join(srcdir, "kvmuser") + if not os.path.exists(local_user_git_srcdir): + os.mkdir(local_user_git_srcdir) - # load kvm modules (unless requested not to) - if params.get('load_modules', "yes") == "yes": - __load_kvm_modules() - else: - kvm_log.info("user requested not to load kvm modules") + kvm_utils.get_git_branch(repo, branch, local_git_srcdir, tag, lbranch) + kvm_utils.get_git_branch(user_repo, branch, local_user_git_srcdir, + user_tag, lbranch) -def __cleanup_dir(dir): - # only alerts if src directory is not empty - for root, dirs, files in os.walk(dir): - if dirs or files: - message = "Directory \'%s\' is not empty" % dir - kvm_log.error(message) - raise error.TestError, message - -def __install_kvm_release(test, srcdir, release_dir, release_tag): - if not release_tag: - try: - # look for the latest release in the web - page_url = os.path.join(release_dir, "showfiles.php") - local_web_page = utils.unmap_url("/", page_url, "/tmp") - f = open(local_web_page, "r") - data = f.read() - f.close() - rx = re.compile("package_id=(\d+).*\>kvm\<", re.IGNORECASE) - matches = rx.findall(data) - package_id = matches[0] - #package_id = 209008 - rx = re.compile("package_id=%s.*release_id=\d+\">(\d+)" % package_id, re.IGNORECASE) - matches = rx.findall(data) - release_tag = matches[0] # the first match contains the latest release tag - except Exception, e: - message = "Could not fetch latest KVM release tag (%s)" % str(e) - kvm_log.error(message) - raise error.TestError(message) + ## TODO: Needs to see how we are syncing up the directories + return local_user_git_srcdir - kvm_log.info("Installing release %s (kvm-%s)" % (release_tag, release_tag)) - tarball = os.path.join(release_dir, "kvm-%s.tar.gz" % release_tag) - tarball = utils.unmap_url("/", tarball, "/tmp") - __install_kvm_from_local_tarball(test, srcdir, tarball) - - -def __install_kvm_from_git(test, srcdir, repo, user_repo, branch, tag, - user_tag, lbranch): - local_git_srcdir = os.path.join(srcdir, "kvm") - if not os.path.exists(local_git_srcdir): - os.mkdir(local_git_srcdir) - local_user_git_srcdir = os.path.join(srcdir, "kvmuser") - if not os.path.exists(local_user_git_srcdir): - os.mkdir(local_user_git_srcdir) - - __get_git_branch(repo, branch, local_git_srcdir, tag, lbranch) - __get_git_branch(user_repo, branch, local_user_git_srcdir, user_tag, lbranch) - utils.system("cd %s && ./configure && make -C kernel LINUX=%s sync" % (local_user_git_srcdir, local_git_srcdir)) - __install_kvm(test, local_user_git_srcdir) - - -def __get_git_branch(repository, branch, srcdir, commit=None, lbranch=None): - kvm_log.info("Getting sources from git <REP=%s BRANCH=%s TAG=%s> to local directory <%s>" % (repository, branch, commit, srcdir)) - pwd = os.getcwd() - os.chdir(srcdir) - if os.path.exists(".git"): - utils.system("git reset --hard") else: - utils.system("git init") + return None - if not lbranch: - lbranch = branch - utils.system("git fetch -q -f -u -t %s %s:%s" % - (repository, branch, lbranch)) - utils.system("git checkout %s" % lbranch) - if commit: - utils.system("git checkout %s" % commit) +def __build_kvm(build_dir, buildsteps=None): + """ + Builds KVM, given a path to the project source code. - h = utils.system_output('git log --pretty=format:"%H" -1') - desc = utils.system_output("git describe") - kvm_log.info("Commit hash for %s is %s (%s)" % (repository, h.strip(), desc)) - os.chdir(pwd) + @param build_dir: Path to KVM source code (toplevel) + @param buildsteps: Optional parameter. Alternative sequence of build + steps. + """ + if not build_dir: + return + print build_dir + os.chdir(build_dir) -def __install_kvm_from_snapshot(test, srcdir, snapshot_dir ,snapshot_date): - kvm_log.info("Source snapshot dir: %s" % snaphost_dir) - kvm_log.info("Source snapshot date: %s" % snapshot_date) + kvm_log.info("Applying patch to fix a small problem on KVM module build") + utils.system("patch -p0 < ../../adding_kvm_trace_support_qemu.patch") - if not snapshot_date: - d = (date.today() - timedelta(1)).strftime("%Y%m%d") # takes yesterday's snapshot - else: - d = snapshot_date + kvm_build_dir = os.path.join(build_dir, '..', '..', 'build') + kvm_build_dir = os.path.abspath(kvm_build_dir) - tarball = os.path.join(snaphost_dir, "kvm-snapshot-%s.tar.gz" % d) - kvm_log.info("Tarball url: %s" % tarball) - tarball = utils.unmap_url("/", tarball, "/tmp") - __install_kvm_from_local_tarball(test, srcdir, tarball) + if not os.path.exists(kvm_build_dir): + os.mkdir(kvm_build_dir) + if not buildsteps: + repository_type = kvm_utils.check_kvm_source_dir(build_dir) + if repository_type == 1: + buildsteps = ["./configure --prefix=%s" % kvm_build_dir, + "make clean", + "make -j %s" % utils.count_cpus()] + if not os.path.exists('qemu/pc-bios/bios.bin'): + buildsteps.append("make -C bios") + buildsteps.append("make -C extboot") + buildsteps.append("cp -f bios/BIOS-bochs-latest" + " qemu/pc-bios/bios.bin") + buildsteps.append("cp -f vgabios/VGABIOS-lgpl-latest.bin" + " qemu/pc-bios/vgabios.bin") + buildsteps.append("cp -f vgabios/VGABIOS-lgpl-latest.cirrus.bin" + " qemu/pc-bios/vgabios-cirrus.bin") + buildsteps.append("cp -f extboot/extboot.bin" + " qemu/pc-bios/extboot.bin") + elif repository_type == 2: + buildsteps = ["./configure --prefix=%s" + " --with-kvm-trace" % kvm_build_dir, + "make clean", + "make -j %s" % utils.count_cpus()] + + kvm_log.info("Building KVM") + os.chdir(build_dir) + for step in buildsteps: + utils.system(step) + + +def __install_kvm(build_dir, install_mode, test_bindir): + """ + Installs KVM depending on the installation method and creates + appropriate symbolic links on the kvm test directory. + + @param build_dir: KVM source directory + @param install_mode: Type of KVM installation + @param test_bindir: Path of the KVM test directory + """ + prefix = None + if install_mode in ['release', 'snapshot', 'git', 'localtar', 'localsrc']: + os.chdir(build_dir) + repository_type = kvm_utils.check_kvm_source_dir(build_dir) + if repository_type == 1: + utils.system("make -C qemu install") + if repository_type == 2: + utils.system("make install") + + kvm_build_dir = os.path.join(test_bindir, 'build') + prefix = os.path.abspath(kvm_build_dir) + + if not prefix: + raise error.TestError("Can't determine KVM userspace install prefix") -def __install_kvm_from_local_tarball(test, srcdir, tarball): - pwd = os.getcwd() - os.chdir(srcdir) - newdir = utils.extract_tarball(tarball) - if newdir: - srcdir = os.path.join(srcdir, newdir) - os.chdir(pwd) - __install_kvm(test, srcdir) + # create symlinks + qemu_path = os.path.join(test_bindir, "qemu") + qemu_img_path = os.path.join(test_bindir, "qemu-img") + if os.path.lexists(qemu_path): + os.unlink(qemu_path) + if os.path.lexists(qemu_img_path): + os.unlink(qemu_img_path) + kvm_qemu = os.path.join(prefix, "bin", "qemu-system-x86_64") + if not os.path.isfile(kvm_qemu): + raise error.TestError('Invalid qemu path') + kvm_qemu_img = os.path.join(prefix, "bin", "qemu-img") + if not os.path.isfile(kvm_qemu_img): + raise error.TestError('Invalid qemu-img path') + os.symlink(kvm_qemu, qemu_path) + os.symlink(kvm_qemu_img, qemu_img_path) -def __load_kvm_modules(): - kvm_log.info("Detecting CPU vendor...") +def __load_kvm_modules(build_dir): vendor = "intel" if os.system("grep vmx /proc/cpuinfo 1>/dev/null") != 0: vendor = "amd" kvm_log.info("Detected CPU vendor as '%s'" %(vendor)) - #if self.config.load_modules == "yes": - # remove existing in kernel kvm modules - kvm_log.info("Unloading loaded KVM modules (if present)...") - #utils.system("pkill qemu 1>/dev/null 2>&1", ignore_status=True) + kvm_log.info("Killing any qemu processes that might be left behind") utils.system("pkill qemu", ignore_status=True) - #if utils.system("grep kvm_%s /proc/modules 1>/dev/null" % vendor, ignore_status=True) == 0: - utils.system("/sbin/rmmod kvm_%s" % vendor, ignore_status=True) - #if utils.system("grep kvm /proc/modules 1>/dev/null", ignore_status=True) == 0: - utils.system("/sbin/rmmod kvm", ignore_status=True) - if utils.system("grep kvm /proc/modules 1>/dev/null", ignore_status=True) == 0: + kvm_log.info("Unloading previously loaded KVM modules") + kvm_utils.unload_module("kvm") + if utils.module_is_loaded("kvm"): message = "Failed to remove old KVM modules" kvm_log.error(message) - raise error.TestError, message + raise error.TestError(message) kvm_log.info("Loading new KVM modules...") - os.chdir("kernel") - if os.path.exists("x86"): - os.chdir("x86") - utils.system("/sbin/insmod ./kvm.ko && sleep 1 && /sbin/insmod ./kvm-%s.ko" % vendor) - - #elif self.config.load_modules == "no": - #kvm_log.info("user requested not to load kvm modules") - - ### no matter if new kvm modules are to be loaded or not - ### make sure there are kvm modules installed. - if utils.system("grep kvm_%s /proc/modules 1>/dev/null" %(vendor), ignore_status=True) != 0: - message = "Failed to load KVM modules" - kvm_log.error(message) - raise error.TestError, message - -def __install_kvm(test, srcdir): - # create destination dir - - kvm_build_dir = os.path.join(srcdir, '..', '..', 'build') - kvm_build_dir = os.path.abspath(kvm_build_dir) - - if not os.path.exists(kvm_build_dir): - os.mkdir(kvm_build_dir) - - # change to source dir - os.chdir(srcdir) - - # start working... - kvm_log.info("Building KVM...") - - def run(cmd, title, timeout): - (status, pid, output) = kvm_utils.run_bg(cmd, None, kvm_log.info, - '(%s)' % title, timeout=timeout) - if status != 0: - kvm_utils.safe_kill(pid, signal.SIGTERM) - raise error.TestFail, "'%s' failed" % cmd - - # configure + make - run("./configure --prefix=%s" % kvm_build_dir, "configure", 30) - run("make clean", "make clean", 30) - run("make", "make", 1200) - - # make bios binaries if missing - if not os.path.exists('qemu/pc-bios/bios.bin'): - run("make -C bios", "make bios", 60) - run("make -C vgabios", "make vgabios", 60) - run("make -C extboot", "make extboot", 60) - cp1 = "cp -f bios/BIOS-bochs-latest qemu/pc-bios/bios.bin" - cp2 = "cp -f vgabios/VGABIOS-lgpl-latest.bin qemu/pc-bios/vgabios.bin" - cp3 = "cp -f vgabios/VGABIOS-lgpl-latest.cirrus.bin qemu/pc-bios/vgabios-cirrus.bin" - cp4 = "cp -f extboot/extboot.bin qemu/pc-bios/extboot.bin" - cmd = "&& ".join([cp1, cp2, cp3, cp4]) - run(cmd, "copy bios binaries", 30) - - # install from qemu directory - run("make -C qemu install", "(make install) ", 120) - - # create symlinks - qemu_path = os.path.join(test.bindir, "qemu") - qemu_img_path = os.path.join(test.bindir, "qemu-img") - if os.path.lexists(qemu_path): - os.unlink(qemu_path) - if os.path.lexists(qemu_img_path): - os.unlink(qemu_img_path) - kvm_qemu = os.path.join(kvm_build_dir, "bin", "qemu-system-x86_64") - kvm_qemu_img = os.path.join(kvm_build_dir, "bin", "qemu-img") - os.symlink(kvm_qemu, qemu_path) - os.symlink(kvm_qemu_img, qemu_img_path) - kvm_log.info("Done building and installing KVM") + kvm_module_path = None + kvm_vendor_module_path = None + for folder, subdirs, files in os.walk(build_dir): + if "kvm.ko" in files: + kvm_module_path = os.path.join(folder, "kvm.ko") + kvm_vendor_module_path = os.path.join(folder, "kvm-%s.ko" % vendor) + + abort = False + if not kvm_module_path: + kvm_log.error("Could not find KVM module that was supposed to be" + " built on the source dir") + abort = True + elif not kvm_vendor_module_path: + kvm_log.error("Could not find KVM (%s) module that was supposed to be" + " built on the source dir" % vendor) + abort = True + if abort: + raise error.TestError("Could not load KVM modules.") + + utils.system("/sbin/insmod %s" % kvm_module_path) + time.sleep(1) + utils.system("/sbin/insmod %s" % kvm_vendor_module_path) + + if not utils.module_is_loaded("kvm"): + message = "Failed to load the KVM modules built for the test" + kvm_log.error(message) + raise error.TestError(message) diff --git a/client/tests/kvm_runtest_2/kvm_utils.py b/client/tests/kvm_runtest_2/kvm_utils.py index be8ad95..eb6afb4 100644 --- a/client/tests/kvm_runtest_2/kvm_utils.py +++ b/client/tests/kvm_runtest_2/kvm_utils.py @@ -15,6 +15,8 @@ import re import kvm_log +from autotest_lib.client.common_lib import error +from autotest_lib.client.bin import utils # Functions for working with dicts obtained from the test config file @@ -99,6 +101,100 @@ def safe_kill(pid, signal): return False +def get_latest_kvm_release_tag(release_dir): + try: + page_url = os.path.join(release_dir, "showfiles.php") + local_web_page = utils.unmap_url("/", page_url, "/tmp") + f = open(local_web_page, "r") + data = f.read() + f.close() + rx = re.compile("package_id=(\d+).*\>kvm\<", re.IGNORECASE) + matches = rx.findall(data) + package_id = matches[0] + #package_id = 209008 + rx = re.compile("package_id=%s.*release_id=\d+\">(\d+)" % package_id, + re.IGNORECASE) + matches = rx.findall(data) + return matches[0] # the first match contains the latest release tag + except Exception, e: + message = "Could not fetch latest KVM release tag (%s)" % str(e) + kvm_log.error(message) + raise error.TestError(message) + + +def get_git_branch(repository, branch, srcdir, commit=None, lbranch=None): + """ + Retrieves a given git code repository. + + @param repository: Git repository URL + """ + kvm_log.info("Getting sources from git <REP=%s BRANCH=%s TAG=%s>" + " to local directory <%s>" % (repository, branch, commit, + srcdir)) + pwd = os.getcwd() + os.chdir(srcdir) + if os.path.exists(".git"): + utils.system("git reset --hard") + else: + utils.system("git init") + + if not lbranch: + lbranch = branch + + utils.system("git fetch -q -f -u -t %s %s:%s" % + (repository, branch, lbranch)) + utils.system("git checkout %s" % lbranch) + if commit: + utils.system("git checkout %s" % commit) + + h = utils.system_output('git log --pretty=format:"%H" -1') + desc = utils.system_output("git describe") + kvm_log.info("Commit hash for %s is %s (%s)" % (repository, h.strip(), + desc)) + os.chdir(pwd) + + +def unload_module(module_name): + """ + Removes a module. Handles dependencies. If even then it's not possible + to remove one of the modules, it will trhow an error.CmdError exception. + + @param module_name: Name of the module we want to remove + """ + l_raw = utils.system_output("/sbin/lsmod").splitlines() + lsmod = [x for x in l_raw if x.split()[0] == module_name] + if len(lsmod) > 0: + line_parts = lsmod[0].split() + if len(line_parts) == 4: + submodules = line_parts[3].split(",") + for submodule in submodules: + unload_module(submodule) + utils.system("/sbin/modprobe -r %s" % module_name) + kvm_log.info("Module %s unloaded" % module_name) + else: + kvm_log.info("Module %s is already unloaded" % module_name) + + +def check_kvm_source_dir(source_dir): + """ + Inspects the kvm source directory and verifies its disposition. In some + occasions build may be dependant on the source directory disposition. + + @param source_dir: Source code path that will be inspected. + """ + os.chdir(source_dir) + has_qemu_dir = os.path.isdir('qemu') + has_kvm_dir = os.path.isdir('kvm') + if has_qemu_dir and not has_kvm_dir: + kvm_log.info("qemu directory detected, source dir layout 1") + return 1 + if has_kvm_dir and not has_qemu_dir: + kvm_log.info("kvm directory detected, source dir layout 2") + return 2 + else: + raise error.TestError("Unknown source dir layout, cannot proceed.") + + # The following are a class and functions used for SSH, SCP and Telnet communication with guests. class kvm_spawn: -- 1.6.2.2 -- 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