2017. 8. 17. 12:47

paramiko를 이용하여 python으로 ssh command line 실행 및 file, directory upload 하기

pip install paramiko로 paramiko를 설치한 다음, 아래 코드를 실행하면 됩니다.

#!/usr/bin/python
# -*- coding: utf-8 -*-

import paramiko
import os
import platform

# sample code
"""
ssh = get_ssh("xxx.xxx.xxx.xxx", 22, "admin", "admin")

exitcode=ssh_execute(ssh, "ls /root -laR")
print("result : %d" % exitcode)

sftp = get_sftp(ssh)
file_upload(sftp, "aaa.txt", "/home/admin/bbb")

close_ssh(ssh)
close_sftp(sftp)
"""

# sftp 상에 경로를 생성한다.
# remote 경로가 directory이면, is_dir에 True를 전달한다.
def mkdir_p(sftp, remote, is_dir=False):
    dirs_ = []
    if is_dir:
        dir_ = remote
    else:
        dir_, basename = os.path.split(remote)
    while len(dir_) > 1:
        dirs_.append(dir_)
        dir_, _  = os.path.split(dir_)

    if len(dir_) == 1 and not dir_.startswith("/"):
        dirs_.append(dir_) # For a remote path like y/x.txt

    while len(dirs_):
        dir_ = dirs_.pop()
        try:
            sftp.stat(dir_)
        except:
            print("making ... dir",  dir_)
            sftp.mkdir(dir_)

# sftp 상에 파일을 업로드한다.
# src_path에 dest_path로 업로드한다. 두개 모두 file full path여야 한다.
def file_upload(sftp, src_path, dest_path):
    mkdir_p(sftp, dest_path)
    try:
        sftp.put(src_path, dest_path)
    except Exception as e:
        print("fail to upload " + src_path + " ==> " + dest_path)
        raise e
    print("success to upload " + src_path + " ==> " + dest_path)

# sftp 상에 directory를 업로드한다.
# src_directory, dest_directory 모두 directory 경로여야 한다.
# dest_directory에 src_directory가 포함되어 복사된다.
# 즉, src_directory에 CTRL+C, dest_directory에 CTRL+V한 효과가 있다.
def directory_upload(sftp, src_directory, dest_directory):
    mkdir_p(sftp, dest_directory, True)
    cwd = os.getcwd()
    os.chdir(os.path.split(src_directory)[0])
    parent=os.path.split(src_directory)[1]
    is_window=(platform.system() == "Windows")
    for walker in os.walk(parent):
        try:
            for file in walker[2]:
                pathname=os.path.join(dest_directory, walker[0], file)
                if (True == is_window):
                    pathname=pathname.replace('\\', '/')
                    file_upload(sftp, os.path.join(walker[0],file), pathname)
        except Exception as e:
            print(e)
            raise e

# ssh 명령을 수행한다.
# exit status를 리턴한다.
def ssh_execute(ssh, command, is_print=True):
    # ssh 명령의 결과로 exit status를 구하는게 쉽지 않다.
    # 따라서, 명령의 끝에 "mark=$?"를 출력하여,
    # 최종 exit statud를 구할 수 있도록 한다.
    exit_status=0
    mark="ssh_helper_result_mark!!@@="
    command=command+";echo " + mark + "$?"

    try:
        stdin, stdout, stderr = ssh.exec_command(command, get_pty=True)
    except Exception as e:
        print(e)
        raise e

    for line in stdout:
        msg=line.strip('\n')
        if (msg.startswith(mark)):
            exit_status=msg[len(mark):]
        else:
            if (True == is_print):
                print(line.strip('\n'))

    return int(exit_status)

def get_ssh(host_ip, port, id, pw):
    try:
        # ssh client 생성
        ssh = paramiko.SSHClient()

        # ssh 정책 설정
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        
        # connect
        ssh.connect(hostname=host_ip, port=port, username=id, password=pw)
    except Exception as e:
        print(e)
        raise e

    return ssh

def get_sftp(ssh):
    try:
        sftp = paramiko.SFTPClient.from_transport(ssh.get_transport())
    except Exception as e:
        print(e)
        raise e
    return sftp

def close_ssh(ssh):
    ssh.close()

def close_sftp(sftp):
    sftp.close()


사용예는 아래와 같습니다.


ssh=get_ssh("xxx.xxx.xxx.xxx", 22, "id", "password")

즉, 위와 같은 방법으로 ip, port(=22), 그리고 id, password를 입력받아 ssh context를 생성합니다.

close(ssh)를 통해 context를 제거할 수 있습니다.


명령 실행을 위해서는,

exitcode=ssh_execute(ssh, "command-line")

을 실행합니다.

해당 명령의 stdout, stderr를 함께 출력합니다.

exitcode는 해당 command-line을 원격으로 실행한 exitcode입니다.

만일, command-line을 실행하기 위한 과정에서 에러가 발생하면 exception이 발생합니다.


upload를 위해서는 아래와 같이 sftp context를 생성해야 합니다.

sftp=get_sftp(ssh)


file upload는,

file_upload(sftp, "C:\test.txt", "/tmp/uploaded.txt")

와 깉이 실행합니다. 앞의 경로는 upload할 local 경로입니다.

뒤의 경로는 upload할 경로입니다. 만일, directory가 존재하지 않는다면, 생성도 해줍니다.


directory upload는

directory_upload(sftp, "C:\test", "/tmp/")

와 같이 합니다. 앞의 경로는 upload할 local directory입니다.

뒤의 경로는 upload할 directory입니다.

위 예는,

/tmp/test 에 복사하게 되는데,

이해를 위해서,

C:\test 아이콘을 탐색기에서 CTRL+C하고,

/tmp/ 경로에서 CTRL+V한 것과 동일하다고 생각하면 됩니다.


close_sftp(sftp)

를 통해 생성된 sftp context를 제거합니다.