提交 87eb04a4 authored 作者: 陈泽健's avatar 陈泽健

fix(health-check): 优化服务器健康检查脚本的报告输出

- 修复防火墙状态检测逻辑,改进对 firewalld 和 iptables 的判断准确性
- 增加防火墙修复后状态复检机制,确保修复操作的有效性
- 扩大 iptables 端口检测数量限制,从 20 个增加到 50 个
- 在报告中添加修复时间线记录,展示防火墙和 NTP 服务的异常检测到修复过程
- 为报告添加状态图标标识,使用   ️ 等表情符号直观显示各项检测结果
- 优化 Markdown 报告生成,改进错误处理和文件写入逻辑
- 简化代码结构,移除冗余注释和重复的变量声明
上级 67672af8
...@@ -245,4 +245,4 @@ ...@@ -245,4 +245,4 @@
- 目标服务器是运维集控系统,那么需检测22、8443、1883和8306是否开启,未开启就是异常,需要执行修复脚本; - 目标服务器是运维集控系统,那么需检测22、8443、1883和8306是否开启,未开启就是异常,需要执行修复脚本;
##### 服务自检报告输出(✅ 已实现): ##### 服务自检报告输出(✅ 已实现):
将服务自检的所有操作步骤与结果输出到日志文件中!自检报告需要补充成md格式! 将服务自检的所有操作步骤与结果输出到日志文件中!自检报告需要补充成md格式!检测报告输出需要带有明显图标,标识错误或正常状态
\ No newline at end of file \ No newline at end of file
...@@ -1200,7 +1200,7 @@ function Test-ServerResources { ...@@ -1200,7 +1200,7 @@ function Test-ServerResources {
# 6. 检测防火墙开放端口情况 # 6. 检测防火墙开放端口情况
Write-Log -Level "INFO" -Message "检测防火墙开放端口..." Write-Log -Level "INFO" -Message "检测防火墙开放端口..."
# 先检测防火墙状态 # 先检测防火墙状态
$firewallStatusCmd = "systemctl is-active firewalld 2>/dev/null || service iptables status 2>/dev/null | head -n 1 || echo 'unknown'" $firewallStatusCmd = "systemctl is-active firewalld 2>/dev/null || service iptables status 2>/dev/null | head -n 1 || echo 'unknown'"
$firewallStatusResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $firewallStatusCmd $firewallStatusResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $firewallStatusCmd
...@@ -1208,7 +1208,7 @@ function Test-ServerResources { ...@@ -1208,7 +1208,7 @@ function Test-ServerResources {
$firewallActive = $false $firewallActive = $false
$firewallType = "unknown" $firewallType = "unknown"
# 严格匹配 firewalld 的 is-active 输出(active/inactive/failed/unknown) # 严格匹配 firewalld 的 is-active
$statusLine = ($firewallStatusResult.Output | Select-Object -First 1) $statusLine = ($firewallStatusResult.Output | Select-Object -First 1)
if ($statusLine) { $statusLine = $statusLine.Trim().ToLower() } else { $statusLine = "unknown" } if ($statusLine) { $statusLine = $statusLine.Trim().ToLower() } else { $statusLine = "unknown" }
...@@ -1217,7 +1217,6 @@ function Test-ServerResources { ...@@ -1217,7 +1217,6 @@ function Test-ServerResources {
$firewallType = "firewalld" $firewallType = "firewalld"
} elseif ($statusLine -eq "inactive" -or $statusLine -eq "failed" -or $statusLine -eq "unknown") { } elseif ($statusLine -eq "inactive" -or $statusLine -eq "failed" -or $statusLine -eq "unknown") {
$firewallActive = $false $firewallActive = $false
# 尝试判断 iptables(service iptables status 的首行)
$ipLine = ($firewallStatusResult.Output | Select-Object -Last 1) $ipLine = ($firewallStatusResult.Output | Select-Object -Last 1)
if ($ipLine) { if ($ipLine) {
$ipl = $ipLine.Trim().ToLower() $ipl = $ipLine.Trim().ToLower()
...@@ -1230,66 +1229,70 @@ function Test-ServerResources { ...@@ -1230,66 +1229,70 @@ function Test-ServerResources {
} }
} }
} }
# 获取当前开放端口
$openPorts = @() $openPorts = @()
if ($firewallActive) { if ($firewallActive) {
if ($firewallType -eq "firewalld") { if ($firewallType -eq "firewalld") {
# 使用 firewalld 获取开放端口
$portsCmd = "firewall-cmd --list-ports 2>/dev/null && firewall-cmd --list-services 2>/dev/null" $portsCmd = "firewall-cmd --list-ports 2>/dev/null && firewall-cmd --list-services 2>/dev/null"
$portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd $portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd
if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) { if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) {
$openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '\S' }) -join ", " $openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '\S' }) -join ", "
} }
} } else {
else { $portsCmd = "iptables -L INPUT -n 2>/dev/null | grep ACCEPT | grep -oP 'dpt:\d+' | cut -d: -f2 | sort -u | head -n 50"
# 使用 iptables 获取开放端口
$portsCmd = "iptables -L INPUT -n 2>/dev/null | grep ACCEPT | grep -oP 'dpt:\d+' | cut -d: -f2 | sort -u | head -n 20"
$portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd $portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd
if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) { if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) {
$openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '^\d+$' }) -join ", " $openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '^\d+$' }) -join ", "
} }
} }
} else {
Write-Log -Level "INFO" -Message " 防火墙状态: 已启用 ($firewallType)"
if ($openPorts) {
Write-Log -Level "INFO" -Message " 开放端口/服务: $openPorts"
}
else {
Write-Log -Level "WARN" -Message " 未检测到明确开放的端口"
}
}
else {
Write-Log -Level "WARN" -Message " 防火墙状态: 未启用或未安装"
$openPorts = "防火墙未启用" $openPorts = "防火墙未启用"
} }
# 记录初始快照(用于报告时间线)
$results.Firewall = @{ $results.Firewall = @{
Active = $firewallActive Active = $firewallActive
Type = $firewallType Type = $firewallType
OpenPorts = $openPorts OpenPorts = $openPorts
Status = if ($firewallActive) { "已启用" } else { "未启用" } Status = if ($firewallActive) { "已启用" } else { "未启用" }
Pre = @{
Active = $firewallActive
Type = $firewallType
OpenPorts = $openPorts
}
} }
# 当防火墙未启用或状态异常时,尝试远端修复(参考 NTP 的上传执行 # 触发远端修复(仅当未启用或类型未知
if (-not $firewallActive -or ($firewallType -eq "unknown")) { if (-not $firewallActive -or ($firewallType -eq "unknown")) {
Write-Log -Level "WARN" -Message "[FIREWALL] 检测到防火墙未启用或状态异常,准备执行远端修复" Write-Log -Level "WARN" -Message "[FIREWALL] 检测到防火墙未启用或状态异常,准备执行远端修复"
try { try {
$serverForRepair = @{ IP = $Server.IP; User = $Server.User; Pass = $Server.Pass; Port = $Server.Port } $serverForRepair = @{ IP = $Server.IP; User = $Server.User; Pass = $Server.Pass; Port = $Server.Port }
# 触发远端修复:修复端口开放/防火墙配置
Write-Log -Level "INFO" -Message "[FIREWALL] 触发远端修复: ./issue_handler.sh --action fix_port_access --platform auto --non-interactive" Write-Log -Level "INFO" -Message "[FIREWALL] 触发远端修复: ./issue_handler.sh --action fix_port_access --platform auto --non-interactive"
$fwRepairRes = Upload_the_repair_script -Server $serverForRepair -Action "fix_port_access" -Platform "auto" -RemoteDir "/home/repair_scripts" # 非交互在函数内拼接 $fwRepairRes = Upload_the_repair_script -Server $serverForRepair -Action "fix_port_access" -Platform "auto" -RemoteDir "/home/repair_scripts"
# 默认标记为已尝试
$results.Firewall.Repair = @{ Attempted = $true; Succeeded = $false; Message = "fix_port_access (platform=auto)" }
if ($fwRepairRes -and $fwRepairRes['Success']) { if ($fwRepairRes -and $fwRepairRes['Success']) {
Write-Log -Level "SUCCESS" -Message "[FIREWALL] 远端修复已执行成功 (fix_port_access)" Write-Log -Level "SUCCESS" -Message "[FIREWALL] 远端修复已执行成功 (fix_port_access)"
# 修复后复检防火墙状态与开放端口 $results.Firewall.Repair.Succeeded = $true
$firewallStatusCmd = "systemctl is-active firewalld 2>/dev/null || service iptables status 2>/dev/null | head -n 1 || echo 'unknown'"
$firewallStatusResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $firewallStatusCmd # 修复后复检
$firewallActive = $false; $firewallType = "unknown" $firewallActive = $false; $firewallType = "unknown"
if ($firewallStatusResult.Output -match 'active') { $firewallActive = $true; $firewallType = "firewalld" } $firewallStatusResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $firewallStatusCmd
elseif ($firewallStatusResult.Output -match 'running|OK') { $firewallActive = $true; $firewallType = "iptables" } $statusLine = ($firewallStatusResult.Output | Select-Object -First 1)
if ($statusLine) { $statusLine = $statusLine.Trim().ToLower() } else { $statusLine = "unknown" }
if ($statusLine -eq "active") { $firewallActive = $true; $firewallType = "firewalld" }
else {
$ipLine = ($firewallStatusResult.Output | Select-Object -Last 1)
if ($ipLine) {
$ipl = $ipLine.Trim().ToLower()
if ($ipl -match '\brunning\b' -or $ipl -match '\bok\b') { $firewallActive = $true; $firewallType = "iptables" }
elseif ($ipl -match 'stopped|not running|inactive|failed') { $firewallActive = $false; $firewallType = "iptables" }
}
}
# 修复后端口
$openPorts = @() $openPorts = @()
if ($firewallActive) { if ($firewallActive) {
if ($firewallType -eq "firewalld") { if ($firewallType -eq "firewalld") {
...@@ -1299,44 +1302,33 @@ function Test-ServerResources { ...@@ -1299,44 +1302,33 @@ function Test-ServerResources {
$openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '\S' }) -join ", " $openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '\S' }) -join ", "
} }
} else { } else {
$portsCmd = "iptables -L INPUT -n 2>/dev/null | grep ACCEPT | grep -oP 'dpt:\d+' | cut -d: -f2 | sort -u | head -n 20" $portsCmd = "iptables -L INPUT -n 2>/dev/null | grep ACCEPT | grep -oP 'dpt:\d+' | cut -d: -f2 | sort -u | head -n 50"
$portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd $portsResult = Invoke-SSHCommand -HostName $Server.IP -User $Server.User -Pass $Server.Pass -Port $Server.Port -Command $portsCmd
if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) { if ($portsResult.ExitCode -eq 0 -and $portsResult.Output) {
$openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '^\d+$' }) -join ", " $openPorts = ($portsResult.Output -split "`n" | Where-Object { $_ -match '^\d+$' }) -join ", "
} }
} }
Write-Log -Level "INFO" -Message "[FIREWALL] 修复后状态: 已启用 ($firewallType)"
if ($openPorts) { Write-Log -Level "INFO" -Message "[FIREWALL] 修复后开放端口/服务: $openPorts" }
else { Write-Log -Level "WARN" -Message "[FIREWALL] 修复后未检测到明确开放的端口" }
} else {
Write-Log -Level "WARN" -Message "[FIREWALL] 修复后仍未启用防火墙"
} }
# 更新结果集 # 更新最终状态
$results.Firewall = @{ $results.Firewall.Active = $firewallActive
Active = $firewallActive $results.Firewall.Type = $firewallType
Type = $firewallType $results.Firewall.OpenPorts = $openPorts
OpenPorts = $openPorts $results.Firewall.Status = if ($firewallActive) { "已启用" } else { "未启用" }
Status = if ($firewallActive) { "已启用" } else { "未启用" }
}
} else { } else {
# 安全读取错误信 # 记录失败消
$errMsg = "未知错误" $errMsg = "未知错误"
if ($fwRepairRes -is [hashtable]) { if ($fwRepairRes -is [hashtable]) {
if ($fwRepairRes.ContainsKey('Error') -and $fwRepairRes['Error']) { if ($fwRepairRes.ContainsKey('Error') -and $fwRepairRes['Error']) { $errMsg = [string]::Join(' ', $fwRepairRes['Error']) }
$errMsg = [string]::Join(' ', $fwRepairRes['Error']) elseif ($fwRepairRes.ContainsKey('Output') -and $fwRepairRes['Output']) { $errMsg = [string]::Join(' ', $fwRepairRes['Output']) }
} elseif ($fwRepairRes.ContainsKey('Output') -and $fwRepairRes['Output']) { elseif ($fwRepairRes.ContainsKey('Message') -and $fwRepairRes['Message']) { $errMsg = $fwRepairRes['Message'] }
$errMsg = [string]::Join(' ', $fwRepairRes['Output']) } elseif ($fwRepairRes) { $errMsg = $fwRepairRes.ToString() }
} elseif ($fwRepairRes.ContainsKey('Message') -and $fwRepairRes['Message']) {
$errMsg = $fwRepairRes['Message']
}
} elseif ($fwRepairRes) {
$errMsg = $fwRepairRes.ToString()
}
Write-Log -Level "ERROR" -Message "[FIREWALL] 远端修复执行失败: $errMsg" Write-Log -Level "ERROR" -Message "[FIREWALL] 远端修复执行失败: $errMsg"
$results.Firewall.Repair.Message = "修复失败: $errMsg"
} }
} catch { } catch {
Write-Log -Level "ERROR" -Message "[FIREWALL] 调用 Upload_the_repair_script 异常: $($_.Exception.Message)" Write-Log -Level "ERROR" -Message "[FIREWALL] 调用 Upload_the_repair_script 异常: $($_.Exception.Message)"
$results.Firewall.Repair = @{ Attempted = $true; Succeeded = $false; Message = "异常: $($_.Exception.Message)" }
} }
} }
...@@ -1819,22 +1811,25 @@ function Show-HealthReport { ...@@ -1819,22 +1811,25 @@ function Show-HealthReport {
[array]$ContainerInfo [array]$ContainerInfo
) )
# Markdown 报告初始化(健壮性与回退) if (-not $SCRIPT_DIR -or [string]::IsNullOrWhiteSpace($SCRIPT_DIR)) { $SCRIPT_DIR = (Get-Location).Path }
if (-not $SCRIPT_DIR -or [string]::IsNullOrWhiteSpace($SCRIPT_DIR)) {
$SCRIPT_DIR = (Get-Location).Path
}
$reportDir = Join-Path $SCRIPT_DIR "Reports" $reportDir = Join-Path $SCRIPT_DIR "Reports"
try { try { if (-not (Test-Path $reportDir)) { New-Item -ItemType Directory -Path $reportDir -Force | Out-Null } }
if (-not (Test-Path $reportDir)) { New-Item -ItemType Directory -Path $reportDir -Force | Out-Null } catch { $reportDir = Join-Path $env:TEMP "ubains_reports"; if (-not (Test-Path $reportDir)) { New-Item -ItemType Directory -Path $reportDir -Force | Out-Null } }
} catch {
$reportDir = Join-Path $env:TEMP "ubains_reports"
if (-not (Test-Path $reportDir)) { New-Item -ItemType Directory -Path $reportDir -Force | Out-Null }
}
$ts = Get-Date -Format "yyyyMMdd_HHmmss" $ts = Get-Date -Format "yyyyMMdd_HHmmss"
$safeIp = if ($Server -and $Server.IP) { ($Server.IP -replace '[^\w\.\-]', '_') } else { 'unknown' } $safeIp = if ($Server -and $Server.IP) { ($Server.IP -replace '[^\w\.\-]', '_') } else { 'unknown' }
$mdFile = Join-Path $reportDir "health_report_${safeIp}_${ts}.md" $mdFile = Join-Path $reportDir "health_report_${safeIp}_${ts}.md"
if (-not $mdFile -or [string]::IsNullOrWhiteSpace($mdFile)) { if (-not $mdFile -or [string]::IsNullOrWhiteSpace($mdFile)) { $mdFile = Join-Path $reportDir ("health_report_" + $ts + ".md") }
$mdFile = Join-Path $reportDir ("health_report_" + $ts + ".md")
function Get-StateIcon {
param([string]$Status)
switch ($Status) {
"正常" { "✅" }
"警告" { "⚠️" }
"危险" { "❌" }
"偏差" { "⚠️" }
"未执行" { "ℹ️" }
default { "❌" }
}
} }
$md = @() $md = @()
...@@ -1846,21 +1841,12 @@ function Show-HealthReport { ...@@ -1846,21 +1841,12 @@ function Show-HealthReport {
$md += "- 检测时间: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" $md += "- 检测时间: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
$md += "" $md += ""
# 控制台抬头
Write-Host "" Write-Host ""
Write-Host "==================================================================" -ForegroundColor Cyan Write-Host "==================================================================" -ForegroundColor Cyan
Write-Host " 服务自检报告" -ForegroundColor Cyan Write-Host " 服务自检报告" -ForegroundColor Cyan
Write-Host "==================================================================" -ForegroundColor Cyan Write-Host "==================================================================" -ForegroundColor Cyan
Write-Host "" Write-Host ""
# 基本信息
Write-Host "【基本信息】" -ForegroundColor Yellow
Write-Host " 服务器地址: $($Server.IP)"
Write-Host " 服务器描述: $($Server.Desc)"
Write-Host " 平台类型: $(if ($PlatformType -eq 'new') { '新统一平台' } else { '传统平台' })"
Write-Host " 检测时间: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host ""
# 系统类型识别 # 系统类型识别
$md += "## 系统类型识别" $md += "## 系统类型识别"
if ($SystemInfo.HasUjava) { $md += "- 会议预定系统 (ujava): 已部署 [$($SystemInfo.UjavaContainer)]" } if ($SystemInfo.HasUjava) { $md += "- 会议预定系统 (ujava): 已部署 [$($SystemInfo.UjavaContainer)]" }
...@@ -1870,9 +1856,7 @@ function Show-HealthReport { ...@@ -1870,9 +1856,7 @@ function Show-HealthReport {
$md += "" $md += ""
# 统计信息 # 统计信息
$totalServices = 0 $totalServices = 0; $runningServices = 0; $failedServices = 0
$runningServices = 0
$failedServices = 0
# ujava 容器内服务 # ujava 容器内服务
if ($UjavaContainerResults -and $UjavaContainerResults.Count -gt 0) { if ($UjavaContainerResults -and $UjavaContainerResults.Count -gt 0) {
...@@ -1880,14 +1864,10 @@ function Show-HealthReport { ...@@ -1880,14 +1864,10 @@ function Show-HealthReport {
$md += "## ujava 容器内服务状态" $md += "## ujava 容器内服务状态"
foreach ($r in $UjavaContainerResults) { foreach ($r in $UjavaContainerResults) {
$totalServices++ $totalServices++
if ($r.Running) { if ($r.Running) { $runningServices++; Write-Host " [OK] $($r.Service): $($r.Status)" -ForegroundColor Green }
$runningServices++ else { $failedServices++; Write-Host " [FAIL] $($r.Service): $($r.Status)" -ForegroundColor Red }
Write-Host " [OK] $($r.Service): $($r.Status)" -ForegroundColor Green $icon = if ($r.Running) { "✅" } else { "❌" }
} else { $md += "- $icon $($r.Service) ($($r.Pattern)): $($r.Status)"
$failedServices++
Write-Host " [FAIL] $($r.Service): $($r.Status)" -ForegroundColor Red
}
$md += "- $($r.Service) ($($r.Pattern)): $($r.Status)"
} }
$md += "" $md += ""
Write-Host "" Write-Host ""
...@@ -1899,14 +1879,10 @@ function Show-HealthReport { ...@@ -1899,14 +1879,10 @@ function Show-HealthReport {
$md += "## ujava 宿主机服务状态" $md += "## ujava 宿主机服务状态"
foreach ($r in $UjavaHostResults) { foreach ($r in $UjavaHostResults) {
$totalServices++ $totalServices++
if ($r.Running) { if ($r.Running) { $runningServices++; Write-Host " [OK] $($r.Service): $($r.Status)" -ForegroundColor Green }
$runningServices++ else { $failedServices++; Write-Host " [FAIL] $($r.Service): $($r.Status)" -ForegroundColor Red }
Write-Host " [OK] $($r.Service): $($r.Status)" -ForegroundColor Green $icon = if ($r.Running) { "✅" } else { "❌" }
} else { $md += "- $icon $($r.Service) ($($r.Pattern)): $($r.Status)"
$failedServices++
Write-Host " [FAIL] $($r.Service): $($r.Status)" -ForegroundColor Red
}
$md += "- $($r.Service) ($($r.Pattern)): $($r.Status)"
} }
$md += "" $md += ""
Write-Host "" Write-Host ""
...@@ -1918,14 +1894,10 @@ function Show-HealthReport { ...@@ -1918,14 +1894,10 @@ function Show-HealthReport {
$md += "## upython 容器服务状态" $md += "## upython 容器服务状态"
foreach ($r in $UpythonResults) { foreach ($r in $UpythonResults) {
$totalServices++ $totalServices++
if ($r.Listening) { if ($r.Listening) { $runningServices++; Write-Host " [OK] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Green }
$runningServices++ else { $failedServices++; Write-Host " [FAIL] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Red }
Write-Host " [OK] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Green $icon = if ($r.Listening) { "✅" } else { "❌" }
} else { $md += "- $icon 端口 $($r.Port) ($($r.Description)): $($r.Status)"
$failedServices++
Write-Host " [FAIL] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Red
}
$md += "- 端口 $($r.Port) ($($r.Description)): $($r.Status)"
} }
$md += "" $md += ""
Write-Host "" Write-Host ""
...@@ -1937,14 +1909,10 @@ function Show-HealthReport { ...@@ -1937,14 +1909,10 @@ function Show-HealthReport {
$md += "## upython_voice 容器服务状态" $md += "## upython_voice 容器服务状态"
foreach ($r in $UpythonVoiceResults) { foreach ($r in $UpythonVoiceResults) {
$totalServices++ $totalServices++
if ($r.Listening) { if ($r.Listening) { $runningServices++; Write-Host " [OK] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Green }
$runningServices++ else { $failedServices++; Write-Host " [FAIL] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Red }
Write-Host " [OK] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Green $icon = if ($r.Listening) { "✅" } else { "❌" }
} else { $md += "- $icon 端口 $($r.Port) ($($r.Description)): $($r.Status)"
$failedServices++
Write-Host " [FAIL] 端口 $($r.Port) ($($r.Description)): $($r.Status)" -ForegroundColor Red
}
$md += "- 端口 $($r.Port) ($($r.Description)): $($r.Status)"
} }
$md += "" $md += ""
Write-Host "" Write-Host ""
...@@ -1957,64 +1925,111 @@ function Show-HealthReport { ...@@ -1957,64 +1925,111 @@ function Show-HealthReport {
foreach ($r in $DNSResults) { foreach ($r in $DNSResults) {
$statusIcon = if ($r.Success) { "OK" } else { "FAIL" } $statusIcon = if ($r.Success) { "OK" } else { "FAIL" }
Write-Host " [$statusIcon] $($r.Check): $($r.Status)" Write-Host " [$statusIcon] $($r.Check): $($r.Status)"
$line = "- $($r.Check): $($r.Status)" $icon = if ($r.Success) { "✅" } else { "❌" }
if ($r.Details) { $line += ",$($r.Details)" } $line = "- $icon $($r.Check): $($r.Status)"; if ($r.Details) { $line += ",$($r.Details)" }
$md += $line $md += $line
} }
$md += "" $md += ""
Write-Host "" Write-Host ""
} }
# 服务器资源 # 服务器资源(含异常->修复时间线)
if ($ResourceResults) { if ($ResourceResults) {
Write-Host "【服务器资源分析】" -ForegroundColor Yellow Write-Host "【服务器资源分析】" -ForegroundColor Yellow
$md += "## 服务器资源分析" $md += "## 服务器资源分析"
if ($ResourceResults.OS) { if ($ResourceResults.OS) { Write-Host " 操作系统: $($ResourceResults.OS.Info)" -ForegroundColor Cyan; $md += "- 操作系统: $($ResourceResults.OS.Info)" }
Write-Host " 操作系统: $($ResourceResults.OS.Info)" -ForegroundColor Cyan if ($ResourceResults.Architecture) { Write-Host " 系统架构: $($ResourceResults.Architecture.Arch) | 内核: $($ResourceResults.Architecture.Kernel)" -ForegroundColor Cyan; $md += "- 系统架构: $($ResourceResults.Architecture.Arch) | 内核: $($ResourceResults.Architecture.Kernel)" }
$md += "- 操作系统: $($ResourceResults.OS.Info)"
}
if ($ResourceResults.Architecture) {
Write-Host " 系统架构: $($ResourceResults.Architecture.Arch) | 内核: $($ResourceResults.Architecture.Kernel)" -ForegroundColor Cyan
$md += "- 系统架构: $($ResourceResults.Architecture.Arch) | 内核: $($ResourceResults.Architecture.Kernel)"
}
if ($ResourceResults.CPU) { if ($ResourceResults.CPU) {
$cpuIcon = Get-StateIcon -Status $ResourceResults.CPU.Status
Write-Host " CPU 使用率: $($ResourceResults.CPU.Usage)% (核心数: $($ResourceResults.CPU.Cores)) [$($ResourceResults.CPU.Status)]" Write-Host " CPU 使用率: $($ResourceResults.CPU.Usage)% (核心数: $($ResourceResults.CPU.Cores)) [$($ResourceResults.CPU.Status)]"
$md += "- CPU 使用率: $($ResourceResults.CPU.Usage)% (核心数: $($ResourceResults.CPU.Cores)) [$($ResourceResults.CPU.Status)]" $md += "- $cpuIcon CPU 使用率: $($ResourceResults.CPU.Usage)% (核心数: $($ResourceResults.CPU.Cores)) [$($ResourceResults.CPU.Status)]"
} }
if ($ResourceResults.Memory) { if ($ResourceResults.Memory) {
$memIcon = Get-StateIcon -Status $ResourceResults.Memory.Status
Write-Host " 内存使用: $($ResourceResults.Memory.Used)GB / $($ResourceResults.Memory.Total)GB ($($ResourceResults.Memory.Percent)%) [$($ResourceResults.Memory.Status)]" Write-Host " 内存使用: $($ResourceResults.Memory.Used)GB / $($ResourceResults.Memory.Total)GB ($($ResourceResults.Memory.Percent)%) [$($ResourceResults.Memory.Status)]"
$md += "- 内存使用: $($ResourceResults.Memory.Used)GB / $($ResourceResults.Memory.Total)GB ($($ResourceResults.Memory.Percent)%) [$($ResourceResults.Memory.Status)]" $md += "- $memIcon 内存使用: $($ResourceResults.Memory.Used)GB / $($ResourceResults.Memory.Total)GB ($($ResourceResults.Memory.Percent)%) [$($ResourceResults.Memory.Status)]"
} }
if ($ResourceResults.Disk -and $ResourceResults.Disk.Count -gt 0) { if ($ResourceResults.Disk -and $ResourceResults.Disk.Count -gt 0) {
Write-Host " 磁盘使用情况:" Write-Host " 磁盘使用情况:"; $md += "- 磁盘使用:"
$md += "- 磁盘使用:"
foreach ($disk in $ResourceResults.Disk) { foreach ($disk in $ResourceResults.Disk) {
$diskIcon = switch ($disk.Status) { "正常" {"✅"} "警告" {"⚠️"} default {"❌"} }
Write-Host " $($disk.MountPoint): $($disk.Used)/$($disk.Size) ($($disk.Percent)%) [$($disk.Status)]" Write-Host " $($disk.MountPoint): $($disk.Used)/$($disk.Size) ($($disk.Percent)%) [$($disk.Status)]"
$md += " - $($disk.MountPoint): $($disk.Used)/$($disk.Size) ($($disk.Percent)%) [$($disk.Status)]" $md += " - $diskIcon $($disk.MountPoint): $($disk.Used)/$($disk.Size) ($($disk.Percent)%) [$($disk.Status)]"
} }
} }
# 防火墙详细时间线
if ($ResourceResults.Firewall) { if ($ResourceResults.Firewall) {
$fwIcon = if ($ResourceResults.Firewall.Active) { "🟢" } else { "🔴" }
Write-Host " 防火墙状态: $($ResourceResults.Firewall.Status) ($($ResourceResults.Firewall.Type))" Write-Host " 防火墙状态: $($ResourceResults.Firewall.Status) ($($ResourceResults.Firewall.Type))"
$md += "- 防火墙状态: $($ResourceResults.Firewall.Status) ($($ResourceResults.Firewall.Type))" $md += "- $fwIcon 防火墙状态: $($ResourceResults.Firewall.Status) ($($ResourceResults.Firewall.Type))"
if ($ResourceResults.Firewall.OpenPorts -and $ResourceResults.Firewall.Active) { if ($ResourceResults.Firewall.OpenPorts -and $ResourceResults.Firewall.Active) {
Write-Host " 开放端口/服务: $($ResourceResults.Firewall.OpenPorts)" Write-Host " 开放端口/服务: $($ResourceResults.Firewall.OpenPorts)"
$md += " - 开放端口/服务: $($ResourceResults.Firewall.OpenPorts)" $md += " - 开放端口/服务: $($ResourceResults.Firewall.OpenPorts)"
} }
# 时间线:初始异常 -> 触发修复 -> 修复后
$hasPre = $ResourceResults.Firewall.PSObject.Properties['Pre'] -ne $null
$hasRep = $ResourceResults.Firewall.PSObject.Properties['Repair'] -ne $null
if ($hasPre -or $hasRep) {
$md += "- 修复时间线"
if ($hasPre -and $ResourceResults.Firewall.Pre) {
$pre = $ResourceResults.Firewall.Pre
$preIcon = if ($pre.Active) { "🟢" } else { "🔴" }
$preType = if ($pre.Type) { $pre.Type } else { "unknown" }
$md += " - $preIcon 初始状态: $(if ($pre.Active) {'已启用'} else {'未启用'}) ($preType)"
if ($pre.OpenPorts) { $md += " - 初始端口/服务: $($pre.OpenPorts)" }
}
if ($hasRep -and $ResourceResults.Firewall.Repair) {
$rep = $ResourceResults.Firewall.Repair
$attempted = ($rep.PSObject.Properties['Attempted'] -and $rep.Attempted)
$succeeded = ($rep.PSObject.Properties['Succeeded'] -and $rep.Succeeded)
$message = if ($rep.PSObject.Properties['Message']) { $rep.Message } else { $null }
$md += " - 🛠️ 修复触发: $(if ($attempted) {'已触发'} else {'未触发'})"
if ($message) { $md += " - 修复说明: $message" }
$md += " - $(if ($succeeded) {'✅ 修复结果: 成功'} else {'⚠️ 修复结果: 未确认/失败'})"
}
# 修复后对照
$postIcon = if ($ResourceResults.Firewall.Active) { "🟢" } else { "🔴" }
$postType = if ($ResourceResults.Firewall.Type) { $ResourceResults.Firewall.Type } else { "unknown" }
$md += " - $postIcon 修复后状态: $($ResourceResults.Firewall.Status) ($postType)"
if ($ResourceResults.Firewall.OpenPorts) { $md += " - 修复后端口/服务: $($ResourceResults.Firewall.OpenPorts)" }
}
} }
$md += "" $md += ""
Write-Host "" Write-Host ""
} }
# NTP 服务 # NTP 服务(异常->修复时间线)
Write-Host "【NTP 服务检测】" -ForegroundColor Yellow Write-Host "【NTP 服务检测】" -ForegroundColor Yellow
$md += "## NTP 服务检测" $md += "## NTP 服务检测"
if ($NTPResults) { if ($NTPResults) {
Write-Host " $($NTPResults.Status): $($NTPResults.Detail)" Write-Host " $($NTPResults.Status): $($NTPResults.Detail)"
$md += "- 状态: $($NTPResults.Status)" $ntpIcon = Get-StateIcon -Status $NTPResults.Status
$md += "- 状态: $ntpIcon $($NTPResults.Status)"
if ($NTPResults.Detail) { $md += "- 详情: $($NTPResults.Detail)" } if ($NTPResults.Detail) { $md += "- 详情: $($NTPResults.Detail)" }
# 解析时间线:初始异常->修复->复检
$timeline = @()
if ($NTPResults.Detail -match '未检测到|未运行|不可用|偏差|失败') {
$timeline += "🔴 初次检测: 异常/偏差"
}
if ($NTPResults.Detail -match '已修复') {
$timeline += "✅ 修复执行: 已自动修复"
} elseif ($NTPResults.Detail -match '已尝试修复') {
$timeline += "⚠️ 修复执行: 已尝试修复"
}
if ($NTPResults.Detail -match '时间差\s*\d+s') {
$diffStr = [regex]::Match($NTPResults.Detail, '时间差\s*\d+s').Value
$timeline += "🟢 复检结果: $diffStr"
}
if ($timeline.Count -gt 0) {
$md += "- 修复时间线"
foreach ($t in $timeline) { $md += " - $t" }
}
} else { } else {
Write-Host " 未执行" -ForegroundColor Yellow Write-Host " 未执行" -ForegroundColor Yellow
$md += "- 未执行" $md += "- 状态: ℹ️ 未执行"
} }
$md += "" $md += ""
Write-Host "" Write-Host ""
...@@ -2024,15 +2039,12 @@ function Show-HealthReport { ...@@ -2024,15 +2039,12 @@ function Show-HealthReport {
$md += "## 文件权限检测" $md += "## 文件权限检测"
if ($FilePermResults) { if ($FilePermResults) {
Write-Host " $($FilePermResults.Summary)" Write-Host " $($FilePermResults.Summary)"
$md += "- 总结: $($FilePermResults.Summary)" $permHasMiss = $false
if ($FilePermResults.Lines) { if ($FilePermResults.Lines) { foreach ($l in $FilePermResults.Lines) { if ($l -match '^\s*MISS\s+') { $permHasMiss = $true; break } } }
$md += "- 明细:" $permIcon = if ($permHasMiss) { "⚠️" } else { "✅" }
foreach ($l in $FilePermResults.Lines) { $md += " - $l" } $md += "- 总结: $permIcon $($FilePermResults.Summary)"
} if ($FilePermResults.Lines) { $md += "- 明细:"; foreach ($l in $FilePermResults.Lines) { $md += " - $l" } }
} else { } else { Write-Host " 未执行" -ForegroundColor Yellow; $md += "- 总结: ℹ️ 未执行" }
Write-Host " 未执行" -ForegroundColor Yellow
$md += "- 未执行"
}
$md += "" $md += ""
# 容器信息 # 容器信息
...@@ -2040,8 +2052,7 @@ function Show-HealthReport { ...@@ -2040,8 +2052,7 @@ function Show-HealthReport {
if (-not $ContainerInfo -or $ContainerInfo.Count -eq 0) { if (-not $ContainerInfo -or $ContainerInfo.Count -eq 0) {
Write-Host "【容器信息】未发现容器或容器运行时不可用" -ForegroundColor Yellow Write-Host "【容器信息】未发现容器或容器运行时不可用" -ForegroundColor Yellow
$md += "- 未发现容器或容器运行时不可用" $md += "- 未发现容器或容器运行时不可用"
} } else {
else {
$runningCount = ($ContainerInfo | Where-Object { $_.Running }).Count $runningCount = ($ContainerInfo | Where-Object { $_.Running }).Count
$stoppedCount = $ContainerInfo.Count - $runningCount $stoppedCount = $ContainerInfo.Count - $runningCount
Write-Host "【容器信息】总数: $($ContainerInfo.Count) | 运行中: $runningCount | 已停止: $stoppedCount" -ForegroundColor Yellow Write-Host "【容器信息】总数: $($ContainerInfo.Count) | 运行中: $runningCount | 已停止: $stoppedCount" -ForegroundColor Yellow
...@@ -2060,41 +2071,14 @@ function Show-HealthReport { ...@@ -2060,41 +2071,14 @@ function Show-HealthReport {
$showNets = if ($nets.Count -gt 6) { ($nets | Select-Object -First 6) + "..." } else { $nets } $showNets = if ($nets.Count -gt 6) { ($nets | Select-Object -First 6) + "..." } else { $nets }
$mountsStr = '-' $mountsStr = '-'
if ($c.Mounts -and $c.Mounts.Count -gt 0) { if ($c.Mounts -and $c.Mounts.Count -gt 0) {
$showMounts = $null $showMounts = if ($c.Mounts.Count -gt 3) { ($c.Mounts | Select-Object -First 3) + '...' } else { $c.Mounts }
if ($c.Mounts.Count -gt 3) {
$showMounts = @()
$showMounts += ($c.Mounts | Select-Object -First 3)
$showMounts += '...'
}
else {
$showMounts = $c.Mounts
}
$mountsStr = ($showMounts -join '; ') $mountsStr = ($showMounts -join '; ')
} }
$szParts = @(); if ($c.SizeRw -ne $null) { $szParts += ("rw={0}" -f $c.SizeRw) }; if ($c.SizeRootFs -ne $null) { $szParts += ("root={0}" -f $c.SizeRootFs) } $szParts = @(); if ($c.SizeRw -ne $null) { $szParts += ("rw={0}" -f $c.SizeRw) }; if ($c.SizeRootFs -ne $null) { $szParts += ("root={0}" -f $c.SizeRootFs) }
$szStr = if ($szParts.Count -gt 0) { ($szParts -join ', ') } else { '-' } $szStr = if ($szParts.Count -gt 0) { ($szParts -join ', ') } else { '-' }
$md += "- $statusIcon 名称: $($c.Name) | 镜像: $($c.Image) | 状态: $($c.Status) | 健康: $health | 重启: $rp/$rc | IP: $ip" $md += "- $statusIcon 名称: $($c.Name) | 镜像: $($c.Image) | 状态: $($c.Status) | 健康: $health | 重启: $rp/$rc | IP: $ip"
# 规范化端口/网络字符串,兼容数组或字符串 $portsStr = '-'; if ($null -ne $showPorts) { if ($showPorts -is [string]) { $portsStr = $showPorts } else { $tmp = @($showPorts); if ($tmp.Count -gt 0) { $portsStr = ($tmp -join ', ') } } }
$portsStr = '-' $netsStr = '-'; if ($null -ne $showNets) { if ($showNets -is [string]) { $netsStr = $showNets } else { $tmpn = @($showNets); if ($tmpn.Count -gt 0) { $netsStr = ($tmpn -join ', ') } } }
if ($null -ne $showPorts) {
if ($showPorts -is [string]) {
$portsStr = $showPorts
}
else {
$tmp = @($showPorts)
if ($tmp.Count -gt 0) { $portsStr = ($tmp -join ', ') }
}
}
$netsStr = '-'
if ($null -ne $showNets) {
if ($showNets -is [string]) {
$netsStr = $showNets
}
else {
$tmpn = @($showNets)
if ($tmpn.Count -gt 0) { $netsStr = ($tmpn -join ', ') }
}
}
$md += " - 端口: $portsStr" $md += " - 端口: $portsStr"
$md += " - 网络: $netsStr" $md += " - 网络: $netsStr"
if ($mountsStr -ne '-') { $md += " - 挂载: $mountsStr" } if ($mountsStr -ne '-') { $md += " - 挂载: $mountsStr" }
...@@ -2110,72 +2094,43 @@ function Show-HealthReport { ...@@ -2110,72 +2094,43 @@ function Show-HealthReport {
Write-Host " 正常运行: $runningServices" -ForegroundColor Green Write-Host " 正常运行: $runningServices" -ForegroundColor Green
Write-Host " 异常服务: $failedServices" -ForegroundColor $(if ($failedServices -gt 0) { "Red" } else { "Green" }) Write-Host " 异常服务: $failedServices" -ForegroundColor $(if ($failedServices -gt 0) { "Red" } else { "Green" })
$overallIcon = if ($failedServices -eq 0 -and $totalServices -gt 0) { "✅" } elseif ($failedServices -gt 0) { "❌" } else { "ℹ️" }
$md += "## 检测总结" $md += "## 检测总结"
$md += "- 总服务数: $totalServices" $md += "- 总服务数: $totalServices"
$md += "- 正常运行: $runningServices" $md += "- 正常运行: $runningServices"
$md += "- 异常服务: $failedServices" $md += "- 异常服务: $failedServices"
if ($failedServices -eq 0 -and $totalServices -gt 0) { if ($failedServices -eq 0 -and $totalServices -gt 0) { Write-Host ""; Write-Host " 所有服务运行正常!" -ForegroundColor Green; $md += "- 结论: $overallIcon 所有服务运行正常!" }
Write-Host "" elseif ($failedServices -gt 0) { Write-Host ""; Write-Host " 存在异常服务,请及时处理!" -ForegroundColor Red; $md += "- 结论: $overallIcon 存在异常服务,请及时处理!" }
Write-Host " 所有服务运行正常!" -ForegroundColor Green elseif ($totalServices -eq 0) { Write-Host ""; Write-Host " 未检测到任何服务" -ForegroundColor Yellow; $md += "- 结论: $overallIcon 未检测到任何服务" }
$md += "- 结论: 所有服务运行正常!"
} elseif ($failedServices -gt 0) {
Write-Host ""
Write-Host " 存在异常服务,请及时处理!" -ForegroundColor Red
$md += "- 结论: 存在异常服务,请及时处理!"
} elseif ($totalServices -eq 0) {
Write-Host ""
Write-Host " 未检测到任何服务" -ForegroundColor Yellow
$md += "- 结论: 未检测到任何服务"
}
# 日志导出 # 日志导出
if ($LogExportResults) { if ($LogExportResults) {
Write-Host "【日志导出结果】" -ForegroundColor Yellow Write-Host "【日志导出结果】" -ForegroundColor Yellow
$md += "" $md += ""; $md += "## 服务日志导出"
$md += "## 服务日志导出"
if ($LogExportResults.Success -and $LogExportResults.ExportedFiles.Count -gt 0) { if ($LogExportResults.Success -and $LogExportResults.ExportedFiles.Count -gt 0) {
Write-Host " 导出状态: 成功" -ForegroundColor Green Write-Host " 导出状态: 成功" -ForegroundColor Green
Write-Host " 导出目录: $($LogExportResults.ExportDir)" -ForegroundColor Cyan Write-Host " 导出目录: $($LogExportResults.ExportDir)" -ForegroundColor Cyan
Write-Host " 成功导出 $($LogExportResults.ExportedFiles.Count) 个文件:" -ForegroundColor Green Write-Host " 成功导出 $($LogExportResults.ExportedFiles.Count) 个文件:" -ForegroundColor Green
$md += "- 导出状态: 成功" $md += "- 导出状态: 成功"
$md += "- 导出目录: $($LogExportResults.ExportDir)" $md += "- 导出目录: $($LogExportResults.ExportDir)"
$md += "- 成功文件:" $md += "- 成功文件:"
foreach ($file in $LogExportResults.ExportedFiles) { foreach ($file in $LogExportResults.ExportedFiles) { $sizeKB = [math]::Round($file.Size / 1024, 2); Write-Host " - $($file.Name) ($sizeKB KB)" -ForegroundColor Gray; $md += " - $($file.Name) ($sizeKB KB)" }
$sizeKB = [math]::Round($file.Size / 1024, 2)
Write-Host " - $($file.Name) ($sizeKB KB)" -ForegroundColor Gray
$md += " - $($file.Name) ($sizeKB KB)"
}
} elseif ($LogExportResults.ExportedFiles.Count -eq 0 -and $LogExportResults.FailedFiles.Count -eq 0) { } elseif ($LogExportResults.ExportedFiles.Count -eq 0 -and $LogExportResults.FailedFiles.Count -eq 0) {
Write-Host " 导出状态: 无需导出的日志文件" -ForegroundColor Yellow Write-Host " 导出状态: 无需导出的日志文件" -ForegroundColor Yellow
$md += "- 导出状态: 无需导出的日志文件" $md += "- 导出状态: ℹ️ 无需导出的日志文件"
} else { } else {
Write-Host " 导出状态: 部分失败" -ForegroundColor Yellow Write-Host " 导出状态: 部分失败" -ForegroundColor Yellow
$md += "- 导出状态: 部分失败" $md += "- 导出状态: ⚠️ 部分失败"
if ($LogExportResults.FailedFiles.Count -gt 0) { if ($LogExportResults.FailedFiles.Count -gt 0) { $md += "- 失败文件:"; foreach ($file in $LogExportResults.FailedFiles) { $md += " - $($file.Name): $($file.Reason)" } }
$md += "- 失败文件:"
foreach ($file in $LogExportResults.FailedFiles) {
$md += " - $($file.Name): $($file.Reason)"
}
}
} }
Write-Host "" Write-Host ""
} }
Write-Host "==================================================================" -ForegroundColor Cyan Write-Host "==================================================================" -ForegroundColor Cyan
Write-Host "" Write-Host ""; Write-Host "日志文件: $LOG_FILE"; Write-Host ""
Write-Host "日志文件: $LOG_FILE"
Write-Host ""
# 写入 Markdown 文件(带异常处理) try { ($md -join "`r`n") | Out-File -FilePath $mdFile -Encoding utf8; Write-Log -Level "SUCCESS" -Message "Markdown 报告已生成: $mdFile" }
try { catch { Write-Log -Level "ERROR" -Message "Markdown 写入失败: $($_.Exception.Message)"; Write-Log -Level "ERROR" -Message "报告目录: $reportDir"; Write-Log -Level "ERROR" -Message "文件路径: $mdFile" }
$content = ($md -join "`r`n")
$content | Out-File -FilePath $mdFile -Encoding utf8
Write-Log -Level "SUCCESS" -Message "Markdown 报告已生成: $mdFile"
} catch {
Write-Log -Level "ERROR" -Message "Markdown 写入失败: $($_.Exception.Message)"
Write-Log -Level "ERROR" -Message "报告目录: $reportDir"
Write-Log -Level "ERROR" -Message "文件路径: $mdFile"
}
} }
# ================================ # ================================
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论