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():
9 Kills all qemu processes, also kills all processes holding /dev/kvm down.
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):
20 Create symbolic links for the appropriate qemu and qemu-img commands on
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
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
):
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")
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
)
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
)
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):
75 class FailedKvmInstall(KvmInstallException
):
79 class KvmNotInstalled(KvmInstallException
):
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
):
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',
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")
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',
144 self
.test_srcdir
= test_srcdir
146 unittest_cfg
= os
.path
.join(userspace_srcdir
, 'kvm', 'test', 'x86',
148 self
.unittest_cfg
= None
149 if os
.path
.isfile(unittest_cfg
):
150 self
.unittest_cfg
= unittest_cfg
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
)
162 utils
.system('make install')
163 self
.unittest_prefix
= os
.path
.join(self
.prefix
, 'share', 'qemu',
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
181 yield 'kvm-%s' % (self
.cpu_vendor
)
182 if self
.extra_modules
:
183 for module
in self
.extra_modules
:
187 def _load_modules(self
, mod_list
):
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):
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
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()
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")
280 self
.install_unittests()
281 self
._clean
_previous
_installs
()
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
)
322 koji_client
.get_pkgs(pkg
, dst_dir
=self
.srcdir
)
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
)
335 self
._clean
_previous
_installs
()
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
):
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
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':
397 raise error
.TestError("Install from source directory specified"
398 "but no source directory provided on the"
401 shutil
.copytree(srcdir
, self
.srcdir
)
403 elif self
.install_mode
== 'localtar':
404 tarball
= params
.get("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
)
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")
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
,
445 unittest
=self
.unittest_prefix
)
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
):
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
))
483 os
.chdir(self
.srcdir
)
484 for step
in self
.build_steps
:
489 class GitInstaller(SourceDirInstaller
):
490 def _pull_code(self
):
492 Retrieves code from git repositories.
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
,
504 'make -j %s' % (make_jobs
),
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
),
510 'make -j %s' % (make_jobs
),
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
]:
535 repo
.fetch_and_patch()
538 if self
.spice_protocol
.repo
:
539 logging
.info('Building Spice-protocol')
540 self
.spice_protocol
.build()
543 logging
.info('Building Spice')
546 logging
.info('Building KVM userspace code')
547 self
.userspace
.build()
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
,
559 unittest
=self
.unittest_prefix
)
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
):
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"):
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
,
596 'koji': KojiInstaller
,
597 'preinstalled': PreInstalledKvm
,
601 def _installer_class(install_mode
):
602 c
= installer_classes
.get(install_mode
)
604 raise error
.TestError('Invalid or unsupported'
605 ' install mode: %s' % install_mode
)
609 def make_installer(params
):
611 # - 'install_mode' param
613 mode
= params
.get("install_mode", params
.get("mode"))
614 klass
= _installer_class(mode
)