Python MySSH 實現劇本執行器

lyshark發表於2021-11-11

通過封裝Paramiko這個SSH模組,我們可以實現遠端批量管理Linux主機,在上一篇文章中我們封裝過一個MySSH類,這個類可以執行命令上傳下載檔案等,我們在這個類的基礎上,實現一個簡單的任務執行功能。

  • 執行方式
  • 程式會在Json檔案中解析引數,並將引數與所對應的主機進行關聯,對不同的主機組執行不同的命令,實現批量指令碼執行。

實現批量命令執行: 首先利用封裝好的MySSH類為基礎,實現一個批量命令執行器,該工具通過命令列引數傳遞執行不同的操作。

import os,json,sys
from MySSH import MySSH

def ping(group):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)
        print("-" * 120)
        print("{0:15} \t {1:6} \t {2:5}".format("IP地址", "使用者名稱", "狀態"))
        print("-" * 120)

        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            ref = ssh.GetPing()
            if ref == True:
                print("{0:15} \t {1:6} \t {2:5}".format(x[0],x[1],"已連線"))
            else:
                print("{0:15} \t {1:6} \t {2:5}".format(x[0], x[1], "未連線"))
        print("\n")

def run(group,command):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)
        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            ref = ssh.BatchCMD(command)
            print("\n")
            print("-" * 120)
            print("執行IP: {0:15} ".format(x[0]))
            print("-" * 120)
            print(ref)

def memory(group):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)
        print("-" * 120)
        print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "總記憶體", "剩餘記憶體", "利用率(百分比)"))
        print("-" * 120)

        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            ref = ssh.GetAllMemSpace()
            if ref != None:
                print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0],ref["Total"],ref["Free"],ref["Percentage"]))

def disk(group):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)

        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            print("-" * 120)
            print("IP地址: {0:15} \t".format(x[0]))
            print("-" * 120)

            ref = ssh.GetAllDiskSpace()
            if len(ref) !=0:
                for k,v in ref.items():
                    print("磁碟路徑: {0:30} \t 磁碟利用率: {1:8}".format(k,v))

def cpu(group):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)
        print("-" * 120)
        print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "使用者態", "核心態", "空閒率(百分比)"))
        print("-" * 120)

        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            ref = ssh.GetCPUPercentage()
            if len(ref)!=0:
                print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], ref["us"], ref["sys"], ref["idea"]))

def load_avg(group):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)
        print("-" * 120)
        print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "一分鐘負載", "五分鐘負載", "十五分鐘負載"))
        print("-" * 120)

        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            ref = ssh.GetLoadAVG()
            if len(ref)!=0:
                print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], ref["1avg"], ref["5avg"], ref["15avg"]))

def checkproc(group,proc):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)
        print("-" * 120)
        print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format("IP地址", "PID號","CPU佔用率", "記憶體佔用率"))
        print("-" * 120)

        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            ref = ssh.CheckProcessName(proc)
            if len(ref):
                print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], ref["PID"], ref["CPU"], ref["Mem"]))
            else:
                print("{0:15} \t {1:7} \t {2:7} \t {3:5} \t".format(x[0], 0, 0, 0))

def put_group(group,src,dst):
    with open("config.json" , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        ptr = config_load.get(group)
        print("-" * 120)
        print("{0:15} \t {1:7} \t {2:7} \t ".format("IP地址", "原始檔","傳輸到"))
        print("-" * 120)

        for x in ptr:
            ssh = MySSH(x[0],x[1],x[2],22)
            ssh.Init()
            ref = ssh.PutLocalFile(src,dst)
            if ref:
                print("{0:15} \t {1:7} \t {2:7} \t ".format(x[0], src,dst))

if __name__ == '__main__':
    while True:
        try:
            cmd = str(input("[LyShark Shell] # ")).split()
            cmd_len = len(cmd)
            if (cmd == ""):
                continue
            elif (cmd[0] == "exit"):
                exit(1)

            # ping --group=aix
            elif (cmd[0] == "ping"):
                if (cmd_len - 1 >= 1):
                    arg = cmd[1].split("=")[1]
                    ping(arg)

            # run --group=aix --cmd=ls
            elif (cmd[0] == "run"):
                if (cmd_len - 1 >= 2):
                    arg1 = cmd[1].split("=")[1]
                    arg2 = cmd[2].split("=")[1]
                    run(arg1,arg2)

            # memory --group=aix
            elif (cmd[0] =="memory"):
                if (cmd_len - 1 >= 1):
                    arg1 = cmd[1].split("=")[1]
                    memory(arg1)

            # disk --group=aix
            elif (cmd[0] =="disk"):
                if (cmd_len - 1 >= 1):
                    arg1 = cmd[1].split("=")[1]
                    disk(arg1)

            # cpu --group=aix
            elif (cmd[0] =="cpu"):
                if (cmd_len - 1 >= 1):
                    arg1 = cmd[1].split("=")[1]
                    cpu(arg1)

            # load --group=aix
            elif (cmd[0] =="load"):
                if (cmd_len - 1 >= 1):
                    arg1 = cmd[1].split("=")[1]
                    load_avg(arg1)

            # checkproc --group=aix --process=bash
            elif (cmd[0] =="checkproc"):
                if (cmd_len - 1 >= 1):
                    arg1 = cmd[1].split("=")[1]
                    arg2 = cmd[2].split("=")[1]
                    checkproc(arg1,arg2)

            # put_group --group=aix --src=./aaa.txt --dst=/tmp/aaa.txt
            elif (cmd[0] =="put_group"):
                if (cmd_len - 1 >= 3):
                    arg1 = cmd[1].split("=")[1]
                    arg2 = cmd[2].split("=")[1]
                    arg3 = cmd[3].split("=")[1]
                    put_group(arg1,arg2,arg3)
            else:
                print("[-] error version 1.0")
        except Exception:
            continue

解析檔案config.json配置如下所示,每個組中包括一定數量的機器。

{
  "aix":
  [
    ["127.0.0.1","root","1233"],
    ["127.0.0.1","root","123456"]

  ],
  "suse":
  [
    ["127.0.0.1","root","123123123"]
  ],
}

程式執行後會進入互動Shell環境,我們可以根據需要執行不同的key獲取資料。

此外指令碼還支援如下引數.

  • 執行命令: run --group=aix --cmd=ls
  • 記憶體檢查: memory --group=aix
  • 磁碟檢查: disk --group=aix
  • CPU檢查: cpu --group=aix
  • 負載檢查: load --group=aix
  • 程式檢查: checkproc --group=aix --process=bash
  • 檔案上傳: put_group --group=aix --src=./aaa.txt --dst=/tmp/aaa.txt

劇本執行器,這部分內容為擴充套件部分,我們定義兩個函式,函式DisplayAllRule用來獲取特定目錄下的特定劇本,而RunRule函式則用於解析這個劇本並執行劇本中的命令集合。

import MySSH
import os,json,sys

# 獲取特定目錄下所有的劇本
def DisplayAllRule():
    print("{0:15} \t {1:10} \t {2:10} \t {3:15} \t {4:5} \t {5:30}".
          format("名稱","應用平臺","埠","主機組","命令條數","描述資訊"))

    for switch in all_files:
        # 首先判斷檔案結尾是否為Json
        if( switch.endswith(".json") == True):
            all_switch_dir = rootdir + switch
            try:
                # 判斷檔案內部是否符合JSON規範
                with open(all_switch_dir , "r" ,encoding="utf-8") as read_file:
                    # 判斷是否存在指定欄位來識別規範
                    load = json.loads(read_file.read())
                    if load.get("framework") != None and load.get("task_sequence") != None:
                        print("{0:15} \t {1:10} \t {2:10} \t {3:15} \t {4:5} \t\t {5:30}".
                              format(switch,load.get("framework"),load.get("default_port"),load.get("Group"),len(load.get("task_sequence")),load.get("describe")))
            except ValueError:
                pass

# 執行命令列
def RunRule(rule_name):
    # 先開啟配置恩建並讀取到資料
    with open(config_dir , "r" ,encoding="utf-8") as read_config_ptr:
        config_load = json.loads(read_config_ptr.read())
        # 接著讀取選中劇本
        with open(rule_dir,"r",encoding="utf-8") as read_rule_ptr:
            rule_load = json.loads(read_rule_ptr.read())
            # 先找到組名稱,並以組名稱查詢組內主機數
            ref = config_load.get(rule_load.get("Group"))
            if ref != None:
                # 載入劇本中的任務命令
                task_sequence = rule_load.get("task_sequence")

                # 迴圈執行鍼對組內主機
                for addr in ref:
                    print("-" * 130 , "\n針對地址執行: {}\n".format(addr[0]),"-" * 130)
                    # 每個主機需要執行的命令
                    for cmd in task_sequence:
                        ssh = MySSH.MySSH(addr[0],addr[1],addr[2],int(rule_load.get("default_port")))
                        ssh.Init()
                        if cmd[0] == "PUT" and len(cmd) >= 3:
                            if ssh.PutLocalFile(cmd[1],cmd[2]):
                                print("命令序列: {0} {1}".format("PUT",cmd[2]))
                            else:
                                break
                        else:
                            ret = ssh.BatchCMD_NotRef(cmd[0])
                            print("命令序列: {0}".format(cmd[0]))
            else:
                print("[-] 主機組不存在,無法繼續執行.")
                exit(0)

if __name__ == "__main__":
    arg = sys.argv
    if arg[1] == "display":
        DisplayAllRule()
    elif arg[1] == "run":
        RunRule(arg[2])

檔案規劃put_file目錄用於存放需要上傳的檔案,rule目錄用來存放執行劇本內容,我們先來看一個編譯安裝Apache伺服器的劇本寫法。

{
  "framework": "Linux",
  "default_port": "22",
  "describe": "編譯安裝Apache元件",
  "Group": "MyWebServer",
  "task_sequence":
  [
    ["iptables -F"],
    ["setenforce 0"],
    ["yum -y install gcc make pcre-devel openssl-devel expat-devel bzip2"],

    ["PUT","./put_file/httpd-2.4.46.tar.gz","/tmp/apache.tar.gz"],
    ["PUT","./put_file/apr-1.7.0.tar.bz2","/tmp/apr-1.7.0.tar.bz2"],
    ["PUT","./put_file/apr-util-1.6.1.tar.bz2","/tmp/apr-util-1.6.1.tar.bz2"],

    ["tar -xzf /tmp/apache.tar.gz -C /tmp/"],
    ["tar -xf /tmp/apr-1.7.0.tar.bz2 -C /tmp/"],
    ["tar -xf /tmp/apr-util-1.6.1.tar.bz2 -C /tmp/"],

    ["mv /tmp/apr-util-1.6.1 /tmp/httpd-2.4.46/srclib/apr-util"],
    ["mv /tmp/apr-1.7.0 /tmp/httpd-2.4.46/srclib/apr"],

    ["/tmp/httpd-2.4.46/configure --prefix=/tmp/httpd --with-zlib -with-included-apr"],
    ["make && make install"],

    ["echo 'hello lyshark' > /tmp/httpd/htdocs/index.html"],
    ["/tmp/httpd/bin/httpd"]
  ]
}

如上劇本中的Group欄位則是需要執行編譯安裝的所屬組,該組記憶體放執行地址,來看一下組的規劃。

{
  "MyWebServer":
    [
      ["192.168.191.4","root","1233"],
      ["192.168.191.5","root","1233"],
      ["192.168.191.6","root","1233"]
    ]
}

我們首先可以執行main.py display命令,獲取當前裝置中的所有劇本資訊。

在需要執行時輸入main.py run test.json尾部加上劇本名字即可。

相關文章