Python——批量管理服务器

本文介绍了一种使用Python和paramiko模块进行多台服务器批量管理的方法,包括并发连接、执行指令和回收结果,以及如何利用线程池实现高效并发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考资料

本文参考了ZingpLiu的博客。

前言

运维行业从人肉运维到DevOps,再到近几年落地开花的AIOps已经不需要只会敲几个命令写几个简单脚本的佛系运维了——不懂开发的运维不是好运维。

P.S. 抱怨:这行业发展也太快了,我这还石器时代,你那已经量子力学了,哭了哭了。

现下开源的主机批量管理工具有Agent类的SaltStack、Puppet等,以及无Agent类的ansible。这里只是想通过编写python工具来提高自己对这两类工具的认识,我这方车轮造了也没人用鸭。
这里利用python编写个批量管理多台服务器的工具,然后再次感谢这个无私的共享网络。

分析

既然是批量管理,那么就要考虑一下几点:
1.如何并发连接多台主机?
2.如何对多台主机发布指令操作?
3.如何回收并标明结果?

对应准备

所幸热衷于节约时间的python为我们准备好了一个魔法模块——paramiko

如果对模块安装有问题的小伙伴戳这里

准备好了该模块就可以上手了,哦,当然,咱们还需要有操作的对象,这部分我是使用vmware创建几个CentOS系统。

对应代码

这部分代码我是参考开篇的那位大佬的。
以下代码是核心模块,用于处理主机的连接,以及在其上执行命令并得到反馈。

import paramiko   class SSHParamiko(object):
 
    err = "argument passwd or rsafile can not be None"
 
    def __init__(self, host, port, user, passwd=None, rsafile=None):
        self.h = host
        self.p = port
        self.u = user
        self.w = passwd
        self.rsa = rsafile
 
    def _connect(self):
        if self.w:
            return self.pwd_connect()
        elif self.rsa:
            return self.rsa_connect()
        else:
            raise ConnectionError(self.err)

    def pwd_connect(self):
        conn = paramiko.SSHClient()
        conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        conn.connect(self.h, self.p, self.u, self.w)
        return conn
 
    def rsa_connect(self):
        pkey = paramiko.RSAKey.from_private_key_file(self.rsa)
        conn = paramiko.SSHClient()
        conn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        conn.connect(hostname=self.h, port=self.p, username=self.u, pkey=pkey)
        return conn
 
    def run_cmd(self, cmd):
        conn = self._connect()
        stdin, stdout, stderr = conn.exec_command(cmd)
        code = stdout.channel.recv_exit_status()
        stdout, stderr = stdout.read(), stderr.read()
        conn.close()
        if not stderr:
            return code, stdout.decode()
        else:
            return code, stderr.decode()

测试代码:

if __name__ == '__main__':
    h = "我的测试机IP"
    p = 22
    u = "我的用户名"
    w = "我的密码"
 
    obj = SSHParamiko(h, p, u, w)
    r = obj.run_cmd("df -h")
    print(r[0])
    print(r[1])  

可能会有和我一样的小白不明白if __name__ == '__main__':的含义,可以戳这里哦

下边就是执行后的结果:

0
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        476M     0  476M   0% /dev
tmpfs           487M     0  487M   0% /dev/shm
tmpfs           487M  7.7M  479M   2% /run
tmpfs           487M     0  487M   0% /sys/fs/cgroup
/dev/sda3        49G  2.0G   47G   4% /
/dev/sda1       197M  109M   89M  56% /boot
tmpfs            98M     0   98M   0% /run/user/0

执行结果:
r[0]——状态码;
r[1]——命令返回结果。

那么如何并发呢?

利用Python自带的线程模块,这里用的是其中一个——from concurrent.futures import ThreadPoolExecutor。嘛,当然这个原作者还添加了红绿显示的针对非红绿色盲的人性化功能。不过我还没来得及分析,持续更新。

from concurrent.futures import ThreadPoolExecutor
 
class AllRun(object):
    def __init__(self, ssh_objs, cmds, max_worker=50):
        self.objs = [o for o in ssh_objs]
        self.cmds = [c for c in cmds]
        self.max_worker = max_worker  # 最大并发线程数
 
        self.success_hosts = []       # 存放成功机器数目
        self.failed_hosts = []        # 存放失败的机器IP
        self.mode = None
        self.func = None
 
    def serial_exec(self, obj):
        """单台机器上串行执行命令,并返回结果至字典"""
        result = list()
        for c in self.cmds:
            r = obj.run_cmd(c)
            result.append([c, r])
        return obj, result
 
    def concurrent_run(self):
        """并发执行"""
        future = ThreadPoolExecutor(self.max_worker)
        for obj in self.objs:
            try:
                future.submit(self.serial_exec, obj).add_done_callback(self.callback)
            except Exception as err:
                err = self.color_str(err, "red")
                print(err)
        future.shutdown(wait=True)
 
    def callback(self, future_obj):
        """回调函数,处理返回结果"""
        ssh_obj, rlist = future_obj.result()
        print(self.color_str("{} execute detail:".format(ssh_obj.h), "yellow"))
        is_success = True
        for item in rlist:
            cmd, [code, res] = item
            info = f"{cmd} | code => {code}\nResult:\n{res}"
            if code != 0:
                info = self.color_str(info, "red")
                is_success = False
                if ssh_obj.h not in self.failed_hosts:
                    self.failed_hosts.append(ssh_obj.h)
            else:
                info = self.color_str(info, "green")
            print(info)
        if is_success:
            self.success_hosts.append(ssh_obj.h)
            if ssh_obj.h in self.failed_hosts:
                self.failed_hosts.remove(ssh_obj.h)
 
    def overview(self):
        """展示总的执行结果"""
        for i in self.success_hosts:
            print(self.color_str(i, "green"))
        print("-" * 30)
        for j in self.failed_hosts:
            print(self.color_str(j, "red"))
        info = "Success hosts {}; Failed hosts {}."
        s, f = len(self.success_hosts), len(self.failed_hosts)
        info = self.color_str(info.format(s, f), "yellow")
        print(info)
 
    @staticmethod
    def color_str(old, color=None):
        """给字符串添加颜色"""
        if color == "red":
            new = "\033[31;1m{}\033[0m".format(old)
        elif color == "yellow":
            new = "\033[33;1m{}\033[0m".format(old)
        elif color == "blue":
            new = "\033[34;1m{}\033[0m".format(old)
        elif color == "green":
            new = "\033[36;1m{}\033[0m".format(old)
        else:
            new = old
        return new
 
if __name__ == '__main__':
    h1 = "adime01.shouji.sjs.ted"
    p1 = 22
    u1 = "odin"
    w1 = "*****"
 
    h = "10.129.206.97"
    p = 22
    u = "root"
    w = "*****"
 
    obj1 = SSHParamiko(h1, p1, u1, w1)
    obj = SSHParamiko(h, p, u, w)
 
    cmds = ["df -h", "ls"]
 
    all_obj = AllRun([obj1, obj], cmds)
    all_obj.concurrent_run()
    all_obj.overview()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值