def run(self, connection, max_end_time, args=None): connection = super(PowerOn, self).run(connection, max_end_time, args) if self.job.device.power_state is 'off': if self.job.device.pre_power_command: command = self.job.device.pre_power_command self.logger.info("Running pre power command") if isinstance(command, list): for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % cmd) else: if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) command = self.job.device.power_command if not command: return connection if isinstance(command, list): for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % cmd) else: if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) self.results = {'success': self.name} self.job.device.power_state = 'on' 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: 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: raise JobError("Failed to deploy recovery image to %s" % mount_point) return connection
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, args=None): if not connection: raise RuntimeError("Called %s without an active Connection" % self.name) if self.job.device.power_state is 'off' and self.job.device.power_command is not '': # power on action used instead return connection if self.job.device.power_state is 'on' and self.job.device.soft_reset_command is not '': command = self.job.device['commands']['soft_reset'] if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("Command '%s' failed" % command) self.results = {"success": self.job.device.power_state} else: connection = super(RebootDevice, self).run(connection, args) connection.prompt_str = self.parameters.get('parameters', {}).get('shutdown-message', SHUTDOWN_MESSAGE) connection.timeout = self.connection_timeout connection.sendline("reboot") # FIXME: possibly deployment data, possibly separate actions, possibly adjuvants. connection.sendline("reboot -n") # initramfs may require -n for *now* connection.sendline("reboot -n -f") # initrd may require -n for *now* and -f for *force* self.results = {"success": connection.prompt_str} self.data[PDUReboot.key()] = False if 'bootloader_prompt' in self.data['common']: self.reboot_prompt = self.get_common_data('bootloader_prompt', 'prompt') try: self.wait(connection) except TestError: self.logger.info("Wait for prompt after soft reboot failed") self.results = {'status': "failed"} self.data[PDUReboot.key()] = True connection.prompt_str = self.reboot_prompt return connection
def run(self, connection, max_end_time, args=None): connection = super(PowerOff, self).run(connection, max_end_time, args) if not hasattr(self.job.device, 'power_state'): return connection if self.job.device.power_state is 'on': # allow for '' and skip command = self.job.device['commands']['power_off'] if isinstance(command, list): for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % cmd) else: if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) self.results = {'status': 'success'} self.job.device.power_state = 'off' return connection
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): decompress_file(tmp_recovery_image, 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 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 run(self, connection, max_end_time, args=None): connection = super(FlashCMSISAction, self).run(connection, max_end_time, args) dstdir = mkdtemp() mount_command = "mount -t vfat %s %s" % (self.usb_mass_device, dstdir) self.run_command(mount_command.split(' '), allow_silent=True) # mount for f in self.filelist: self.logger.debug("Copying %s to %s", f, dstdir) shutil.copy2(f, dstdir) # umount umount_command = "umount %s" % self.usb_mass_device self.run_command(umount_command.split(' '), allow_silent=True) if self.errors: raise InfrastructureError("Unable to (un)mount USB device: %s" % self.usb_mass_device) res = 'failed' if self.errors else 'success' self.set_namespace_data(action='boot', label='shared', key='boot-result', value=res) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def read_settings(self, filename): """ NodeDispatchers need to use the same port and blocksize as the Coordinator, so read the same conffile. The protocol header is hard-coded into the server & here. """ settings = { "port": 3079, "blocksize": 4 * 1024, "poll_delay": 1, "coordinator_hostname": "localhost" } self.logger = logging.getLogger('dispatcher') json_default = {} with open(filename) as stream: jobdata = stream.read() try: json_default = json.loads(jobdata) except ValueError as exc: raise InfrastructureError("Invalid JSON settings for %s: %s" % (self.name, exc)) if "port" in json_default: settings['port'] = json_default['port'] if "blocksize" in json_default: settings['blocksize'] = json_default["blocksize"] if "poll_delay" in json_default: settings['poll_delay'] = json_default['poll_delay'] if "coordinator_hostname" in json_default: settings['coordinator_hostname'] = json_default[ 'coordinator_hostname'] return settings
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 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): output = os.path.join(self.job.parameters['output_dir'], "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) cur_dir = os.getcwd() try: with tarfile.open(output, "w:gz") as tar: os.chdir(location) 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) os.chdir(cur_dir) self.set_namespace_data(action=self.name, label='output', key='file', value=output) return connection
def validate(self): super(ExtractNfsAction, self).validate() if not self.valid: return lava_test_results_dir = self.parameters['deployment_data'][ 'lava_test_results_dir'] lava_test_results_dir = lava_test_results_dir % self.job.job_id self.set_namespace_data(action='test', label='results', key='lava_test_results_dir', value=lava_test_results_dir) if not self.parameters['images'].get(self.param_key, None): # idempotency return if not self.get_namespace_data( action='download-action', label=self.param_key, key='file'): self.errors = "no file specified extract as %s" % self.param_key if not os.path.exists('/usr/sbin/exportfs'): raise InfrastructureError( "NFS job requested but nfs-kernel-server not installed.") if 'prefix' in self.parameters['images'][self.param_key]: prefix = self.parameters['images'][self.param_key]['prefix'] if prefix.startswith('/'): self.errors = 'prefix must not be an absolute path' if not prefix.endswith('/'): self.errors = 'prefix must be a directory and end with /'
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 validate(self): super(MonitorQemuAction, self).validate() try: boot = self.job.device['actions']['boot']['methods']['qemu'] qemu_binary = which(boot['parameters']['command']) self.sub_command = [qemu_binary] self.sub_command.extend(boot['parameters'].get('options', [])) except AttributeError as exc: raise InfrastructureError(exc) except (KeyError, TypeError): self.errors = "Invalid parameters for %s" % self.name substitutions = {} commands = [] for action in self.data['download_action'].keys(): if action == 'offset' or action == 'available_loops' or action == 'uefi': continue image_arg = self.data['download_action'][action].get( 'image_arg', None) action_arg = self.data['download_action'][action].get('file', None) if not image_arg or not action_arg: self.errors = "Missing image_arg for %s. " % action continue substitutions["{%s}" % action] = action_arg commands.append(image_arg) self.sub_command.extend(substitute(commands, substitutions)) if not self.sub_command: self.errors = "No QEMU command to execute"
def clone(self, dest_path, revision=None): try: subprocess.check_output( [self.binary, 'clone', self.url, dest_path], stderr=subprocess.STDOUT) if revision is not None: subprocess.check_output([ self.binary, '--git-dir', os.path.join(dest_path, '.git'), 'checkout', str(revision) ], stderr=subprocess.STDOUT) commit_id = subprocess.check_output( [ self.binary, '--git-dir', os.path.join(dest_path, '.git'), 'log', '-1', '--pretty=%H' ], stderr=subprocess.STDOUT).strip() except subprocess.CalledProcessError as exc: logger = logging.getLogger('dispatcher') logger.exception({ 'command': [i.strip() for i in exc.cmd], 'message': [i.strip() for i in exc.message], 'output': exc.output.split('\n') }) raise InfrastructureError("Unable to fetch git repository '%s'" % (self.url)) return commit_id
def run(self, connection, args=None): connection = super(PDUReboot, self).run(connection, args) if not self.adjuvant: return connection if not self.job.device.hard_reset_command: raise InfrastructureError("Hard reset required but not defined for %s." % self.job.device['hostname']) command = self.job.device.hard_reset_command if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) try: self.wait(connection) except TestError: raise InfrastructureError("%s failed to reset device" % self.key()) self.data[PDUReboot.key()] = False self.results = {'status': 'success'} self.job.device.power_state = 'on' 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 run(self, connection, max_end_time, args=None): connection = super(EnterFastbootAction, 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: self.errors = "Unable to use fastboot" return connection self.logger.debug("[%s] lxc name: %s", self.parameters['namespace'], lxc_name) fastboot_serial_number = self.job.device['fastboot_serial_number'] # Try to enter fastboot mode with adb. adb_serial_number = self.job.device['adb_serial_number'] adb_cmd = [ 'lxc-attach', '-n', lxc_name, '--', 'adb', '-s', adb_serial_number, 'devices' ] command_output = self.run_command(adb_cmd, allow_fail=True) if command_output and adb_serial_number in command_output: self.logger.debug("Device is in adb: %s", command_output) adb_cmd = [ 'lxc-attach', '-n', lxc_name, '--', 'adb', '-s', adb_serial_number, 'reboot-bootloader' ] self.run_command(adb_cmd) return connection # Enter fastboot mode with fastboot. fastboot_opts = self.job.device['fastboot_options'] fastboot_cmd = [ 'lxc-attach', '-n', lxc_name, '--', 'fastboot', '-s', fastboot_serial_number, 'devices' ] + fastboot_opts command_output = self.run_command(fastboot_cmd) if command_output and fastboot_serial_number in command_output: self.logger.debug("Device is in fastboot: %s", command_output) fastboot_cmd = [ 'lxc-attach', '-n', lxc_name, '--', 'fastboot', '-s', fastboot_serial_number, 'reboot-bootloader' ] + fastboot_opts command_output = self.run_command(fastboot_cmd) if command_output and 'OKAY' not in command_output: raise InfrastructureError("Unable to enter fastboot: %s" % command_output) else: status = [ status.strip() for status in command_output.split('\n') if 'finished' in status ][0] self.results = {'status': status} return connection
def validate(self): super(CompressRamdisk, self).validate() if not self.parameters.get('ramdisk', None): # idempotency return try: which('mkimage') except InfrastructureError: raise InfrastructureError( "Unable to find mkimage - is u-boot-tools installed?")
def run(self, connection, max_end_time, args=None): if not connection: raise LAVABug("Called %s without an active Connection" % self.name) if self.job.device.power_state is 'off' and self.job.device.power_command is not '': # power on action used instead return connection if self.job.device.power_state is 'on' and self.job.device.soft_reset_command is not '': command = self.job.device['commands']['soft_reset'] if isinstance(command, list): for cmd in command: if not self.run_command(cmd.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % cmd) else: if not self.run_command(command.split(' '), allow_silent=True): raise InfrastructureError("%s failed" % command) self.results = {"success": self.job.device.power_state} else: connection = super(RebootDevice, self).run(connection, max_end_time, args) connection.prompt_str = self.parameters.get('parameters', {}).get( 'shutdown-message', self.job.device.get_constant('shutdown-message')) connection.timeout = self.connection_timeout connection.sendline("reboot") # FIXME: possibly deployment data, possibly separate actions, possibly adjuvants. connection.sendline( "reboot -n") # initramfs may require -n for *now* connection.sendline( "reboot -n -f" ) # initrd may require -n for *now* and -f for *force* self.results = {"success": connection.prompt_str} self.data[PDUReboot.key()] = False reboot_prompt = self.get_namespace_data(action='uboot-retry', label='bootloader_prompt', key='prompt') if reboot_prompt: self.reboot_prompt = reboot_prompt try: self.wait(connection) except TestError: self.logger.info("Wait for prompt after soft reboot failed") self.results = {'status': "failed"} self.data[PDUReboot.key()] = True connection.prompt_str = self.reboot_prompt 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: self.errors = "Unable to use fastboot" return connection 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('/', 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) 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} res = 'failed' if self.errors else 'success' self.set_namespace_data(action='boot', label='shared', key='boot-result', value=res) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) 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) return connection
def _find(self, path, match=os.path.isfile): """ Simple replacement for the `which` command found on Debian based systems. """ for dirname in sys.path: candidate = os.path.join(dirname, path) if match(candidate): return candidate raise InfrastructureError("Cannot find file %s" % path)
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 validate(self): if 'download_action' not in self.data: raise RuntimeError("download_action not found %s" % self.name) # return # already failed elsewhere 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.data['download_action']['available_loops'] = available_loops
def run(self, connection, args=None): connection = super(PowerOff, self).run(connection, args) if not hasattr(self.job.device, 'power_state'): return connection if self.job.device.power_state is 'on': # allow for '' and skip command = self.job.device['commands']['power_off'] if not self.run_command(command.split(' ')): raise InfrastructureError("%s command failed" % command) self.job.device.power_state = 'off' return connection
def run(self, connection, args=None): connection = super(PowerOn, self).run(connection, args) if self.job.device.power_state is 'off': command = self.job.device.power_command if not command: return connection if not self.run_command(command.split(' ')): raise InfrastructureError("%s command failed" % command) self.job.device.power_state = 'on' return connection
def validate(self): super(ExtractNfsRootfs, self).validate() if not self.parameters.get(self.param_key, None): # idempotency return if 'download_action' not in self.data: self.errors = "missing download_action in parameters" elif 'file' not in self.data['download_action'][self.param_key]: self.errors = "no file specified extract as %s" % self.param_key if not os.path.exists('/usr/sbin/exportfs'): raise InfrastructureError( "NFS job requested but nfs-kernel-server not installed.")
def dispatcher_ip(): """ Retrieves the IP address of the interface associated with the current default gateway. """ gateways = netifaces.gateways() if 'default' not in gateways: raise InfrastructureError("Unable to find default gateway") iface = gateways['default'][netifaces.AF_INET][1] addr = netifaces.ifaddresses(iface) return addr[netifaces.AF_INET][0]['addr']