Source code for cup.shell.expect

#!/usr/bin/env python
# -*- coding: utf-8 -*
# Copyright: [CUP] - See LICENSE for details.
# Authors: Weiyan Lin
"""
:description:
    made a wraper out of paramiko.**
    The original copyright belongs to the author of paramiko module.
    See it at http://docs.paramiko.org/en/stable/
"""
import os
import traceback
import stat

import cup
import paramiko
from paramiko import ssh_exception


__all__ = [
    'checkssh', 'go_ex', 'lscp', 'dscp', 'go_with_scp'
]


def _connect(hostname, username, passwd, timeout, port=22):
    """
    get connect
    :param hostname:
    :param username:
    :param passwd:
    :param timeout:
    :param port:
    :return:
    """
    client = None
    try:
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(hostname, port, username, passwd, timeout=timeout)
    except ssh_exception.NoValidConnectionsError as e:
        print("username not exists")
    except ssh_exception.AuthenticationException as e:
        print("passwd not correct")
    except Exception as e:
        print("*** Caught exception: %s: %s" % (e.__class__, e))
        traceback.print_exc()
    return client


def _sftp_connect(hostname, username, passwd, timeout, port=22):
    """
    get sftp connect
    :param hostname:
    :param username:
    :param passwd:
    :param timeout:
    :param port:
    :return:
    """
    client = None
    sftp = None
    try:
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        client.connect(hostname, port, username, passwd, timeout=timeout)
        sftp = paramiko.SFTPClient.from_transport(client.get_transport())
    except ssh_exception.NoValidConnectionsError as e:
        print("username not exists")
    except ssh_exception.AuthenticationException as e:
        print("passwd not correct")
    except Exception as e:
        print("*** Caught exception: %s: %s" % (e.__class__, e))
        traceback.print_exc()
    return client, sftp


def _close(client):
    """
    disconnect client
    :param client:
    :return:
    """
    try:
        client.close()
    except Exception as e:
        print("*** Caught exception: %s: %s" % (e.__class__, e))
        traceback.print_exc()


def _is_exists(path, function):
    """
    check path exists
    :param path:
    :param function:
    :return:
    """
    path = path.replace('\\', '/')
    try:
        function(path)
    except Exception as e:
        return False
    else:
        return True


def _check_local(local):
    """
    check dir exists, if not exists then make dir
    :param local:
    :return:
    """
    if not os.path.exists(local):
        try:
            os.mkdir(local)
            return True
        except IOError as err:
            print(err)
            return False


def _put(sftp, src, dst, msg=''):
    """
    put file/dir
    :param sftp:
    :param src:
    :param dst:
    :return: msg
    """
    name = os.path.basename(os.path.normpath(src))
    dst = os.path.join(dst, name).replace('\\', '/')
    if os.path.isdir(src):
        _is_exists(dst, function=sftp.mkdir)
        for file in os.listdir(src):
            src_file = os.path.join(src, file).replace('\\', '/')
            msg = _put(sftp=sftp, src=src_file, dst=dst, msg=msg)
    if os.path.isfile(src):
        try:
            sftp.put(src, dst)
        except Exception as e:
            msg += '[put] ' + src + '==>' + dst + ' failed\n'
        else:
            msg += '[put] ' + src + '==>' + dst + ' successed\n'
    return msg


def _get(sftp, src, dst, msg=''):
    """
    get file/dir
    :param sftp:
    :param src:
    :param dst:
    :return: msg
    """
    result = sftp.stat(src)
    if stat.S_ISDIR(result.st_mode):
        dirname = os.path.basename(os.path.normpath(src))
        dst = os.path.join(dst, dirname)
        _check_local(dst)
        for file in sftp.listdir(src):
            sub_remote = os.path.join(src, file).replace('\\', '/')
            msg = _get(sftp, sub_remote, dst, msg=msg)
    else:
        dst = os.path.join(dst, os.path.basename(src))
        try:
            sftp.get(src, dst)
        except IOError as err:
            msg += '[get] ' + src + '==>' + dst + ' failed\n'
        else:
            msg += '[get] ' + src + '==>' + dst + ' successed\n'
    return msg


def to_str(bytes_or_str):
    """
    把byte类型转换为str
    :param bytes_or_str:
    :return:
    """
    if isinstance(bytes_or_str, bytes):
        value = bytes_or_str.decode('utf-8')
    else:
        value = bytes_or_str
    return value


[docs]def checkssh(hostname, username, passwd): """ check if we can ssh to hostname. :return:a dict with keys ('exitstatus', 'result') exitstatus 0 success -1 others """ return go_ex(hostname, username, passwd, 'echo "testSSH"', timeout=8)
[docs]def go_ex(hostname, username, passwd, command='', timeout=600, b_print_stdout=True ): """ execute command at remote. :return:a dict with keys ('exitstatus', 'result') exitstatus 0 success -1 others """ ret = { 'exitstatus': -1, 'remote_exitstatus': -1, 'result': '', 'result_stderr': '' } client = _connect(hostname, username, passwd, timeout, 22) try: stdin, stdout, stderr = client.exec_command(command) ret['remote_exitstatus'] = stdout.channel.recv_exit_status() ret['exitstatus'] = 0 ret['result'] = to_str(stdout.read()) ret['result_stderr'] = to_str(stderr.read()) if b_print_stdout: print(ret['result']) except Exception as e: print("*** Caught exception: %s: %s" % (e.__class__, e)) traceback.print_exc() ret['exitstatus'] = -1 ret['remote_exitstatus'] = -1 ret['result_stderr'] = e finally: _close(client) return ret
[docs]def lscp(src, hostname, username, passwd, dst, timeout=800): """ copy [localhost]:src to [hostname]:[dst] :return:a dict with keys ('exitstatus', 'result') exitstatus 0 success -1 others """ ret = { 'exitstatus': -1, 'remote_exitstatus': -1, 'result': '' } client, sftp = _sftp_connect(hostname, username, passwd, timeout, 22) if not client or not sftp: ret['result'] = 'connect remote host error' return ret if not _is_exists(src, function=os.stat): ret['result'] = "'" + src + "': No such file or directory in local" return ret if not _is_exists(dst, function=sftp.stat): ret['result'] = "'" + dst + "': No such directory at remote" return ret try: msg = _put(sftp, src, dst) ret['result'] = msg if msg.__contains__('failed'): return ret ret['exitstatus'] = 0 ret['remote_exitstatus'] = 0 except Exception as e: print("*** Caught exception: %s: %s" % (e.__class__, e)) traceback.print_exc() ret['exitstatus'] = -1 ret['remote_exitstatus'] = -1 finally: _close(client) return ret
[docs]def dscp(hostname, username, passwd, src, dst, timeout=9000): """ copy [hostname]:[src] to [localhost]:[dst]. :return: a dict with keys ('exitstatus', 'result') exitstatus 0 success -1 others """ ret = { 'exitstatus': -1, 'remote_exitstatus': -1, 'result': '' } client, sftp = _sftp_connect(hostname, username, passwd, timeout, 22) if not client or not sftp: ret['result'] = 'connect remote host error' return ret if not _is_exists(src, function=sftp.stat): ret['result'] = "'" + src + "': No such file or directory at remote" return ret if not _is_exists(dst, function=os.stat): ret['result'] = "'" + dst + "': No such file or directory in local" return ret try: msg = _get(sftp, src, dst) ret['result'] = msg if msg.__contains__('failed'): return ret ret['exitstatus'] = 0 ret['remote_exitstatus'] = 0 except Exception as e: print("*** Caught exception: %s: %s" % (e.__class__, e)) traceback.print_exc() ret['exitstatus'] = -1 ret['remote_exitstatus'] = -1 finally: _close(client) return ret
def _judge_ret(ret, msg=''): if not (ret['exitstatus']): return True ret['result'] = msg + ' \n ' + str(ret['result']) return False
[docs]def go_with_scp( hostname, username, passwd, command='', host_tmp='/tmp/', remote_tmp='/tmp/', timeout=800 ): """ Recommand using this function to remotely execute cmds. go_witch_scp will write a temp script file and scp to hostname:[host_tmp]. Then execute it and get the result back. :param host_tmp: temp folder for keeping the temporary script file (contains the cmd) :param remote_tmp: remote temp folder for keeping the temporary script file :param timeout: timeout :return: a dict with keys ('exitstatus', 'result') """ ret = { 'exitstatus': -1, 'remote_exitstatus': -1, 'result': 'write host file fail' } tmp_filename = cup.util.CGeneratorMan().get_uniqname() host_file = host_tmp + '/' + tmp_filename remote_file = remote_tmp + '/' + tmp_filename with open(host_file, 'w') as fhandle: fhandle.write(command) if not os.path.exists(host_file): return ret ret = lscp(host_file, hostname, username, passwd, remote_tmp, timeout) if not _judge_ret(ret, 'scp ret:'): return ret cmd = ' sh %s ' % remote_file ret = go_ex(hostname, username, passwd, cmd, timeout) os.unlink(host_file) cmd = ' rm -f %s ' % remote_file res = go_ex(hostname, username, passwd, cmd, 10) if not _judge_ret(res, 'rm -f remote_file ret:'): return res return ret