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  __getattr__与__setattr__使用方法
Sep 06 Python
Python读写文件方法总结
Jun 09 Python
django中的setting最佳配置小结
Nov 21 Python
Python Paramiko模块的使用实际案例
Feb 01 Python
详解python3中tkinter知识点
Jun 21 Python
python 输入一个数n,求n个数求乘或求和的实例
Nov 13 Python
pycharm 实现显示project 选项卡的方法
Jan 17 Python
opencv实现静态手势识别 opencv实现剪刀石头布游戏
Jan 22 Python
在Python中os.fork()产生子进程的例子
Aug 08 Python
Python3标准库之dbm UNIX键-值数据库问题
Mar 24 Python
Python气泡提示与标签的实现
Apr 01 Python
使用Python Tkinter实现剪刀石头布小游戏功能
Oct 23 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
解析CI即CodeIgniter框架在Nginx下的重写规则
2013/06/03 PHP
php+ajax实现无刷新动态加载数据技术
2015/04/28 PHP
jquery判断单个复选框是否被选中的代码
2009/09/03 Javascript
jQuery UI Autocomplete 体验分享
2012/02/14 Javascript
JS获取下拉列表所选中的TEXT和Value的实现代码
2014/01/11 Javascript
禁用JavaScript控制台调试的方法
2014/03/07 Javascript
两种方法实现在HTML页面加载完毕后运行某个js
2014/06/16 Javascript
node.js中的fs.chown方法使用说明
2014/12/16 Javascript
Bootstrap每天必学之导航
2015/11/26 Javascript
JavaScript获取IP获取的是IPV6 如何校验
2016/06/12 Javascript
基于Angularjs+mybatis实现二级评论系统(仿简书)
2017/02/13 Javascript
webpack配置文件和常用配置项介绍
2017/04/28 Javascript
基于vue-cli配置lib-flexible + rem实现移动端自适应
2017/12/26 Javascript
基于Vue实现拖拽效果
2018/04/27 Javascript
javascript中call,apply,callee,caller用法实例分析
2019/07/24 Javascript
Vue的Eslint配置文件eslintrc.js说明与规则介绍
2020/02/03 Javascript
JavaScript监听一个DOM元素大小变化
2020/04/26 Javascript
微信小程序绘制半圆(弧形)进度条
2020/11/18 Javascript
使用C#配合ArcGIS Engine进行地理信息系统开发
2016/02/19 Python
python 获取网页编码方式实现代码
2017/03/11 Python
python编写简易聊天室实现局域网内聊天功能
2018/07/28 Python
Python除法之传统除法、Floor除法及真除法实例详解
2019/05/23 Python
python中metaclass原理与用法详解
2019/06/25 Python
Python中BeautifuSoup库的用法使用详解
2019/11/15 Python
tensorflow模型的save与restore,及checkpoint中读取变量方式
2020/05/26 Python
使用python matploblib库绘制准确率,损失率折线图
2020/06/16 Python
一款纯css3实现简单的checkbox复选框和radio单选框
2014/11/05 HTML / CSS
详解通过变换矩阵实现canvas的缩放功能
2019/01/14 HTML / CSS
将HTML5 Canvas的内容保存为图片借助toDataURL实现
2013/05/20 HTML / CSS
苹果Mac升级:MacSales.com
2017/11/20 全球购物
诺思信科技(南京)有限公司.NET笔试题答案
2013/07/06 面试题
经典优秀个人求职自荐信格式
2013/09/25 职场文书
八年级语文教学反思
2016/03/03 职场文书
mysql在项目中怎么选事务隔离级别
2021/05/25 MySQL
python如何利用traceback获取详细的异常信息
2021/06/05 Python
Redis实战之Lettuce的使用技巧详解
2022/12/24 Redis