def test_check_char(self): shell = ShellCommand("%s\n" % 'ls', Timeout('fake', 30), logger=logging.getLogger()) if shell.exitstatus: raise JobError("%s command exited %d: %s" % ('ls', shell.exitstatus, shell.readlines())) connection = ShellSession(self.job, shell) self.assertFalse(hasattr(shell, 'check_char')) self.assertTrue(hasattr(connection, 'check_char')) self.assertIsNotNone(connection.check_char)
def run(self, connection, max_end_time): # substitutions substitutions = { "{emptyimage}": self.get_namespace_data(action="prepare-empty-image", label="prepare-empty-image", key="output") } sub_command = self.get_namespace_data( action="prepare-qemu-commands", label="prepare-qemu-commands", key="sub_command", ) sub_command = substitute(sub_command, substitutions) command_line = " ".join(sub_command) commands = [] # get the download args in run() image_arg = self.get_namespace_data(action="download-action", label="iso", key="image_arg") action_arg = self.get_namespace_data(action="download-action", label="iso", key="file") substitutions["{%s}" % "iso"] = action_arg commands.append(image_arg) command_line += " ".join(substitute(commands, substitutions)) preseed_file = self.get_namespace_data(action="download-action", label="file", key="preseed") if not preseed_file: raise JobError("Unable to identify downloaded preseed filename.") substitutions = {"{preseed}": preseed_file} append_args = self.get_namespace_data(action="prepare-qemu-commands", label="prepare-qemu-commands", key="append") append_args = substitute([append_args], substitutions) command_line += " ".join(append_args) self.logger.info(command_line) shell = ShellCommand(command_line, self.timeout, logger=self.logger) if shell.exitstatus: raise JobError( "%s command exited %d: %s" % (sub_command[0], shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection.prompt_str = self.get_namespace_data( action="prepare-qemu-commands", label="prepare-qemu-commands", key="prompts") shell_connection = super().run(shell_connection, max_end_time) return shell_connection
def run(self, connection, max_end_time, args=None): # substitutions substitutions = { '{emptyimage}': self.get_namespace_data(action='prepare-empty-image', label='prepare-empty-image', key='output') } sub_command = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='sub_command') sub_command = substitute(sub_command, substitutions) command_line = ' '.join(sub_command) commands = [] # get the download args in run() image_arg = self.get_namespace_data(action='download-action', label='iso', key='image_arg') action_arg = self.get_namespace_data(action='download-action', label='iso', key='file') substitutions["{%s}" % 'iso'] = action_arg commands.append(image_arg) command_line += ' '.join(substitute(commands, substitutions)) preseed_file = self.get_namespace_data(action='download-action', label='file', key='preseed') if not preseed_file: raise JobError("Unable to identify downloaded preseed filename.") substitutions = {'{preseed}': preseed_file} append_args = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='append') append_args = substitute([append_args], substitutions) command_line += ' '.join(append_args) self.logger.info(command_line) shell = ShellCommand(command_line, self.timeout, logger=self.logger) if shell.exitstatus: raise JobError( "%s command exited %d: %s" % (sub_command[0], shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection.prompt_str = self.get_namespace_data( action='prepare-qemu-commands', label='prepare-qemu-commands', key='prompts') shell_connection = super(IsoCommandLine, self).run(shell_connection, max_end_time, args) return shell_connection
def run(self, connection, max_end_time): if connection: self.logger.debug("Already connected") return connection # ShellCommand executes the connection command self._check_params() command = self.command[:] # local copy for idempotency overrides = None if self.key: overrides = self.get_namespace_data(action='prepare-scp-overlay', label="prepare-scp-overlay", key=self.key) host_address = None if overrides: host_address = str( self.get_namespace_data(action='prepare-scp-overlay', label="prepare-scp-overlay", key=overrides[0])) if host_address: self.logger.info( "Using common data to retrieve host_address for secondary connection." ) command_str = " ".join(str(item) for item in command) self.logger.info("%s Connecting to device %s using '%s'", self.name, host_address, command_str) command.append("%s@%s" % (self.ssh_user, host_address)) elif self.host and self.primary: self.logger.info( "Using device data host_address for primary connection.") command_str = " ".join(str(item) for item in command) self.logger.info("%s Connecting to device %s using '%s'", self.name, self.host, command_str) command.append("%s@%s" % (self.ssh_user, self.host)) else: raise JobError("Unable to identify host address. Primary? %s" % self.primary) command_str = " ".join(str(item) for item in command) shell = ShellCommand("%s\n" % command_str, self.timeout, logger=self.logger) if shell.exitstatus: raise JobError("%s command exited %d: %s" % (self.command, shell.exitstatus, shell.readlines())) # SshSession monitors the pexpect connection = SShSession(self.job, shell) connection = super().run(connection, max_end_time) connection.prompt_str = list(self.parameters.get('prompts', [])) connection.connected = True self.wait(connection) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def test_check_char(self): shell = ShellCommand("%s\n" % "ls", Timeout("fake", 30), logger=logging.getLogger()) if shell.exitstatus: raise JobError("%s command exited %d: %s" % ("ls", shell.exitstatus, shell.readlines())) connection = ShellSession(self.job, shell) self.assertFalse(hasattr(shell, "check_char")) self.assertTrue(hasattr(connection, "check_char")) self.assertIsNotNone(connection.check_char)
def run(self, connection, max_end_time): """ qemu needs help to reboot after running the debian installer and typically the boot is quiet, so there is almost nothing to log. """ base_image = self.get_namespace_data( action="prepare-empty-image", label="prepare-empty-image", key="output" ) self.sub_command.append("-drive format=raw,file=%s" % base_image) guest = self.get_namespace_data( action="apply-overlay-guest", label="guest", key="filename" ) if guest: self.logger.info("Extending command line for qcow2 test overlay") self.sub_command.append( "-drive format=qcow2,file=%s,media=disk" % (os.path.realpath(guest)) ) # push the mount operation to the test shell pre-command to be run # before the test shell tries to execute. shell_precommand_list = [] mountpoint = self.get_namespace_data( action="test", label="results", key="lava_test_results_dir" ) shell_precommand_list.append("mkdir %s" % mountpoint) shell_precommand_list.append("mount -L LAVA %s" % mountpoint) self.set_namespace_data( action="test", label="lava-test-shell", key="pre-command-list", value=shell_precommand_list, ) self.logger.info("Boot command: %s", " ".join(self.sub_command)) shell = ShellCommand( " ".join(self.sub_command), self.timeout, logger=self.logger ) if shell.exitstatus: raise JobError( "%s command exited %d: %s" % (self.sub_command, shell.exitstatus, shell.readlines()) ) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) shell_connection.prompt_str = [INSTALLER_QUIET_MSG] self.wait(shell_connection) self.set_namespace_data( action="shared", label="shared", key="connection", value=shell_connection ) return shell_connection
def run(self, connection, max_end_time): serial_port = self.get_namespace_data(action=StartFVPAction.name, label="fvp", key="serial_port") feedback_ports = self.get_namespace_data(action=StartFVPAction.name, label="fvp", key="feedback_ports") container = self.get_namespace_data(action=StartFVPAction.name, label="fvp", key="container") for feedback_dict in feedback_ports: cmd = "docker exec --interactive --tty %s telnet localhost %s" % ( container, feedback_dict["port"], ) self.logger.debug("Feedback command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) shell_connection.raw_connection.logfile.is_feedback = True feedback_name = feedback_dict.get("name") if not feedback_name: feedback_name = "_namespace_feedback_%s" % feedback_dict["port"] self.set_namespace_data( action="shared", label="shared", key="connection", value=shell_connection, parameters={"namespace": feedback_name}, ) cmd = "docker exec --interactive --tty %s telnet localhost %s" % ( container, serial_port, ) self.logger.debug("Connect command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) self.set_namespace_data(action="shared", label="shared", key="connection", value=shell_connection) return shell_connection
def run(self, connection, max_end_time): start = time.time() if not self.local_docker_image: self.logger.debug("Pulling image %s", self.docker_image) self.run_cmd(["docker", "pull", self.docker_image]) fvp_arguments = self.parameters.get("version_args", "--version") # Build the command line # The docker image is safe to be included in the command line cmd = self.construct_docker_fvp_command(self.docker_image, fvp_arguments) self.logger.debug("Boot command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) # Wait for the version shell_connection.prompt_str = self.fvp_version_string self.wait(shell_connection) matched_version_string = shell_connection.raw_connection.match.group() result = { "definition": "lava", "case": "fvp-version", "level": self.level, "extra": {"fvp-version": matched_version_string}, "result": "pass", "duration": "%.02f" % (time.time() - start), } self.logger.results(result) return shell_connection
def run(self, connection, max_end_time, args=None): location = self.get_namespace_data(action='test', label='shared', key='location') docker_image = self.get_namespace_data(action='deploy-docker', label='image', key='name') # Build the command line # The docker image is safe to be included in the command line cmd = "docker run --interactive --tty --hostname lava" cmd += " --name %s" % self.container if self.test_needs_overlay(self.parameters): overlay = self.get_namespace_data(action='test', label='results', key='lava_test_results_dir') cmd += " --volume %s:%s" % (os.path.join( location, overlay.strip("/")), overlay) cmd += self.extra_options cmd += " %s %s" % (docker_image, self.parameters["command"]) self.logger.debug("Boot command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) self.cleanup_required = True shell_connection = ShellSession(self.job, shell) shell_connection = super(CallDockerAction, self).run(shell_connection, max_end_time, args) self.set_namespace_data(action='shared', label='shared', key='connection', value=shell_connection) return shell_connection
def run(self, connection, max_end_time, args=None): # substitutions substitutions = {'{emptyimage}': self.get_namespace_data(action='prepare-empty-image', label='prepare-empty-image', key='output')} sub_command = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='sub_command') sub_command = substitute(sub_command, substitutions) command_line = ' '.join(sub_command) commands = [] # get the download args in run() image_arg = self.get_namespace_data(action='download-action', label='iso', key='image_arg') action_arg = self.get_namespace_data(action='download-action', label='iso', key='file') substitutions["{%s}" % 'iso'] = action_arg commands.append(image_arg) command_line += ' '.join(substitute(commands, substitutions)) preseed_file = self.get_namespace_data(action='download-action', label='file', key='preseed') if not preseed_file: raise JobError("Unable to identify downloaded preseed filename.") substitutions = {'{preseed}': preseed_file} append_args = self.get_namespace_data(action='prepare-qemu-commands', label='prepare-qemu-commands', key='append') append_args = substitute([append_args], substitutions) command_line += ' '.join(append_args) self.logger.info(command_line) shell = ShellCommand(command_line, self.timeout, logger=self.logger) if shell.exitstatus: raise JobError("%s command exited %d: %s" % (sub_command[0], shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection.prompt_str = self.get_namespace_data( action='prepare-qemu-commands', label='prepare-qemu-commands', key='prompts') shell_connection = super(IsoCommandLine, self).run(shell_connection, max_end_time, args) return shell_connection
def run(self, connection, max_end_time): location = self.get_namespace_data( action="test", label="shared", key="location" ) docker_image = self.get_namespace_data( action="deploy-docker", label="image", key="name" ) # Build the command line # The docker image is safe to be included in the command line cmd = "docker run --rm --interactive --tty --hostname lava" cmd += " --name %s" % self.container if self.test_needs_overlay(self.parameters): overlay = self.get_namespace_data( action="test", label="results", key="lava_test_results_dir" ) cmd += " --volume %s:%s" % ( os.path.join(location, overlay.strip("/")), overlay, ) cmd += self.extra_options cmd += " %s %s" % (docker_image, self.parameters["command"]) self.logger.debug("Boot command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) self.cleanup_required = True shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) self.set_namespace_data( action="shared", label="shared", key="connection", value=shell_connection ) return shell_connection
def run(self, connection, max_end_time, args=None): """ qemu needs help to reboot after running the debian installer and typically the boot is quiet, so there is almost nothing to log. """ base_image = self.get_namespace_data(action='prepare-empty-image', label='prepare-empty-image', key='output') self.sub_command.append('-drive format=raw,file=%s' % base_image) guest = self.get_namespace_data(action='apply-overlay-guest', label='guest', key='filename') if guest: self.logger.info("Extending command line for qcow2 test overlay") self.sub_command.append('-drive format=qcow2,file=%s,media=disk' % (os.path.realpath(guest))) # push the mount operation to the test shell pre-command to be run # before the test shell tries to execute. shell_precommand_list = [] mountpoint = self.get_namespace_data(action='test', label='results', key='lava_test_results_dir') shell_precommand_list.append('mkdir %s' % mountpoint) shell_precommand_list.append('mount -L LAVA %s' % mountpoint) self.set_namespace_data(action='test', label='lava-test-shell', key='pre-command-list', value=shell_precommand_list) self.logger.info("Boot command: %s", ' '.join(self.sub_command)) shell = ShellCommand(' '.join(self.sub_command), self.timeout, logger=self.logger) if shell.exitstatus: raise JobError("%s command exited %d: %s" % (self.sub_command, shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection = super(IsoRebootAction, self).run(shell_connection, max_end_time, args) shell_connection.prompt_str = [INSTALLER_QUIET_MSG] self.wait(shell_connection) self.set_namespace_data(action='shared', label='shared', key='connection', value=shell_connection) return shell_connection
def finalise_protocol(self, device=None): """Called by Finalize action to power down and clean up the assigned device. """ # shutdown xnbd for the given device/job based in the port-number used try: self.logger.debug("%s cleanup", self.name) self.port = self.parameters['protocols']['lava-xnbd']['port'] nbd_cmd = "pkill -f xnbd-server.*%s" % (self.port) shell = ShellCommand("%s\n" % nbd_cmd, self.system_timeout, logger=self.logger) shell.expect(pexpect.EOF) except Exception as e: self.logger.debug(str(e)) self.logger.debug( "xnbd-finalize-protocol failed, but continuing anyway.")
def run(self, connection, max_end_time, args=None): """ CommandRunner expects a pexpect.spawn connection which is the return value of target.device.power_on executed by boot in the old dispatcher. In the new pipeline, the pexpect.spawn is a ShellCommand and the connection is a ShellSession. CommandRunner inside the ShellSession turns the ShellCommand into a runner which the ShellSession uses via ShellSession.run() to run commands issued *after* the device has booted. pexpect.spawn is one of the raw_connection objects for a Connection class. """ # initialise the first Connection object, a command line shell into the running QEMU. guest = self.get_namespace_data(action='apply-overlay-guest', label='guest', key='filename') # check for NFS if 'qemu-nfs' in self.methods and self.parameters.get('media', None) == 'nfs': self.logger.debug("Adding NFS arguments to kernel command line.") root_dir = self.get_namespace_data(action='extract-rootfs', label='file', key='nfsroot') self.substitutions["{NFSROOTFS}"] = root_dir params = self.methods['qemu-nfs']['parameters']['append'] # console=ttyAMA0 root=/dev/nfs nfsroot=10.3.2.1:/var/lib/lava/dispatcher/tmp/dirname,tcp,hard,intr ip=dhcp append = [ 'console=%s' % params['console'], 'root=/dev/nfs', '%s rw' % substitute([params['nfsrootargs']], self.substitutions)[0], "%s" % params['ipargs'] ] self.sub_command.append('--append') self.sub_command.append('"%s"' % ' '.join(append)) elif guest: self.logger.info("Extending command line for qcow2 test overlay") # interface is ide by default in qemu interface = self.job.device['actions']['deploy']['methods']['image']['parameters']['guest'].get('interface', 'ide') self.sub_command.append('-drive format=qcow2,file=%s,media=disk,if=%s' % (os.path.realpath(guest), interface)) # push the mount operation to the test shell pre-command to be run # before the test shell tries to execute. shell_precommand_list = [] mountpoint = self.get_namespace_data(action='test', label='results', key='lava_test_results_dir') uuid = '/dev/disk/by-uuid/%s' % self.get_namespace_data(action='apply-overlay-guest', label='guest', key='UUID') shell_precommand_list.append('mkdir %s' % mountpoint) # prepare_guestfs always uses ext2 shell_precommand_list.append('mount %s -t ext2 %s' % (uuid, mountpoint)) # debug line to show the effect of the mount operation # also allows time for kernel messages from the mount operation to be processed. shell_precommand_list.append('ls -la %s/bin/lava-test-runner' % mountpoint) self.set_namespace_data(action='test', label='lava-test-shell', key='pre-command-list', value=shell_precommand_list) self.logger.info("Boot command: %s", ' '.join(self.sub_command)) shell = ShellCommand(' '.join(self.sub_command), self.timeout, logger=self.logger) if shell.exitstatus: raise JobError("%s command exited %d: %s" % (self.sub_command, shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection = super(CallQemuAction, self).run(shell_connection, max_end_time, args) self.set_namespace_data(action='shared', label='shared', key='connection', value=shell_connection) return shell_connection
def run(self, connection, max_end_time): connection = super().run(connection, max_end_time) # Build the substitutions dictionary substitutions = {} paths = set() for action in self.get_namespace_keys("download-action"): filename = self.get_namespace_data(action="download-action", label=action, key="file") if filename is None: self.logger.warning( "Empty value for action='download-action' label='%s' key='file'", action, ) continue substitutions["{%s}" % action.upper()] = filename paths.add(os.path.dirname(filename)) # If needed, prepend with docker if self.container is None: cmd = self.gdb else: cmd = "docker run --rm -it --name lava-%s-%s" % ( self.job.job_id, self.level, ) for path in paths: cmd += " --volume %s:%s" % (path, path) for device in self.devices: cmd += " --device %s:%s:rw" % (device, device) cmd += " %s %s" % (self.container, self.gdb) for arg in substitute(self.arguments, substitutions): cmd += " " + arg # Start gdb self.logger.info("Starting gdb: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) gdb = ShellSession(self.job, shell) gdb.prompt_str = "\\(gdb\\) " self.gdb_connection = gdb self.gdb_connection.wait() # Send all gdb commands for cmd in substitute(self.commands, substitutions): self.gdb_connection.sendline(cmd) self.gdb_connection.wait() # "continue" is send last if self.wait_before_continue: self.logger.debug("Sleeping %ss before sending 'continue'", self.wait_before_continue) time.sleep(self.wait_before_continue) self.gdb_connection.sendline("continue") return connection
def run(self, connection, max_end_time, args=None): if connection: self.logger.debug("Already connected") return connection # ShellCommand executes the connection command self._check_params() command = self.command[:] # local copy for idempotency overrides = None if self.key: overrides = self.get_namespace_data(action='prepare-scp-overlay', label="prepare-scp-overlay", key=self.key) host_address = None if overrides: host_address = str(self.get_namespace_data( action='prepare-scp-overlay', label="prepare-scp-overlay", key=overrides[0])) if host_address: self.logger.info("Using common data to retrieve host_address for secondary connection.") command_str = " ".join(str(item) for item in command) self.logger.info("%s Connecting to device %s using '%s'", self.name, host_address, command_str) command.append("%s@%s" % (self.ssh_user, host_address)) elif self.host and self.primary: self.logger.info("Using device data host_address for primary connection.") command_str = " ".join(str(item) for item in command) self.logger.info("%s Connecting to device %s using '%s'", self.name, self.host, command_str) command.append("%s@%s" % (self.ssh_user, self.host)) else: raise JobError("Unable to identify host address. Primary? %s" % self.primary) command_str = " ".join(str(item) for item in command) shell = ShellCommand("%s\n" % command_str, self.timeout, logger=self.logger) if shell.exitstatus: raise JobError("%s command exited %d: %s" % ( self.command, shell.exitstatus, shell.readlines())) # SshSession monitors the pexpect connection = SShSession(self.job, shell) connection = super(ConnectSsh, self).run(connection, max_end_time, args) connection.sendline('export PS1="%s"' % self.job.device.get_constant( 'default-shell-prompt')) connection.prompt_str = [self.job.device.get_constant( 'default-shell-prompt')] connection.connected = True self.wait(connection) self.set_namespace_data(action='shared', label='shared', key='connection', value=connection) return connection
def run(self, connection, max_end_time): # obtain lava overlay # start container # create USB device mapping to container # connect to container, and run lava-test-shell over it location = self.get_namespace_data(action="test", label="shared", key="location") overlay = self.get_namespace_data( action="test", label="results", key="lava_test_results_dir").strip("/") image = self.parameters["docker"]["image"] container = "lava-docker-test-shell-%s-%s" % (self.job.job_id, self.level) board_id = self.get_board_id() device_info = {"board_id": board_id} add_device_container_mapping( job_id=self.job.job_id, device_info=device_info, container=container, container_type="docker", logging_info=self.get_logging_info(), ) docker = DockerRun(image) docker.bind_mount(os.path.join(location, overlay), "/" + overlay) docker.interactive() docker.hostname("lava") docker.name(container) docker.environment("PS1", "docker-test-shell:$ ") if self.wait_for_device: devices = get_udev_devices(device_info=[device_info]) for dev in devices: docker.add_device(dev) docker_cmd = docker.cmdline("bash", "--norc", "-i") cmd = " ".join([shlex.quote(s) for s in docker_cmd]) self.logger.debug("Starting docker test shell container: %s" % cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection.prompt_str = "docker-test-shell:" self.__set_connection__(shell_connection) super().run(shell_connection, max_end_time) # finish the container shell_connection.finalise() # return the original connection untouched self.__set_connection__(connection) return connection
def run(self, connection, max_end_time): fvp_arguments = " ".join(self.parameters.get("arguments")) # Build the command line # The docker image is safe to be included in the command line cmd = self.construct_docker_fvp_command(self.docker_image, fvp_arguments) self.logger.debug("Boot command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) # Wait for the console string shell_connection.prompt_str = self.fvp_console_string self.wait(shell_connection) # We should now have the matched output if "PORT" not in shell_connection.raw_connection.match.groupdict(): raise JobError( "'console_string' should contain a regular expression section, such as '(?P<PORT>\\d+)' to extract the serial port of the FVP. Group name must be 'PORT'" ) serial_port = shell_connection.raw_connection.match.groupdict()["PORT"] self.set_namespace_data( action=StartFVPAction.name, label="fvp", key="serial_port", value=serial_port, ) self.logger.info("Found FVP port %s", serial_port) self.set_namespace_data( action=StartFVPAction.name, label="fvp", key="container", value=self.container, ) # Although we don't require any more output on this connection, # discarding this may cause SIGHUPs to be sent to the model # which will terminate the model. self.shell = shell return shell_connection
def run(self, connection, max_end_time): serial_port = self.get_namespace_data( action=StartFVPAction.name, label="fvp", key="serial_port" ) container = self.get_namespace_data( action=StartFVPAction.name, label="fvp", key="container" ) cmd = "docker exec --interactive --tty %s telnet localhost %s" % ( container, serial_port, ) self.logger.debug("Connect command: %s", cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) self.set_namespace_data( action="shared", label="shared", key="connection", value=shell_connection ) return shell_connection
def run(self, connection, max_end_time): """ CommandRunner expects a pexpect.spawn connection which is the return value of target.device.power_on executed by boot in the old dispatcher. In the new pipeline, the pexpect.spawn is a ShellCommand and the connection is a ShellSession. CommandRunner inside the ShellSession turns the ShellCommand into a runner which the ShellSession uses via ShellSession.run() to run commands issued *after* the device has booted. pexpect.spawn is one of the raw_connection objects for a Connection class. """ # initialise the first Connection object, a command line shell into the running QEMU. self.results = self.qemu_data guest = self.get_namespace_data(action='apply-overlay-guest', label='guest', key='filename') # check for NFS if 'qemu-nfs' in self.methods and self.parameters.get( 'media') == 'nfs': self.logger.debug("Adding NFS arguments to kernel command line.") root_dir = self.get_namespace_data(action='extract-rootfs', label='file', key='nfsroot') self.substitutions["{NFSROOTFS}"] = root_dir params = self.methods['qemu-nfs']['parameters']['append'] # console=ttyAMA0 root=/dev/nfs nfsroot=10.3.2.1:/var/lib/lava/dispatcher/tmp/dirname,tcp,hard,intr ip=dhcp append = [ 'console=%s' % params['console'], 'root=/dev/nfs', '%s rw' % substitute([params['nfsrootargs']], self.substitutions)[0], "%s" % params['ipargs'] ] self.sub_command.append('--append') self.sub_command.append('"%s"' % ' '.join(append)) elif guest: self.logger.info("Extending command line for qcow2 test overlay") # interface is ide by default in qemu interface = self.job.device['actions']['deploy']['methods'][ 'image']['parameters']['guest'].get('interface', 'ide') self.sub_command.append( '-drive format=qcow2,file=%s,media=disk,if=%s' % (os.path.realpath(guest), interface)) # push the mount operation to the test shell pre-command to be run # before the test shell tries to execute. shell_precommand_list = [] mountpoint = self.get_namespace_data(action='test', label='results', key='lava_test_results_dir') uuid = '/dev/disk/by-uuid/%s' % self.get_namespace_data( action='apply-overlay-guest', label='guest', key='UUID') shell_precommand_list.append('mkdir %s' % mountpoint) # prepare_guestfs always uses ext2 shell_precommand_list.append('mount %s -t ext2 %s' % (uuid, mountpoint)) # debug line to show the effect of the mount operation # also allows time for kernel messages from the mount operation to be processed. shell_precommand_list.append('ls -la %s/bin/lava-test-runner' % mountpoint) self.set_namespace_data(action='test', label='lava-test-shell', key='pre-command-list', value=shell_precommand_list) self.logger.info("Boot command: %s", ' '.join(self.sub_command)) shell = ShellCommand(' '.join(self.sub_command), self.timeout, logger=self.logger) if shell.exitstatus: raise JobError( "%s command exited %d: %s" % (self.sub_command, shell.exitstatus, shell.readlines())) self.logger.debug("started a shell command") shell_connection = ShellSession(self.job, shell) shell_connection = super().run(shell_connection, max_end_time) self.set_namespace_data(action='shared', label='shared', key='connection', value=shell_connection) return shell_connection
def run(self, connection, max_end_time): # obtain lava overlay # start container # create USB device mapping to container # connect to container, and run lava-test-shell over it location = self.get_namespace_data(action="test", label="shared", key="location") overlay = self.get_namespace_data( action="test", label="results", key="lava_test_results_dir").strip("/") container = "lava-docker-test-shell-%s-%s" % (self.job.job_id, self.level) docker = DockerRun.from_parameters(self.parameters["docker"]) docker.prepare() docker.bind_mount(os.path.join(location, overlay), "/" + overlay) namespace = self.parameters.get("downloads-namespace", self.parameters.get("namespace")) if namespace: downloads_dir = pathlib.Path( self.job.tmp_dir) / "downloads" / namespace if downloads_dir.exists(): docker.bind_mount(downloads_dir, LAVA_DOWNLOADS) for bind_mount in self.test_docker_bind_mounts: read_only = True if len(bind_mount) == 2 else False docker.bind_mount(bind_mount[0], bind_mount[1], read_only) docker.interactive() docker.tty() docker.hostname("lava") docker.name(container) docker.environment("PS1", "docker-test-shell:$ ") docker_cmd = docker.cmdline("bash", "--norc", "-i") cmd = " ".join([shlex.quote(s) for s in docker_cmd]) self.logger.debug("Starting docker test shell container: %s" % cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection.prompt_str = "docker-test-shell:" self.__set_connection__(shell_connection) self.add_device_container_mappings(container, "docker") devices = get_udev_devices(device_info=self.device_info, logger=self.logger, required=False) docker.wait() # share all the devices as there isn't a 1:1 relationship between # the trigger and actual sharing of the devices for dev in devices: if not os.path.islink(dev): self.trigger_share_device_with_container(dev) for dev in devices: docker.wait_file(dev) try: super().run(shell_connection, max_end_time) finally: # finish the container shell_connection.finalise() docker.destroy() # return the original connection untouched self.__set_connection__(connection) return connection
def run(self, connection, max_end_time): # obtain lava overlay # start container # create USB device mapping to container # connect to container, and run lava-test-shell over it location = self.get_namespace_data(action="test", label="shared", key="location") overlay = self.get_namespace_data( action="test", label="results", key="lava_test_results_dir").strip("/") container = "lava-docker-test-shell-%s-%s" % (self.job.job_id, self.level) docker = DockerRun.from_parameters(self.parameters["docker"], self.job) docker.prepare() docker.bind_mount(os.path.join(location, overlay), "/" + overlay) docker_method_conf = (self.job.device["actions"].get("test", {}).get( "methods", {}).get("docker", {})) # Preprocess docker option list, to better support partial # overriding of them via device dict: # 1. Filter out None, to make it easier to template # YAML syntactic lists with Jinja2: # '- {{ some_opt_from_device_dict }}' # (if not default, will be set to None). # 2. Flatten sublists, `- ['--opt1', '--opt2']`. def preproc_opts(opts): res = [] for o in opts: if o is None: continue elif isinstance(o, list): res += o else: res.append(o) return res if "global_options" in docker_method_conf: docker.add_docker_options( *preproc_opts(docker_method_conf["global_options"])) if "options" in docker_method_conf: docker.add_docker_run_options( *preproc_opts(docker_method_conf["options"])) namespace = self.parameters.get("downloads-namespace", self.parameters.get("namespace")) if namespace: downloads_dir = pathlib.Path( self.job.tmp_dir) / "downloads" / namespace if downloads_dir.exists(): docker.bind_mount(downloads_dir, LAVA_DOWNLOADS) for bind_mount in self.test_docker_bind_mounts: read_only = True if len(bind_mount) == 2 else False docker.bind_mount(bind_mount[0], bind_mount[1], read_only) docker.interactive() docker.tty() docker.name(container) docker.environment("PS1", "docker-test-shell:$ ") docker_cmd = docker.cmdline("bash", "--norc", "-i") cmd = " ".join([shlex.quote(s) for s in docker_cmd]) self.logger.debug("Starting docker test shell container: %s" % cmd) shell = ShellCommand(cmd, self.timeout, logger=self.logger) shell_connection = ShellSession(self.job, shell) shell_connection.prompt_str = "docker-test-shell:" self.__set_connection__(shell_connection) self.add_device_container_mappings(container, "docker") devices = get_udev_devices(device_info=self.device_info, logger=self.logger, required=False) docker.wait(shell) # share all the devices as there isn't a 1:1 relationship between # the trigger and actual sharing of the devices for dev in devices: if not os.path.islink(dev): self.trigger_share_device_with_container(dev) for dev in devices: docker.wait_file(dev) try: super().run(shell_connection, max_end_time) finally: # finish the container shell_connection.finalise() docker.destroy() # return the original connection untouched self.__set_connection__(connection) return connection