第324篇:自动化巡检与智能报告生成
关键词
自动化巡检、智能报告、网络健康检查、巡检脚本、HTML 报告、定时巡检、巡检模板
一、自动化巡检概述
1.1 为什么要自动化巡检
传统巡检 vs 自动化巡检:
传统巡检:
┌──────────────────────────────────────────┐
│ 工程师逐台 SSH 登录设备 │
│ ├─ show version │
│ ├─ display interface brief │
│ ├─ display bgp peer │
│ ├─ display ospf peer │
│ └─ ...(可能漏掉某个关键命令) │
│ │
│ 结果记录在 Excel 或 Word 中 │
│ ├─ 数据靠人工复制粘贴 │
│ ├─ 容易出错和漏项 │
│ └─ 无法做趋势分析 │
│ │
│ 100 台设备 × 每天巡检 = 4-6 小时 │
│ └─ 每天花半天时间做重复劳动 │
└──────────────────────────────────────────┘
自动化巡检:
┌──────────────────────────────────────────┐
│ 脚本自动登录所有设备 │
│ ├─ 标准化命令集 │
│ ├─ 并发执行(100 台设备 3-5 分钟) │
│ └─ 不遗漏任何命令 │
│ │
│ 自动生成 HTML/PDF 报告 │
│ ├─ 数据已格式化 │
│ ├─ 异常数据自动标记 │
│ ├─ 合规检查集成 │
│ └─ 趋势变化一目了然 │
│ │
│ 100 台设备 × 每天巡检 = 5 分钟 │
│ └─ 工程师只需关注异常部分 │
└──────────────────────────────────────────┘
1.2 巡检架构
自动化巡检系统架构:
| 巡检任务调度器 ┌─ 定时触发(每天 08:00 / 20:00) ├─ 事件触发(变更后自动巡检) └─ 手动触发(按需巡检) 巡检执行引擎 ┌─────────────────────────────────────┐ └─────────────────────────────────────┘ 输出层 ┌─ 报告推送(邮件/IM/Web) ├─ 告警通知 └─ 数据接口(API 供上层消费) | 并发采集器(ThreadPoolExecutor) ┌─ 登录设备 ├─ 执行巡检命令 └─ 收集回显 数据处理器 ┌─ 解析输出为结构化数据 ├─ 异常检测(阈值/基线) └─ 数据归档(InfluxDB/ES) 报告生成器 ┌─ HTML 报告 ├─ 趋势图表 └─ 异常告警 | |
|---|---|---|
二、巡检模板定义
2.1 分级巡检
巡检级别设计:
日常巡检(每天):
┌──────────────────────────────────────────┐
│ 目标:快速发现异常 │
│ 耗时:5 分钟(100 台设备) │
│ │
│ 检查项: │
│ ┌─ 设备可达性(ping 设备管理 IP) │
│ ├─ 关键接口状态(上联/下联口) │
│ ├─ BGP/OSPF 邻居状态 │
│ ├─ CPU/内存利用率 │
│ └─ 环境温度(异常高温告警) │
└──────────────────────────────────────────┘
深度巡检(每周):
┌──────────────────────────────────────────┐
│ 目标:全面体检 │
│ 耗时:15 分钟(100 台设备) │
│ │
│ 检查项(在日常巡检基础上增加): │
│ ┌─ 所有接口状态和错误计数 │
│ ├─ 路由表检查(数量变化/黑洞路由) │
│ ├─ 日志错误检查 │
│ ├─ NTP 同步状态 │
│ ├─ LLDP 邻居一致性检查 │
│ ├─ VRRP/堆叠状态 │
│ └─ QoS 队列丢弃检查 │
└──────────────────────────────────────────┘
全量巡检(每月):
┌──────────────────────────────────────────┐
│ 目标:配置基线审计 │
│ 耗时:30 分钟(100 台设备) │
│ │
│ 检查项(在深度巡检基础上增加): │
│ ┌─ 全量 running-config 采集 │
│ ├─ 配置漂移检查(与基线对比) │
│ ├─ 合规检查(安全基线/最佳实践) │
│ ├─ 版本审计(推荐版本/已知漏洞) │
│ ├─ ACL 规则审计 │
│ └─ 设备证书有效期检查 │
└──────────────────────────────────────────┘
2.2 巡检命令模板
#!/usr/bin/env python3
# patrol_templates.py — 巡检命令模板
from dataclasses import dataclass
from typing import List, Dict, Optional
@dataclass
class PatrolCommand:
"""巡检命令定义"""
name: str # 命令名称(如 bgp_peer)
cli_command: str # 实际 CLI 命令
description: str # 检查目的
critical: bool = False # 是否关键检查
parser: str = "text" # 解析方式
def __post_init__(self):
self.cli_command = self.cli_command.strip()
# =============================================
# 华为 VRP 设备巡检命令模板
# =============================================
HUAWEI_VRP_DAILY = [
PatrolCommand(
name="device_uptime",
cli_command="display version",
description="设备运行时间",
critical=False,
parser="uptime",
),
PatrolCommand(
name="cpu_usage",
cli_command="display cpu-usage",
description="CPU 利用率",
critical=True,
parser="cpu",
),
PatrolCommand(
name="memory_usage",
cli_command="display memory-usage",
description="内存利用率",
critical=True,
parser="memory",
),
PatrolCommand(
name="interface_brief",
cli_command="display interface brief",
description="接口状态概览",
critical=True,
parser="interface_brief",
),
PatrolCommand(
name="bgp_peer",
cli_command="display bgp peer",
description="BGP 邻居状态",
critical=True,
parser="bgp_peer",
),
PatrolCommand(
name="ospf_peer",
cli_command="display ospf peer brief",
description="OSPF 邻居状态",
critical=True,
parser="ospf_peer",
),
PatrolCommand(
name="temperature",
cli_command="display device temperature",
description="设备温度",
critical=False,
parser="temperature",
),
]
HUAWEI_VRP_WEEKLY = HUAWEI_VRP_DAILY + [
PatrolCommand(
name="interface_errors",
cli_command="display interface",
description="接口错误计数",
critical=False,
parser="interface_errors",
),
PatrolCommand(
name="routing_table",
cli_command="display ip routing-table statistics",
description="路由表统计",
critical=False,
parser="routing_stats",
),
PatrolCommand(
name="log_buffer",
cli_command="display logbuffer",
description="日志错误检查",
critical=False,
parser="log",
),
PatrolCommand(
name="ntp_status",
cli_command="display ntp status",
description="NTP 同步状态",
critical=False,
parser="ntp",
),
PatrolCommand(
name="lldp_neighbor",
cli_command="display lldp neighbor brief",
description="LLDP 邻居",
critical=False,
parser="lldp",
),
]
HUAWEI_VRP_MONTHLY = HUAWEI_VRP_WEEKLY + [
PatrolCommand(
name="running_config",
cli_command="display current-configuration",
description="当前配置",
critical=False,
parser="config",
),
PatrolCommand(
name="acl_config",
cli_command="display acl all",
description="ACL 规则",
critical=False,
parser="acl",
),
PatrolCommand(
name="certificate",
cli_command="display pki certificate",
description="证书状态",
critical=False,
parser="cert",
),
]
# =============================================
# 思科 IOS 设备巡检命令模板
# =============================================
CISCO_IOS_DAILY = [
PatrolCommand(
name="device_uptime",
cli_command="show version | include uptime",
description="设备运行时间",
critical=False,
),
PatrolCommand(
name="cpu_usage",
cli_command="show processes cpu | include CPU",
description="CPU 利用率",
critical=True,
),
PatrolCommand(
name="memory_usage",
cli_command="show memory statistics",
description="内存利用率",
critical=True,
),
PatrolCommand(
name="interface_brief",
cli_command="show ip interface brief",
description="接口状态概览",
critical=True,
),
PatrolCommand(
name="bgp_peer",
cli_command="show bgp summary",
description="BGP 邻居状态",
critical=True,
),
PatrolCommand(
name="ospf_peer",
cli_command="show ip ospf neighbor",
description="OSPF 邻居状态",
critical=True,
),
]
# =============================================
# 巡检模板管理器
# =============================================
PATROL_TEMPLATES = {
"huawei_vrp": {
"daily": HUAWEI_VRP_DAILY,
"weekly": HUAWEI_VRP_WEEKLY,
"monthly": HUAWEI_VRP_MONTHLY,
},
"cisco_ios": {
"daily": CISCO_IOS_DAILY,
"weekly": CISCO_IOS_DAILY, # 简化
"monthly": CISCO_IOS_DAILY,
},
}
def get_patrol_commands(
device_type: str,
patrol_level: str = "daily",
) -> List[PatrolCommand]:
"""获取指定级别和厂商的巡检命令"""
template = PATROL_TEMPLATES.get(device_type, {})
return template.get(patrol_level, [])
三、巡检执行引擎
3.1 核心引擎
#!/usr/bin/env python3
# patrol_engine.py — 巡检执行引擎
from netmiko import ConnectHandler
from concurrent.futures import (
ThreadPoolExecutor, as_completed
)
from typing import Dict, List, Any, Optional
from datetime import datetime
import time
import json
import logging
from patrol_templates import (
PatrolCommand, get_patrol_commands
)
logger = logging.getLogger(__name__)
class PatrolResult:
"""单台设备巡检结果"""
def __init__(self, hostname: str, device_type: str):
self.hostname = hostname
self.device_type = device_type
self.timestamp = datetime.now().isoformat()
self.commands: Dict[str, Any] = {}
self.errors: List[str] = []
self.alerts: List[Dict] = []
self.alive = False
def add_command_result(
self, cmd: PatrolCommand, output: str
):
"""添加命令执行结果"""
self.commands[cmd.name] = {
"command": cmd.cli_command,
"output": output,
"description": cmd.description,
}
def add_alert(
self, level: str, message: str, detail: str = ""
):
"""添加告警"""
self.alerts.append({
"level": level,
"message": message,
"detail": detail,
})
def to_dict(self) -> Dict:
return {
"hostname": self.hostname,
"device_type": self.device_type,
"timestamp": self.timestamp,
"alive": self.alive,
"commands": self.commands,
"errors": self.errors,
"alerts": self.alerts,
"alert_count": len(self.alerts),
}
class PatrolEngine:
"""巡检执行引擎"""
def __init__(
self,
max_workers: int = 10,
command_timeout: int = 30,
):
self.max_workers = max_workers
self.command_timeout = command_timeout
def patrol_device(
self,
device_info: Dict,
commands: List[PatrolCommand],
) -> PatrolResult:
"""巡检单台设备"""
result = PatrolResult(
hostname=device_info.get("hostname", ""),
device_type=device_info.get("device_type", ""),
)
try:
conn = ConnectHandler(**device_info)
conn.enable()
result.alive = True
# 获取设备 hostname
prompt = conn.find_prompt()
result.hostname = prompt.rstrip("#>")
# 执行每条巡检命令
for cmd in commands:
try:
output = conn.send_command(
cmd.cli_command,
read_timeout=self.command_timeout,
)
result.add_command_result(cmd, output)
# 异常检测
self._detect_anomalies(result, cmd, output)
except Exception as e:
error_msg = (
f"命令 {cmd.name} 执行失败: {e}"
)
result.errors.append(error_msg)
logger.warning(
f"{result.hostname}: {error_msg}"
)
conn.disconnect()
except Exception as e:
result.alive = False
result.errors.append(f"设备连接失败: {e}")
logger.error(
f"{device_info.get('host')}: {e}"
)
return result
def _detect_anomalies(
self,
result: PatrolResult,
cmd: PatrolCommand,
output: str,
):
"""检测异常"""
# CPU 利用率过高
if cmd.name == "cpu_usage":
import re
match = re.search(r"(\d+)%", output)
if match:
cpu = int(match.group(1))
if cpu > 80:
result.add_alert(
"WARNING",
f"CPU 利用率过高: {cpu}%",
cmd.cli_command,
)
elif cpu > 90:
result.add_alert(
"CRITICAL",
f"CPU 利用率极高: {cpu}%",
cmd.cli_command,
)
# 内存利用率过高
if cmd.name == "memory_usage":
import re
match = re.search(r"(\d+)%", output)
if match:
mem = int(match.group(1))
if mem > 80:
result.add_alert(
"WARNING",
f"内存利用率过高: {mem}%",
cmd.cli_command,
)
# 接口 down
if cmd.name == "interface_brief":
for line in output.splitlines():
if "down" in line.lower() and "GigabitEthernet" in line:
result.add_alert(
"WARNING",
f"接口 DOWN: {line.strip()}",
cmd.cli_command,
)
# BGP 邻居非 Established
if cmd.name == "bgp_peer":
if "Established" not in output:
result.add_alert(
"CRITICAL",
"BGP 邻居异常 - 无 Established 状态的 peer",
cmd.cli_command,
)
def patrol_all(
self,
devices: List[Dict],
patrol_level: str = "daily",
) -> Dict[str, PatrolResult]:
"""并发巡检所有设备"""
results = {}
with ThreadPoolExecutor(
max_workers=self.max_workers
) as executor:
futures = {}
for dev in devices:
device_type = dev.get("device_type", "huawei_vrp")
commands = get_patrol_commands(
device_type, patrol_level
)
future = executor.submit(
self.patrol_device, dev, commands
)
futures[future] = dev
for future in as_completed(futures):
result = future.result()
results[result.hostname] = result
return results
3.2 异常检测器
class AnomalyDetector:
"""巡检异常检测器"""
# 默认阈值
THRESHOLDS = {
"cpu_usage": {"warning": 70, "critical": 90},
"memory_usage": {"warning": 70, "critical": 90},
"temperature": {"warning": 45, "critical": 55},
"interface_errors": {"warning": 100, "critical": 1000},
}
def __init__(
self,
thresholds: Optional[Dict] = None,
baseline_data: Optional[Dict] = None,
):
self.thresholds = thresholds or self.THRESHOLDS
self.baseline = baseline_data or {}
def check_cpu(self, cpu_value: float) -> Optional[Dict]:
"""检查 CPU 利用率"""
threshold = self.thresholds.get("cpu_usage", {})
if cpu_value >= threshold.get("critical", 90):
return {
"level": "CRITICAL",
"message": f"CPU {cpu_value}% (阈值: {threshold.get('critical')}%)",
}
elif cpu_value >= threshold.get("warning", 70):
return {
"level": "WARNING",
"message": f"CPU {cpu_value}% (阈值: {threshold.get('warning')}%)",
}
return None
def check_memory(self, mem_value: float) -> Optional[Dict]:
"""检查内存利用率"""
threshold = self.thresholds.get("memory_usage", {})
if mem_value >= threshold.get("critical", 90):
return {
"level": "CRITICAL",
"message": f"内存 {mem_value}% (阈值: {threshold.get('critical')}%)",
}
elif mem_value >= threshold.get("warning", 70):
return {
"level": "WARNING",
"message": f"内存 {mem_value}% (阈值: {threshold.get('warning')}%)",
}
return None
def check_interface_errors(
self, error_count: int
) -> Optional[Dict]:
"""检查接口错误计数"""
threshold = self.thresholds.get("interface_errors", {})
if error_count >= threshold.get("critical", 1000):
return {
"level": "CRITICAL",
"message": f"接口错误 {error_count} (阈值: {threshold.get('critical')})",
}
elif error_count >= threshold.get("warning", 100):
return {
"level": "WARNING",
"message": f"接口错误 {error_count} (阈值: {threshold.get('warning')})",
}
return None
def check_trend(
self,
metric: str,
current_value: float,
device: str,
) -> Optional[Dict]:
"""基于基线的趋势异常检测"""
device_baseline = self.baseline.get(device, {})
baseline_value = device_baseline.get(metric)
if baseline_value is None:
return None
change_ratio = abs(
current_value - baseline_value
) / baseline_value if baseline_value else 0
if change_ratio > 0.3: # 变化超过 30%
return {
"level": "WARNING",
"message": (
f"{metric} 趋势异常: "
f"当前 {current_value} vs "
f"基线 {baseline_value} "
f"(变化 {change_ratio*100:.0f}%)"
),
}
return None
四、智能报告生成
4.1 HTML 报告生成
#!/usr/bin/env python3
# patrol_report.py — 巡检报告生成
from typing import Dict, List
from datetime import datetime
import json
class PatrolReportGenerator:
"""巡检报告生成器"""
@staticmethod
def generate_html(
results: Dict[str, PatrolResult],
output_file: str = "patrol_report.html",
):
"""生成 HTML 巡检报告"""
# 统计
total = len(results)
alive = sum(1 for r in results.values() if r.alive)
dead = total - alive
total_alerts = sum(
len(r.alerts) for r in results.values()
)
critical_alerts = sum(
len([a for a in r.alerts
if a["level"] == "CRITICAL"])
for r in results.values()
)
warning_alerts = sum(
len([a for a in r.alerts
if a["level"] == "WARNING"])
for r in results.values()
)
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 告警设备列表
alerted_devices = [
r for r in results.values()
if r.alerts
]
# 生成告警表格行
alert_rows = ""
for r in alerted_devices:
for alert in r.alerts:
level_class = (
"critical"
if alert["level"] == "CRITICAL"
else "warning"
)
alert_rows += f"""
<tr class="{level_class}">
<td>{r.hostname}</td>
<td><span class="badge badge-{level_class}">{alert['level']}</span></td>
<td>{alert['message']}</td>
</tr>"""
# 设备状态列表
device_rows = ""
for hostname, result in sorted(results.items()):
status_icon = "✅" if result.alive else "❌"
alert_count = len(result.alerts)
alert_badge = (
f'<span class="badge badge-danger">{alert_count}</span>'
if alert_count > 0 else '<span class="badge badge-success">0</span>'
)
device_rows += f"""
<tr>
<td>{hostname}</td>
<td>{result.device_type}</td>
<td>{status_icon} {'在线' if result.alive else '离线'}</td>
<td>{alert_badge}</td>
</tr>"""
# 完整 HTML
html = f"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<title>网络巡检报告</title>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: 'Microsoft YaHei', Arial, sans-serif;
background: #f5f5f5; padding: 20px; }}
.header {{ background: linear-gradient(135deg, #1a237e, #283593);
color: white; padding: 30px; border-radius: 10px;
margin-bottom: 20px; }}
.header h1 {{ font-size: 28px; margin-bottom: 10px; }}
.summary {{ display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 15px; margin-bottom: 20px; }}
.summary-card {{ background: white; padding: 20px; border-radius: 10px;
text-align: center; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
.summary-card .number {{ font-size: 36px; font-weight: bold; }}
.summary-card .label {{ font-size: 14px; color: #666; margin-top: 5px; }}
.green {{ color: #4CAF50; }} .red {{ color: #f44336; }}
.orange {{ color: #ff9800; }}
table {{ width: 100%; border-collapse: collapse;
background: white; border-radius: 10px;
overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px; }}
th, td {{ padding: 12px 15px; text-align: left;
border-bottom: 1px solid #f0f0f0; }}
th {{ background: #283593; color: white; font-weight: 500; }}
tr:hover {{ background: #f8f9fa; }}
.badge {{ padding: 4px 8px; border-radius: 4px; font-size: 12px;
font-weight: bold; }}
.badge-danger {{ background: #ffebee; color: #c62828; }}
.badge-warning {{ background: #fff3e0; color: #ef6c00; }}
.badge-success {{ background: #e8f5e9; color: #2e7d32; }}
.critical {{ background: #fff5f5; }}
.warning {{ background: #fffbf0; }}
.section-title {{ font-size: 20px; margin: 20px 0 10px 0;
color: #1a237e; }}
</style>
</head>
<body>
<div class="header">
<h1>📡 网络巡检报告</h1>
<p>巡检时间: {now}</p>
<p>巡检级别: 日常巡检</p>
</div>
<div class="summary">
<div class="summary-card">
<div class="number">{total}</div>
<div class="label">总设备数</div>
</div>
<div class="summary-card">
<div class="number green">{alive}</div>
<div class="label">在线设备</div>
</div>
<div class="summary-card">
<div class="number red">{dead}</div>
<div class="label">离线设备</div>
</div>
<div class="summary-card">
<div class="number red">{critical_alerts}</div>
<div class="label">严重告警</div>
</div>
<div class="summary-card">
<div class="number orange">{warning_alerts}</div>
<div class="label">警告告警</div>
</div>
</div>
<h2 class="section-title">告警列表</h2>
<table>
<tr>
<th>设备</th>
<th>级别</th>
<th>告警内容</th>
</tr>
{alert_rows if alert_rows else '<tr><td colspan="3" style="text-align:center; color:#666;">🎉 无告警</td></tr>'}
</table>
<h2 class="section-title">设备状态</h2>
<table>
<tr>
<th>设备名称</th>
<th>设备类型</th>
<th>状态</th>
<th>告警</th>
</tr>
{device_rows}
</table>
</body>
</html>
"""
with open(output_file, "w", encoding="utf-8") as f:
f.write(html)
print(f"巡检报告已生成: {output_file}")
return output_file
4.2 JSON 结构化报告
class PatrolJSONReport:
"""JSON 格式巡检报告(便于程序消费)"""
def __init__(self, results: Dict[str, PatrolResult]):
self.results = results
def generate(self) -> Dict:
"""生成结构化报告"""
return {
"report_time": datetime.now().isoformat(),
"summary": {
"total_devices": len(self.results),
"alive": sum(
1 for r in self.results.values()
if r.alive
),
"dead": sum(
1 for r in self.results.values()
if not r.alive
),
"total_alerts": sum(
len(r.alerts)
for r in self.results.values()
),
"critical_alerts": sum(
len([a for a in r.alerts
if a["level"] == "CRITICAL"])
for r in self.results.values()
),
"warning_alerts": sum(
len([a for a in r.alerts
if a["level"] == "WARNING"])
for r in self.results.values()
),
},
"devices": {
hostname: result.to_dict()
for hostname, result in self.results.items()
},
}
def save(self, output_file: str):
"""保存为 JSON 文件"""
data = self.generate()
with open(output_file, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f"结构化报告已生成: {output_file}")
五、定时调度与通知
5.1 集成定时任务
#!/usr/bin/env python3
# schedule_patrol.py — 定时巡检调度
import schedule
import time
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import requests
import json
class PatrolScheduler:
"""巡检任务调度器"""
def __init__(self, patrol_engine, report_generator):
self.engine = patrol_engine
self.report_generator = report_generator
self.notifiers = []
def add_notifier(self, notifier):
"""添加通知器"""
self.notifiers.append(notifier)
def run_daily_patrol(self):
"""执行每日巡检"""
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 开始每日巡检")
# 加载设备清单
with open("inventory.json", "r") as f:
devices = json.load(f)
# 执行巡检
results = self.engine.patrol_all(
devices, patrol_level="daily"
)
# 生成报告
report_file = self.report_generator.generate_html(
results,
f"patrol_report_{time.strftime('%Y%m%d')}.html",
)
# 发送通知
for notifier in self.notifiers:
notifier.notify(results, report_file)
print(f"每日巡检完成")
def schedule_jobs(self):
"""配置定时任务"""
# 每天早上 8 点和晚上 8 点执行日常巡检
schedule.every().day.at("08:00").do(
self.run_daily_patrol
)
schedule.every().day.at("20:00").do(
self.run_daily_patrol
)
while True:
schedule.run_pending()
time.sleep(60)
5.2 企业微信通知
class WeChatNotifier:
"""企业微信通知"""
def __init__(self, webhook_url: str):
self.webhook_url = webhook_url
def notify(self, results, report_file=None):
"""发送巡检结果通知"""
critical_count = sum(
len([a for a in r.alerts
if a["level"] == "CRITICAL"])
for r in results.values()
)
warning_count = sum(
len([a for a in r.alerts
if a["level"] == "WARNING"])
for r in results.values()
)
if critical_count == 0 and warning_count == 0:
message = (
f"✅ 巡检完成\n"
f"时间: {time.strftime('%Y-%m-%d %H:%M')}\n"
f"设备: {len(results)} 台\n"
f"状态: 全部正常,无告警"
)
else:
message = (
f"⚠️ 巡检完成(发现异常)\n"
f"时间: {time.strftime('%Y-%m-%d %H:%M')}\n"
f"设备: {len(results)} 台\n"
f"严重告警: {critical_count} 个\n"
f"警告: {warning_count} 个\n"
f"请查看详细报告"
)
payload = {
"msgtype": "text",
"text": {"content": message},
}
try:
requests.post(
self.webhook_url, json=payload, timeout=10
)
except Exception as e:
print(f"企业微信通知失败: {e}")
六、最佳实践总结
自动化巡检最佳实践:
1. 分级巡检
┌─ 日常 5 分钟覆盖关键指标
├─ 每周深入检查所有设备
├─ 每月全量合规审计
└─ 变更后立即触发深度巡检
2. 智能告警
┌─ 阈值告警(CPU > 80% 告警)
├─ 趋势告警(相比基线变化 > 30%)
├─ 关联告警(BGP down + 接口 down 一起报)
└─ 告警聚合(同一设备多条告警合并)
3. 报告可视化
┌─ 概览卡:一目了然的全网状态
├─ 告警列表:突出问题
├─ 设备列表:逐台状态
└─ 趋势图:关键指标随时间变化
4. 多通道通知
┌─ 严重告警:短信/电话
├─ 警告告警:IM(企业微信/钉钉)
├─ 信息提示:邮件月报
└─ 报告归档:Web 门户
5. 持续完善
┌─ 每次故障复盘后补充巡检项
├─ 定期调整告警阈值
├─ 收集工程师反馈优化报告
└─ 与 CMDB 联动,新设备自动纳入巡检
下篇预告:第325篇 — 网络变更自动化与回滚,深入网络变更自动化的流程设计、执行与一键回滚机制。
下篇预告:第325篇 — 网络变更自动化与回滚,深入网络变更自动化的流程设计、执行与一键回滚机制。