def run(self, connection, max_end_time, args=None): connection = super(MountVExpressMassStorageDevice, self).run(connection, max_end_time, args) device_path = "/dev/disk/by-label/%s" % self.microsd_fs_label try: os.path.realpath(device_path) except OSError: raise InfrastructureError("Unable to find disk by label %s" % device_path) mount_point = "/mnt/%s" % self.microsd_fs_label if not os.path.exists(mount_point): try: self.logger.debug("Creating mount point '%s'", mount_point) os.makedirs(mount_point, 0o755) except OSError: raise InfrastructureError("Failed to create mount point %s" % mount_point) mount_cmd = ['mount', device_path, mount_point] if not self.run_command(mount_cmd, allow_silent=True): raise InfrastructureError("Failed to mount device %s to %s" % (device_path, mount_point)) self.set_namespace_data(action=self.name, label='vexpress-fw', key='mount-point', value=mount_point) return connection
def copy_in_overlay(image, root_partition, overlay): """ Mounts test image partition as specified by the test writer and extracts overlay at the root, if root_partition is None the image is handled as a filesystem instead of partitioned image. """ guest = guestfs.GuestFS(python_return_dict=True) guest.add_drive(image) guest.launch() if root_partition: partitions = guest.list_partitions() if not partitions: raise InfrastructureError("Unable to prepare guestfs") guest_partition = partitions[root_partition] guest.mount(guest_partition, '/') else: devices = guest.list_devices() if not devices: raise InfrastructureError("Unable to prepare guestfs") guest.mount(devices[0], '/') # FIXME: max message length issues when using tar_in # on tar.gz. Works fine with tar so decompressing # overlay first. if os.path.exists(overlay[:-3]): os.unlink(overlay[:-3]) decompressed_overlay = decompress_file(overlay, 'gz') guest.tar_in(decompressed_overlay, '/') if root_partition: guest.umount(guest_partition) else: guest.umount(devices[0])
def validate(self): super(DockerAction, self).validate() err = infrastructure_error("docker") if err is not None: self.errors = err return # Print docker version try: out = subprocess.check_output( ["docker", "version", "-f", "{{.Server.Version}}"]) out = out.decode("utf-8").strip("\n") self.logger.debug("docker server, installed at version: %s", out) out = subprocess.check_output( ["docker", "version", "-f", "{{.Client.Version}}"]) out = out.decode("utf-8").strip("\n") self.logger.debug("docker client, installed at version: %s", out) except subprocess.CalledProcessError as exc: raise InfrastructureError("Unable to call '%s': %s" % (exc.cmd, exc.output)) except OSError: raise InfrastructureError("Command 'docker' does not exist") # check docker image name # The string should be safe for command line inclusion image_name = self.parameters["image"] if re.compile("^[a-z0-9._:/-]+$").match(image_name) is None: self.errors = "image_name '%s' is invalid" % image_name self.set_namespace_data(action=self.name, label='image', key='name', value=image_name)
def run(self, connection, max_end_time, args=None): if not self.parameters.get(self.param_key, None): # idempotency return connection connection = super(ExtractVExpressRecoveryImage, self).run(connection, max_end_time, args) # copy recovery image to a temporary directory and unpack recovery_image = self.get_namespace_data(action='download-action', label=self.param_key, key='file') recovery_image_dir = self.mkdtemp() shutil.copy(recovery_image, recovery_image_dir) tmp_recovery_image = os.path.join(recovery_image_dir, os.path.basename(recovery_image)) if os.path.isfile(tmp_recovery_image): if self.compression == "zip": decompress_file(tmp_recovery_image, self.compression) elif self.compression == "gz": untar_file(tmp_recovery_image, recovery_image_dir) else: raise InfrastructureError( "Unsupported compression for VExpress recovery: %s" % self.compression) os.remove(tmp_recovery_image) self.set_namespace_data(action='extract-vexpress-recovery-image', label='file', key=self.file_key, value=recovery_image_dir) self.logger.debug("Extracted %s to %s", self.file_key, recovery_image_dir) else: raise InfrastructureError("Unable to decompress recovery image") return connection
def which(path, match=os.path.isfile): exefile = _which_check(path, match) if not exefile: raise InfrastructureError("Cannot find command '%s' in $PATH" % path) if os.stat(exefile).st_mode & S_IXUSR != S_IXUSR: raise InfrastructureError("Cannot execute '%s' at '%s'" % (path, exefile)) return exefile
def run(self, connection, max_end_time, args=None): connection = super(PDUReboot, self).run(connection, max_end_time, args) if not self.job.device.hard_reset_command: raise InfrastructureError("Hard reset required but not defined.") command = self.job.device.hard_reset_command if not isinstance(command, list): command = [command] for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % cmd) self.results = {'status': 'success'} return connection
def append_dtb(self, kernel_file, dtb_file, dest_file): self.logger.info("Appending %s to %s", dtb_file, kernel_file) # Can't use cat here because it will be called with subprocess.check_output that catches stdout cmd = ["dd", "if=%s" % kernel_file, "of=%s" % dest_file] cmd2 = [ "dd", "if=%s" % dtb_file, "of=%s" % dest_file, "oflag=append", "conv=notrunc" ] if not self.run_command(cmd): raise InfrastructureError("DTB appending failed") if not self.run_command(cmd2): raise InfrastructureError("DTB appending failed")
def run(self, connection, max_end_time, args=None): lxc_active = any([ protocol for protocol in self.job.protocols if protocol.name == LxcProtocol.name ]) if self.job.device.pre_os_command and not lxc_active: self.logger.info("Running pre OS command.") command = self.job.device.pre_os_command if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) if not connection: self.logger.debug("Existing connection in %s", self.name) return connection connection.prompt_str = self.selector.prompt connection.raw_connection.linesep = self.line_sep self.logger.debug("Looking for %s", self.selector.prompt) self.wait(connection) connection = super(UefiMenuSelector, self).run(connection, max_end_time, args) if self.boot_message: self.logger.debug("Looking for %s", self.boot_message) connection.prompt_str = self.boot_message self.wait(connection) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def run(self, connection, max_end_time, args=None): connection = super(PrepareFITAction, self).run(connection, max_end_time, args) params = { label: self.get_namespace_data(action='download-action', label=label, key='file') for label in ['kernel', 'dtb', 'ramdisk'] } fit_path = os.path.join(os.path.dirname(params['kernel']), 'image.itb') params.update({ 'arch': self.deploy_params['mkimage_arch'], 'load_addr': self.device_params['load_address'], 'fit_path': fit_path, }) ramdisk_with_overlay = self.get_namespace_data( action='compress-ramdisk', label='file', key='full-path') if ramdisk_with_overlay: params['ramdisk'] = ramdisk_with_overlay cmd = self._make_mkimage_command(params) if not self.run_command(cmd): raise InfrastructureError("FIT image creation failed") kernel_tftp = self.get_namespace_data(action='download-action', label='file', key='kernel') fit_tftp = os.path.join(os.path.dirname(kernel_tftp), 'image.itb') self.set_namespace_data(action=self.name, label='file', key='fit', value=fit_tftp) return connection
def run(self, connection, max_end_time, args=None): output = os.path.join(self.mkdtemp(), "overlay-%s.tar.gz" % self.level) location = self.get_namespace_data(action='test', label='shared', key='location') lava_test_results_dir = self.get_namespace_data(action='test', label='results', key='lava_test_results_dir') self.set_namespace_data(action='test', label='shared', key='output', value=output) if not location: raise LAVABug("Missing lava overlay location") if not os.path.exists(location): raise LAVABug("Unable to find overlay location") if not self.valid: self.logger.error(self.errors) return connection connection = super(CompressOverlay, self).run(connection, max_end_time, args) with chdir(location): try: with tarfile.open(output, "w:gz") as tar: tar.add(".%s" % lava_test_results_dir) # ssh authorization support if os.path.exists('./root/'): tar.add(".%s" % '/root/') except tarfile.TarError as exc: raise InfrastructureError("Unable to create lava overlay tarball: %s" % exc) self.set_namespace_data(action=self.name, label='output', key='file', value=output) return connection
def _decompressor_stream(self): # pylint: disable=too-many-branches dwnld_file = None compression = False if 'images' in self.parameters and self.key in self.parameters[ 'images']: compression = self.parameters['images'][self.key].get( 'compression', False) else: if self.key == 'ramdisk': self.logger.debug( "Not decompressing ramdisk as can be used compressed.") else: compression = self.parameters[self.key].get( 'compression', False) fname, _ = self._url_to_fname_suffix(self.path, compression) if os.path.isdir(fname): raise JobError("Download '%s' is a directory, not a file" % fname) if os.path.exists(fname): os.remove(fname) decompressor = None if compression: if compression == 'gz': decompressor = zlib.decompressobj(16 + zlib.MAX_WBITS) elif compression == 'bz2': decompressor = bz2.BZ2Decompressor() elif compression == 'xz': decompressor = lzma.LZMADecompressor() # pylint: disable=no-member self.logger.debug("Using %s decompression" % compression) else: self.logger.debug("No compression specified.") def write(buff): if decompressor: try: buff = decompressor.decompress(buff) except EOFError as eof_exc: # EOFError can be raised when decompressing a bz2 archive # generated by pbzip2. If there is something in unsused_data # try to continue decompression. if compression == 'bz2' and decompressor.unused_data: buff = decompressor.unused_data else: error_message = str(eof_exc) self.logger.exception(error_message) raise JobError(error_message) except (IOError, lzma.error, zlib.error) as exc: # pylint: disable=no-member error_message = str(exc) self.logger.exception(error_message) raise JobError(error_message) dwnld_file.write(buff) try: with open(fname, 'wb') as dwnld_file: yield (write, fname) except (IOError, OSError) as exc: msg = "Unable to open %s: %s" % (fname, exc.strerror) self.logger.error(msg) raise InfrastructureError(msg)
def run(self, connection, max_end_time, args=None): if not connection: self.errors = "%s started without a connection already in use" % self.name connection = super(BootloaderCommandsAction, self).run(connection, max_end_time, args) connection.raw_connection.linesep = self.line_separator() connection.prompt_str = [self.params['bootloader_prompt']] at_bootloader_prompt = self.get_namespace_data(action='interrupt', label='interrupt', key='at_bootloader_prompt') if not at_bootloader_prompt: self.wait(connection, max_end_time) i = 1 commands = self.get_namespace_data(action='bootloader-overlay', label=self.method, key='commands') error_messages = self.job.device.get_constant('error-messages', prefix=self.method, missing_ok=True) final_message = self.job.device.get_constant('final-message', prefix=self.method, missing_ok=True) if error_messages: connection.prompt_str = connection.prompt_str + error_messages for line in commands: connection.sendline(line, delay=self.character_delay) if i != (len(commands)): res = self.wait(connection, max_end_time) if res != 0: raise InfrastructureError('matched a bootloader error message') i += 1 if final_message and self.expect_final: connection.prompt_str = final_message self.wait(connection, max_end_time) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def run(self, connection, max_end_time, args=None): # pylint: disable=too-many-locals connection = super(FastbootRebootBootloader, self).run(connection, max_end_time, args) # this is the device namespace - the lxc namespace is not accessible lxc_name = None protocol = [ protocol for protocol in self.job.protocols if protocol.name == LxcProtocol.name ][0] if protocol: lxc_name = protocol.lxc_name if not lxc_name: raise JobError("Unable to use fastboot") serial_number = self.job.device['fastboot_serial_number'] fastboot_opts = self.job.device['fastboot_options'] self.logger.info("fastboot reboot device to bootloader.") fastboot_cmd = [ 'lxc-attach', '-n', lxc_name, '--', 'fastboot', '-s', serial_number, 'reboot-bootloader' ] + fastboot_opts command_output = self.run_command(fastboot_cmd) if not command_output: raise InfrastructureError("Unable to reboot to bootloader") return connection
def run(self, connection, max_end_time, args=None): connection = super(TestMonitorAction, self).run(connection, max_end_time, args) if not connection: raise InfrastructureError("Connection closed") for monitor in self.parameters['monitors']: self.test_suite_name = monitor['name'] self.fixupdict = monitor.get('fixupdict') # pattern order is important because we want to match the end before # it can possibly get confused with a test result self.patterns = OrderedDict() self.patterns["eof"] = pexpect.EOF self.patterns["timeout"] = pexpect.TIMEOUT self.patterns["end"] = monitor['end'] self.patterns["test_result"] = monitor['pattern'] # Find the start string before parsing any output. connection.prompt_str = monitor['start'] connection.wait() self.logger.info("ok: start string found, lava test monitoring started") with connection.test_connection() as test_connection: while self._keep_running(test_connection, timeout=test_connection.timeout): pass return connection
def dispatcher_gateway(): """ Retrieves the IP address of the current default gateway. """ gateways = netifaces.gateways() if 'default' not in gateways: raise InfrastructureError("Unable to find default gateway") return gateways['default'][netifaces.AF_INET][0]
def reader(self): res = None try: res = requests.get(self.url.geturl(), allow_redirects=True, stream=True) if res.status_code != requests.codes.OK: # pylint: disable=no-member # This is an Infrastructure error because the validate function # checked that the file does exist. raise InfrastructureError("Unable to download '%s'" % (self.url.geturl())) for buff in res.iter_content(HTTP_DOWNLOAD_CHUNK_SIZE): yield buff except requests.RequestException as exc: raise InfrastructureError("Unable to download '%s': %s" % (self.url.geturl(), str(exc))) finally: if res is not None: res.close()
def run(self, connection, max_end_time, args=None): connection = super(UnmountVExpressMassStorageDevice, self).run(connection, max_end_time, args) mount_point = self.get_namespace_data(action='mount-vexpress-usbmsd', label='vexpress-fw', key='mount-point') if not self.run_command(["umount", mount_point], allow_silent=True): raise InfrastructureError("Failed to unmount device %s" % mount_point) return connection
def validate(self): super(LoopCheckAction, self).validate() if len(glob.glob('/sys/block/loop*')) <= 0: raise InfrastructureError( "Could not mount the image without loopback devices. " "Is the 'loop' kernel module activated?") available_loops = len(glob.glob('/sys/block/loop*')) self.set_namespace_data(action=self.name, label=self.key, key='available_loops', value=available_loops)
def run(self, connection, max_end_time, args=None): connection = super(PowerOff, self).run(connection, max_end_time, args) if not self.job.device.get('commands', None): return connection command = self.job.device['commands'].get('power_off', []) if not isinstance(command, list): command = [command] for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % cmd) self.results = {'status': 'success'} return connection
def run(self, connection, max_end_time, args=None): connection = super(DeployVExpressRecoveryImage, self).run(connection, max_end_time, args) mount_point = self.get_namespace_data(action='mount-vexpress-usbmsd', label='vexpress-fw', key='mount-point') try: os.path.realpath(mount_point) except OSError: raise InfrastructureError("Unable to locate mount point: %s" % mount_point) src_dir = self.get_namespace_data( action='extract-vexpress-recovery-image', label='file', key='recovery_image') try: os.path.realpath(src_dir) except OSError: raise InfrastructureError( "Unable to locate recovery image source directory: %s" % src_dir) self.logger.debug( "Removing existing recovery image from Versatile Express mass storage device.." ) try: remove_directory_contents(mount_point) except Exception: raise JobError("Failed to erase old recovery image") self.logger.debug( "Transferring new recovery image to Versatile Express mass storage device.." ) try: copy_directory_contents(src_dir, mount_point) except Exception: raise JobError("Failed to deploy recovery image to %s" % mount_point) return connection
def check_patterns(self, event, test_connection, check_char): # pylint: disable=unused-argument """ Defines the base set of pattern responses. Stores the results of testcases inside the TestAction Call from subclasses before checking subclass-specific events. """ ret_val = False if event == "exit": self.logger.info("ok: lava_test_shell seems to have completed") self.testset_name = None elif event == "error": # Parsing is not finished ret_val = self.pattern_error(test_connection) elif event == "eof": self.testset_name = None raise InfrastructureError("lava_test_shell connection dropped.") elif event == "timeout": # allow feedback in long runs ret_val = True elif event == "signal": name, params = test_connection.match.groups() self.logger.debug("Received signal: <%s> %s" % (name, params)) params = params.split() if name == "STARTRUN": self.signal_start_run(params) elif name == "ENDRUN": self.signal_end_run(params) elif name == "TESTCASE": self.signal_test_case(params) elif name == "TESTFEEDBACK": self.signal_test_feedback(params) elif name == "TESTREFERENCE": self.signal_test_reference(params) elif name == "TESTSET": ret = self.signal_test_set(params) if ret: name = ret elif name == "TESTRAISE": raise TestError(' '.join(params)) self.signal_director.signal(name, params) ret_val = True elif event == "test_case": ret_val = self.pattern_test_case(test_connection) elif event == 'test_case_result': ret_val = self.pattern_test_case_result(test_connection) return ret_val
def _api_select(self, data, action=None): if not data: raise TestError("[%s] Protocol called without any data." % self.name) if not action: raise LAVABug('LXC protocol needs to be called from an action.') for item in data: if 'request' not in item: raise LAVABug("[%s] Malformed protocol request data." % self.name) if 'pre-os-command' in item['request']: action.logger.info("[%s] Running pre OS command via protocol.", self.name) command = action.job.device.pre_os_command if not action.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) continue elif 'pre-power-command' in item['request']: action.logger.info("[%s] Running pre-power-command via protocol.", self.name) command = action.job.device.pre_power_command if not action.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) continue else: raise JobError("[%s] Unrecognised protocol request: %s" % (self.name, item))
def tftpd_dir(): """ read in 'TFTP_DIRECTORY' from /etc/default/tftpd-hpa Any file to be offered using tftp must use this directory or a subdirectory of it. Default installation value: /srv/tftp/ :return: real path to the TFTP directory or raises InfrastructureError """ var_name = 'TFTP_DIRECTORY' if os.path.exists('/etc/default/tftpd-hpa'): config = ConfigObj('/etc/default/tftpd-hpa') value = config.get(var_name) return os.path.realpath(value) raise InfrastructureError("Unable to identify tftpd directory")
def run(self, connection, max_end_time, args=None): connection = super(FlashDFUAction, self).run(connection, max_end_time, args) count = 1 for dfu_command in self.exec_list: if count == (len(self.exec_list)): if self.job.device['actions']['boot']['methods']['dfu'].get( 'reset_works', True): dfu_command.extend(['--reset']) dfu = ' '.join(dfu_command) output = self.run_command(dfu.split(' ')) if output: if "No error condition is present\nDone!\n" not in output: raise InfrastructureError("command failed: %s" % dfu) else: raise InfrastructureError("command failed: %s" % dfu) count += 1 self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def reader(self): reader = None try: reader = open(self.url.path, 'rb') buff = reader.read(FILE_DOWNLOAD_CHUNK_SIZE) while buff: yield buff buff = reader.read(FILE_DOWNLOAD_CHUNK_SIZE) except IOError as exc: raise InfrastructureError("Unable to write to %s: %s" % (self.url.path, str(exc))) finally: if reader is not None: reader.close()
def prepare_install_base(output, size): """ Create an empty image of the specified size (in bytes), ready for an installer to partition, create filesystem(s) and install files. """ guest = guestfs.GuestFS(python_return_dict=True) guest.disk_create(output, "raw", size) guest.add_drive_opts(output, format="raw", readonly=False) guest.launch() devices = guest.list_devices() if len(devices) != 1: raise InfrastructureError("Unable to prepare guestfs") guest.shutdown()
def run(self, connection, max_end_time, args=None): # pylint: disable=too-many-locals connection = super(FastbootFlashAction, self).run(connection, max_end_time, args) # this is the device namespace - the lxc namespace is not accessible lxc_name = None protocol = [ protocol for protocol in self.job.protocols if protocol.name == LxcProtocol.name ][0] if protocol: lxc_name = protocol.lxc_name if not lxc_name: raise JobError("Unable to use fastboot") src = self.get_namespace_data(action='download-action', label=self.command, key='file') if not src: return connection dst = copy_to_lxc(lxc_name, src, self.job.parameters['dispatcher']) sequence = self.job.device['actions']['boot']['methods'].get( 'fastboot', []) if 'no-flash-boot' in sequence and self.command in ['boot']: return connection # if a reboot is requested, will need to wait for the prompt # if not, continue in the existing mode. reboot = self.get_namespace_data(action=self.name, label='interrupt', key='reboot') if self.interrupt_prompt and reboot: connection.prompt_str = self.interrupt_prompt self.logger.debug("Changing prompt to '%s'", connection.prompt_str) self.wait(connection) serial_number = self.job.device['fastboot_serial_number'] fastboot_opts = self.job.device['fastboot_options'] fastboot_cmd = [ 'lxc-attach', '-n', lxc_name, '--', 'fastboot', '-s', serial_number, 'flash', self.command, dst ] + fastboot_opts self.logger.info("Handling %s", self.command) command_output = self.run_command(fastboot_cmd) if command_output and 'error' in command_output: raise InfrastructureError("Unable to flash %s using fastboot: %s" % (self.command, command_output)) self.results = {'label': self.command} return connection
def expect(self, *args, **kw): """ No point doing explicit logging here, the SignalDirector can help the TestShellAction make much more useful reports of what was matched """ try: proc = super(ShellCommand, self).expect(*args, **kw) except pexpect.TIMEOUT: raise TestError("ShellCommand command timed out.") except ValueError as exc: raise TestError(exc) except pexpect.EOF: # FIXME: deliberately closing the connection (and starting a new one) needs to be supported. raise InfrastructureError("Connection closed") return proc
def run(self, connection, max_end_time, args=None): # to enable power to a device, either power_on or hard_reset are needed. if self.job.device.power_command == '': self.logger.warning("Unable to power on the device") return connection connection = super(PowerOn, self).run(connection, max_end_time, args) if self.job.device.pre_power_command: command = self.job.device.pre_power_command self.logger.info("Running pre power command") if not isinstance(command, list): command = [command] for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % cmd) command = self.job.device.power_command if not command: return connection if not isinstance(command, list): command = [command] for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): # pylint: disable=no-member raise InfrastructureError("%s failed" % cmd) self.results = {'success': self.name} return connection
def run(self, connection, max_end_time, args=None): connection = super(FastbootBootAction, self).run(connection, max_end_time, args) # this is the device namespace - the lxc namespace is not accessible lxc_name = None protocol = [ protocol for protocol in self.job.protocols if protocol.name == LxcProtocol.name ][0] if protocol: lxc_name = protocol.lxc_name if not lxc_name: raise JobError("Unable to use fastboot") self.logger.debug("[%s] lxc name: %s", self.parameters['namespace'], lxc_name) serial_number = self.job.device['fastboot_serial_number'] boot_img = self.get_namespace_data(action='download-action', label='boot', key='file') if not boot_img: raise JobError("Boot image not found, unable to boot") else: boot_img = os.path.join(LAVA_LXC_HOME, os.path.basename(boot_img)) fastboot_cmd = [ 'lxc-attach', '-n', lxc_name, '--', 'fastboot', '-s', serial_number, 'boot', boot_img ] + self.job.device['fastboot_options'] command_output = self.run_command(fastboot_cmd, allow_fail=True) if command_output and 'booting' not in command_output: raise JobError("Unable to boot with fastboot: %s" % command_output) else: status = [ status.strip() for status in command_output.split('\n') if 'finished' in status ][0] self.results = {'status': status} self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) lxc_active = any( [pc for pc in self.job.protocols if pc.name == LxcProtocol.name]) if self.job.device.pre_os_command and not lxc_active: self.logger.info("Running pre OS command.") command = self.job.device.pre_os_command if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) return connection