예제 #1
0
def scp_remote_escape(filename):
    """
    Escape special characters from a filename so that it can be passed
    to scp (within double quotes) as a remote file.

    Bis-quoting has to be used with scp for remote files, "bis-quoting"
    as in quoting x 2
    scp does not support a newline in the filename

    Args:
            filename: the filename string to escape.

    Returns:
            The escaped filename string. The required englobing double
            quotes are NOT added and so should be added at some point by
            the caller.
    """
    escape_chars = r' !"$&' "'" r'()*,:;<=>?[\]^`{|}'

    new_name = []
    for char in filename:
        if char in escape_chars:
            new_name.append("\\%s" % (char,))
        else:
            new_name.append(char)

    return utils.sh_escape("".join(new_name))
예제 #2
0
def scp_remote_escape(filename):
    """
    Escape special characters from a filename so that it can be passed
    to scp (within double quotes) as a remote file.

    Bis-quoting has to be used with scp for remote files, "bis-quoting"
    as in quoting x 2
    scp does not support a newline in the filename

    Args:
            filename: the filename string to escape.

    Returns:
            The escaped filename string. The required englobing double
            quotes are NOT added and so should be added at some point by
            the caller.
    """
    escape_chars = r' !"$&' "'" r'()*,:;<=>?[\]^`{|}'

    new_name = []
    for char in filename:
        if char in escape_chars:
            new_name.append("\\%s" % (char, ))
        else:
            new_name.append(char)

    return utils.sh_escape("".join(new_name))
예제 #3
0
 def _test_in_shell(self, text):
     escaped_text = utils.sh_escape(text)
     proc = subprocess.Popen(['/bin/bash', '-c', 'echo "%s"' % escaped_text],
                             stdin=open(os.devnull, 'r'),
                             stdout=subprocess.PIPE,
                             stderr=open(os.devnull, 'w'))
     stdout, _ = proc.communicate()
     self.assertEqual(proc.returncode, 0)
     self.assertEqual(stdout[:-1], text)
예제 #4
0
 def _test_in_shell(self, text):
     escaped_text = utils.sh_escape(text)
     proc = subprocess.Popen(
         ['/bin/bash', '-c', 'echo "%s"' % escaped_text],
         stdin=open(os.devnull, 'r'),
         stdout=subprocess.PIPE,
         stderr=open(os.devnull, 'w'))
     stdout, _ = proc.communicate()
     self.assertEqual(proc.returncode, 0)
     self.assertEqual(stdout[:-1], text)
예제 #5
0
def unarchive(host, source_material):
    """Uncompress and untar an archive on a host.

    If the "source_material" is compresses (according to the file
    extension) it will be uncompressed. Supported compression formats
    are gzip and bzip2. Afterwards, if the source_material is a tar
    archive, it will be untarred.

    Args:
            host: the host object on which the archive is located
            source_material: the path of the archive on the host

    Returns:
            The file or directory name of the unarchived source material.
            If the material is a tar archive, it will be extracted in the
            directory where it is and the path returned will be the first
            entry in the archive, assuming it is the topmost directory.
            If the material is not an archive, nothing will be done so this
            function is "harmless" when it is "useless".
    """
    # uncompress
    if (source_material.endswith(".gz") or source_material.endswith(".gzip")):
        host.run('gunzip "%s"' % (utils.sh_escape(source_material)))
        source_material = ".".join(source_material.split(".")[:-1])
    elif source_material.endswith("bz2"):
        host.run('bunzip2 "%s"' % (utils.sh_escape(source_material)))
        source_material = ".".join(source_material.split(".")[:-1])

    # untar
    if source_material.endswith(".tar"):
        retval = host.run('tar -C "%s" -xvf "%s"' % (
            utils.sh_escape(os.path.dirname(source_material)),
            utils.sh_escape(source_material),
        ))
        source_material = os.path.join(os.path.dirname(source_material),
                                       retval.stdout.split()[0])

    return source_material
예제 #6
0
def unarchive(host, source_material):
    """Uncompress and untar an archive on a host.

    If the "source_material" is compresses (according to the file
    extension) it will be uncompressed. Supported compression formats
    are gzip and bzip2. Afterwards, if the source_material is a tar
    archive, it will be untarred.

    Args:
            host: the host object on which the archive is located
            source_material: the path of the archive on the host

    Returns:
            The file or directory name of the unarchived source material.
            If the material is a tar archive, it will be extracted in the
            directory where it is and the path returned will be the first
            entry in the archive, assuming it is the topmost directory.
            If the material is not an archive, nothing will be done so this
            function is "harmless" when it is "useless".
    """
    # uncompress
    if (source_material.endswith(".gz") or
            source_material.endswith(".gzip")):
        host.run('gunzip "%s"' % (utils.sh_escape(source_material)))
        source_material = ".".join(source_material.split(".")[:-1])
    elif source_material.endswith("bz2"):
        host.run('bunzip2 "%s"' % (utils.sh_escape(source_material)))
        source_material = ".".join(source_material.split(".")[:-1])

    # untar
    if source_material.endswith(".tar"):
        retval = host.run('tar -C "%s" -xvf "%s"' % (
            utils.sh_escape(os.path.dirname(source_material)),
            utils.sh_escape(source_material),))
        source_material = os.path.join(os.path.dirname(source_material),
                                       retval.stdout.split()[0])

    return source_material
예제 #7
0
    def run(self,
            command,
            timeout=3600,
            ignore_status=False,
            stdout_tee=utils.TEE_TO_LOGS,
            stderr_tee=utils.TEE_TO_LOGS,
            connect_timeout=30,
            stdin=None,
            verbose=True,
            args=()):
        """
        Run a command on the remote host.
        @see shared.hosts.host.run()

        :param connect_timeout: connection timeout (in seconds)
        :param options: string with additional ssh command options
        :param verbose: log the commands

        :raise AutoservRunError: if the command failed
        :raise AutoservSSHTimeout: ssh connection has timed out
        """

        stdout = utils.get_stream_tee_file(stdout_tee,
                                           utils.DEFAULT_STDOUT_LEVEL,
                                           prefix=utils.STDOUT_PREFIX)
        stderr = utils.get_stream_tee_file(
            stderr_tee,
            utils.get_stderr_level(ignore_status),
            prefix=utils.STDERR_PREFIX)

        for arg in args:
            command += ' "%s"' % utils.sh_escape(arg)

        if verbose:
            logging.debug("Running (ssh-paramiko) '%s'" % command)

        # start up the command
        start_time = time.time()
        try:
            channel = self._open_channel(timeout)
            channel.exec_command(command)
        except (socket.error, paramiko.SSHException, EOFError), e:
            # This has to match the string from paramiko *exactly*.
            if str(e) != 'Channel closed.':
                raise error.AutoservSSHTimeout("ssh failed: %s" % e)
예제 #8
0
    def run(
        self,
        command,
        timeout=3600,
        ignore_status=False,
        stdout_tee=utils.TEE_TO_LOGS,
        stderr_tee=utils.TEE_TO_LOGS,
        connect_timeout=30,
        stdin=None,
        verbose=True,
        args=(),
    ):
        """
        Run a command on the remote host.
        @see shared.hosts.host.run()

        @param connect_timeout: connection timeout (in seconds)
        @param options: string with additional ssh command options
        @param verbose: log the commands

        :raise AutoservRunError: if the command failed
        :raise AutoservSSHTimeout: ssh connection has timed out
        """

        stdout = utils.get_stream_tee_file(stdout_tee, utils.DEFAULT_STDOUT_LEVEL, prefix=utils.STDOUT_PREFIX)
        stderr = utils.get_stream_tee_file(
            stderr_tee, utils.get_stderr_level(ignore_status), prefix=utils.STDERR_PREFIX
        )

        for arg in args:
            command += ' "%s"' % utils.sh_escape(arg)

        if verbose:
            logging.debug("Running (ssh-paramiko) '%s'" % command)

        # start up the command
        start_time = time.time()
        try:
            channel = self._open_channel(timeout)
            channel.exec_command(command)
        except (socket.error, paramiko.SSHException, EOFError), e:
            # This has to match the string from paramiko *exactly*.
            if str(e) != "Channel closed.":
                raise error.AutoservSSHTimeout("ssh failed: %s" % e)
예제 #9
0
 def path_exists(self, path):
     """ Determine if path exists on the remote machine. """
     result = self.run('ls "%s" > /dev/null' % utils.sh_escape(path),
                       ignore_status=True)
     return result.exit_status == 0
예제 #10
0
 def path_exists(self, path):
     """ Determine if path exists on the remote machine. """
     result = self.run('ls "%s" > /dev/null' % utils.sh_escape(path),
                       ignore_status=True)
     return result.exit_status == 0
예제 #11
0
    def run(self,
            command,
            timeout=3600,
            ignore_status=False,
            stdout_tee=utils.TEE_TO_LOGS,
            stderr_tee=utils.TEE_TO_LOGS,
            connect_timeout=30,
            stdin=None,
            verbose=True,
            args=()):
        """
        Run a command on the remote host.
        @see shared.hosts.host.run()

        :param connect_timeout: connection timeout (in seconds)
        :param options: string with additional ssh command options
        :param verbose: log the commands

        :raise AutoservRunError: if the command failed
        :raise AutoservSSHTimeout: ssh connection has timed out
        """

        stdout = utils.get_stream_tee_file(stdout_tee,
                                           utils.DEFAULT_STDOUT_LEVEL,
                                           prefix=utils.STDOUT_PREFIX)
        stderr = utils.get_stream_tee_file(
            stderr_tee,
            utils.get_stderr_level(ignore_status),
            prefix=utils.STDERR_PREFIX)

        for arg in args:
            command += ' "%s"' % utils.sh_escape(arg)

        if verbose:
            logging.debug("Running (ssh-paramiko) '%s'" % command)

        # start up the command
        start_time = time.time()
        try:
            channel = self._open_channel(timeout)
            channel.exec_command(command)
        except (socket.error, paramiko.SSHException, EOFError) as e:
            # This has to match the string from paramiko *exactly*.
            if str(e) != 'Channel closed.':
                raise error.AutoservSSHTimeout("ssh failed: %s" % e)

        # pull in all the stdout, stderr until the command terminates
        raw_stdout, raw_stderr = [], []
        timed_out = False
        while not channel.exit_status_ready():
            if channel.recv_ready():
                raw_stdout.append(channel.recv(self.BUFFSIZE))
                stdout.write(raw_stdout[-1])
            if channel.recv_stderr_ready():
                raw_stderr.append(channel.recv_stderr(self.BUFFSIZE))
                stderr.write(raw_stderr[-1])
            if timeout and time.time() - start_time > timeout:
                timed_out = True
                break
            stdin = self.__send_stdin(channel, stdin)
            time.sleep(1)

        if timed_out:
            exit_status = -signal.SIGTERM
        else:
            exit_status = channel.recv_exit_status()
        channel.settimeout(10)
        self._exhaust_stream(stdout, raw_stdout, channel.recv)
        self._exhaust_stream(stderr, raw_stderr, channel.recv_stderr)
        channel.close()
        duration = time.time() - start_time

        # create the appropriate results
        stdout = "".join(raw_stdout)
        stderr = "".join(raw_stderr)
        result = utils.CmdResult(command, stdout, stderr, exit_status,
                                 duration)
        if exit_status == -signal.SIGHUP:
            msg = "ssh connection unexpectedly terminated"
            raise error.AutoservRunError(msg, result)
        if timed_out:
            logging.warn('Paramiko command timed out after %s sec: %s',
                         timeout, command)
            raise error.AutoservRunError("command timed out", result)
        if not ignore_status and exit_status:
            raise error.AutoservRunError(command, result)
        return result