灌水唠嗑区
技术分享
文章

Python 自动化办公系列(04)批量配置 SSH 单向互信

发表于2025-10-14 14:09:4056次浏览1个评论

# -*- coding: utf-8 -*-
__author__ = "HaoJun"
__doc__ = "批量配置 SSH 单向互信,master 对 多个 client;目的:为 ansible 工具打通 SSH 通路!"
'''
安装了 ansible 工具的主服务器 169 上部署了 ansible 组件,作用是为学员正在使用的节点批量安装多个开发组件,
以顺利安装 8a 8s 8c 数据库。前提:需要 169 和学员节点做 SSH 互信,才能执行 ansible 批处理。每期开班前,
我都要将 50 个节点重做操作系统,原来的互信将失效,程序没有开发出来之前,我使用《自动化办公 03》程序,
安装一个组件需要执行 50 次程序。本程序开发完毕之后,执行一次,然后 169 上的 ansible 批处理一次搞定!
'''
# [2024.11.29] 调试通过。全程使用 root 账户
from fabric2 import Connection
import pandas as pd
from enum import Enum

MasterID = 169 # 安装了 ansible 工具的主服务器 ID
excelFile = r"D:\6_生态发展部\云服务器管理\阿里云\阿里云云服务器_Python.xlsx"
excelData = pd.DataFrame(pd.read_excel(excelFile,keep_default_na=False)) # Need install module named 'xlrd'

class KEYS(Enum):
    COLUMN_ID = "ID" # 示例唯一标识
    COLUMN_NAME = "instName" # 示例名称
    COLUMN_PIP = "Public IP" # 公网 IP
    COLUMN_IIP = "内网 IP"  # 内网 IP
    COLUMN_PORT = "SSH Port" # SSH 协议外部映射端口
    COLUMN_PWD = "root password" # root 账户密码

def chkExcelHead():
    excelColumns = excelData.columns.to_list() # 从 Excel 文件读出来的表头,转换成列表
    bExit = True
    for oKey in KEYS:
        if oKey.value not in excelColumns:
            print('在"{0}"中找不到表头"{1}"!'.format(excelFile,oKey.value))
            bExit = False
            break
    return bExit

def getMasterConn(ip, port, user, password):
    conSSH = None
    try:
        conSSH = Connection(ip, user, port=port, connect_kwargs={'password': password})
    except BaseException as e:
        print("The host of ip {} : {}".format(ip, e))
    finally:
        pass
        #if conSSH is not None:
        #    conSSH.close()
    return conSSH

def main():
    if not chkExcelHead():
        exit(1)
    masConn = None
    masterHostName = 'peixun{}'.format(MasterID)
    dfMaster = excelData[excelData['instName'] == masterHostName] # 找到 master 服务器 DataFrame
    ip = dfMaster['Public IP'].values[0]
    port = dfMaster['SSH Port'].values[0]
    password = dfMaster['root password'].values[0]
    masConn = getMasterConn(ip, int(port), "root", password)
    if masConn is None:exit(1)

    # 主服务器生成秘钥
    result = masConn.run("if [ ! -f ~/.ssh/authorized_keys ]; then ssh-keygen; fi")

    dfValid = excelData[excelData['ID']!=''] # 找到 ID 非空的、有效的 Client 服务器
    for i in range(len(dfValid)):
        id = dfValid[KEYS.COLUMN_ID.value].values[i]
        if id == '': continue
        iip = dfValid[KEYS.COLUMN_IIP.value].values[i] # 得到 client 服务器内网 IP
        iport = 22 #内网 SSH 缺省端口
        password = dfValid[KEYS.COLUMN_PWD.value].values[i]
        hostname = dfValid[KEYS.COLUMN_NAME.value].values[i]
        cmdStr = "sshpass -p '{passwd}' ssh-copy-id -f -i ~/.ssh/id_rsa.pub -p {port} root@{ip}".\
            format(passwd=password,ip=iip,port=iport)
        try:
            result = masConn.run(cmdStr) # 将秘钥拷贝到指定服务器。命令中的单引号是必须的,解决密码中存在特殊字符问题
        except BaseException as e:
            #print(result.stdout)
            print(e)

    if masConn is not None:
        masConn.close()

if __name__ == "__main__":
    main()

''' 秘钥拷贝到指定服务器后,ansible ping 模组校验通过,即实现了 master 向 client 服务器的单向 SSH 互信
[root@peixun169 .ssh]# ansible servers -m ping
172.27.237.163 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
172.27.237.168 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
'''

评论

登录后才可以发表评论
用户头像
GBase用户28017发表于 9个月前
好CODING。