def setup_tests(options): """ Load and instantiate all client tests. This function is inspired from runtest() on client/shared/test.py. :param options: an object passed in from command line OptionParser. See all options defined on client/autotest. """ assert options.client_test_setup, 'Specify prebuild client tests on the ' \ 'command line.' requested_tests = options.client_test_setup.split(',') candidates, broken_tests = load_all_client_tests(options) failed_tests = [] if 'all' in requested_tests: need_to_setup = candidates failed_tests += broken_tests else: need_to_setup = [] for candidate in candidates: if candidate.__class__.__name__ in requested_tests: need_to_setup.append(candidate) for broken_test in broken_tests: if broken_test in requested_tests: failed_tests.append(broken_test) if need_to_setup: cwd = os.getcwd() os.chdir(need_to_setup[0].job.clientdir) os.system('tools/make_clean') os.chdir(cwd) elif not failed_tests: logging.error('### No test setup candidates ###') raise error.AutoservError('No test setup candidates.') for client_test in need_to_setup: good_setup = setup_test(client_test) if not good_setup: failed_tests.append(client_test.__class__.__name__) logging.info('############################# SUMMARY ' '#############################') # Print out tests that failed if failed_tests: logging.info('Finished setup -- The following tests failed') for failed_test in failed_tests: logging.info(failed_test) else: logging.info('Finished setup -- All tests built successfully') logging.info('######################### END SUMMARY ' '##############################') if failed_tests: raise error.AutoservError('Finished setup with errors.')
def install(self, host, label='autotest', default=False, kernel_args='', install_vmlinux=True): """ Install a kernel on the remote host. This will also invoke the guest's bootloader to set this kernel as the default kernel if default=True. Args: host: the host on which to install the kernel [kwargs]: remaining keyword arguments will be passed to Bootloader.add_kernel() Raises: AutoservError: no package has yet been obtained. Call RPMKernel.get() with a .rpm package. """ if len(label) > 15: raise error.AutoservError("label for kernel is too long \ (> 15 chars): %s" % label) if self.source_material is None: raise error.AutoservError("A kernel must first be \ specified via get()") rpm = self.source_material remote_tmpdir = host.get_tmp_dir() remote_rpm = os.path.join(remote_tmpdir, os.path.basename(rpm)) rpm_package = utils.run('/usr/bin/rpm -q -p %s' % rpm).stdout vmlinuz = self.get_image_name() host.send_file(rpm, remote_rpm) host.run('rpm -e ' + rpm_package, ignore_status=True) host.run('rpm --force -i ' + remote_rpm) # Copy over the uncompressed image if there is one if install_vmlinux: vmlinux = self.get_vmlinux_name() host.run('cd /;rpm2cpio %s | cpio -imuv .%s' % (remote_rpm, vmlinux)) host.run('ls ' + vmlinux) # Verify host.bootloader.remove_kernel(label) host.bootloader.add_kernel(vmlinuz, label, args=kernel_args, default=default) if kernel_args: host.bootloader.add_args(label, kernel_args) if not default: host.bootloader.boot_once(label)
def install(self, host, **kwargs): """ Install a kernel on the remote host. This will also invoke the guest's bootloader to set this kernel as the default kernel. Args: host: the host on which to install the kernel [kwargs]: remaining keyword arguments will be passed to Bootloader.add_kernel() Raises: AutoservError: no package has yet been obtained. Call DEBKernel.get() with a .deb package. """ if self.source_material is None: raise error.AutoservError("A kernel must first be " "specified via get()") remote_tmpdir = host.get_tmp_dir() basename = os.path.basename(self.source_material) remote_filename = os.path.join(remote_tmpdir, basename) host.send_file(self.source_material, remote_filename) host.run('dpkg -i "%s"' % (utils.sh_escape(remote_filename), )) host.run('mkinitramfs -o "%s" "%s"' % ( utils.sh_escape(self.get_initrd_name()), utils.sh_escape(self.get_version()), )) host.bootloader.add_kernel(self.get_image_name(), initrd=self.get_initrd_name(), **kwargs)
def extract(self, host): """Extract the kernel package. This function is only useful to access the content of the package (for example the kernel image) without installing it. It is not necessary to run this function to install the kernel. Args: host: the host on which to extract the kernel package. Returns: The full path to the temporary directory on host where the package was extracted. Raises: AutoservError: no package has yet been obtained. Call DEBKernel.get() with a .deb package. """ if self.source_material is None: raise error.AutoservError("A kernel must first be " "specified via get()") remote_tmpdir = host.get_tmp_dir() basename = os.path.basename(self.source_material) remote_filename = os.path.join(remote_tmpdir, basename) host.send_file(self.source_material, remote_filename) content_dir = os.path.join(remote_tmpdir, "contents") host.run('dpkg -x "%s" "%s"' % ( utils.sh_escape(remote_filename), utils.sh_escape(content_dir), )) return content_dir
def repair(self, host_protection): if not self.machines: raise error.AutoservError('No machines specified to repair') if self.resultdir: os.chdir(self.resultdir) namespace = {'machines': self.machines, 'job': self, 'ssh_user': self._ssh_user, 'ssh_port': self._ssh_port, 'ssh_pass': self._ssh_pass, 'protection_level': host_protection} self._execute_code(REPAIR_CONTROL_FILE, namespace, protect=False)
def _install_boottool(self): ''' Installs boottool on the host ''' if self._host() is None: raise error.AutoservError( "Host does not exist anymore") tmpdir = self._host().get_tmp_dir() self._host().send_file(os.path.abspath(os.path.join( utils.get_server_dir(), BOOTTOOL_CLI_PATH)), tmpdir) self._boottool_path = os.path.join(tmpdir, os.path.basename(BOOTTOOL_CLI_PATH))
def verify(self): if not self.machines: raise error.AutoservError('No machines specified to verify') if self.resultdir: os.chdir(self.resultdir) try: namespace = {'machines' : self.machines, 'job' : self, 'ssh_user' : self._ssh_user, 'ssh_port' : self._ssh_port, 'ssh_pass' : self._ssh_pass} self._execute_code(VERIFY_CONTROL_FILE, namespace, protect=False) except Exception, e: msg = ('Verify failed\n' + str(e) + '\n' + traceback.format_exc()) self.record('ABORT', None, None, msg) raise
def get_initrd_name(self): """Get the name of the initrd file to be installed. Returns: The full path to the initrd file as it will be installed on the host. If the package includes no initrd file, None is returned Raises: AutoservError: no package has yet been obtained. Call DEBKernel.get() with a .deb package. """ if self.source_material is None: raise error.AutoservError("A kernel must first be " "specified via get()") return "/boot/initrd.img-%s" % (self.get_version(), )
def _import_names(module_name, names=()): """ Import a module and assign named attributes into namespace. Args: module_name: The string module name. names: A limiting list of names to import from module_name. If empty (the default), all names are imported from the module similar to a "from foo.bar import *" statement. Raises: error.AutoservError: When a name being imported would clobber a name already in namespace. """ module = __import__(module_name, {}, {}, names) # No names supplied? Import * from the lowest level module. # (Ugh, why do I have to implement this part myself?) if not names: for submodule_name in module_name.split('.')[1:]: module = getattr(module, submodule_name) if hasattr(module, '__all__'): names = getattr(module, '__all__') else: names = dir(module) # Install each name into namespace, checking to make sure it # doesn't override anything that already exists. for name in names: # Check for conflicts to help prevent future problems. if name in namespace and protect: if namespace[name] is not getattr(module, name): raise error.AutoservError( 'importing name ' '%s from %s %r would override %r' % (name, module_name, getattr( module, name), namespace[name])) else: # Encourage cleanliness and the use of __all__ for a # more concrete API with less surprises on '*' imports. warnings.warn('%s (%r) being imported from %s for use ' 'in server control files is not the ' 'first occurrence of that import.' % (name, namespace[name], module_name)) namespace[name] = getattr(module, name)
def get_version(self): """Get the version of the kernel to be installed. Returns: The version string, as would be returned by 'make kernelrelease'. Raises: AutoservError: no package has yet been obtained. Call DEBKernel.get() with a .deb package. """ if self.source_material is None: raise error.AutoservError("A kernel must first be " "specified via get()") retval = utils.run('dpkg-deb -f "%s" version' % utils.sh_escape(self.source_material),) return retval.stdout.strip()
def get_image_name(self): """Get the name of the kernel image to be installed. Returns: The full path to the kernel image file as it will be installed on the host. Raises: AutoservError: no package has yet been obtained. Call RPMKernel.get() with a .rpm package. """ if self.source_material is None: raise error.AutoservError("A kernel must first be \ specified via get()") vmlinuz = utils.run('rpm -q -l -p %s | grep /boot/vmlinuz' % self.source_material).stdout.strip() return vmlinuz
def get_version(self): """Get the version of the kernel to be installed. Returns: The version string, as would be returned by 'make kernelrelease'. Raises: AutoservError: no package has yet been obtained. Call RPMKernel.get() with a .rpm package. """ if self.source_material is None: raise error.AutoservError("A kernel must first be \ specified via get()") retval = utils.run( 'rpm -qpi %s | grep Version | awk \'{print($3);}\'' % utils.sh_escape(self.source_material)) return retval.stdout.strip()
def parallel(tasklist, timeout=None, return_results=False): """ Run a set of predefined subcommands in parallel. :param tasklist: A list of subcommand instances to execute. :param timeout: Number of seconds after which the commands should timeout. :param return_results: If True instead of an AutoServError being raised on any error a list of the results|exceptions from the tasks is returned. [default: False] """ run_error = False for task in tasklist: task.fork_start() remaining_timeout = None if timeout: endtime = time.time() + timeout results = [] for task in tasklist: if timeout: remaining_timeout = max(endtime - time.time(), 1) try: status = task.fork_waitfor(timeout=remaining_timeout) except error.AutoservSubcommandError: run_error = True else: if status != 0: run_error = True results.append(pickle.load(task.result_pickle)) task.result_pickle.close() if return_results: return results elif run_error: message = 'One or more subcommands failed:\n' for task, result in zip(tasklist, results): message += 'task: %s returned/raised: %r\n' % (task, result) raise error.AutoservError(message)
def get_initrd_name(self): """Get the name of the initrd file to be installed. Returns: The full path to the initrd file as it will be installed on the host. If the package includes no initrd file, None is returned Raises: AutoservError: no package has yet been obtained. Call RPMKernel.get() with a .rpm package. """ if self.source_material is None: raise error.AutoservError("A kernel must first be \ specified via get()") res = utils.run('rpm -q -l -p %s | grep /boot/initrd' % self.source_material, ignore_status=True) if res.exit_status: return None return res.stdout.strip()
def setup_test(client_test): """ Direct invoke test.setup() method. :return: A boolean to represent success or not. """ # TODO: check if its already build. .version? hash? test_name = client_test.__class__.__name__ cwd = os.getcwd() good_setup = False try: try: outputdir = os.path.join(client_test.job.resultdir, test_name) try: os.makedirs(outputdir) os.chdir(outputdir) except OSError: pass logging.info('setup %s.' % test_name) client_test.setup() # Touch .version file under src to prevent further setup on client # host. See client/shared/utils.py update_version() if os.path.exists(client_test.srcdir): versionfile = os.path.join(client_test.srcdir, '.version') pickle.dump(client_test.version, open(versionfile, 'w')) good_setup = True except Exception as err: logging.error(err) raise error.AutoservError('Failed to build client test %s on ' 'server.' % test_name) finally: # back to original working dir os.chdir(cwd) return good_setup
def create_host(hostname, auto_monitor=True, follow_paths=None, pattern_paths=None, netconsole=False, **args): # parse out the profile up-front, if it's there, or else console monitoring # will not work # Here, ssh_user, ssh_pass and ssh_port are injected in the namespace # pylint: disable=E0602 hostname, args['user'], args['password'], args['port'], args['profile'] = ( server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port)) # @UndefinedVariable # by default assume we're using SSH support if SSH_ENGINE == 'paramiko': from autotest.server.hosts import paramiko_host classes = [paramiko_host.ParamikoHost] elif SSH_ENGINE == 'raw_ssh': classes = [ssh_host.SSHHost, ssh_host.AsyncSSHMixin] else: raise error.AutoservError( "Unknown SSH engine %s. Please verify the " "value of the configuration key 'ssh_engine' " "on autotest's global_config.ini file." % SSH_ENGINE) # by default mix in run_test support classes.append(autotest_remote.AutotestHostMixin) # if the user really wants to use netconsole, let them if netconsole: classes.append(netconsole.NetconsoleHost) if auto_monitor: # use serial console support if it's available conmux_args = {} for key in ("conmux_server", "conmux_attach"): if key in args: conmux_args[key] = args[key] if serial.SerialHost.host_is_supported(hostname, **conmux_args): classes.append(serial.SerialHost) else: # no serial available, fall back to direct dmesg logging if follow_paths is None: follow_paths = [DEFAULT_FOLLOW_PATH] else: follow_paths = list(follow_paths) + [DEFAULT_FOLLOW_PATH] if pattern_paths is None: pattern_paths = [DEFAULT_PATTERNS_PATH] else: pattern_paths = (list(pattern_paths) + [DEFAULT_PATTERNS_PATH]) logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin( follow_paths, pattern_paths) classes.append(logfile_monitor_class) elif follow_paths: logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin( follow_paths, pattern_paths) classes.append(logfile_monitor_class) # do any site-specific processing of the classes list site_factory.postprocess_classes(classes, hostname, auto_monitor=auto_monitor, **args) # create a custom host class for this machine and return an instance of it host_class = type("%s_host" % hostname, tuple(classes), {}) host_instance = host_class(hostname, **args) # call job_start if this is the first time this host is being used if hostname not in _started_hostnames: host_instance.job_start() _started_hostnames.add(hostname) return host_instance
def _install(self, host=None, autodir=None, use_autoserv=True, use_packaging=True): """ Install autotest. :param host A Host instance on which autotest will be installed :param autodir Location on the remote host to install to :param use_autoserv Enable install modes that depend on the client running with the autoserv harness :param use_packaging Enable install modes that use the packaging system @exception AutoservError If it wasn't possible to install the client after trying all available methods """ if not host: host = self.host if not self.got: self.get() host.wait_up(timeout=30) host.setup() logging.info("Installing autotest on %s", host.hostname) if self.server_system_wide_install: msg_install = ("Autotest seems to be installed in the " "client on a system wide location, proceeding...") logging.info("Verifying client package install") if _client_system_wide_install(host): logging.info(msg_install) self.installed = True return install_cmd = INSTALL_CLIENT_CMD_MAPPING.get(self.os_vendor, None) if install_cmd is not None: logging.info(msg_install) host.run(install_cmd) if _client_system_wide_install(host): logging.info("Autotest seems to be installed in the " "client on a system wide location, proceeding...") self.installed = True return raise error.AutoservError("The autotest client package " "does not seem to be installed " "on %s" % host.hostname) # set up the autotest directory on the remote machine if not autodir: autodir = self.get_install_dir(host) logging.info('Using installation dir %s', autodir) host.set_autodir(autodir) host.run('mkdir -p %s' % utils.sh_escape(autodir)) # make sure there are no files in $AUTODIR/results results_path = os.path.join(autodir, 'results') host.run('rm -rf %s/*' % utils.sh_escape(results_path), ignore_status=True) # Fetch the autotest client from the nearest repository if use_packaging: try: self._install_using_packaging(host, autodir) self._create_test_output_dir(host, autodir) logging.info("Installation of autotest completed") self.installed = True return except (error.PackageInstallError, error.AutoservRunError, SettingsError), e: logging.info("Could not install autotest using the packaging " "system: %s. Trying other methods", e)
class BaseAutotest(installable_object.InstallableObject): """ This class represents the Autotest program. Autotest is used to run tests automatically and collect the results. It also supports profilers. Implementation details: This is a leaf class in an abstract class hierarchy, it must implement the unimplemented methods in parent classes. """ def __init__(self, host=None): self.host = host self.got = False self.installed = False self.serverdir = utils.get_server_dir() super(BaseAutotest, self).__init__() install_in_tmpdir = False @classmethod def set_install_in_tmpdir(cls, flag): """ Sets a flag that controls whether or not Autotest should by default be installed in a "standard" directory (e.g. /home/autotest) or a temporary directory. """ cls.install_in_tmpdir = flag @classmethod def get_client_autodir_paths(cls, host): return global_config.global_config.get_config_value( 'AUTOSERV', 'client_autodir_paths', type=list) @classmethod def get_installed_autodir(cls, host): """ Find where the Autotest client is installed on the host. @returns an absolute path to an installed Autotest client root. @raises AutodirNotFoundError if no Autotest installation can be found. """ autodir = host.get_autodir() if autodir: logging.debug('Using existing host autodir: %s', autodir) return autodir for path in Autotest.get_client_autodir_paths(host): try: autotest_binary = os.path.join(path, 'autotest') host.run('test -x %s' % utils.sh_escape(autotest_binary)) host.run('test -w %s' % utils.sh_escape(path)) logging.debug('Found existing autodir at %s', path) return path except error.AutoservRunError: logging.debug('%s does not exist on %s', autotest_binary, host.hostname) raise AutodirNotFoundError @classmethod def get_install_dir(cls, host): """ Determines the location where autotest should be installed on host. If self.install_in_tmpdir is set, it will return a unique temporary directory that autotest can be installed in. Otherwise, looks for an existing installation to use; if none is found, looks for a usable directory in the global config client_autodir_paths. """ try: install_dir = cls.get_installed_autodir(host) except AutodirNotFoundError: install_dir = cls._find_installable_dir(host) if cls.install_in_tmpdir: return host.get_tmp_dir(parent=install_dir) return install_dir @classmethod def _find_installable_dir(cls, host): client_autodir_paths = cls.get_client_autodir_paths(host) for path in client_autodir_paths: try: host.run('mkdir -p %s' % utils.sh_escape(path)) host.run('test -w %s' % utils.sh_escape(path)) return path except error.AutoservRunError: logging.debug('Failed to create %s', path) raise error.AutoservInstallError( 'Unable to find a place to install Autotest; tried %s' % ', '.join(client_autodir_paths)) def _create_test_output_dir(self, host, autodir): tmpdir = os.path.join(autodir, 'tmp') state_autodir = global_config.global_config.get_config_value( 'COMMON', 'test_output_dir', default=tmpdir) host.run('mkdir -p %s' % utils.sh_escape(state_autodir)) def get_fetch_location(self): c = global_config.global_config repos = c.get_config_value("PACKAGES", 'fetch_location', type=list, default=[]) repos.reverse() return repos def install(self, host=None, autodir=None): self._install(host=host, autodir=autodir) def install_full_client(self, host=None, autodir=None): self._install(host=host, autodir=autodir, use_autoserv=False, use_packaging=False) def install_no_autoserv(self, host=None, autodir=None): self._install(host=host, autodir=autodir, use_autoserv=False) def _install_using_packaging(self, host, autodir): repos = self.get_fetch_location() if not repos: raise error.PackageInstallError("No repos to install an " "autotest client from") pkgmgr = packages.PackageManager(autodir, hostname=host.hostname, repo_urls=repos, do_locking=False, run_function=host.run, run_function_dargs=dict(timeout=600)) # The packages dir is used to store all the packages that # are fetched on that client. (for the tests,deps etc. # too apart from the client) pkg_dir = os.path.join(autodir, 'packages') # clean up the autodir except for the packages directory host.run('cd %s && ls | grep -v "^packages$"' ' | xargs rm -rf && rm -rf .[^.]*' % autodir) pkgmgr.install_pkg('autotest', 'client', pkg_dir, autodir, preserve_install_dir=True) self._create_test_output_dir(host, autodir) logging.info("Installation of autotest completed") self.installed = True def _install_using_send_file(self, host, autodir): dirs_to_exclude = set(["tests", "site_tests", "deps", "profilers"]) light_files = [ os.path.join(self.source_material, f) for f in os.listdir(self.source_material) if f not in dirs_to_exclude ] # there should be one and only one grubby tarball grubby_glob = os.path.join(self.source_material, "deps/grubby/grubby-*.tar.bz2") grubby_tarball_paths = glob.glob(grubby_glob) if grubby_tarball_paths: grubby_tarball_path = grubby_tarball_paths[0] if os.path.exists(grubby_tarball_path): light_files.append(grubby_tarball_path) host.send_file(light_files, autodir, delete_dest=True) profilers_autodir = os.path.join(autodir, 'profilers') profilers_init = os.path.join(self.source_material, 'profilers', '__init__.py') host.run("mkdir -p %s" % profilers_autodir) host.send_file(profilers_init, profilers_autodir, delete_dest=True) dirs_to_exclude.discard("profilers") # create empty dirs for all the stuff we excluded commands = [] for path in dirs_to_exclude: abs_path = os.path.join(autodir, path) abs_path = utils.sh_escape(abs_path) commands.append("mkdir -p '%s'" % abs_path) commands.append("touch '%s'/__init__.py" % abs_path) host.run(';'.join(commands)) def _install(self, host=None, autodir=None, use_autoserv=True, use_packaging=True): """ Install autotest. If get() was not called previously, an attempt will be made to install from the autotest svn repository. @param host A Host instance on which autotest will be installed @param autodir Location on the remote host to install to @param use_autoserv Enable install modes that depend on the client running with the autoserv harness @param use_packaging Enable install modes that use the packaging system @exception AutoservError If it wasn't possible to install the client after trying all available methods """ if not host: host = self.host if not self.got: self.get() host.wait_up(timeout=30) host.setup() logging.info("Installing autotest on %s", host.hostname) # set up the autotest directory on the remote machine if not autodir: autodir = self.get_install_dir(host) logging.info('Using installation dir %s', autodir) host.set_autodir(autodir) host.run('mkdir -p %s' % utils.sh_escape(autodir)) # make sure there are no files in $AUTODIR/results results_path = os.path.join(autodir, 'results') host.run('rm -rf %s/*' % utils.sh_escape(results_path), ignore_status=True) # Fetch the autotest client from the nearest repository if use_packaging: try: self._install_using_packaging(host, autodir) self._create_test_output_dir(host, autodir) logging.info("Installation of autotest completed") self.installed = True return except (error.PackageInstallError, error.AutoservRunError, global_config.ConfigError), e: logging.info( "Could not install autotest using the packaging " "system: %s. Trying other methods", e) # try to install from file or directory if self.source_material: c = global_config.global_config supports_autoserv_packaging = c.get_config_value( "PACKAGES", "serve_packages_from_autoserv", type=bool) # Copy autotest recursively if supports_autoserv_packaging and use_autoserv: self._install_using_send_file(host, autodir) else: host.send_file(self.source_material, autodir, delete_dest=True) self._create_test_output_dir(host, autodir) logging.info("Installation of autotest completed") self.installed = True return raise error.AutoservError('Could not install autotest on ' 'target machine: %s' % host.name)