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使用ctypes模块调用windowsapi获取系统版本示例
Apr 17 Python
Django开发中的日志输出的方法
Jul 02 Python
ubuntu16.04制作vim和python3的开发环境
Sep 23 Python
Python GUI布局尺寸适配方法
Oct 11 Python
python实现大转盘抽奖效果
Jan 22 Python
解决py2exe打包后,总是多显示一个DOS黑色窗口的问题
Jun 21 Python
Python绘图Matplotlib之坐标轴及刻度总结
Jun 28 Python
python3+opencv生成不规则黑白mask实例
Feb 19 Python
利用4行Python代码监测每一行程序的运行时间和空间消耗
Apr 22 Python
python库skimage给灰度图像染色的方法示例
Apr 27 Python
Python Selenium异常处理的实例分析
Feb 28 Python
python使用openpyxl库读写Excel表格的方法(增删改查操作)
May 02 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截取后台登陆密码的代码
2012/05/05 PHP
使用PHP下载CSS文件中的所有图片【几行代码即可实现】
2016/12/14 PHP
PHP与Web页面的交互示例详解二
2020/08/04 PHP
JQuery 构建客户/服务分离的链接模型中Table中的排序分析
2010/01/22 Javascript
使用jquery获取网页中图片高度的两种方法
2013/09/26 Javascript
js阻止事件追加的具体实现
2014/10/15 Javascript
基于javascript实现句子翻牌网页版小游戏
2016/03/23 Javascript
js中json处理总结之JSON.parse
2016/10/14 Javascript
AngularJS入门教程之Helloworld示例
2016/12/25 Javascript
jQuery的中 is(':visible') 解析及用法(必看)
2017/02/12 Javascript
详解vue.js全局组件和局部组件
2017/04/10 Javascript
使用JavaScript进行表单校验功能
2017/08/01 Javascript
详解如何用模块化的方式写vuejs
2017/12/16 Javascript
使用Bootstrap4 + Vue2实现分页查询的示例代码
2017/12/21 Javascript
浏览器调试动态js脚本的方法(图解)
2018/01/19 Javascript
jQuery实现新闻播报滚动及淡入淡出效果示例
2018/03/23 jQuery
layui自定义插件citySelect实现省市区三级联动选择
2019/07/26 Javascript
如何基于viewport vm适配移动端页面
2020/11/13 Javascript
[53:13]DOTA2-DPC中国联赛 正赛 DLG vs PHOENIX BO3 第三场 1月18日
2021/03/11 DOTA
Python选课系统开发程序
2016/09/02 Python
解决Jupyter无法导入已安装的 module问题
2020/04/17 Python
Django 解决阿里云部署同步数据库报错的问题
2020/05/14 Python
基于TensorFlow的CNN实现Mnist手写数字识别
2020/06/17 Python
OpenCV Python实现图像指定区域裁剪
2021/03/12 Python
Html5 APP中监听返回事件处理的方法示例
2018/03/15 HTML / CSS
健康监测猫砂:Pretty Litter
2017/05/25 全球购物
法国发饰品牌:Alexandre De Paris
2018/12/04 全球购物
升职演讲稿范文
2014/05/23 职场文书
学校运动会霸气口号
2014/06/07 职场文书
四风自我剖析材料
2014/09/30 职场文书
2014社会治安综合治理工作总结
2014/12/04 职场文书
2016年圣诞节义工活动总结
2016/04/01 职场文书
导游词范文之颐和园/重庆/云台山
2019/09/10 职场文书
Python快速实现一键抠图功能的全过程
2021/06/29 Python
MySQL系列之四 SQL语法
2021/07/02 MySQL
小程序与后端Java接口交互实现HelloWorld入门
2021/07/09 Java/Android