You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在sendmail发送的SSH连接失败告警邮件中添加主机名?

解决SSH连接失败时邮件包含主机及异常信息的问题

我完全懂你的困扰:现在脚本循环处理多台主机时,连接失败的邮件只有固定文本,根本没法定位到底是哪台主机出了问题,也看不到具体的错误原因。咱们来一步步把这个问题解决掉。

原代码的核心问题

  • 异常块里的邮件正文是固定字符串,没有插入主机名/IP这类关键定位信息
  • 没有区分具体的异常类型,没法判断是「主机不可达」「认证失败」还是其他问题
  • 邮件对象msg在循环外初始化,多次失败时会重复添加内容(逻辑上不够严谨)

修复后的代码

import csv
import paramiko
import socket
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from datetime import date

# 收集所有失败主机信息,最后统一发汇总邮件(推荐这种方式,避免频繁发邮件)
failed_hosts = []

# 邮件配置
msg_password = 'something'
sender = "servicedesk@mail.com"
recipient = "user@mail.com"

with open('test.csv', 'r', newline='') as csvinput:
    reader = csv.DictReader(csvinput)
    for item in reader:
        ip = item['IP']
        hostname = item['Hostname']
        username = item['Username']
        password = item['Password']
        type_ = item['Type']
        today = date.today()
        
        if 'Network' in type_:
            try:
                # 初始化SSH客户端
                remote_conn_pre = paramiko.SSHClient()
                remote_conn_pre.set_missing_host_key_policy(paramiko.AutoAddPolicy())
                # 设置10秒超时,避免脚本卡在无法连接的主机上
                remote_conn_pre.connect(ip, username=username, password=password, port=22, timeout=10)
                print(f"SSH连接成功: {hostname} ({ip})")
                # 这里可以添加你的交互式会话逻辑
                remote_conn_pre.close()  # 记得关闭连接
                
            except paramiko.AuthenticationException:
                err_detail = f"认证失败: {hostname} ({ip}) - 用户名或密码错误"
                print(err_detail)
                failed_hosts.append(err_detail)
            except socket.timeout:
                err_detail = f"连接超时: {hostname} ({ip}) - 主机可能不可达或22端口未开放"
                print(err_detail)
                failed_hosts.append(err_detail)
            except socket.error as e:
                err_detail = f"网络错误: {hostname} ({ip}) - 详情: {str(e)}"
                print(err_detail)
                failed_hosts.append(err_detail)
            except paramiko.SSHException as e:
                err_detail = f"SSH协议错误: {hostname} ({ip}) - 详情: {str(e)}"
                print(err_detail)
                failed_hosts.append(err_detail)
            except Exception as e:
                # 兜底捕获所有未预料的异常
                err_detail = f"未知错误: {hostname} ({ip}) - 详情: {str(e)}"
                print(err_detail)
                failed_hosts.append(err_detail)

# 所有主机处理完成后,发送汇总邮件
if failed_hosts:
    msg = MIMEMultipart()
    msg['From'] = sender
    msg['To'] = recipient
    msg['Subject'] = f"SSH连接失败汇总 ({today})"
    
    # 构建清晰的邮件正文
    body = f"本次检测共发现{len(failed_hosts)}台主机SSH连接失败:\n\n"
    for idx, err in enumerate(failed_hosts, 1):
        body += f"{idx}. {err}\n"
    
    msg.attach(MIMEText(body, 'plain'))
    
    # 发送邮件
    try:
        server = smtplib.SMTP("mailserver.com", 587)
        server.starttls()
        server.login(sender, msg_password)
        server.sendmail(sender, recipient, msg.as_string())
        server.quit()
        print("失败汇总邮件已成功发送")
    except Exception as e:
        print(f"发送邮件失败: {str(e)}")

关键改动说明

  1. 精准捕获异常类型:

    • 区分了认证失败、连接超时、网络错误等不同场景,能给出针对性的错误提示,方便快速排查问题
    • 用Exception兜底,避免脚本因未预料的错误直接崩溃
  2. 统一汇总发送:

    • 用failed_hosts列表收集所有失败信息,最后一次性发送汇总邮件,不会频繁打扰收件人
    • 如果确实需要每失败一台就立即发邮件,可以把邮件发送逻辑移到每个异常块内,记得每次都重新创建MIMEMultipart对象(避免重复添加内容)
  3. 强化主机标识:

    • 错误信息里同时包含主机名和IP,无论哪种标识方式都能快速定位目标主机
    • 邮件标题加入日期,方便区分不同批次的检测结果
  4. 代码细节优化:

    • 变量名更规范(比如避免用Type这类和内置关键字冲突的命名)
    • 添加连接超时设置,避免脚本长时间卡在无响应的主机上
    • 连接成功后主动关闭SSH连接,释放资源

内容的提问来源于stack exchange,提问作者Bharath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:19:09