def _log_report(self, fd, fd_name, issues, testcase): if testcase: raise tcfl.tc.error_e( "issues found on server's " + fd_name, { "lines": " ".join(issues), "server-" + fd_name: commonl.generator_factory_c( open, fd.name) }) else: print("issues found on server's %s: %s" \ % (fd_name, " ".join(issues)), file=sys.stderr) fd.seek(0, 0) count = 0 for line in fd: print(" %d: %s" % (count, line), file=sys.stderr, end="") count += 1
def run(self, cmd, nonzero_e=None): """ Run a shell command over SSH, return exitcode and output Similar to :func:`subprocess.call`; note SSH is normally run in verbose mode (unless ``-q`` has been set in *_ssh_cmdline_options*, so the stderr will contain SSH debug information. :param str cmd: shell command to execute via SSH, substituting any ``%(KEYWORD)[ds]`` field from the target's keywords in :attr:`tcfl.tc.target_c.kws` See :ref:`how to find <finding_testcase_metadata>` which fields are available. :param tcfl.tc.exception nonzero_e: exception to raise in case of non zero exit code. Must be a subclass of :class:`tcfl.tc.exception` (i.e.: :class:`tcfl.tc.failed_e`, :class:`tcfl.tc.error_e`, :class:`tcfl.tc.skip_e`, :class:`tcfl.tc.blocked_e`) or *None* (default) to not raise anything and just return the exit code. :returns: tuple of ``exitcode, stdout, stderr``, the two later being two tempfile file descriptors containing the standard output and standard error of running the command. The stdout (or stderr) can be read with: >>> stdout.read() """ assert nonzero_e == None or issubclass(nonzero_e, tc.exception) self._tunnel() _cmd = cmd % self.target.kws self.target.report_info("running SSH command '%s'" % _cmd, dlevel=1) log_stderr = commonl.logfile_open( tag="stderr", directory=self.target.testcase.tmpdir) log_stdout = commonl.logfile_open( tag="stdout", directory=self.target.testcase.tmpdir) # We always run check_output to capture the output and # display it inthe logs for later analysis # if not doing verbose to debug, add -q to avoid getting # spurious messages if '-v' not in self._ssh_cmdline_options: ql = ['-q'] else: ql = [] env = dict(os.environ) if self.password: cmdline = ['sshpass', "-e"] env['SSHPASS'] = self.password else: ql += ['-o', "BatchMode yes"] cmdline = [] cmdline += [ "ssh", "-p", str(self._ssh_port) ] \ + self._ssh_cmdline_options + ql \ + [ self.login + "@" + self._ssh_host, "-t", _cmd ] self.target.report_info("running SSH command: %s" % " ".join(cmdline), dlevel=2) self._known_hosts_wipe() returncode = subprocess.call(cmdline, stdin=None, shell=False, stdout=log_stdout, stderr=log_stderr, env=env, encoding='utf-8') log_stdout.seek(0, 0) log_stderr.seek(0, 0) if returncode != 0: self._returncode_eval(returncode) if nonzero_e: raise nonzero_e( "failed SSH command '%s': %d" % (cmd, returncode), dict(returncode=returncode, stdout=commonl.generator_factory_c( commonl.file_iterator, log_stdout.name), stderr=commonl.generator_factory_c( commonl.file_iterator, log_stderr.name), ssh_cmd=" ".join(cmdline), cmd=cmd, target=self.target)) self.target.report_info( "ran SSH command '%s': %d" % (_cmd, returncode), attachments=dict( returncode=returncode, stdout=commonl.generator_factory_c(commonl.file_iterator, log_stdout.name), stderr=commonl.generator_factory_c(commonl.file_iterator, log_stderr.name), ssh_cmd=" ".join(cmdline), cmd=cmd, target=self.target)) log_stdout.seek(0, 0) log_stderr.seek(0, 0) return returncode, log_stdout, log_stderr