virt.virt_test_utils: run_autotest - 'tar' needs relative paths to strip the leading '/'
[autotest-zwu.git] / client / virt / kvm_installer.py
blob98bde1291476f4f8f9d3cd90b0a94013da2adbd6
1 import os, logging, datetime, glob, shutil
2 from autotest_lib.client.bin import utils, os_dep
3 from autotest_lib.client.common_lib import error
4 import virt_utils, virt_installer
7 def kill_qemu_processes():
8 """
9 Kills all qemu processes, also kills all processes holding /dev/kvm down.
10 """
11 logging.debug("Killing any qemu processes that might be left behind")
12 utils.system("pkill qemu", ignore_status=True)
13 # Let's double check to see if some other process is holding /dev/kvm
14 if os.path.isfile("/dev/kvm"):
15 utils.system("fuser -k /dev/kvm", ignore_status=True)
18 def create_symlinks(test_bindir, prefix=None, bin_list=None, unittest=None):
19 """
20 Create symbolic links for the appropriate qemu and qemu-img commands on
21 the kvm test bindir.
23 @param test_bindir: KVM test bindir
24 @param prefix: KVM prefix path
25 @param bin_list: List of qemu binaries to link
26 @param unittest: Path to configuration file unittests.cfg
27 """
28 qemu_path = os.path.join(test_bindir, "qemu")
29 qemu_img_path = os.path.join(test_bindir, "qemu-img")
30 qemu_unittest_path = os.path.join(test_bindir, "unittests")
31 if os.path.lexists(qemu_path):
32 os.unlink(qemu_path)
33 if os.path.lexists(qemu_img_path):
34 os.unlink(qemu_img_path)
35 if unittest and os.path.lexists(qemu_unittest_path):
36 os.unlink(qemu_unittest_path)
38 logging.debug("Linking qemu binaries")
40 if bin_list:
41 for bin in bin_list:
42 if os.path.basename(bin) == 'qemu-kvm':
43 os.symlink(bin, qemu_path)
44 elif os.path.basename(bin) == 'qemu-img':
45 os.symlink(bin, qemu_img_path)
47 elif prefix:
48 kvm_qemu = os.path.join(prefix, "bin", "qemu-system-x86_64")
49 if not os.path.isfile(kvm_qemu):
50 raise error.TestError('Invalid qemu path')
51 kvm_qemu_img = os.path.join(prefix, "bin", "qemu-img")
52 if not os.path.isfile(kvm_qemu_img):
53 raise error.TestError('Invalid qemu-img path')
54 os.symlink(kvm_qemu, qemu_path)
55 os.symlink(kvm_qemu_img, qemu_img_path)
57 if unittest:
58 logging.debug("Linking unittest dir")
59 os.symlink(unittest, qemu_unittest_path)
62 def install_roms(rom_dir, prefix):
63 logging.debug("Path to roms specified. Copying roms to install prefix")
64 rom_dst_dir = os.path.join(prefix, 'share', 'qemu')
65 for rom_src in glob.glob('%s/*.bin' % rom_dir):
66 rom_dst = os.path.join(rom_dst_dir, os.path.basename(rom_src))
67 logging.debug("Copying rom file %s to %s", rom_src, rom_dst)
68 shutil.copy(rom_src, rom_dst)
71 class KvmInstallException(Exception):
72 pass
75 class FailedKvmInstall(KvmInstallException):
76 pass
79 class KvmNotInstalled(KvmInstallException):
80 pass
83 class BaseInstaller(object):
84 def __init__(self, mode=None):
85 self.install_mode = mode
86 self._full_module_list = None
88 def set_install_params(self, test, params):
89 self.params = params
91 load_modules = params.get('load_modules', 'no')
92 if not load_modules or load_modules == 'yes':
93 self.should_load_modules = True
94 elif load_modules == 'no':
95 self.should_load_modules = False
96 default_extra_modules = str(None)
97 self.extra_modules = eval(params.get("extra_modules",
98 default_extra_modules))
100 self.cpu_vendor = virt_utils.get_cpu_vendor()
102 self.srcdir = test.srcdir
103 if not os.path.isdir(self.srcdir):
104 os.makedirs(self.srcdir)
106 self.test_bindir = test.bindir
107 self.results_dir = test.resultsdir
109 # KVM build prefix, for the modes that do need it
110 prefix = os.path.join(test.bindir, 'build')
111 self.prefix = os.path.abspath(prefix)
113 # Current host kernel directory
114 default_host_kernel_source = '/lib/modules/%s/build' % os.uname()[2]
115 self.host_kernel_srcdir = params.get('host_kernel_source',
116 default_host_kernel_source)
118 # Extra parameters that can be passed to the configure script
119 self.extra_configure_options = params.get('extra_configure_options',
120 None)
122 # Do we want to save the result of the build on test.resultsdir?
123 self.save_results = True
124 save_results = params.get('save_results', 'no')
125 if save_results == 'no':
126 self.save_results = False
128 self._full_module_list = list(self._module_list())
131 def install_unittests(self):
132 userspace_srcdir = os.path.join(self.srcdir, "kvm_userspace")
133 test_repo = self.params.get("test_git_repo")
134 test_branch = self.params.get("test_branch", "master")
135 test_commit = self.params.get("test_commit", None)
136 test_lbranch = self.params.get("test_lbranch", "master")
138 if test_repo:
139 test_srcdir = os.path.join(self.srcdir, "kvm-unit-tests")
140 virt_utils.get_git_branch(test_repo, test_branch, test_srcdir,
141 test_commit, test_lbranch)
142 unittest_cfg = os.path.join(test_srcdir, 'x86',
143 'unittests.cfg')
144 self.test_srcdir = test_srcdir
145 else:
146 unittest_cfg = os.path.join(userspace_srcdir, 'kvm', 'test', 'x86',
147 'unittests.cfg')
148 self.unittest_cfg = None
149 if os.path.isfile(unittest_cfg):
150 self.unittest_cfg = unittest_cfg
151 else:
152 if test_repo:
153 logging.error("No unittest config file %s found, skipping "
154 "unittest build", self.unittest_cfg)
156 self.unittest_prefix = None
157 if self.unittest_cfg:
158 logging.info("Building and installing unittests")
159 os.chdir(os.path.dirname(os.path.dirname(self.unittest_cfg)))
160 utils.system('./configure --prefix=%s' % self.prefix)
161 utils.system('make')
162 utils.system('make install')
163 self.unittest_prefix = os.path.join(self.prefix, 'share', 'qemu',
164 'tests')
167 def full_module_list(self):
168 """Return the module list used by the installer
170 Used by the module_probe test, to avoid using utils.unload_module().
172 if self._full_module_list is None:
173 raise KvmNotInstalled("KVM modules not installed yet (installer: %s)" % (type(self)))
174 return self._full_module_list
177 def _module_list(self):
178 """Generate the list of modules that need to be loaded
180 yield 'kvm'
181 yield 'kvm-%s' % (self.cpu_vendor)
182 if self.extra_modules:
183 for module in self.extra_modules:
184 yield module
187 def _load_modules(self, mod_list):
189 Load the KVM modules
191 May be overridden by subclasses.
193 logging.info("Loading KVM modules")
194 for module in mod_list:
195 utils.system("modprobe %s" % module)
198 def load_modules(self, mod_list=None):
199 if mod_list is None:
200 mod_list = self.full_module_list()
201 self._load_modules(mod_list)
204 def _unload_modules(self, mod_list=None):
206 Just unload the KVM modules, without trying to kill Qemu
208 if mod_list is None:
209 mod_list = self.full_module_list()
210 logging.info("Unloading previously loaded KVM modules")
211 for module in reversed(mod_list):
212 utils.unload_module(module)
215 def unload_modules(self, mod_list=None):
217 Kill Qemu and unload the KVM modules
219 kill_qemu_processes()
220 self._unload_modules(mod_list)
223 def reload_modules(self):
225 Reload the KVM modules after killing Qemu and unloading the current modules
227 self.unload_modules()
228 self.load_modules()
231 def reload_modules_if_needed(self):
232 if self.should_load_modules:
233 self.reload_modules()
236 class YumInstaller(BaseInstaller):
238 Class that uses yum to install and remove packages.
240 def set_install_params(self, test, params):
241 super(YumInstaller, self).set_install_params(test, params)
242 # Checking if all required dependencies are available
243 os_dep.command("rpm")
244 os_dep.command("yum")
246 default_pkg_list = str(['qemu-kvm', 'qemu-kvm-tools'])
247 default_qemu_bin_paths = str(['/usr/bin/qemu-kvm', '/usr/bin/qemu-img'])
248 default_pkg_path_list = str(None)
249 self.pkg_list = eval(params.get("pkg_list", default_pkg_list))
250 self.pkg_path_list = eval(params.get("pkg_path_list",
251 default_pkg_path_list))
252 self.qemu_bin_paths = eval(params.get("qemu_bin_paths",
253 default_qemu_bin_paths))
256 def _clean_previous_installs(self):
257 kill_qemu_processes()
258 removable_packages = ""
259 for pkg in self.pkg_list:
260 removable_packages += " %s" % pkg
262 utils.system("yum remove -y %s" % removable_packages)
265 def _get_packages(self):
266 for pkg in self.pkg_path_list:
267 utils.get_file(pkg, os.path.join(self.srcdir,
268 os.path.basename(pkg)))
271 def _install_packages(self):
273 Install all downloaded packages.
275 os.chdir(self.srcdir)
276 utils.system("yum install --nogpgcheck -y *.rpm")
279 def install(self):
280 self.install_unittests()
281 self._clean_previous_installs()
282 self._get_packages()
283 self._install_packages()
284 create_symlinks(test_bindir=self.test_bindir,
285 bin_list=self.qemu_bin_paths,
286 unittest=self.unittest_prefix)
287 self.reload_modules_if_needed()
288 if self.save_results:
289 virt_utils.archive_as_tarball(self.srcdir, self.results_dir)
292 class KojiInstaller(YumInstaller):
294 Class that handles installing KVM from the fedora build service, koji.
296 It uses yum to install and remove packages. Packages are specified
297 according to the syntax defined in the PkgSpec class.
299 def set_install_params(self, test, params):
301 Gets parameters and initializes the package downloader.
303 @param test: kvm test object
304 @param params: Dictionary with test arguments
306 super(KojiInstaller, self).set_install_params(test, params)
307 self.tag = params.get("koji_tag", None)
308 self.koji_cmd = params.get("koji_cmd", None)
309 if self.tag is not None:
310 virt_utils.set_default_koji_tag(self.tag)
311 self.koji_pkgs = eval(params.get("koji_pkgs", "[]"))
314 def _get_packages(self):
316 Downloads the specific arch RPMs for the specific build name.
318 koji_client = virt_utils.KojiClient(cmd=self.koji_cmd)
319 for pkg_text in self.koji_pkgs:
320 pkg = virt_utils.KojiPkgSpec(pkg_text)
321 if pkg.is_valid():
322 koji_client.get_pkgs(pkg, dst_dir=self.srcdir)
323 else:
324 logging.error('Package specification (%s) is invalid: %s', pkg,
325 pkg.describe_invalid())
328 def _clean_previous_installs(self):
329 kill_qemu_processes()
330 removable_packages = " ".join(self._get_rpm_names())
331 utils.system("yum -y remove %s" % removable_packages)
334 def install(self):
335 self._clean_previous_installs()
336 self._get_packages()
337 self._install_packages()
338 self.install_unittests()
339 create_symlinks(test_bindir=self.test_bindir,
340 bin_list=self.qemu_bin_paths,
341 unittest=self.unittest_prefix)
342 self.reload_modules_if_needed()
343 if self.save_results:
344 virt_utils.archive_as_tarball(self.srcdir, self.results_dir)
347 def _get_rpm_names(self):
348 all_rpm_names = []
349 koji_client = virt_utils.KojiClient(cmd=self.koji_cmd)
350 for pkg_text in self.koji_pkgs:
351 pkg = virt_utils.KojiPkgSpec(pkg_text)
352 rpm_names = koji_client.get_pkg_rpm_names(pkg)
353 all_rpm_names += rpm_names
354 return all_rpm_names
357 def _get_rpm_file_names(self):
358 all_rpm_file_names = []
359 koji_client = virt_utils.KojiClient(cmd=self.koji_cmd)
360 for pkg_text in self.koji_pkgs:
361 pkg = virt_utils.KojiPkgSpec(pkg_text)
362 rpm_file_names = koji_client.get_pkg_rpm_file_names(pkg)
363 all_rpm_file_names += rpm_file_names
364 return all_rpm_file_names
367 def _install_packages(self):
369 Install all downloaded packages.
371 os.chdir(self.srcdir)
372 rpm_file_names = " ".join(self._get_rpm_file_names())
373 utils.system("yum --nogpgcheck -y localinstall %s" % rpm_file_names)
376 class SourceDirInstaller(BaseInstaller):
378 Class that handles building/installing KVM directly from a tarball or
379 a single source code dir.
381 def set_install_params(self, test, params):
383 Initializes class attributes, and retrieves KVM code.
385 @param test: kvm test object
386 @param params: Dictionary with test arguments
388 super(SourceDirInstaller, self).set_install_params(test, params)
390 self.mod_install_dir = os.path.join(self.prefix, 'modules')
392 srcdir = params.get("srcdir", None)
393 self.path_to_roms = params.get("path_to_rom_images", None)
395 if self.install_mode == 'localsrc':
396 if srcdir is None:
397 raise error.TestError("Install from source directory specified"
398 "but no source directory provided on the"
399 "control file.")
400 else:
401 shutil.copytree(srcdir, self.srcdir)
403 elif self.install_mode == 'localtar':
404 tarball = params.get("tarball")
405 if not tarball:
406 raise error.TestError("KVM Tarball install specified but no"
407 " tarball provided on control file.")
408 logging.info("Installing KVM from a local tarball")
409 logging.info("Using tarball %s")
410 tarball = utils.unmap_url("/", params.get("tarball"), "/tmp")
411 utils.extract_tarball_to_dir(tarball, self.srcdir)
413 if self.install_mode in ['localtar', 'srcdir']:
414 self.repo_type = virt_utils.check_kvm_source_dir(self.srcdir)
415 p = os.path.join(self.srcdir, 'configure')
416 self.configure_options = virt_installer.check_configure_options(p)
419 def _build(self):
420 make_jobs = utils.count_cpus()
421 os.chdir(self.srcdir)
422 # For testing purposes, it's better to build qemu binaries with
423 # debugging symbols, so we can extract more meaningful stack traces.
424 cfg = "./configure --prefix=%s" % self.prefix
425 if "--disable-strip" in self.configure_options:
426 cfg += " --disable-strip"
427 steps = [cfg, "make clean", "make -j %s" % make_jobs]
428 logging.info("Building KVM")
429 for step in steps:
430 utils.system(step)
433 def _install(self):
434 os.chdir(self.srcdir)
435 logging.info("Installing KVM userspace")
436 if self.repo_type == 1:
437 utils.system("make -C qemu install")
438 elif self.repo_type == 2:
439 utils.system("make install")
440 if self.path_to_roms:
441 install_roms(self.path_to_roms, self.prefix)
442 self.install_unittests()
443 create_symlinks(test_bindir=self.test_bindir,
444 prefix=self.prefix,
445 unittest=self.unittest_prefix)
448 def install(self):
449 self._build()
450 self._install()
451 self.reload_modules_if_needed()
452 if self.save_results:
453 virt_utils.archive_as_tarball(self.srcdir, self.results_dir)
455 class GitRepo(object):
456 def __init__(self, installer, prefix,
457 srcdir, build_steps=[], repo_param=None):
458 params = installer.params
459 self.installer = installer
460 self.repo = params.get(repo_param or (prefix + '_repo'))
461 self.branch = params.get(prefix + '_branch', 'master')
462 self.lbranch = params.get(prefix + '_lbranch', 'master')
463 self.commit = params.get(prefix + '_commit', None)
464 # The config system yields strings, which have to be evalued
465 self.patches = eval(params.get(prefix + '_patches', "[]"))
466 self.build_steps = build_steps
467 self.srcdir = os.path.join(self.installer.srcdir, srcdir)
470 def fetch_and_patch(self):
471 if not self.repo:
472 return
473 virt_utils.get_git_branch(self.repo, self.branch, self.srcdir,
474 self.commit, self.lbranch)
475 os.chdir(self.srcdir)
476 for patch in self.patches:
477 utils.get_file(patch, os.path.join(self.srcdir,
478 os.path.basename(patch)))
479 utils.system('patch -p1 < %s' % os.path.basename(patch))
482 def build(self):
483 os.chdir(self.srcdir)
484 for step in self.build_steps:
485 logging.info(step)
486 utils.run(step)
489 class GitInstaller(SourceDirInstaller):
490 def _pull_code(self):
492 Retrieves code from git repositories.
494 params = self.params
495 make_jobs = utils.count_cpus()
496 cfg = 'PKG_CONFIG_PATH="%s/lib/pkgconfig:%s/share/pkgconfig" ./configure' % (
497 self.prefix, self.prefix)
499 self.spice_protocol = GitRepo(installer=self, prefix='spice_protocol',
500 srcdir='spice-protocol',
501 build_steps= ['./autogen.sh',
502 './configure --prefix=%s' % self.prefix,
503 'make clean',
504 'make -j %s' % (make_jobs),
505 'make install'])
507 self.spice = GitRepo(installer=self, prefix='spice', srcdir='spice',
508 build_steps= ['PKG_CONFIG_PATH="%s/lib/pkgconfig:%s/share/pkgconfig" CXXFLAGS=-Wl,--add-needed ./autogen.sh --prefix=%s' % (self.prefix, self.prefix, self.prefix),
509 'make clean',
510 'make -j %s' % (make_jobs),
511 'make install'])
513 self.userspace = GitRepo(installer=self, prefix='user',
514 repo_param='user_git_repo', srcdir='kvm_userspace')
516 p = os.path.join(self.userspace.srcdir, 'configure')
517 self.configure_options = virt_installer.check_configure_options(p)
519 cfg = cfg + ' --prefix=%s' % self.prefix
520 if "--disable-strip" in self.configure_options:
521 cfg += ' --disable-strip'
522 if self.extra_configure_options:
523 cfg += ' %s' % self.extra_configure_options
525 self.userspace.build_steps=[cfg, 'make clean', 'make -j %s' % make_jobs]
527 if not self.userspace.repo:
528 message = "KVM user git repository path not specified"
529 logging.error(message)
530 raise error.TestError(message)
532 for repo in [self.userspace, self.spice_protocol, self.spice]:
533 if not repo.repo:
534 continue
535 repo.fetch_and_patch()
537 def _build(self):
538 if self.spice_protocol.repo:
539 logging.info('Building Spice-protocol')
540 self.spice_protocol.build()
542 if self.spice.repo:
543 logging.info('Building Spice')
544 self.spice.build()
546 logging.info('Building KVM userspace code')
547 self.userspace.build()
550 def _install(self):
551 os.chdir(self.userspace.srcdir)
552 utils.system('make install')
554 if self.path_to_roms:
555 install_roms(self.path_to_roms, self.prefix)
556 self.install_unittests()
557 create_symlinks(test_bindir=self.test_bindir, prefix=self.prefix,
558 bin_list=None,
559 unittest=self.unittest_prefix)
562 def install(self):
563 self._pull_code()
564 self._build()
565 self._install()
566 self.reload_modules_if_needed()
567 if self.save_results:
568 virt_utils.archive_as_tarball(self.srcdir, self.results_dir)
571 class PreInstalledKvm(BaseInstaller):
572 def install(self):
573 logging.info("Expecting KVM to be already installed. Doing nothing")
576 class FailedInstaller:
578 Class used to be returned instead of the installer if a installation fails
580 Useful to make sure no installer object is used if KVM installation fails.
582 def __init__(self, msg="KVM install failed"):
583 self._msg = msg
586 def load_modules(self):
587 """Will refuse to load the KVM modules as install failed"""
588 raise FailedKvmInstall("KVM modules not available. reason: %s" % (self._msg))
591 installer_classes = {
592 'localsrc': SourceDirInstaller,
593 'localtar': SourceDirInstaller,
594 'git': GitInstaller,
595 'yum': YumInstaller,
596 'koji': KojiInstaller,
597 'preinstalled': PreInstalledKvm,
601 def _installer_class(install_mode):
602 c = installer_classes.get(install_mode)
603 if c is None:
604 raise error.TestError('Invalid or unsupported'
605 ' install mode: %s' % install_mode)
606 return c
609 def make_installer(params):
610 # priority:
611 # - 'install_mode' param
612 # - 'mode' param
613 mode = params.get("install_mode", params.get("mode"))
614 klass = _installer_class(mode)
615 return klass(mode)