Eric Li | 7edb304 | 2011-01-06 17:57:17 -0800 | [diff] [blame^] | 1 | import time, os, sys, urllib, re, signal, logging, datetime, glob, ConfigParser |
| 2 | import shutil |
| 3 | from autotest_lib.client.bin import utils, test, os_dep |
| 4 | from autotest_lib.client.common_lib import error |
| 5 | import kvm_utils |
| 6 | |
| 7 | |
| 8 | def check_configure_options(script_path): |
| 9 | """ |
| 10 | Return the list of available options (flags) of a given kvm configure build |
| 11 | script. |
| 12 | |
| 13 | @param script: Path to the configure script |
| 14 | """ |
| 15 | abspath = os.path.abspath(script_path) |
| 16 | help_raw = utils.system_output('%s --help' % abspath, ignore_status=True) |
| 17 | help_output = help_raw.split("\n") |
| 18 | option_list = [] |
| 19 | for line in help_output: |
| 20 | cleaned_line = line.lstrip() |
| 21 | if cleaned_line.startswith("--"): |
| 22 | option = cleaned_line.split()[0] |
| 23 | option = option.split("=")[0] |
| 24 | option_list.append(option) |
| 25 | |
| 26 | return option_list |
| 27 | |
| 28 | |
| 29 | def kill_qemu_processes(): |
| 30 | """ |
| 31 | Kills all qemu processes, also kills all processes holding /dev/kvm down. |
| 32 | """ |
| 33 | logging.debug("Killing any qemu processes that might be left behind") |
| 34 | utils.system("pkill qemu", ignore_status=True) |
| 35 | # Let's double check to see if some other process is holding /dev/kvm |
| 36 | if os.path.isfile("/dev/kvm"): |
| 37 | utils.system("fuser -k /dev/kvm", ignore_status=True) |
| 38 | |
| 39 | |
| 40 | def cpu_vendor(): |
| 41 | vendor = "intel" |
| 42 | if os.system("grep vmx /proc/cpuinfo 1>/dev/null") != 0: |
| 43 | vendor = "amd" |
| 44 | logging.debug("Detected CPU vendor as '%s'" %(vendor)) |
| 45 | return vendor |
| 46 | |
| 47 | |
| 48 | def _unload_kvm_modules(mod_list): |
| 49 | logging.info("Unloading previously loaded KVM modules") |
| 50 | for module in reversed(mod_list): |
| 51 | utils.unload_module(module) |
| 52 | |
| 53 | |
| 54 | def _load_kvm_modules(mod_list, module_dir=None, load_stock=False): |
| 55 | """ |
| 56 | Just load the KVM modules, without killing Qemu or unloading previous |
| 57 | modules. |
| 58 | |
| 59 | Load modules present on any sub directory of module_dir. Function will walk |
| 60 | through module_dir until it finds the modules. |
| 61 | |
| 62 | @param module_dir: Directory where the KVM modules are located. |
| 63 | @param load_stock: Whether we are going to load system kernel modules. |
| 64 | @param extra_modules: List of extra modules to load. |
| 65 | """ |
| 66 | if module_dir: |
| 67 | logging.info("Loading the built KVM modules...") |
| 68 | kvm_module_path = None |
| 69 | kvm_vendor_module_path = None |
| 70 | abort = False |
| 71 | |
| 72 | list_modules = ['%s.ko' % (m) for m in mod_list] |
| 73 | |
| 74 | list_module_paths = [] |
| 75 | for folder, subdirs, files in os.walk(module_dir): |
| 76 | for module in list_modules: |
| 77 | if module in files: |
| 78 | module_path = os.path.join(folder, module) |
| 79 | list_module_paths.append(module_path) |
| 80 | |
| 81 | # We might need to arrange the modules in the correct order |
| 82 | # to avoid module load problems |
| 83 | list_modules_load = [] |
| 84 | for module in list_modules: |
| 85 | for module_path in list_module_paths: |
| 86 | if os.path.basename(module_path) == module: |
| 87 | list_modules_load.append(module_path) |
| 88 | |
| 89 | if len(list_module_paths) != len(list_modules): |
| 90 | logging.error("KVM modules not found. If you don't want to use the " |
| 91 | "modules built by this test, make sure the option " |
| 92 | "load_modules: 'no' is marked on the test control " |
| 93 | "file.") |
| 94 | raise error.TestError("The modules %s were requested to be loaded, " |
| 95 | "but the only modules found were %s" % |
| 96 | (list_modules, list_module_paths)) |
| 97 | |
| 98 | for module_path in list_modules_load: |
| 99 | try: |
| 100 | utils.system("insmod %s" % module_path) |
| 101 | except Exception, e: |
| 102 | raise error.TestFail("Failed to load KVM modules: %s" % e) |
| 103 | |
| 104 | if load_stock: |
| 105 | logging.info("Loading current system KVM modules...") |
| 106 | for module in mod_list: |
| 107 | utils.system("modprobe %s" % module) |
| 108 | |
| 109 | |
| 110 | def create_symlinks(test_bindir, prefix=None, bin_list=None, unittest=None): |
| 111 | """ |
| 112 | Create symbolic links for the appropriate qemu and qemu-img commands on |
| 113 | the kvm test bindir. |
| 114 | |
| 115 | @param test_bindir: KVM test bindir |
| 116 | @param prefix: KVM prefix path |
| 117 | @param bin_list: List of qemu binaries to link |
| 118 | @param unittest: Path to configuration file unittests.cfg |
| 119 | """ |
| 120 | qemu_path = os.path.join(test_bindir, "qemu") |
| 121 | qemu_img_path = os.path.join(test_bindir, "qemu-img") |
| 122 | qemu_unittest_path = os.path.join(test_bindir, "unittests") |
| 123 | if os.path.lexists(qemu_path): |
| 124 | os.unlink(qemu_path) |
| 125 | if os.path.lexists(qemu_img_path): |
| 126 | os.unlink(qemu_img_path) |
| 127 | if unittest and os.path.lexists(qemu_unittest_path): |
| 128 | os.unlink(qemu_unittest_path) |
| 129 | |
| 130 | logging.debug("Linking qemu binaries") |
| 131 | |
| 132 | if bin_list: |
| 133 | for bin in bin_list: |
| 134 | if os.path.basename(bin) == 'qemu-kvm': |
| 135 | os.symlink(bin, qemu_path) |
| 136 | elif os.path.basename(bin) == 'qemu-img': |
| 137 | os.symlink(bin, qemu_img_path) |
| 138 | |
| 139 | elif prefix: |
| 140 | kvm_qemu = os.path.join(prefix, "bin", "qemu-system-x86_64") |
| 141 | if not os.path.isfile(kvm_qemu): |
| 142 | raise error.TestError('Invalid qemu path') |
| 143 | kvm_qemu_img = os.path.join(prefix, "bin", "qemu-img") |
| 144 | if not os.path.isfile(kvm_qemu_img): |
| 145 | raise error.TestError('Invalid qemu-img path') |
| 146 | os.symlink(kvm_qemu, qemu_path) |
| 147 | os.symlink(kvm_qemu_img, qemu_img_path) |
| 148 | |
| 149 | if unittest: |
| 150 | logging.debug("Linking unittest dir") |
| 151 | os.symlink(unittest, qemu_unittest_path) |
| 152 | |
| 153 | |
| 154 | def install_roms(rom_dir, prefix): |
| 155 | logging.debug("Path to roms specified. Copying roms to install prefix") |
| 156 | rom_dst_dir = os.path.join(prefix, 'share', 'qemu') |
| 157 | for rom_src in glob.glob('%s/*.bin' % rom_dir): |
| 158 | rom_dst = os.path.join(rom_dst_dir, os.path.basename(rom_src)) |
| 159 | logging.debug("Copying rom file %s to %s", rom_src, rom_dst) |
| 160 | shutil.copy(rom_src, rom_dst) |
| 161 | |
| 162 | |
| 163 | def save_build(build_dir, dest_dir): |
| 164 | logging.debug('Saving the result of the build on %s', dest_dir) |
| 165 | base_name = os.path.basename(build_dir) |
| 166 | tarball_name = base_name + '.tar.bz2' |
| 167 | os.chdir(os.path.dirname(build_dir)) |
| 168 | utils.system('tar -cjf %s %s' % (tarball_name, base_name)) |
| 169 | shutil.move(tarball_name, os.path.join(dest_dir, tarball_name)) |
| 170 | |
| 171 | |
| 172 | class KvmInstallException(Exception): |
| 173 | pass |
| 174 | |
| 175 | |
| 176 | class FailedKvmInstall(KvmInstallException): |
| 177 | pass |
| 178 | |
| 179 | |
| 180 | class KvmNotInstalled(KvmInstallException): |
| 181 | pass |
| 182 | |
| 183 | |
| 184 | class BaseInstaller(object): |
| 185 | # default value for load_stock argument |
| 186 | load_stock_modules = True |
| 187 | def __init__(self, mode=None): |
| 188 | self.install_mode = mode |
| 189 | self._full_module_list = None |
| 190 | |
| 191 | def set_install_params(self, test, params): |
| 192 | self.params = params |
| 193 | |
| 194 | load_modules = params.get('load_modules', 'no') |
| 195 | if not load_modules or load_modules == 'yes': |
| 196 | self.should_load_modules = True |
| 197 | elif load_modules == 'no': |
| 198 | self.should_load_modules = False |
| 199 | default_extra_modules = str(None) |
| 200 | self.extra_modules = eval(params.get("extra_modules", |
| 201 | default_extra_modules)) |
| 202 | |
| 203 | self.cpu_vendor = cpu_vendor() |
| 204 | |
| 205 | self.srcdir = test.srcdir |
| 206 | if not os.path.isdir(self.srcdir): |
| 207 | os.makedirs(self.srcdir) |
| 208 | |
| 209 | self.test_bindir = test.bindir |
| 210 | self.results_dir = test.resultsdir |
| 211 | |
| 212 | # KVM build prefix, for the modes that do need it |
| 213 | prefix = os.path.join(test.bindir, 'build') |
| 214 | self.prefix = os.path.abspath(prefix) |
| 215 | |
| 216 | # Current host kernel directory |
| 217 | default_host_kernel_source = '/lib/modules/%s/build' % os.uname()[2] |
| 218 | self.host_kernel_srcdir = params.get('host_kernel_source', |
| 219 | default_host_kernel_source) |
| 220 | |
| 221 | # Extra parameters that can be passed to the configure script |
| 222 | self.extra_configure_options = params.get('extra_configure_options', |
| 223 | None) |
| 224 | |
| 225 | # Do we want to save the result of the build on test.resultsdir? |
| 226 | self.save_results = True |
| 227 | save_results = params.get('save_results', 'no') |
| 228 | if save_results == 'no': |
| 229 | self.save_results = False |
| 230 | |
| 231 | self._full_module_list = list(self._module_list()) |
| 232 | |
| 233 | |
| 234 | def full_module_list(self): |
| 235 | """Return the module list used by the installer |
| 236 | |
| 237 | Used by the module_probe test, to avoid using utils.unload_module(). |
| 238 | """ |
| 239 | if self._full_module_list is None: |
| 240 | raise KvmNotInstalled("KVM modules not installed yet (installer: %s)" % (type(self))) |
| 241 | return self._full_module_list |
| 242 | |
| 243 | |
| 244 | def _module_list(self): |
| 245 | """Generate the list of modules that need to be loaded |
| 246 | """ |
| 247 | yield 'kvm' |
| 248 | yield 'kvm-%s' % (self.cpu_vendor) |
| 249 | if self.extra_modules: |
| 250 | for module in self.extra_modules: |
| 251 | yield module |
| 252 | |
| 253 | |
| 254 | def _load_modules(self, mod_list): |
| 255 | """ |
| 256 | Load the KVM modules |
| 257 | |
| 258 | May be overridden by subclasses. |
| 259 | """ |
| 260 | _load_kvm_modules(mod_list, load_stock=self.load_stock_modules) |
| 261 | |
| 262 | |
| 263 | def load_modules(self, mod_list=None): |
| 264 | if mod_list is None: |
| 265 | mod_list = self.full_module_list() |
| 266 | self._load_modules(mod_list) |
| 267 | |
| 268 | |
| 269 | def _unload_modules(self, mod_list=None): |
| 270 | """ |
| 271 | Just unload the KVM modules, without trying to kill Qemu |
| 272 | """ |
| 273 | if mod_list is None: |
| 274 | mod_list = self.full_module_list() |
| 275 | _unload_kvm_modules(mod_list) |
| 276 | |
| 277 | |
| 278 | def unload_modules(self, mod_list=None): |
| 279 | """ |
| 280 | Kill Qemu and unload the KVM modules |
| 281 | """ |
| 282 | kill_qemu_processes() |
| 283 | self._unload_modules(mod_list) |
| 284 | |
| 285 | |
| 286 | def reload_modules(self): |
| 287 | """ |
| 288 | Reload the KVM modules after killing Qemu and unloading the current modules |
| 289 | """ |
| 290 | self.unload_modules() |
| 291 | self.load_modules() |
| 292 | |
| 293 | |
| 294 | def reload_modules_if_needed(self): |
| 295 | if self.should_load_modules: |
| 296 | self.reload_modules() |
| 297 | |
| 298 | |
| 299 | class YumInstaller(BaseInstaller): |
| 300 | """ |
| 301 | Class that uses yum to install and remove packages. |
| 302 | """ |
| 303 | load_stock_modules = True |
| 304 | def set_install_params(self, test, params): |
| 305 | super(YumInstaller, self).set_install_params(test, params) |
| 306 | # Checking if all required dependencies are available |
| 307 | os_dep.command("rpm") |
| 308 | os_dep.command("yum") |
| 309 | |
| 310 | default_pkg_list = str(['qemu-kvm', 'qemu-kvm-tools']) |
| 311 | default_qemu_bin_paths = str(['/usr/bin/qemu-kvm', '/usr/bin/qemu-img']) |
| 312 | default_pkg_path_list = str(None) |
| 313 | self.pkg_list = eval(params.get("pkg_list", default_pkg_list)) |
| 314 | self.pkg_path_list = eval(params.get("pkg_path_list", |
| 315 | default_pkg_path_list)) |
| 316 | self.qemu_bin_paths = eval(params.get("qemu_bin_paths", |
| 317 | default_qemu_bin_paths)) |
| 318 | |
| 319 | |
| 320 | def _clean_previous_installs(self): |
| 321 | kill_qemu_processes() |
| 322 | removable_packages = "" |
| 323 | for pkg in self.pkg_list: |
| 324 | removable_packages += " %s" % pkg |
| 325 | |
| 326 | utils.system("yum remove -y %s" % removable_packages) |
| 327 | |
| 328 | |
| 329 | def _get_packages(self): |
| 330 | for pkg in self.pkg_path_list: |
| 331 | utils.get_file(pkg, os.path.join(self.srcdir, |
| 332 | os.path.basename(pkg))) |
| 333 | |
| 334 | |
| 335 | def _install_packages(self): |
| 336 | """ |
| 337 | Install all downloaded packages. |
| 338 | """ |
| 339 | os.chdir(self.srcdir) |
| 340 | utils.system("yum install --nogpgcheck -y *.rpm") |
| 341 | |
| 342 | |
| 343 | def install(self): |
| 344 | self._clean_previous_installs() |
| 345 | self._get_packages() |
| 346 | self._install_packages() |
| 347 | create_symlinks(test_bindir=self.test_bindir, |
| 348 | bin_list=self.qemu_bin_paths) |
| 349 | self.reload_modules_if_needed() |
| 350 | if self.save_results: |
| 351 | save_build(self.srcdir, self.results_dir) |
| 352 | |
| 353 | |
| 354 | class KojiInstaller(YumInstaller): |
| 355 | """ |
| 356 | Class that handles installing KVM from the fedora build service, koji. |
| 357 | It uses yum to install and remove packages. |
| 358 | """ |
| 359 | load_stock_modules = True |
| 360 | def set_install_params(self, test, params): |
| 361 | """ |
| 362 | Gets parameters and initializes the package downloader. |
| 363 | |
| 364 | @param test: kvm test object |
| 365 | @param params: Dictionary with test arguments |
| 366 | """ |
| 367 | super(KojiInstaller, self).set_install_params(test, params) |
| 368 | default_koji_cmd = '/usr/bin/koji' |
| 369 | default_src_pkg = 'qemu' |
| 370 | self.src_pkg = params.get("src_pkg", default_src_pkg) |
| 371 | self.tag = params.get("koji_tag", None) |
| 372 | self.build = params.get("koji_build", None) |
| 373 | self.koji_cmd = params.get("koji_cmd", default_koji_cmd) |
| 374 | |
| 375 | |
| 376 | def _get_packages(self): |
| 377 | """ |
| 378 | Downloads the specific arch RPMs for the specific build name. |
| 379 | """ |
| 380 | downloader = kvm_utils.KojiDownloader(cmd=self.koji_cmd) |
| 381 | downloader.get(src_package=self.src_pkg, tag=self.tag, |
| 382 | build=self.build, dst_dir=self.srcdir) |
| 383 | |
| 384 | |
| 385 | def install(self): |
| 386 | super(KojiInstaller, self)._clean_previous_installs() |
| 387 | self._get_packages() |
| 388 | super(KojiInstaller, self)._install_packages() |
| 389 | create_symlinks(test_bindir=self.test_bindir, |
| 390 | bin_list=self.qemu_bin_paths) |
| 391 | self.reload_modules_if_needed() |
| 392 | if self.save_results: |
| 393 | save_build(self.srcdir, self.results_dir) |
| 394 | |
| 395 | |
| 396 | class SourceDirInstaller(BaseInstaller): |
| 397 | """ |
| 398 | Class that handles building/installing KVM directly from a tarball or |
| 399 | a single source code dir. |
| 400 | """ |
| 401 | def set_install_params(self, test, params): |
| 402 | """ |
| 403 | Initializes class attributes, and retrieves KVM code. |
| 404 | |
| 405 | @param test: kvm test object |
| 406 | @param params: Dictionary with test arguments |
| 407 | """ |
| 408 | super(SourceDirInstaller, self).set_install_params(test, params) |
| 409 | |
| 410 | self.mod_install_dir = os.path.join(self.prefix, 'modules') |
| 411 | self.installed_kmods = False # it will be set to True in case we |
| 412 | # installed our own modules |
| 413 | |
| 414 | srcdir = params.get("srcdir", None) |
| 415 | self.path_to_roms = params.get("path_to_rom_images", None) |
| 416 | |
| 417 | if self.install_mode == 'localsrc': |
| 418 | if srcdir is None: |
| 419 | raise error.TestError("Install from source directory specified" |
| 420 | "but no source directory provided on the" |
| 421 | "control file.") |
| 422 | else: |
| 423 | shutil.copytree(srcdir, self.srcdir) |
| 424 | |
| 425 | if self.install_mode == 'release': |
| 426 | release_tag = params.get("release_tag") |
| 427 | release_dir = params.get("release_dir") |
| 428 | release_listing = params.get("release_listing") |
| 429 | logging.info("Installing KVM from release tarball") |
| 430 | if not release_tag: |
| 431 | release_tag = kvm_utils.get_latest_kvm_release_tag( |
| 432 | release_listing) |
| 433 | tarball = os.path.join(release_dir, 'kvm', release_tag, |
| 434 | "kvm-%s.tar.gz" % release_tag) |
| 435 | logging.info("Retrieving release kvm-%s" % release_tag) |
| 436 | tarball = utils.unmap_url("/", tarball, "/tmp") |
| 437 | |
| 438 | elif self.install_mode == 'snapshot': |
| 439 | logging.info("Installing KVM from snapshot") |
| 440 | snapshot_dir = params.get("snapshot_dir") |
| 441 | if not snapshot_dir: |
| 442 | raise error.TestError("Snapshot dir not provided") |
| 443 | snapshot_date = params.get("snapshot_date") |
| 444 | if not snapshot_date: |
| 445 | # Take yesterday's snapshot |
| 446 | d = (datetime.date.today() - |
| 447 | datetime.timedelta(1)).strftime("%Y%m%d") |
| 448 | else: |
| 449 | d = snapshot_date |
| 450 | tarball = os.path.join(snapshot_dir, "kvm-snapshot-%s.tar.gz" % d) |
| 451 | logging.info("Retrieving kvm-snapshot-%s" % d) |
| 452 | tarball = utils.unmap_url("/", tarball, "/tmp") |
| 453 | |
| 454 | elif self.install_mode == 'localtar': |
| 455 | tarball = params.get("tarball") |
| 456 | if not tarball: |
| 457 | raise error.TestError("KVM Tarball install specified but no" |
| 458 | " tarball provided on control file.") |
| 459 | logging.info("Installing KVM from a local tarball") |
| 460 | logging.info("Using tarball %s") |
| 461 | tarball = utils.unmap_url("/", params.get("tarball"), "/tmp") |
| 462 | |
| 463 | if self.install_mode in ['release', 'snapshot', 'localtar']: |
| 464 | utils.extract_tarball_to_dir(tarball, self.srcdir) |
| 465 | |
| 466 | if self.install_mode in ['release', 'snapshot', 'localtar', 'srcdir']: |
| 467 | self.repo_type = kvm_utils.check_kvm_source_dir(self.srcdir) |
| 468 | configure_script = os.path.join(self.srcdir, 'configure') |
| 469 | self.configure_options = check_configure_options(configure_script) |
| 470 | |
| 471 | |
| 472 | def _build(self): |
| 473 | make_jobs = utils.count_cpus() |
| 474 | os.chdir(self.srcdir) |
| 475 | # For testing purposes, it's better to build qemu binaries with |
| 476 | # debugging symbols, so we can extract more meaningful stack traces. |
| 477 | cfg = "./configure --prefix=%s" % self.prefix |
| 478 | if "--disable-strip" in self.configure_options: |
| 479 | cfg += " --disable-strip" |
| 480 | steps = [cfg, "make clean", "make -j %s" % make_jobs] |
| 481 | logging.info("Building KVM") |
| 482 | for step in steps: |
| 483 | utils.system(step) |
| 484 | |
| 485 | |
| 486 | def _install_kmods_old_userspace(self, userspace_path): |
| 487 | """ |
| 488 | Run the module install command. |
| 489 | |
| 490 | This is for the "old userspace" code, that contained a 'kernel' subdirectory |
| 491 | with the kmod build code. |
| 492 | |
| 493 | The code would be much simpler if we could specify the module install |
| 494 | path as parameter to the toplevel Makefile. As we can't do that and |
| 495 | the module install code doesn't use --prefix, we have to call |
| 496 | 'make -C kernel install' directly, setting the module directory |
| 497 | parameters. |
| 498 | |
| 499 | If the userspace tree doens't have a 'kernel' subdirectory, the |
| 500 | module install step will be skipped. |
| 501 | |
| 502 | @param userspace_path: the path the kvm-userspace directory |
| 503 | """ |
| 504 | kdir = os.path.join(userspace_path, 'kernel') |
| 505 | if os.path.isdir(kdir): |
| 506 | os.chdir(kdir) |
| 507 | # INSTALLDIR is the target dir for the modules |
| 508 | # ORIGMODDIR is the dir where the old modules will be removed. we |
| 509 | # don't want to mess with the system modules, so set it |
| 510 | # to a non-existing directory |
| 511 | utils.system('make install INSTALLDIR=%s ORIGMODDIR=/tmp/no-old-modules' % (self.mod_install_dir)) |
| 512 | self.installed_kmods = True |
| 513 | |
| 514 | |
| 515 | def _install_kmods(self, kmod_path): |
| 516 | """Run the module install command for the kmod-kvm repository |
| 517 | |
| 518 | @param kmod_path: the path to the kmod-kvm.git working copy |
| 519 | """ |
| 520 | os.chdir(kmod_path) |
| 521 | utils.system('make modules_install DESTDIR=%s' % (self.mod_install_dir)) |
| 522 | self.installed_kmods = True |
| 523 | |
| 524 | |
| 525 | def _install(self): |
| 526 | os.chdir(self.srcdir) |
| 527 | logging.info("Installing KVM userspace") |
| 528 | if self.repo_type == 1: |
| 529 | utils.system("make -C qemu install") |
| 530 | self._install_kmods_old_userspace(self.srcdir) |
| 531 | elif self.repo_type == 2: |
| 532 | utils.system("make install") |
| 533 | if self.path_to_roms: |
| 534 | install_roms(self.path_to_roms, self.prefix) |
| 535 | create_symlinks(self.test_bindir, self.prefix) |
| 536 | |
| 537 | |
| 538 | def _load_modules(self, mod_list): |
| 539 | # load the installed KVM modules in case we installed them |
| 540 | # ourselves. Otherwise, just load the system modules. |
| 541 | if self.installed_kmods: |
| 542 | logging.info("Loading installed KVM modules") |
| 543 | _load_kvm_modules(mod_list, module_dir=self.mod_install_dir) |
| 544 | else: |
| 545 | logging.info("Loading stock KVM modules") |
| 546 | _load_kvm_modules(mod_list, load_stock=True) |
| 547 | |
| 548 | |
| 549 | def install(self): |
| 550 | self._build() |
| 551 | self._install() |
| 552 | self.reload_modules_if_needed() |
| 553 | if self.save_results: |
| 554 | save_build(self.srcdir, self.results_dir) |
| 555 | |
| 556 | |
| 557 | class GitInstaller(SourceDirInstaller): |
| 558 | def _pull_code(self): |
| 559 | """ |
| 560 | Retrieves code from git repositories. |
| 561 | """ |
| 562 | params = self.params |
| 563 | |
| 564 | kernel_repo = params.get("git_repo") |
| 565 | user_repo = params.get("user_git_repo") |
| 566 | kmod_repo = params.get("kmod_repo") |
| 567 | test_repo = params.get("test_git_repo") |
| 568 | |
| 569 | kernel_branch = params.get("kernel_branch", "master") |
| 570 | user_branch = params.get("user_branch", "master") |
| 571 | kmod_branch = params.get("kmod_branch", "master") |
| 572 | test_branch = params.get("test_branch", "master") |
| 573 | |
| 574 | kernel_lbranch = params.get("kernel_lbranch", "master") |
| 575 | user_lbranch = params.get("user_lbranch", "master") |
| 576 | kmod_lbranch = params.get("kmod_lbranch", "master") |
| 577 | test_lbranch = params.get("test_lbranch", "master") |
| 578 | |
| 579 | kernel_commit = params.get("kernel_commit", None) |
| 580 | user_commit = params.get("user_commit", None) |
| 581 | kmod_commit = params.get("kmod_commit", None) |
| 582 | test_commit = params.get("test_commit", None) |
| 583 | |
| 584 | kernel_patches = eval(params.get("kernel_patches", "[]")) |
| 585 | user_patches = eval(params.get("user_patches", "[]")) |
| 586 | kmod_patches = eval(params.get("user_patches", "[]")) |
| 587 | |
| 588 | if not user_repo: |
| 589 | message = "KVM user git repository path not specified" |
| 590 | logging.error(message) |
| 591 | raise error.TestError(message) |
| 592 | |
| 593 | userspace_srcdir = os.path.join(self.srcdir, "kvm_userspace") |
| 594 | kvm_utils.get_git_branch(user_repo, user_branch, userspace_srcdir, |
| 595 | user_commit, user_lbranch) |
| 596 | self.userspace_srcdir = userspace_srcdir |
| 597 | |
| 598 | if user_patches: |
| 599 | os.chdir(self.userspace_srcdir) |
| 600 | for patch in user_patches: |
| 601 | utils.get_file(patch, os.path.join(self.userspace_srcdir, |
| 602 | os.path.basename(patch))) |
| 603 | utils.system('patch -p1 %s' % os.path.basename(patch)) |
| 604 | |
| 605 | if test_repo: |
| 606 | test_srcdir = os.path.join(self.srcdir, "kvm-unit-tests") |
| 607 | kvm_utils.get_git_branch(test_repo, test_branch, test_srcdir, |
| 608 | test_commit, test_lbranch) |
| 609 | unittest_cfg = os.path.join(test_srcdir, 'x86', |
| 610 | 'unittests.cfg') |
| 611 | self.test_srcdir = test_srcdir |
| 612 | else: |
| 613 | unittest_cfg = os.path.join(userspace_srcdir, 'kvm', 'test', 'x86', |
| 614 | 'unittests.cfg') |
| 615 | |
| 616 | self.unittest_cfg = None |
| 617 | if os.path.isfile(unittest_cfg): |
| 618 | self.unittest_cfg = unittest_cfg |
| 619 | |
| 620 | if kernel_repo: |
| 621 | kernel_srcdir = os.path.join(self.srcdir, "kvm") |
| 622 | kvm_utils.get_git_branch(kernel_repo, kernel_branch, kernel_srcdir, |
| 623 | kernel_commit, kernel_lbranch) |
| 624 | self.kernel_srcdir = kernel_srcdir |
| 625 | if kernel_patches: |
| 626 | os.chdir(self.kernel_srcdir) |
| 627 | for patch in kernel_patches: |
| 628 | utils.get_file(patch, os.path.join(self.userspace_srcdir, |
| 629 | os.path.basename(patch))) |
| 630 | utils.system('patch -p1 %s' % os.path.basename(patch)) |
| 631 | else: |
| 632 | self.kernel_srcdir = None |
| 633 | |
| 634 | if kmod_repo: |
| 635 | kmod_srcdir = os.path.join (self.srcdir, "kvm_kmod") |
| 636 | kvm_utils.get_git_branch(kmod_repo, kmod_branch, kmod_srcdir, |
| 637 | kmod_commit, kmod_lbranch) |
| 638 | self.kmod_srcdir = kmod_srcdir |
| 639 | if kmod_patches: |
| 640 | os.chdir(self.kmod_srcdir) |
| 641 | for patch in kmod_patches: |
| 642 | utils.get_file(patch, os.path.join(self.userspace_srcdir, |
| 643 | os.path.basename(patch))) |
| 644 | utils.system('patch -p1 %s' % os.path.basename(patch)) |
| 645 | else: |
| 646 | self.kmod_srcdir = None |
| 647 | |
| 648 | configure_script = os.path.join(self.userspace_srcdir, 'configure') |
| 649 | self.configure_options = check_configure_options(configure_script) |
| 650 | |
| 651 | |
| 652 | def _build(self): |
| 653 | make_jobs = utils.count_cpus() |
| 654 | cfg = './configure' |
| 655 | if self.kmod_srcdir: |
| 656 | logging.info('Building KVM modules') |
| 657 | os.chdir(self.kmod_srcdir) |
| 658 | module_build_steps = [cfg, |
| 659 | 'make clean', |
| 660 | 'make sync LINUX=%s' % self.kernel_srcdir, |
| 661 | 'make'] |
| 662 | elif self.kernel_srcdir: |
| 663 | logging.info('Building KVM modules') |
| 664 | os.chdir(self.userspace_srcdir) |
| 665 | cfg += ' --kerneldir=%s' % self.host_kernel_srcdir |
| 666 | module_build_steps = [cfg, |
| 667 | 'make clean', |
| 668 | 'make -C kernel LINUX=%s sync' % self.kernel_srcdir] |
| 669 | else: |
| 670 | module_build_steps = [] |
| 671 | |
| 672 | for step in module_build_steps: |
| 673 | utils.run(step) |
| 674 | |
| 675 | logging.info('Building KVM userspace code') |
| 676 | os.chdir(self.userspace_srcdir) |
| 677 | cfg += ' --prefix=%s' % self.prefix |
| 678 | if "--disable-strip" in self.configure_options: |
| 679 | cfg += ' --disable-strip' |
| 680 | if self.extra_configure_options: |
| 681 | cfg += ' %s' % self.extra_configure_options |
| 682 | utils.system(cfg) |
| 683 | utils.system('make clean') |
| 684 | utils.system('make -j %s' % make_jobs) |
| 685 | |
| 686 | self.unittest_prefix = None |
| 687 | if self.unittest_cfg: |
| 688 | os.chdir(os.path.dirname(os.path.dirname(self.unittest_cfg))) |
| 689 | utils.system('./configure --prefix=%s' % self.prefix) |
| 690 | utils.system('make') |
| 691 | utils.system('make install') |
| 692 | self.unittest_prefix = os.path.join(self.prefix, 'share', 'qemu', |
| 693 | 'tests') |
| 694 | |
| 695 | |
| 696 | def _install(self): |
| 697 | if self.kernel_srcdir: |
| 698 | os.chdir(self.userspace_srcdir) |
| 699 | # the kernel module install with --prefix doesn't work, and DESTDIR |
| 700 | # wouldn't work for the userspace stuff, so we clear WANT_MODULE: |
| 701 | utils.system('make install WANT_MODULE=') |
| 702 | # and install the old-style-kmod modules manually: |
| 703 | self._install_kmods_old_userspace(self.userspace_srcdir) |
| 704 | elif self.kmod_srcdir: |
| 705 | # if we have a kmod repository, it is easier: |
| 706 | # 1) install userspace: |
| 707 | os.chdir(self.userspace_srcdir) |
| 708 | utils.system('make install') |
| 709 | # 2) install kmod: |
| 710 | self._install_kmods(self.kmod_srcdir) |
| 711 | else: |
| 712 | # if we don't have kmod sources, we just install |
| 713 | # userspace: |
| 714 | os.chdir(self.userspace_srcdir) |
| 715 | utils.system('make install') |
| 716 | |
| 717 | if self.path_to_roms: |
| 718 | install_roms(self.path_to_roms, self.prefix) |
| 719 | create_symlinks(test_bindir=self.test_bindir, prefix=self.prefix, |
| 720 | bin_list=None, |
| 721 | unittest=self.unittest_prefix) |
| 722 | |
| 723 | |
| 724 | def install(self): |
| 725 | self._pull_code() |
| 726 | self._build() |
| 727 | self._install() |
| 728 | self.reload_modules_if_needed() |
| 729 | if self.save_results: |
| 730 | save_build(self.srcdir, self.results_dir) |
| 731 | |
| 732 | |
| 733 | class PreInstalledKvm(BaseInstaller): |
| 734 | # load_modules() will use the stock modules: |
| 735 | load_stock_modules = True |
| 736 | def install(self): |
| 737 | logging.info("Expecting KVM to be already installed. Doing nothing") |
| 738 | |
| 739 | |
| 740 | class FailedInstaller: |
| 741 | """ |
| 742 | Class used to be returned instead of the installer if a installation fails |
| 743 | |
| 744 | Useful to make sure no installer object is used if KVM installation fails. |
| 745 | """ |
| 746 | def __init__(self, msg="KVM install failed"): |
| 747 | self._msg = msg |
| 748 | |
| 749 | |
| 750 | def load_modules(self): |
| 751 | """Will refuse to load the KVM modules as install failed""" |
| 752 | raise FailedKvmInstall("KVM modules not available. reason: %s" % (self._msg)) |
| 753 | |
| 754 | |
| 755 | installer_classes = { |
| 756 | 'localsrc': SourceDirInstaller, |
| 757 | 'localtar': SourceDirInstaller, |
| 758 | 'release': SourceDirInstaller, |
| 759 | 'snapshot': SourceDirInstaller, |
| 760 | 'git': GitInstaller, |
| 761 | 'yum': YumInstaller, |
| 762 | 'koji': KojiInstaller, |
| 763 | 'preinstalled': PreInstalledKvm, |
| 764 | } |
| 765 | |
| 766 | |
| 767 | def _installer_class(install_mode): |
| 768 | c = installer_classes.get(install_mode) |
| 769 | if c is None: |
| 770 | raise error.TestError('Invalid or unsupported' |
| 771 | ' install mode: %s' % install_mode) |
| 772 | return c |
| 773 | |
| 774 | |
| 775 | def make_installer(params): |
| 776 | # priority: |
| 777 | # - 'install_mode' param |
| 778 | # - 'mode' param |
| 779 | mode = params.get("install_mode", params.get("mode")) |
| 780 | klass = _installer_class(mode) |
| 781 | return klass(mode) |