使用交互式元素实现paramiko的超时

时间:2013-10-15 22:12:41

标签: python paramiko

这个问题是这个问题的后续问题:Using paramiko to send commands to an open shell that has an interactive element所以请在回答之前先阅读。

我能够成功地将数据发送到阵列的系统shell,但是当阵列花费太长时间来运行我发送的脚本时,我需要帮助确定如何实现超时。 Paramiko的exec_command有一个timeout=参数,但这不会有帮助,因为我发送的唯一命令是"script",它立即返回并等待通道上的输入,当我尝试实现它似乎打破了函数的其余部分,因为没有从数组返回。

然后该数组应该处理我发送的脚本并通过stdout返回输出,但是如果数组需要很长时间,我就无法超时连接,并且它保持不变脚本的其余部分。

这是我的代码:

def run_script(self, script_name):
    """ Run a script on the remote array and return the stdout
    """
    try:
        common_functions_file = os.path.join(SCRIPT_DIR, 'zfs_common_functions.js')
        common_functions = open(common_functions_file).read().splitlines()
        # add common_functions to the top of the script
        script_contents = common_functions + open(script_name).read().splitlines()
        stdin, stdout, stderr = self._ssh.exec_command('script')
        for line in script_contents:
            # Skip lines with comments
            if re.match("^//", line):
                continue
            stdin.write(line)
            stdin.write('\n')
        stdin.write('.\n')
        stdin.flush()
        error = stderr.readlines()
        if len(error) == 0:
            try:
                output = ''.join(stdout.readlines())
                if(re.search('aksh', output)):
                    logger.warn("ZFS Shell Error: %s" % output)
                    return None
                return output
            except Exception as e:
                logger.exception(e)

        else:
            logger.error(error)
            return None
    except paramiko.SSHException as e:
        logger.warn(
            "Couldn't execute script on array %s: %s" % (array.name, e))

    except AttributeError as e:
        logger.exception(e)
        raise

    except Exception:
        raise

1 个答案:

答案 0 :(得分:2)

我最终通过直接与频道进行交互来解决问题,您可以设置超时:

def run_script(self, script_name):
    """ Run a script on the remote array and return the stdout
    """
    try:
        chan = self._ssh.get_transport().open_session()
        # five minute timeout on the channel communication
        chan.settimeout(5*60.0)
        common_functions_file = os.path.join(SCRIPT_DIR, 'zfs_common_functions.js')
        common_functions = open(common_functions_file).read().splitlines()
        # add common_functions to the top of the script
        script_contents = common_functions + open(script_name).read().splitlines()
        chan.exec_command('script')
        if chan.send_ready():
            chan.sendall("\n".join(script_contents))
            chan.send("\n.\n")

        results = StringIO()
        error = StringIO()
        bufsize = 1024
        while not chan.exit_status_ready():
            if chan.recv_ready():
                data = chan.recv(bufsize)
                while data:
                    results.write(data)
                    data = chan.recv(bufsize)

            if chan.recv_stderr_ready():
                error_buf = chan.recv_stderr(bufsize)
                while error_buf:
                    error.write(error_buf)
                    error_buf = chan.recv_stderr(bufsize)

        exit_status = chan.recv_exit_status()
        if exit_status == 0:
            return results.getvalue()
        else:
            raise ZfsScriptError(results.getvalue())

    except socket.timeout:
        logger.warn("%s: Timeout running %s" %(self.hostname, script_name))
        return None

    except paramiko.SSHException as e:
        logger.warn(
            "Couldn't execute script on array %s: %s" % (self.hostname, e))
        raise

    except AttributeError as e:
        logger.exception(e)
        raise

    except Exception:
        raise

    finally:
        results.close()
        error.close()
        chan.close()