1.配置文件host.conf
[webserver]
host1=192.168.1.10,root,123456,22
host2=192.168.1.11,root,123456,22
[dbserver]
db1=192.168.1.12,root,123456,22
db2=192.168.1.13,root,123456,22

2.ssh远程命令脚本ssh.py
#!/usr/bin/python
# --*-- coding: utf-8 --*--
import paramiko
import os, sys
import configparser
from multiprocessing import Process, Lock
from optparse import OptionParser   #解析命令行参数
"""
命令行格式如下:
python.exe ssh.py -H host1,host2 -G webserver,dbserver -C "free -m"   # -C 后面命令必须双引号
python.exe ssh.py -H host1,host2  -C "df -Th"
python.exe ssh.py -G webserver,dbserver -C "ls -l"
"""

#加载解析配置文件
home_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
conf_file = home_dir + '\\conf\\cmd.conf'
config = configparser.ConfigParser()
config.read(conf_file)
host1_info = config.get('webserver', 'host1').split(',')
host2_info = config.get('webserver', 'host2').split(',')
db1_info = config.get('dbserver', 'db1').split(',')
db2_info = config.get('dbserver', 'db2').split(',')
sections_list = config.sections()
#print(config.options('dbserver'))
host_dict = {'host1': host1_info, 'host2': host2_info, 'db1': db1_info, 'db2': db2_info}

class MyProcess(Process):
    def __init__(self, hostname, username, password, cmd, name, lock):
        super().__init__()
        self.hostname = hostname
        self.username = username
        self.password = password
        self.cmd = cmd
        self.name = name
        self.lock = lock

    def run(self):
        with self.lock:
            try:
                ssh = paramiko.SSHClient()
                # ssh.load_system_host_keys()
                ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)  # 不使用密钥认证
                ssh.connect(hostname=self.hostname, username=self.username, password=self.password)
                stdin, stdout, stderr = ssh.exec_command(self.cmd)  # 执行远程命令
                print('%s 主机开始执行命令%s' % (self.name,self.cmd))
                print(stdout.read().decode('utf-8'))
                ssh.close()
                print('%s主机执行结束\n' %self.name)
            except Exception as e:
                print(e)

def cmd_parser():
    """命令行解析"""
    parser = OptionParser()
    parser.add_option("-H", "--host", dest="host", help="执行远程命令的主机")
    parser.add_option("-G", "--group", dest="group", help="执行远程命令的主机组")
    parser.add_option("-C", "--command", dest="command", help="需要执行的远程命令")
    (options, _) = parser.parse_args()

    hosts = []
    if options.host:
        temp_hosts = options.host.split(',')
        for i in temp_hosts:
            if i in host_dict:
                    hosts.append(i)

    groups = []
    if options.group:
        temp_groups = options.group.split(',')
        if temp_groups:
            for i in temp_groups:
                if i in sections_list:
                    groups.append(i)
    temp_group_list = []
    if groups:
        for i in groups:
            temp_group_list += config.options(i)

    if options.command:
        cmd = options.command
    else:
        print('命令不存在,退出程序')
        sys.exit(1)

    return (hosts, temp_group_list, cmd)


if __name__ == '__main__':
    lock = Lock()
    cmd_tuple = cmd_parser()
    hosts = cmd_tuple[0]
    groups = cmd_tuple[1]
    cmd = cmd_tuple[2]
    #print(cmd_tuple)
    p_l = []
    if hosts:
        for host in hosts:
            p = MyProcess(host_dict[host][0],host_dict[host][1],host_dict[host][2],cmd,host,lock)
            p.daemon = True
            p.start()
            p_l.append(p)
    if groups:
        for host in groups:
            p1 = MyProcess(host_dict[host][0],host_dict[host][1],host_dict[host][2],cmd,host,lock)
            p.daemon = True
            p1.start()
            p_l.append(p1)

    for i in p_l:
        i.join()

    print("所有进程执行结束")

    
sftp上传下载文件脚本
#!/usr/bin/python
# --*-- coding: utf-8 --*--
import paramiko
import os, sys
import configparser
from optparse import OptionParser
from multiprocessing import Process,Lock

"""
命令行格式:
本地是windows主机路径,远端是linux主机路径
上传文件执行格式
python.exe sftp.py -H host1,host2 -G webserver,dbserver -a put -l E:\python\oldboyday9\homework\bin\hyh.txt -r /home/kkjr/hyh.txt
python.exe sftp.py -H host1,host2 -a put -l E:\python\oldboyday9\homework\bin\hyh.txt -r /home/kkjr/hyh.txt
python.exe sftp.py -G webserver,dbserver -a put -l E:\python\oldboyday9\homework\bin\hyh.txt -r /home/kkjr/hyh.txt

下载文件执行命令
python.exe sftp.py -H host1 -a get -l E:\python\oldboyday9\homework\bin\hyh.txt -r /home/kkjr/hyh.txt
"""
bin_dir = os.path.dirname(os.path.abspath(__file__))    #上传下载文件存放目录设置成bin目录

#加载解析配置文件
home_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
conf_file = home_dir + '\\conf\\cmd.conf'
config = configparser.ConfigParser()
config.read(conf_file)
host1_info = config.get('webserver', 'host1').split(',')
host2_info = config.get('webserver', 'host2').split(',')
db1_info = config.get('dbserver', 'db1').split(',')
db2_info = config.get('dbserver', 'db2').split(',')
sections_list = config.sections()
#print(config.options('dbserver'))
host_dict = {'host1': host1_info, 'host2': host2_info, 'db1': db1_info, 'db2': db2_info}


class MyProcess(Process):
    def __init__(self, hostname, username, password, port, cmd, localpath, remotepath):
        super().__init__()
        self.hostname = hostname
        self.username = username
        self.password = password
        self.port = int(port)
        self.cmd = cmd
        self.localpath = localpath
        self.remotepath = remotepath


    def run(self):
        """上传下载文件"""
        try:
            t = paramiko.Transport((self.hostname, self.port))
            t.connect(username=self.username, password=self.password)
            sftp = paramiko.SFTPClient.from_transport(t)
            if self.cmd == 'put':
                sftp.put(self.localpath, self.remotepath)
            elif self.cmd == 'get':
                sftp.get(self.remotepath, self.localpath)
            else:
                print('命令不存在')
                sys.exit(1)
        except Exception as e:
            print(e)

def cmd_parser():
    """命令行解析"""
    parser = OptionParser()
    parser.add_option("-H", "--host", dest="host", help="执行远程命令的主机")
    parser.add_option("-G", "--group", dest="group", help="执行远程命令的主机组")
    parser.add_option("-a", "--action", dest="action", help="上传下载文件参数设置")
    parser.add_option("-l", "--local", dest="local", help="本地文件路径参数设置")
    parser.add_option("-r", "--remote", dest="remote", help="ftp服务器文件路径参数设置")
    (options, _) = parser.parse_args()
    #print(options.host,options.group,options.action,options.local,options.remote)

    hosts = []
    if options.host:
        temp_hosts = options.host.split(',')
        for i in temp_hosts:
            if i in host_dict:
                hosts.append(i)

    groups = []
    if options.group:
        temp_groups = options.group.split(',')
        if temp_groups:
            for i in temp_groups:
                if i in sections_list:
                    groups.append(i)
    temp_group_list = []
    if groups:
        for i in groups:
            temp_group_list += config.options(i)

    if options.action and options.local and options.remote:
        cmd = options.action
        localpath = options.local
        remotepath = options.remote
    else:
        print('命令不存在,退出程序')
        sys.exit(1)


    return (hosts, temp_group_list, cmd, localpath, remotepath)

if __name__ == '__main__':
    cmd_tuple = cmd_parser()
    hosts = cmd_tuple[0]
    groups = cmd_tuple[1]
    cmd = cmd_tuple[2]
    localpath = cmd_tuple[3]
    remotepath = cmd_tuple[4]
    p_l = []

    if hosts:
        for host in hosts:
            p = MyProcess(host_dict[host][0],host_dict[host][1],host_dict[host][2],host_dict[host][3],cmd,localpath,remotepath)
            p.daemon = True
            p.start()
            p_l.append(p)
    if groups:
        for host in groups:
            p1 = MyProcess(host_dict[host][0],host_dict[host][1],host_dict[host][2],host_dict[host][3],cmd,localpath,remotepath)
            p1.daemon = True
            p1.start()
            p_l.append(p1)

    for i in p_l:
        i.join()

    print("所有进程执行结束")