end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

python + ssh で、リモートにあるサーバのdiff

diff & find & ssh & cat for linux で、サーバ間のファイル差分を確認 - end0tknr's kipple - web写経開発

上記 entry は、local x remote の差分でしたが、 今回は、remote x remote の差分で、かつ、python を使用。

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import chardet
import difflib
import getopt
import paramiko
import re
import subprocess
import sys

remote_1 = "54.238.ないしょ.ないしょ"
remote_2 = "54.150.ないしょ.ないしょ"
remote_user = "ないしょ"
private_key = "/home/end0tknr/.ssh/ないしょ"
ssh_timeout = 5 #sec

target_dirs = [
    "/export/prj/hoge01/hon/cgi-bin",
    "/export/prj/hoge01/hon/cgi-bin2",
    "/export/prj/hoge01/hon/conf",
    "/export/prj/hoge01/hon/lib",
]

def main():
    # ssh client を new
    client_1 = init_ssh_client( remote_1 )
    client_2 = init_ssh_client( remote_2 )

    # remote にある file path 一覧を収集
    file_paths = find_remote_files( client_1 )

    reg_pat = re.compile(".*\.log$")

    i = 0

    for file_path in file_paths:
        i += 1
        # if i > 10:
        #     break
        
        # ログファイルは比較対象外
        if reg_pat.match(file_path):
            continue
        
        print("diff", file_path)
        
        remote_file_1 = cat_remote_file(remote_1, file_path)

        file_path_2 = re.sub(r'/hon/', '/kai/', file_path)
        
        remote_file_2 = cat_remote_file(remote_2, file_path_2)
        
        diff_result = difflib.unified_diff(remote_file_1.split(),
                                           remote_file_2.split() )

        diff_result_str = "\n".join( diff_result )
        if diff_result_str:
            print( diff_result_str )


    client_1.close()
    client_2.close()
    
# paramiko における 多種の文字コード処理が不明の為
# subprocess.run() を使用
def cat_remote_file( remote_ip, file_path ):
    ssh_cmd = ["/usr/bin/ssh",
               "-i", private_key,
               f"{remote_user}@{remote_ip}",
               "cat", file_path ]
    try:
        res = subprocess.run(ssh_cmd, stdout=subprocess.PIPE)
    except:
        print('ERROR', file_path)
        return ""

    res_bytes = res.stdout
    if not res_bytes or len(res_bytes) == 0:
        return ""
    

    # 文字コード判定し、unicodeへ
    enc = chardet.detect( res_bytes )
    new_encode = enc["encoding"]
    if not new_encode:
        new_encode= "cp932"

    try:
        res_str = res_bytes.decode( new_encode )
    except:
        return ""
    
    return res_str


def find_remote_files( client ):

    file_paths = []
    for target_dir in target_dirs:
        ssh_cmd = f"find {target_dir} -type f -print"

        stdin, stdout, stderr = client.exec_command( ssh_cmd )

        for line in stdout:
            file_paths.append( line.strip() )

    return file_paths
    
def init_ssh_client( ip_addr ):
    client = paramiko.SSHClient()
    client.set_missing_host_key_policy( paramiko.WarningPolicy() )

    client.connect(ip_addr,
                   username=remote_user,
                   key_filename=private_key,
                   timeout=ssh_timeout)
    return client
    


if __name__ == '__main__':
    main()