Python如何实现Paramiko的二次封装


Posted in Python onJanuary 30, 2021

Paramiko是一个用于执行SSH命令的Python第三方库,使用该库可实现自动化运维的所有任务,如下是一些常用代码的封装方式,多数代码为半成品,只是敲代码时的备份副本防止丢失,仅供参考。

目前本人巡检百台设备完全无压力,如果要巡检过千台则需要多线程的支持,过万台则需要加入智能判断等。

实现命令执行: 直接使用过程化封装,执行CMD命令.

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

def BatchCMD(address,username,password,port,command):
  try:
    ssh.connect(hostname=address,username=username,password=password,port=port,timeout=2)
    stdin , stdout , stderr = ssh.exec_command(command)
    result = stdout.read()
    if len(result) != 0:
      result = str(result).replace("\\n", "\n")
      result = result.replace("b'", "").replace("'", "")
      return result
    else:
      return None
  except Exception:
    return None

 实现磁盘巡检: 获取磁盘空间并返回字典格式

def GetAllDiskSpace(address,username,password,port):
  ref_dict = {}
  cmd_dict = {"Linux\n" : "df | grep -v 'Filesystem' | awk '{print $5 \":\" $6}'",
        "AIX\n" : "df | grep -v 'Filesystem' | awk '{print $4 \":\" $7}'"
        }
  # 首先检测系统版本
  os_version = BatchCMD(address,username,password,port,"uname")
  for version,run_cmd in cmd_dict.items():
    if(version == os_version):
      # 根据不同版本选择不同的命令
      os_ref = BatchCMD(address,username,password,port,run_cmd)
      ref_list= os_ref.split("\n")
      # 循环将其转换为字典
      for each in ref_list:
        # 判断最后是否为空,过滤最后一项
        if each != "":
          ref_dict[str(each.split(":")[1])] = str(each.split(":")[0])
  return ref_dict

# 磁盘巡检总函数
def DiskMain():
  with open("db.json", "r", encoding="utf-8") as read_fp:
    load_json = read_fp.read()
    js = json.loads(load_json)
    base = js.get("base")
    count = len(base)

    for each in range(0,count):
      print("\033[37m-\033[0m" * 80)
      print("\033[35m 检测地址: {0:10} \t 用户名: {1:10} \t 密码: {2:10} \t 端口: {3:4}\033[0m".
        format(base[each][1],base[each][2],base[each][3],base[each][4]))
      print("\033[37m-\033[0m" * 80)

      ref = GetAllDiskSpace(base[each][1],base[each][2],base[each][3],base[each][4])
      for k,v in ref.items():
        # 判断是否存在空盘
        if( v.split("%")[0] != "-"):
          # 将占用百分比转换为整数
          space_ret = int(v.split("%")[0])
          if space_ret >= 70:
            print("\033[31m 磁盘分区: {0:30} \t 磁盘占用: {1:5} \033[0m".format(k,v))
            continue
          if space_ret >= 50:
            print("\033[33m 磁盘分区: {0:30} \t 磁盘占用: {1:5} \033[0m".format(k, v))
            continue
          else:
            print("\033[34m 磁盘分区: {0:30} \t 磁盘占用: {1:5} \033[0m".format(k, v))
            continue
      print()

# 组内传递用户名密码时调用此方法
def GroupDiskMain(address,username,password,port):
  ref = GetAllDiskSpace(address,username,password,port)
  for k, v in ref.items():
    if (v.split("%")[0] != "-"):
      space_ret = int(v.split("%")[0])
      if space_ret >= 70:
        print("磁盘分区: {0:30} \t 磁盘占用: {1:5} -> [警告]".format(k, v))
        continue
      if space_ret >= 50:
        print("磁盘分区: {0:30} \t 磁盘占用: {1:5} -> [警惕]".format(k, v))
        continue
      else:
        print("磁盘分区: {0:30} \t 磁盘占用: {1:5} -> [正常]".format(k, v))
        continue
  print()

获取系统内存利用率: 获取系统内存利用率

def GetAllMemSpace(address,username,password,port):
  cmd_dict = {"Linux\n" : "cat /proc/meminfo | head -n 2 | awk '{print $2}' | xargs | awk '{print $1 \":\" $2}'",
        "AIX\n" : "df | grep -v 'Filesystem' | awk '{print $4 \":\" $7}'"
        }
  # 首先检测系统版本
  os_version = BatchCMD(address,username,password,port,"uname")
  for version,run_cmd in cmd_dict.items():
    if(version == os_version):
      # 根据不同版本选择不同的命令
      os_ref = BatchCMD(address,username,password,port,run_cmd)

      # 首先现将KB转化为MB
      mem_total = math.ceil( int(os_ref.split(":")[0].replace("\n","")) / 1024)
      mem_free = math.ceil(int(os_ref.split(":")[1].replace("\n","")) / 1024)
      mem_used = str( int(mem_total) - int(mem_free))

      # 计算占用空间百分比
      percentage = 100 - int(mem_free / int(mem_total / 100))

      print("内存总计空间: {}".format(str(mem_total) + " MB"))
      print("内存剩余空间: {}".format(str(mem_free) + " MB"))
      print("内存已用空间: {}".format(str(mem_used) + " MB"))
      print("计算百分比: {}".format(str(percentage) + " %"))

获取系统进程信息: 获取系统进程信息,并返回字典格式

def GetAllProcessSpace(address,username,password,port):
  ref_dict = {}
  cmd_dict = {"Linux\n" : "ps aux | grep -v 'USER' | awk '{print $2 \":\" $11}' | uniq",
        "AIX\n" : "ps aux | grep -v 'USER' | awk '{print $2 \":\" $12}' | uniq"
        }
  os_version = BatchCMD(address,username,password,port,"uname")
  for version,run_cmd in cmd_dict.items():
    if(version == os_version):
      os_ref = BatchCMD(address, username, password, port, run_cmd)
      ref_list = os_ref.split("\n")
      for each in ref_list:
        if each != "":
          ref_dict[str(each.split(":")[0])] = str(each.split(":")[1])
  return ref_dict

# 巡检进程是否存在
def ProcessMain():
  with open("db.json", "r", encoding="utf-8") as read_fp:
    load_json = read_fp.read()
    js = json.loads(load_json)

    process = js.get("process")
    process_count = len(process)
    for x in range(0,process_count):
      # 根据process中的值查询base中的账号密码
      base = js.get("base")
      if( list(process[x].keys())[0] == base[x][0] ):
        # 拿到账号密码之后再提取出他们的进程ID于进程名
        print("\033[37m-\033[0m" * 80)
        print("\033[35m 检测地址: {0:10} \t 用户名: {1:10} \t 密码: {2:10} \t 端口: {3:4}\033[0m".
           format(base[x][1], base[x][2], base[x][3], base[x][4]))
        print("\033[37m-\033[0m" * 80)

        ref_dic = GetAllProcessSpace(base[x][1],base[x][2],base[x][3],base[x][4])
        # ref_val = 全部进程列表 proc_val = 需要检测的进程列表
        ref_val = list(ref_dic.values())
        proc_val = list(process[x].values())[0]
        # 循环比较是否在列表中
        for each in proc_val:
          flag = each in ref_val
          if(flag == True):
            print("\033[34m 进程: {0:50} 状态: √ \033[0m".format(each))
          else:
            print("\033[31m 进程: {0:50} 状态: × \033[0m".format(each))

实现剧本运行功能: 针对特定一台主机运行剧本功能,随便写的一个版本,仅供参考

def RunRule(address,username,password,port,playbook):
  os_version = BatchCMD(address,username,password,port,"uname")
  if(os_version == list(playbook.keys())[0]):
    play = list(playbook.values())[0]
    print()
    print("\033[37m-\033[0m" * 130)
    print("\033[35m 系统类型: {0:4} \t 地址: {1:10} \t 用户名: {2:10} \t 密码: {3:15} \t 端口: {4:4}\033[0m"
       .format(os_version.replace("\n",""),address,username,password,port))
    print("\033[37m-\033[0m" * 130)

    for each in range(0,len(play)):
      RunCmd = play[each] + " > /dev/null 2>&1 && echo $?"
      print("\033[30m [>] 派发命令: {0:100} \t 状态: {1:5} \033[0m".format(
        RunCmd.replace(" > /dev/null 2>&1 && echo $?", ""),"正在派发"))

      os_ref = BatchCMD(address, username, password, port, RunCmd)
      if(os_ref == "0\n"):
        print("\033[34m [√] 运行命令: {0:100} \t 状态: {1:5} \033[0m".format(
          RunCmd.replace(" > /dev/null 2>&1 && echo $?",""),"派发完成"))
      else:
        print("\033[31m [×] 运行命令: {0:100} \t 状态: {1:5} \033[0m".format(
          RunCmd.replace(" > /dev/null 2>&1 && echo $?",""),"派发失败"))
        # 既然失败了,就把剩下的也打出来吧,按照失败处理
        for x in range(each+1,len(play)):
          print("\033[31m [×] 运行命令: {0:100} \t 状态: {1:5} \033[0m".format(
            play[x].replace(" > /dev/null 2>&1 && echo $?", ""), "终止执行"))
        break
  else:
    return 0

# 批量: 传入主机组不同主机执行不同剧本
def RunPlayBook(HostList,PlayBook):
  count = len(HostList)
  error = []
  success = []
  for each in range(0,count):
    ref = RunRule(HostList[each][0],HostList[each][1],HostList[each][2],HostList[each][3],PlayBook)
    if ref == 0:
      error.append(HostList[each][0])
    else:
      success.append(HostList[each][0])
  print("\n\n")
  print("-" * 130)
  print("执行清单")
  print("-" * 130)
  for each in success:
    print("成功主机: {}".format(each))
  for each in error:
    print("失败主机: {}".format(each))

# 运行测试
def PlayBookRun():
  playbook = \
    {
      "Linux\n":
        [
          "ifconfig",
          "vmstat",
          "ls",
          "netstat -an",
          "ifconfis",
          "cat /etc/passwd | grep 'root' | awk '{print $2}'"
        ]
    }

  addr_list = \
    [
      ["192.168.1.127", "root", "1233", "22"],
      ["192.168.1.126", "root", "1203", "22"]
    ]

  # 指定addr_list这几台机器执行playbook剧本
  RunPlayBook(addr_list,playbook)

过程化实现文件上传下载: 文件传输功能 PUT上传 GET下载

def BatchSFTP(address,username,password,port,soruce,target,flag):
  transport = paramiko.Transport((address, int(port)))
  transport.connect(username=username, password=password)
  sftp = paramiko.SFTPClient.from_transport(transport)
  if flag == "PUT":
    try:
      ret = sftp.put(soruce, target)
      if ret !="":
        transport.close()
        return 1
      else:
        transport.close()
        return 0
      transport.close()
    except Exception:
      transport.close()
      return 0
  elif flag == "GET":
    try:
      target = str(address + "_" + target)
      os.chdir("./recv_file")
      ret = sftp.get(soruce, target)
      if ret != "":
        transport.close()
        return 1
      else:
        transport.close()
        return 0
      transport.close()
    except Exception:
      transport.close()
      return 0

# 批量将本地文件 source 上传到目标 target 中
def PutRemoteFile(source,target):
  with open("db.json", "r", encoding="utf-8") as fp:
    load_json = fp.read()
    js = json.loads(load_json)
    base = js.get("base")

    print("-" * 130)
    print("接收主机 \t\t 登录用户 \t 登录密码 \t 登录端口 \t 本地文件 \t\t 传输到 \t\t\t 传输状态")
    print("-" * 130)

    for each in range(0,len(base)):
      # 先判断主机是否可通信
      ref = BatchCMD(base[each][1], base[each][2], base[each][3], base[each][4],"uname")
      if ref == None:
        print("\033[31m{0:15} \t {1:6} \t {2:10} \t {3:3} \t {4:10} \t {5:10} \t 未连通\033[0m".format(
          base[each][1],base[each][2],base[each][3],base[each][4],source,target))
        continue

      ref = BatchSFTP(base[each][1],base[each][2],base[each][3],base[each][4],source,target,"PUT")
      if(ref == 1):
        print("\033[34m{0:15} \t {1:6} \t {2:10} \t {3:3} \t {4:10} \t {5:10} \t 传输成功\033[0m".format(
          base[each][1],base[each][2],base[each][3],base[each][4],source,target))
      else:
        print("\033[31m{0:15} \t {1:6} \t {2:10} \t {3:3} \t {4:10} \t {5:10} \t 传输失败\033[0m".format(
          base[each][1], base[each][2], base[each][3], base[each][4], source, target))

# 批量将目标文件拉取到本地特定目录(存在缺陷)
def GetRemoteFile(source,target):
  with open("db.json", "r", encoding="utf-8") as fp:
    load_json = fp.read()
    js = json.loads(load_json)
    base = js.get("base")

    print("-" * 130)
    print("发送主机 \t\t 登录用户 \t 登录密码 \t 登录端口 \t\t 远程文件 \t\t 拉取到 \t\t\t 传输状态")
    print("-" * 130)

    for each in range(0,len(base)):
      ref = BatchCMD(base[each][1], base[each][2], base[each][3], base[each][4], "uname")
      if ref == None:
        print("\033[31m{0:15} \t {1:6} \t {2:10} \t {3:3} \t {4:10} \t {5:10} \t 未连通\033[0m".format(
          base[each][1], base[each][2], base[each][3], base[each][4], source, target))
        continue

      ref = BatchSFTP(base[each][1],base[each][2],base[each][3],base[each][4],source,target,"GET")
      if(ref == 1):
        print("\033[34m{0:15} \t {1:6} \t {2:10} \t {3:3} \t {4:10} \t {5:10} \t 传输成功\033[0m".format(
          base[each][1],base[each][2],base[each][3],base[each][4],source,target))
      else:
        print("\033[31m{0:15} \t {1:6} \t {2:10} \t {3:3} \t {4:10} \t {5:10} \t 传输失败\033[0m".format(
          base[each][1], base[each][2], base[each][3], base[each][4], source, target))

另一种命令执行方法:

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
def BatchCMD(address,username,password,port,command):
  try:
    ssh.connect(hostname=address,username=username,password=password,port=port,timeout=2)
    stdin , stdout , stderr = ssh.exec_command(command)
    result = stdout.read()
    if len(result) != 0:
      return result
    else:
      return -1
  except Exception:
    return -1

# 通过获取主机Ping状态
def GetPing():
  fp = open("unix_base.db", "r", encoding="utf-8")
  count = len(open("unix_base.db", "r", encoding="utf-8").readlines())
  print("-" * 100)
  print("{0:20} \t {1:10} \t {2:13} \t {3:5} \t {4:9} \t {5:40}".format("IP地址","机器系统","设备SN","机房位置","存活状态","主机作用"))
  print("-" * 100)
  for each in range(count):
    ref = eval(fp.readline())
    ret = BatchCMD(ref[0],ref[5],ref[6],22,"pwd | echo $?")
    if(int(ret)==0):
      print("{0:20} \t {1:10} \t {2:11} \t {3:5} \t {4:9} \t {5:40}".
         format(ref[0],ref[1],ref[2],ref[3],"正常",ref[4]))
    else:
      print("{0:20} \t {1:10} \t {2:13} \t {3:5} \t {4:9} \t {5:40}".
         format(ref[0],ref[1],ref[2],ref[3],"异常",ref[4]))
  fp.close()

# ps aux | grep "usbCfgDev" | grep -v "grep" | awk {'print $2'}
def GetProcessStatus():
  fp = open("unix_process.db", "r", encoding="utf-8")
  count = len(open("unix_process.db", "r", encoding="utf-8").readlines())
  for each in range(count):
    proc = eval(fp.readline())
    proc_len = len(proc)
    print("-" * 70)
    print("---> 巡检地址: {0:10} \t 登录用户: {1:7} \t 登录密码: {2:10}".format(proc[0],proc[1],proc[2]))
    print("-" * 70)
    for process in range(3, proc_len):
      command = "ps aux | grep \'{}\' | grep -v \'grep\' | awk '{}' | head -1".format(proc[process],"{print $2}")
      try:
        ref = BatchCMD(proc[0],proc[1],proc[2],22,command)
        if(int(ref)!=-1):
          print("进程: {0:18} \t PID: {1:10} \t 状态: {2}".format(proc[process], int(ref),"√"))
        else:
          print("进程: {0:18} \t PID:{1:10} \t 状态: {2}".format(proc[process], 0,"×"))
      except Exception:
        print("进程: {0:18} \t PID:{1:10} \t 状态: {2}".format(proc[process], 0,"×"))
    print()
  fp.close()


def GetDiskStatus():
  fp = open("unix_disk.db", "r", encoding="utf-8")
  count = len(open("unix_disk.db", "r", encoding="utf-8").readlines())
  for each in range(count):
    proc = eval(fp.readline())
    proc_len = len(proc)
    print("-" * 100)
    print("---> 巡检地址: {0:10} \t 登录系统: {1:7} \t 登录账号: {2:10} 登录密码: {3:10}".
       format(proc[0],proc[1],proc[2],proc[3]))
    print("-" * 100)
    try:
      ref = BatchCMD(proc[0], proc[2], proc[3], 22, "df | grep -v 'Filesystem'")
      st = str(ref).replace("\\n", "\n")
      print(st.replace("b'", "").replace("'", ""))
    except Exception:
      pass
    print()
  fp.close()

# 运行命令
def RunCmd(command,system):
  fp = open("unix_disk.db", "r", encoding="utf-8")
  count = len(open("unix_disk.db", "r", encoding="utf-8").readlines())
  for each in range(count):
    proc = eval(fp.readline())
    proc_len = len(proc)

    if proc[1] == system:
      print("-" * 100)
      print("---> 巡检地址: {0:10} \t 登录系统: {1:7} \t 登录账号: {2:10} 登录密码: {3:10}".
         format(proc[0],proc[1],proc[2],proc[3]))
      print("-" * 100)
      try:
        ref = BatchCMD(proc[0], proc[2], proc[3], 22, command)
        st = str(ref).replace("\\n", "\n")
        print(st.replace("b'", "").replace("'", ""))
      except Exception:
        pass
  fp.close()

面向对象的封装方法: 使用面向对象封装,可极大的提高复用性。

import paramiko

class MySSH:
  def __init__(self,address,username,password,default_port = 22):
    self.address = address
    self.default_port = default_port
    self.username = username
    self.password = password

    self.obj = paramiko.SSHClient()
    self.obj.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    self.obj.connect(self.address,self.default_port,self.username,self.password)
    self.objsftp = self.obj.open_sftp()

  def BatchCMD(self,command):
    stdin , stdout , stderr = self.obj.exec_command(command)
    result = stdout.read()
    if len(result) != 0:
      result = str(result).replace("\\n", "\n")
      result = result.replace("b'", "").replace("'", "")
      return result
    else:
      return None

  def GetRemoteFile(self,remotepath,localpath):
    self.objsftp.get(remotepath,localpath)

  def PutLocalFile(self,localpath,remotepath):
    self.objsftp.put(localpath,remotepath)


  def GetFileSize(self,file_path):
    ref = self.BatchCMD("du -s " + file_path + " | awk '{print $1}'")
    return ref

  def CloseSSH(self):
    self.objsftp.close()
    self.obj.close()

if __name__ == '__main__':
  ssh = MySSH('192.168.191.3','root','1233',22)

  ref = ssh.BatchCMD("ifconfig")
  print(ref)

  sz = ssh.GetFileSize("/etc/passwd")
  print(sz)
  ssh.CloseSSH()
第二次封装完善。

import paramiko,os,json,re

class MySSH:
  def __init__(self,address,username,password,default_port = 22):
    self.address = address
    self.default_port = default_port
    self.username = username
    self.password = password
    try:
      self.obj = paramiko.SSHClient()
      self.obj.set_missing_host_key_policy(paramiko.AutoAddPolicy())

      self.obj.connect(self.address,self.default_port,self.username,self.password,timeout=3,allow_agent=False,look_for_keys=False)
      self.objsftp = self.obj.open_sftp()
    except Exception:
      pass

  def BatchCMD(self,command):
    try:
      stdin , stdout , stderr = self.obj.exec_command(command,timeout=3)
      result = stdout.read()
      if len(result) != 0:
        result = str(result).replace("\\n", "\n")
        result = result.replace("b'", "").replace("'", "")
        return result
      else:
        return None
    except Exception:
      return None

  def GetRemoteFile(self,remote_path,local_path):
    try:
      self.objsftp.get(remote_path,local_path)
      return True
    except Exception:
      return False

  def PutLocalFile(self,localpath,remotepath):
    try:
      self.objsftp.put(localpath,remotepath)
      return True
    except Exception:
      return False

  def CloseSSH(self):
    self.objsftp.close()
    self.obj.close()

  # 获取文件大小
  def GetFileSize(self,file_path):
    ref = self.BatchCMD("du -s " + file_path + " | awk '{print $1}'")
    return ref.replace("\n","")
  # 判断文件是否存在
  def IsFile(self,file_path):
    return self.BatchCMD("[ -e {} ] && echo 'True' || echo 'False'".format(file_path))

通过Eval函数解析执行: 自定义语法规则与函数,通过Eval函数实现解析执行. 没写完,仅供参考。

import json,os,sys,math
import argparse,time,re
import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

def BatchCMD(address,username,password,port,command):
  try:
    ssh.connect(hostname=address,username=username,password=password,port=port,timeout=2)
    stdin , stdout , stderr = ssh.exec_command(command)
    result = stdout.read()
    if len(result) != 0:
      result = str(result).replace("\\n", "\n")
      result = result.replace("b'", "").replace("'", "")
      return result
    else:
      return None
  except Exception:
    return None


# ------------------------------------------------------------------------
# 内置解析方法

def GetDisk(x):
  return str(x)

def GetCPULoad():
  return str(10)

# 句式解析器,解析句子并执行
def judge(string):
  # 如果匹配到IF则执行判断条件解析
  if re.findall(r'IF{ (.*?) }', string, re.M) != []:
    # 则继续提取出他的表达式
    ptr = re.compile(r'IF[{] (.*?) [}]',re.S)
    subject = re.findall(ptr,string)[0]
    subject_list = subject.split(" ")
    # 拼接语句并执行
    sentence = eval(subject_list[0]) + subject_list[1] + subject_list[2]
    # 组合后执行,返回结果
    if eval(sentence):
      return "IF"
    else:
      return False

  # 如果匹配到put则执行上传解析
  elif re.findall(r'PUT{ (.*?) }', string, re.M) != []:
    print("put")
  return False

# 获取特定目录下所有的剧本
def GetAllRule():
  rootdir = os.getcwd() + "\\rule\\"
  all_files = [f for f in os.listdir(rootdir)]
  print("-" * 90)
  print("{0:15} \t {1:10} \t {2:10} \t {3:5} \t {4:5}".format("剧本名称","应用平台","应用端口","执行主机数","命令条数"))
  print("-" * 90)
  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:
            run_addr_count = len(load.get("address_list"))
            command_count = len(load.get("task_sequence"))
            print("{0:15} \t {1:10} {2:10} \t\t {3:5} \t\t {4:5}".
               format(switch,load.get("framework"),load.get("default_port"),run_addr_count,command_count))
      except ValueError:
        pass

# 指定一个剧本并运行
def RunPlayBook(rule_name):
  rootdir = os.getcwd() + "\\rule\\"
  all_files = [f for f in os.listdir(rootdir)]
  for switch in all_files:
    if( switch.endswith(".json") == True):
      all_switch_dir = rootdir + switch
      # 寻找到需要加载的剧本地址
      if( switch == rule_name):
        with open(all_switch_dir , "r" ,encoding="utf-8") as read_file:
          data = json.loads(read_file.read())
          address_list = data.get("address_list")
          # 循环每个主机任务
          for each in address_list:
            # 得到剧本内容
            task_sequence = data.get("task_sequence")
            default_port = data.get("default_port")
            print("-" * 90)
            print("地址: {0:15} 用户名: {1:10} 密码: {2:10}".format(each[0],each[1],each[2]))
            print("-" * 90)
            for task in task_sequence:
              flag = judge(task[0])
              if flag == "IF":
                ref = BatchCMD(each[0],each[1],each[2],default_port,task[1])
                print(ref)
              elif flag == False:
                ref = BatchCMD(each[0],each[1],each[2],default_port,task[0])
                print(ref)

if __name__ == "__main__":
  RunPlayBook("get_log.json")

定义剧本规范如下。

{
 "framework": "Centos",
 "version": "7.0",
 "address_list":
 [
  ["192.168.191.3","root","1233"]
 ],
 "default_port": "22",
 "task_sequence":
 [
  ["ifconfig"],
  ["IF{ GetLastCmdFlag() == True }","uname"]
 ]
}

词法分析: 词法分析解析剧本内容。

# 获取特定目录下所有的剧本
def GetAllRule():
  rootdir = os.getcwd() + "\\rule\\"
  all_files = [f for f in os.listdir(rootdir)]
  print("-" * 90)
  print("{0:15} \t {1:10} \t {2:10} \t {3:5} \t {4:5}".format("剧本名称","应用平台","应用端口","执行主机数","命令条数"))
  print("-" * 90)
  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:
            run_addr_count = len(load.get("address_list"))
            command_count = len(load.get("task_sequence"))
            print("{0:15} \t {1:10} {2:10} \t\t {3:5} \t\t {4:5}".
               format(switch,load.get("framework"),load.get("default_port"),run_addr_count,command_count))
      except ValueError:
        pass

# 句式解析器,解析句子并执行
def judge(string):
  # 如果匹配到IF则执行判断条件解析
  if re.findall(r'IF{ (.*?) }', string, re.M) != []:
    # 则继续提取出他的表达式
    ptr = re.compile(r'IF[{] (.*?) [}]',re.S)
    subject = re.findall(ptr,string)[0]
    subject_list = subject.split(" ")

    # 公开接口,执行命令
    ssh = MySSH("192.168.191.3","root","1233","22")

    # 组合命令并执行
    sentence = str(eval(subject_list[0]) + subject_list[1] + subject_list[2])
    if eval(sentence):
      return "IF",ssh
    else:
      return False

  # 如果匹配到put则执行上传解析
  elif re.findall(r'PUT{ (.*?) }', string, re.M) != []:
    print("put")
  return False

# 指定一个剧本并运行
def RunPlayBook(rule_name):
  rootdir = os.getcwd() + "\\rule\\"
  all_files = [f for f in os.listdir(rootdir)]
  for switch in all_files:
    if( switch.endswith(".json") == True):
      all_switch_dir = rootdir + switch
      # 寻找到需要加载的剧本地址
      if( switch == rule_name):
        with open(all_switch_dir , "r" ,encoding="utf-8") as read_file:
          data = json.loads(read_file.read())
          address_list = data.get("address_list")
          # 循环每个主机任务
          for each in address_list:
            # 得到剧本内容
            task_sequence = data.get("task_sequence")
            default_port = data.get("default_port")
            print("-" * 90)
            print("地址: {0:15} 用户名: {1:10} 密码: {2:10}".format(each[0],each[1],each[2]))
            print("-" * 90)
            for task in task_sequence:

              flag,obj = judge(task[0])

              if flag == "IF":
                ret = obj.BatchCMD(task[1])
                print(ret)
if __name__ == '__main__':
  ret = judge("IF{ ssh.GetFileSize('/etc/passwd') >= 4 }")
  print(ret)

MySSH类最终封装: 通过面向对象对其进行封装,实现了查询CPU,负载,内存利用率,磁盘容量,等通用数据的获取。

import paramiko, math,json

class MySSH:
  def __init__(self, address, username, password, default_port):
    self.address = address
    self.default_port = default_port
    self.username = username
    self.password = password
  # 初始化,远程模块
  def Init(self):
    try:
      self.ssh_obj = paramiko.SSHClient()
      self.ssh_obj.set_missing_host_key_policy(paramiko.AutoAddPolicy())
      self.ssh_obj.connect(self.address, self.default_port, self.username, self.password, timeout=3,
                 allow_agent=False, look_for_keys=False)
      self.sftp_obj = self.ssh_obj.open_sftp()
    except Exception:
      return False
  # 执行非交互命令
  def BatchCMD(self, command):
    try:
      stdin, stdout, stderr = self.ssh_obj.exec_command(command, timeout=3)
      result = stdout.read()
      if len(result) != 0:
        result = str(result).replace("\\n", "\n")
        result = result.replace("b'", "").replace("'", "")
        return result
      else:
        return None
    except Exception:
      return None
  # 将远程文件下载到本地
  def GetRemoteFile(self, remote_path, local_path):
    try:
      self.sftp_obj.get(remote_path, local_path)
      return True
    except Exception:
      return False
  # 将本地文件上传到远程
  def PutLocalFile(self, localpath, remotepath):
    try:
      self.sftp_obj.put(localpath, remotepath)
      return True
    except Exception:
      return False
  # 关闭接口
  def CloseSSH(self):
    try:
      self.sftp_obj.close()
      self.ssh_obj.close()
    except Exception:
      pass
  # 获取文件大小
  def GetFileSize(self, file_path):
    ref = self.BatchCMD("du -s " + file_path + " | awk '{print $1}'")
    return ref.replace("\n", "")
  # 判断文件是否存在
  def IsFile(self, file_path):
    return self.BatchCMD("[ -e {} ] && echo 'True' || echo 'False'".format(file_path))
  # 获取系统型号
  def GetSystemVersion(self):
    return self.BatchCMD("uname")
  # 检测目标主机存活状态
  def GetPing(self):
    try:
      if self.GetSystemVersion() != None:
        return True
      else:
        return False
    except Exception:
      return False
  # 获取文件列表,并得到大小
  def GetFileList(self, path):
    try:
      ref_list = []
      self.sftp_obj.chdir(path)
      file_list = self.sftp_obj.listdir("./")
      for sub_path in file_list:
        dict = {}
        file_size = self.GetFileSize(path + sub_path)
        dict[path + sub_path] = file_size
        ref_list.append(dict)
      return ref_list
    except Exception:
      return False
  # 将远程文件全部打包后拉取到本地
  def GetTarPackageAll(self, path):
    try:
      file_list = self.sftp_obj.listdir(path)
      self.sftp_obj.chdir(path)
      for packageName in file_list:
        self.ssh_obj.exec_command("tar -czf /tmp/{0}.tar.gz {0}".format(packageName))
        self.sftp_obj.get("/tmp/{}.tar.gz".format(packageName), "./file/{}.tar.gz".format(packageName))
        self.sftp_obj.remove("/tmp/{}.tar.gz".format(packageName))
        return True
    except Exception:
      return True
  # 获取磁盘空间并返回字典
  def GetAllDiskSpace(self):
    ref_dict = {}
    cmd_dict = {"Linux\n": "df | grep -v 'Filesystem' | awk '{print $5 \":\" $6}'",
          "AIX\n": "df | grep -v 'Filesystem' | awk '{print $4 \":\" $7}'"
          }
    try:
      os_version = self.GetSystemVersion()
      for version, run_cmd in cmd_dict.items():
        if (version == os_version):
          # 根据不同版本选择不同的命令
          os_ref = self.BatchCMD(run_cmd)
          ref_list = os_ref.split("\n")
          # 循环将其转换为字典
          for each in ref_list:
            # 判断最后是否为空,过滤最后一项
            if each != "":
              ref_dict[str(each.split(":")[1])] = str(each.split(":")[0])
      return ref_dict
    except Exception:
      return False
  # 获取系统内存利用率
  def GetAllMemSpace(self):
    cmd_dict = {"Linux\n": "cat /proc/meminfo | head -n 2 | awk '{print $2}' | xargs | awk '{print $1 \":\" $2}'",
          "AIX\n": "svmon -G | grep -v 'virtual' | head -n 1 | awk '{print $2 \":\" $4}'"
          }
    try:
      os_version = self.GetSystemVersion()
      for version, run_cmd in cmd_dict.items():
        if (version == os_version):
          # 根据不同版本选择不同的命令
          os_ref = self.BatchCMD(run_cmd)
          # 首先现将KB转化为MB
          mem_total = math.ceil(int(os_ref.split(":")[0].replace("\n", "")) / 1024)
          mem_free = math.ceil(int(os_ref.split(":")[1].replace("\n", "")) / 1024)

          # 计算占用空间百分比
          percentage = 100 - int(mem_free / int(mem_total / 100))
          # 拼接字典数据
          return {"Total": str(mem_total), "Free": str(mem_free), "Percentage": str(percentage)}
    except Exception:
      return False
  # 获取系统进程信息,并返回字典格式
  def GetAllProcessSpace(self):
    ref_dict = {}
    cmd_dict = {"Linux\n": "ps aux | grep -v 'USER' | awk '{print $2 \":\" $11}' | uniq",
          "AIX\n": "ps aux | grep -v 'USER' | awk '{print $2 \":\" $12}' | uniq"
          }
    try:
      os_version = self.GetSystemVersion()
      for version, run_cmd in cmd_dict.items():
        if (version == os_version):
          os_ref = self.BatchCMD(run_cmd)
          ref_list = os_ref.split("\n")
          for each in ref_list:
            if each != "":
              ref_dict[str(each.split(":")[0])] = str(each.split(":")[1])
      return ref_dict
    except Exception:
      return False
  # 获取CPU利用率
  def GetCPUPercentage(self):
    ref_dict = {}
    cmd_dict = {"Linux\n": "vmstat | tail -n 1 | awk '{print $13 \":\" $14 \":\" $15}'",
          "AIX\n": "vmstat | tail -n 1 | awk '{print $14 \":\" $15 \":\" $16}'"
          }
    try:
      os_version = self.GetSystemVersion()
      for version, run_cmd in cmd_dict.items():
        if (version == os_version):
          os_ref = self.BatchCMD(run_cmd)
          ref_list = os_ref.split("\n")
          for each in ref_list:
            if each != "":
              each = each.split(":")
              ref_dict = {"us": each[0],"sys":each[1],"idea":each[2]}
      return ref_dict
    except Exception:
      return False
  # 获取机器的负载情况
  def GetLoadAVG(self):
    ref_dict = {}
    cmd_dict = {"Linux\n": "uptime | awk '{print $10 \":\" $11 \":\" $12}'",
          "AIX\n": "uptime | awk '{print $10 \":\" $11 \":\" $12}'"
          }
    try:
      os_version = self.GetSystemVersion()
      for version, run_cmd in cmd_dict.items():
        if (version == os_version):
          os_ref = self.BatchCMD(run_cmd)
          ref_list = os_ref.split("\n")
          for each in ref_list:
            if each != "":
              each = each.replace(",","").split(":")
              ref_dict = {"1avg": each[0],"5avg": each[1],"15avg": each[2]}
              return ref_dict
      return False
    except Exception:
      return False
  # 修改当前用户密码
  def SetPasswd(self,username,password):
    try:
      os_id = self.BatchCMD("id | awk {'print $1'}")
      print(os_id)
      if(os_id == "uid=0(root)\n"):
        self.BatchCMD("echo '{}' | passwd --stdin '{}' > /dev/null".format(password,username))
        return True
    except Exception:
      return False

# 定义超类,集成基类MySSH
class SuperSSH(MySSH):
  def __init__(self,address, username, password, default_port):
    super(SuperSSH, self).__init__(address, username, password, default_port)

我们继续为上面的代码加上命令行,让其可以直接使用,这里需要遵循一定的格式规范,我们使用JSON解析数据,JSON格式如下.

{
 "aix":
 [
  ["192.168.1.1","root","123123"],
  ["192.168.1.1","root","2019"],
 ],
 "suse":
 [
  ["192.168.1.1","root","123123"],
 ],
 "centos":
  [
  ["192.168.1.1","root","123123"],
  ]
}

接着是主程序代码,如下所示.

# -*- coding: utf-8 -*-
from MySSH import MySSH
import json,os,sys,argparse

class InitJson():
  def __init__(self,db):
    self.db_name = db
  def GetPlatform(self,plat):
    with open(self.db_name, "r", encoding="utf-8") as Read_Pointer:
      load_json = json.loads(Read_Pointer.read())
      for k,v in load_json.items():
        try:
          if k == plat:
            return v
        except Exception:
          return None
    return None

if __name__ == "__main__":
  ptr = InitJson("database.json")
  parser = argparse.ArgumentParser()

  parser.add_argument("-G","--group",dest="group",help="指定主机组")
  parser.add_argument("-C","--cmd",dest="cmd",help="指定CMD命令")
  parser.add_argument("--get",dest="get",help="指定获取数据类型<ping>")
  parser.add_argument("--dst", dest="dst_file",help="目标位置")
  parser.add_argument("--src",dest="src_file",help="原文件路径")
  args = parser.parse_args()

  # 批量CMD --group=aix --cmd=ls
  if args.group and args.cmd:
    platform = ptr.GetPlatform(args.group)
    success,error = [],[]
    for each in platform:
      ssh = MySSH(each[0], each[1], each[2], 22)
      if ssh.Init() != False:
        print("-" * 140)
        print("主机: {0:15} \t 账号: {1:10} \t 密码: {2:10} \t 命令: {3:30}".
           format(each[0], each[1], each[2], args.cmd))
        print("-" * 140)
        print(ssh.BatchCMD(args.cmd))
        ssh.CloseSSH()
        success.append(each[0])
      else:
        error.append(each[0])
        ssh.CloseSSH()
    print("\n\n","-" * 140,
       "\n 执行报告 \n",
       "-" * 140,
       "\n失败主机: {}\n".format(error),
       "-" * 140)

  # 批量获取主机其他数据 --group=centos --get=ping
  if args.group and args.get:
      platform = ptr.GetPlatform(args.group)
      success, error = [], []
      for each in platform:
        ssh = MySSH(each[0], each[1], each[2], 22)
        # 判断是否为ping
        if ssh.Init() != False:
          if args.get == "ping":
            ret = ssh.GetPing()
            if ret == True:
              print("[*] 主机: {} 存活中.".format(each[0]))

          # 收集磁盘数据
          elif args.get == "disk":
            print("-" * 140)
            print("主机: {0:15} \t 账号: {1:10} \t 密码: {2:10}".
               format(each[0], each[1], each[2]))
            print("-" * 140)

            ret = ssh.GetAllDiskSpace()
            for k, v in ret.items():
              if (v.split("%")[0] != "-"):
                space_ret = int(v.split("%")[0])
                if space_ret >= 70:
                  print("磁盘分区: {0:30} \t 磁盘占用: {1:5} -> [警告]".format(k, v))
                  continue
                if space_ret >= 50:
                  print("磁盘分区: {0:30} \t 磁盘占用: {1:5} -> [警惕]".format(k, v))
                  continue
                else:
                  print("磁盘分区: {0:30} \t 磁盘占用: {1:5}".format(k, v))
                  continue
            print()
        else:
          error.append(each[0])
          ssh.CloseSSH()
      print("\n\n", "-" * 140,
         "\n 执行报告 \n",
         "-" * 140,
         "\n失败主机: {}\n".format(error),
         "-" * 140)

  # 实现文件上传过程 --group=centos --src=./a.txt --dst=/tmp/test.txt
  if args.group and args.src_file and args.dst_file:
    platform = ptr.GetPlatform(args.group)
    success, error = [], []
    for each in platform:
      ssh = MySSH(each[0], each[1], each[2], 22)
      if ssh.Init() != False:
        ret = ssh.PutLocalFile(args.src_file,args.dst_file)
        if ret == True:
          print("主机: {} \t 本地文件: {} \t ---> 传到: {}".format(each[0], args.src_file,args.dst_file))
      else:
        error.append(each[0])
        ssh.CloseSSH()
    print("\n\n", "-" * 140,
       "\n 执行报告 \n",
       "-" * 140,
       "\n失败主机: {}\n".format(error),
       "-" * 140)

简单的使用命令:

远程CMD: python main.py --group=centos --cmd="free -h | grep -v 'total'"

Python如何实现Paramiko的二次封装

判断存活: python main.py --group=centos --get="ping"

Python如何实现Paramiko的二次封装

拉取磁盘:python main.py --group=suse --get="disk"

Python如何实现Paramiko的二次封装

批量上传文件: python main.py --group=suse --src="./aaa" --dst="/tmp/bbb.txt"

Python如何实现Paramiko的二次封装

由于我的设备少,所以没开多线程,担心开多线程对目标造成过大压力,也没啥必要。

番外: 另外我研究了一个主机分组的小工具,加上命令执行代码量才800行,实现了一个分组数据库,在这里记下使用方法。

默认运行进入一个交互式shell环境。

Init = 初始化json文件,ShowHostList=显示所有主机,ShowGroup=显示所有组,ShowAllGroup=显示所有主机包括组。

Python如何实现Paramiko的二次封装

添加修改与删除记录,命令如下。

Python如何实现Paramiko的二次封装

添加删除主机组。

Python如何实现Paramiko的二次封装

通过UUID向主机组中添加或删除主机记录。

Python如何实现Paramiko的二次封装

测试主机组连通性。

Python如何实现Paramiko的二次封装

以上就是Python如何实现Paramiko的二次封装的详细内容,更多关于Python实现Paramiko的二次封装的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python魔术方法详解
Feb 14 Python
在Python中利用Into包整洁地进行数据迁移的教程
Mar 30 Python
wxPython定时器wx.Timer简单应用实例
Jun 03 Python
详解使用Python处理文件目录的相关方法
Oct 16 Python
python 字典 按key值大小 倒序取值的实例
Jul 06 Python
python实现C4.5决策树算法
Aug 29 Python
对python读取CT医学图像的实例详解
Jan 24 Python
Django框架模板文件使用及模板文件加载顺序分析
May 23 Python
python动态进度条的实现代码
Jul 03 Python
Python 转换文本编码实现解析
Aug 27 Python
python3使用GUI统计代码量
Sep 18 Python
Python Pillow(PIL)库的用法详解
Sep 19 Python
python实现按日期归档文件
Jan 30 #Python
python使用Windows的wmic命令监控文件运行状况,如有异常发送邮件报警
Jan 30 #Python
如何用Django处理gzip数据流
Jan 29 #Python
Spy++的使用方法及下载教程
Jan 29 #Python
Python实现随机爬山算法
Jan 29 #Python
用pushplus+python监控亚马逊到货动态推送微信
Jan 29 #Python
用python监控服务器的cpu,磁盘空间,内存,超过邮件报警
Jan 29 #Python
You might like
PHP的变量总结 新手推荐
2011/04/18 PHP
PHP中的str_repeat函数在JavaScript中的实现
2013/09/16 PHP
win平台安装配置Nginx+php+mysql 环境
2016/01/12 PHP
浅谈PHP匿名函数和闭包
2019/03/08 PHP
PHP简单实现图片格式转换(jpg转png,gif转png等)
2019/10/30 PHP
js停止输出代码
2008/07/20 Javascript
jquery DOM操作 基于命令改变页面
2010/05/06 Javascript
js分页代码分享
2014/04/28 Javascript
javascript实现一个数值加法函数
2015/06/26 Javascript
Javascript的表单验证-揭开正则表达式的面纱
2016/03/18 Javascript
jquery弹出遮掩层效果【附实例代码】
2016/04/28 Javascript
js处理层级数据结构的方法小结
2017/01/17 Javascript
js 性能优化之算法和流程控制
2017/02/15 Javascript
JavaScript 事件流、事件处理程序及事件对象总结
2017/04/01 Javascript
import与export在node.js中的使用详解
2017/09/28 Javascript
微信小程序自定义组件的实现方法及自定义组件与页面间的数据传递问题
2018/10/09 Javascript
微信小程序使用字体图标的方法
2019/05/23 Javascript
vue项目中常见问题及解决方案(推荐)
2019/10/21 Javascript
JavaScript实现轮播图片完整代码
2020/03/07 Javascript
[01:35]2018完美盛典章节片——共竞
2018/12/17 DOTA
python通过elixir包操作mysql数据库实例代码
2018/01/31 Python
python调用系统ffmpeg实现视频截图、http发送
2018/03/06 Python
django迁移数据库错误问题解决
2019/07/29 Python
解决Pytorch 训练与测试时爆显存(out of memory)的问题
2019/08/20 Python
Pyinstaller 打包发布经验总结
2020/06/02 Python
Pandas的Apply函数具体使用
2020/07/21 Python
Django filter动态过滤与排序实现过程解析
2020/11/26 Python
草莓网化妆品日本站:Strawberrynet日本
2017/10/20 全球购物
技术学校毕业生求职信分享
2013/12/02 职场文书
学习十八大报告感言
2014/02/28 职场文书
文明寝室申报材料
2014/05/12 职场文书
单位工作证明范文
2014/09/14 职场文书
2014年惩防体系建设工作总结
2014/12/01 职场文书
旷工检讨书大全
2015/08/15 职场文书
什么是执行力?9个故事告诉您:成功绝非偶然!
2019/07/05 职场文书
关于React Native使用axios进行网络请求的方法
2021/08/02 Javascript