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的线程来解决生产者消费问题的示例
Apr 02 Python
简介Django框架中可使用的各类缓存
Jul 23 Python
python 读写txt文件 json文件的实现方法
Oct 22 Python
python队列通信:rabbitMQ的使用(实例讲解)
Dec 22 Python
python爬虫中get和post方法介绍以及cookie作用
Feb 08 Python
python TCP Socket的粘包和分包的处理详解
Feb 09 Python
PyQt弹出式对话框的常用方法及标准按钮类型
Feb 27 Python
Django中使用Whoosh进行全文检索的方法
Mar 31 Python
python根据时间获取周数代码实例
Sep 30 Python
python文件操作的简单方法总结
Nov 07 Python
python 伯努利分布详解
Feb 25 Python
使用Keras构造简单的CNN网络实例
Jun 29 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
S900/ ETON E1-XM 收音机
2021/03/02 无线电
纯真IP数据库的应用 IP地址转化成十进制
2009/06/14 PHP
php通过array_merge()函数合并两个数组的方法
2015/03/18 PHP
微信公众号支付之坑:调用支付jsapi缺少参数 timeStamp等错误解决方法
2016/01/12 PHP
thinkphp框架使用JWTtoken的方法详解
2019/10/10 PHP
Yii框架小部件(Widgets)用法实例详解
2020/05/15 PHP
JQuery 实现的页面滚动时浮动窗口控件
2009/07/10 Javascript
JQuery 选择和过滤方法代码总结
2010/11/19 Javascript
js限制文本框输入长度两种限制方式(长度、字节数)
2012/12/19 Javascript
JS高级调试技巧:捕获和分析 JavaScript Error详解
2014/03/16 Javascript
jquery库或JS文件在eclipse下报错问题解决方法
2014/04/17 Javascript
jQuery中:eq()选择器用法实例
2014/12/29 Javascript
javascript实现点击按钮弹出一个可关闭层窗口同时网页背景变灰的方法
2015/05/13 Javascript
JavaScript中的Math.atan2()方法使用详解
2015/06/15 Javascript
原生js实现ajax方法(超简单)
2016/09/20 Javascript
jQuery选择器实例应用
2017/01/05 Javascript
Bootstrap学习笔记 轮播(Carousel)插件
2017/03/21 Javascript
Vue项目webpack打包部署到服务器的实例详解
2017/07/17 Javascript
javascript基本常用排序算法解析
2017/09/27 Javascript
Node.js中DNS模块学习总结
2018/02/28 Javascript
vue.js层叠轮播效果的实例代码
2018/11/08 Javascript
javascript设计模式 ? 迭代器模式原理与用法实例分析
2020/04/17 Javascript
python opencv旋转图像(保持图像不被裁减)
2018/07/26 Python
django 实现编写控制登录和访问权限控制的中间件方法
2019/01/15 Python
PyCharm 2020 激活到 2100 年的教程
2020/03/25 Python
django实现将修改好的新模型写入数据库
2020/03/31 Python
HTML5打开手机扫码功能及优缺点
2017/11/27 HTML / CSS
荷兰DOD药房中文官网:DeOnlineDrogist
2020/12/27 全球购物
STP协议的主要用途是什么?为什么要用STP
2012/12/20 面试题
物流专业大学生求职信范文
2013/10/28 职场文书
大专毕业生自我鉴定
2013/11/21 职场文书
中专生求职自荐信范文
2013/12/22 职场文书
岗位廉洁从政承诺书
2014/03/27 职场文书
2014年驻村干部工作总结
2014/11/17 职场文书
奠基仪式致辞
2015/07/30 职场文书
Elasticsearch 配置详解
2022/04/19 Java/Android