# 完全兼容你的环境,所有原有指标和格式不变 Add-PSSnapin -Name VeeamPSSnapIn -ErrorAction SilentlyContinue # 配置项 $JobName = "veeam_backup" $InstanceName = $env:COMPUTERNAME $OutputFile = Join-Path -Path $PWD.Path -ChildPath "veeam_metrics.prom" # Linux 上传配置 $linuxUser = "root" $linuxIP = "10.150.117.190" $linuxPath = "/opt/prom_textfile/" $localFile = ".\veeam_metrics.prom" # 恢复任务采集配置:采集最近多少小时内的恢复会话 $RestoreSessionHours = 24 while ($true) { $Metrics = @() $TodayStart = (Get-Date).Date $GlobalTotalJobs = 0 $GlobalSuccessJobs = 0 $GlobalFailedJobs = 0 $GlobalRunningJobs = 0 $GlobalDailyTotalBackup = 0 # ================== 指标头部(更新包含恢复任务)================== $Metrics += "# HELP veeam_job_status Veeam all job status (0=Success,1=Warning,2=Failed,3=None)" $Metrics += "# TYPE veeam_job_status gauge" $Metrics += "# HELP veeam_job_is_running Veeam job is running (1=running,0=idle)" $Metrics += "# TYPE veeam_job_is_running gauge" $Metrics += "# HELP veeam_job_progress_percent Veeam job progress percentage" $Metrics += "# TYPE veeam_job_progress_percent gauge" $Metrics += "# HELP veeam_job_backup_size_gb Veeam last job processed size in GB" $Metrics += "# TYPE veeam_job_backup_size_gb gauge" $Metrics += "# HELP veeam_job_data_size_gb Veeam last job original data size in GB" $Metrics += "# TYPE veeam_job_data_size_gb gauge" $Metrics += "# HELP veeam_job_duration_seconds Veeam last job duration in seconds" $Metrics += "# TYPE veeam_job_duration_seconds gauge" $Metrics += "# HELP veeam_job_daily_backup_size_gb Veeam job total processed size today in GB" $Metrics += "# TYPE veeam_job_daily_backup_size_gb gauge" $Metrics += "# HELP veeam_repo_free_space_gb Veeam repository free space in GB" $Metrics += "# TYPE veeam_repo_free_space_gb gauge" $Metrics += "# HELP veeam_repo_total_space_gb Veeam repository total space in GB" $Metrics += "# TYPE veeam_repo_total_space_gb gauge" # 全局统计指标 $Metrics += "# HELP veeam_global_total_jobs Total number of all Veeam jobs" $Metrics += "# TYPE veeam_global_total_jobs gauge" $Metrics += "# HELP veeam_global_success_jobs Number of successful Veeam jobs" $Metrics += "# TYPE veeam_global_success_jobs gauge" $Metrics += "# HELP veeam_global_failed_jobs Number of failed Veeam jobs" $Metrics += "# TYPE veeam_global_failed_jobs gauge" $Metrics += "# HELP veeam_global_running_jobs Number of running Veeam jobs" $Metrics += "# TYPE veeam_global_running_jobs gauge" $Metrics += "# HELP veeam_global_daily_total_gb Total processed size of all jobs today in GB" $Metrics += "# TYPE veeam_global_daily_total_gb gauge" $Metrics += "# HELP veeam_global_success_rate Overall Veeam job success rate today (0-100)" $Metrics += "# TYPE veeam_global_success_rate gauge" # 单任务健康度指标 $Metrics += "# HELP veeam_job_last_success_timestamp Last successful job timestamp (Unix milliseconds)" $Metrics += "# TYPE veeam_job_last_success_timestamp gauge" $Metrics += "# HELP veeam_job_since_last_success_hours Hours since last successful job" $Metrics += "# TYPE veeam_job_since_last_success_hours gauge" $Metrics += "# HELP veeam_job_compression_ratio Veeam job compression ratio (data_size/backup_size)" $Metrics += "# TYPE veeam_job_compression_ratio gauge" # 存储智能预警指标 $Metrics += "# HELP veeam_repo_usage_percent Repository usage percentage (0-100)" $Metrics += "# TYPE veeam_repo_usage_percent gauge" $Metrics += "# HELP veeam_repo_remaining_days Estimated remaining days based on daily rate" $Metrics += "# TYPE veeam_repo_remaining_days gauge" $Metrics += "" # ================== 1. 采集所有持久化计划任务 ================== $Jobs = Get-VBRJob | Where-Object { $_.JobType -in "Backup", "Restore", "VmRestore", "FileRestore", "Rescan", "Replication", "BackupCopy", "TapeBackup", "TapeCatalog", "FileToTape", "SureBackup", "BackupSync", "BackupValidation", "ConfigurationBackup", "AgentBackup", "AgentRestore", "NASBackup", "NASRestore", "CloudBackup", "CloudRestore" } $AllSessions = Get-VBRBackupSession $Now = Get-Date # 提前获取当前时间,避免循环中多次调用 # ================== ✅ 新增:采集最近24小时所有恢复会话(手动+计划)================== $RestoreCutoffTime = $Now.AddHours(-$RestoreSessionHours) $AllRestoreSessions = Get-VBRRestoreSession | Where-Object { $_.CreationTime -ge $RestoreCutoffTime } # 将恢复会话转换为统一的任务对象格式 $RestoreTasks = @() foreach ($Session in $AllRestoreSessions) { # 跳过已经在计划任务中的恢复任务(去重) if ($Jobs | Where-Object { $_.Id -eq $Session.JobId }) { continue } $RestoreTask = [PSCustomObject]@{ Id = $Session.Id Name = "Restore_" + $Session.Name -replace '[^a-zA-Z0-9_]', '_' JobType = "RestoreSession" LastSession = $Session } $RestoreTasks += $RestoreTask } # 合并计划任务和恢复任务 $AllJobs = $Jobs + $RestoreTasks $GlobalTotalJobs = $AllJobs.Count # ================== 统一处理所有任务 ================== foreach ($Job in $AllJobs) { # 区分计划任务和临时恢复会话 if ($Job.JobType -eq "RestoreSession") { $LastSession = $Job.LastSession } else { $LastSession = $Job.FindLastSession() } $Status = if ($LastSession) { $LastSession.Result } else { "None" } $State = if ($LastSession) { $LastSession.State } else { "Idle" } $StatusCode = switch ($Status) { "Success" { 0; $GlobalSuccessJobs++ } "Warning" { 1; $GlobalSuccessJobs++ } "Failed" { 2; $GlobalFailedJobs++ } default { 3 } } $IsRunning = if ($State -eq "Working") { 1; $GlobalRunningJobs++ } else { 0 } $CleanJobName = $Job.Name -replace '[^a-zA-Z0-9_]', '_' -replace '[\r\n`"''"]', '' # 基础指标(完全不变) $Metrics += "veeam_job_status{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $StatusCode" $Metrics += "veeam_job_is_running{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $IsRunning" $Metrics += "veeam_job_progress_percent{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $(if($LastSession-and$State-eq'Working'){$LastSession.Progress.Percents}else{0})" # 任务大小和数据量(兼容恢复会话) if ($LastSession) { if ($State -eq "Working") { $BackupSizeGB = if ($LastSession.Info.Progress.BackupSize) { [math]::Round($LastSession.Info.Progress.BackupSize / 1GB, 2) } else { 0 } $DataSizeGB = if ($LastSession.Info.Progress.DataSize) { [math]::Round($LastSession.Info.Progress.DataSize / 1GB, 2) } else { 0 } } else { $BackupSizeGB = if ($LastSession.BackupStats.BackupSize) { [math]::Round($LastSession.BackupStats.BackupSize / 1GB, 2) } else { 0 } $DataSizeGB = if ($LastSession.BackupStats.DataSize) { [math]::Round($LastSession.BackupStats.DataSize / 1GB, 2) } else { 0 } } $DurationSeconds = if ($LastSession.EndTime -and $LastSession.CreationTime) { [math]::Round(($LastSession.EndTime - $LastSession.CreationTime).TotalSeconds, 2) } else { 0 } } else { $BackupSizeGB = 0 $DataSizeGB = 0 $DurationSeconds = 0 } $Metrics += "veeam_job_backup_size_gb{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $BackupSizeGB" $Metrics += "veeam_job_data_size_gb{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $DataSizeGB" $Metrics += "veeam_job_duration_seconds{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $DurationSeconds" # 当日任务量(包含恢复任务) $TodaySessions = $AllSessions | Where-Object { $_.JobId -eq $Job.Id -and $_.CreationTime -ge $TodayStart } $DailyBackupSizeGB = 0 foreach ($Session in $TodaySessions) { if ($Session.BackupStats.BackupSize) { $DailyBackupSizeGB += $Session.BackupStats.BackupSize / 1GB } } $DailyBackupSizeGB = [math]::Round($DailyBackupSizeGB, 2) $GlobalDailyTotalBackup += $DailyBackupSizeGB $Metrics += "veeam_job_daily_backup_size_gb{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $DailyBackupSizeGB" # 任务健康度指标(兼容恢复会话) if ($Job.JobType -eq "RestoreSession") { # 恢复会话直接使用当前会话作为最后成功会话 $LastSuccessSession = if ($Status -eq "Success") { $LastSession } else { $null } } else { $JobSuccessSessions = $AllSessions | Where-Object { $_.JobId -eq $Job.Id -and $_.Result -eq "Success" } | Sort-Object EndTime -Descending $LastSuccessSession = $JobSuccessSessions | Select-Object -First 1 } if ($LastSuccessSession -and $LastSuccessSession.EndTime) { $LastSuccessTimestamp = [math]::Round($LastSuccessSession.EndTime.ToUniversalTime().Subtract((Get-Date "1970-01-01")).TotalMilliseconds, 0) $TimeDiff = ($Now) - $LastSuccessSession.EndTime $SinceLastSuccessHours = [math]::Round($TimeDiff.TotalHours, 1) } else { $LastSuccessTimestamp = 0 $SinceLastSuccessHours = 9999 } $Metrics += "veeam_job_last_success_timestamp{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $LastSuccessTimestamp" $Metrics += "veeam_job_since_last_success_hours{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $SinceLastSuccessHours" # 压缩率(恢复任务压缩率为0) $CompressionRatio = if ($BackupSizeGB -gt 0) { [math]::Round($DataSizeGB / $BackupSizeGB, 2) } else { 0 } $Metrics += "veeam_job_compression_ratio{job=`"$JobName`",instance=`"$InstanceName`",job_name=`"$CleanJobName`"} $CompressionRatio" } # 全局统计(完全不变) $GlobalSuccessRate = if ($GlobalTotalJobs -gt 0) { [math]::Round(($GlobalSuccessJobs / $GlobalTotalJobs) * 100, 1) } else { 0 } $Metrics += "veeam_global_total_jobs{job=`"$JobName`",instance=`"$InstanceName`"} $GlobalTotalJobs" $Metrics += "veeam_global_success_jobs{job=`"$JobName`",instance=`"$InstanceName`"} $GlobalSuccessJobs" $Metrics += "veeam_global_failed_jobs{job=`"$JobName`",instance=`"$InstanceName`"} $GlobalFailedJobs" $Metrics += "veeam_global_running_jobs{job=`"$JobName`",instance=`"$InstanceName`"} $GlobalRunningJobs" $Metrics += "veeam_global_daily_total_gb{job=`"$JobName`",instance=`"$InstanceName`"} $([math]::Round($GlobalDailyTotalBackup, 2))" $Metrics += "veeam_global_success_rate{job=`"$JobName`",instance=`"$InstanceName`"} $GlobalSuccessRate" $Metrics += "" # ================== 2. 存储库指标(完全不变)================== $Repositories = Get-VBRBackupRepository foreach ($Repo in $Repositories) { $Path = $Repo.Path $DriveLetter = (Split-Path $Path -Qualifier).TrimEnd(':') $DiskInfo = Get-WmiObject Win32_LogicalDisk -Filter "DeviceID='$DriveLetter`:'" -ErrorAction SilentlyContinue $FreeSpaceGB = if ($DiskInfo -and $DiskInfo.FreeSpace) { [math]::Round($DiskInfo.FreeSpace / 1GB, 2) } else { 0 } $TotalSpaceGB = if ($DiskInfo -and $DiskInfo.Size) { [math]::Round($DiskInfo.Size / 1GB, 2) } else { 0 } $CleanRepoName = $Repo.Name -replace '[^a-zA-Z0-9_]', '_' -replace '[\r\n`"''"]', '' $Metrics += "veeam_repo_free_space_gb{job=`"$JobName`",instance=`"$InstanceName`",repo_name=`"$CleanRepoName`"} $FreeSpaceGB" $Metrics += "veeam_repo_total_space_gb{job=`"$JobName`",instance=`"$InstanceName`",repo_name=`"$CleanRepoName`"} $TotalSpaceGB" # 存储使用率和剩余天数 $UsagePercent = if ($TotalSpaceGB -gt 0) { [math]::Round((($TotalSpaceGB - $FreeSpaceGB) / $TotalSpaceGB) * 100, 1) } else { 0 } $Metrics += "veeam_repo_usage_percent{job=`"$JobName`",instance=`"$InstanceName`",repo_name=`"$CleanRepoName`"} $UsagePercent" $RemainingDays = if ($GlobalDailyTotalBackup -gt 0 -and $FreeSpaceGB -gt 0) { [math]::Round($FreeSpaceGB / $GlobalDailyTotalBackup, 1) } else { 999 } $Metrics += "veeam_repo_remaining_days{job=`"$JobName`",instance=`"$InstanceName`",repo_name=`"$CleanRepoName`"} $RemainingDays" } # ================== 格式保证 & 上传(完全不变)================== $Metrics += "" $MetricsBody = $Metrics -join "`n" -replace "`r", "" [System.IO.File]::WriteAllText($OutputFile, $MetricsBody, [System.Text.UTF8Encoding]::new($false)) Write-Host "`n📤 正在上传指标文件..." -ForegroundColor Cyan scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL $localFile "${linuxUser}@${linuxIP}:${linuxPath}" if ($LASTEXITCODE -eq 0) { ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=NUL "${linuxUser}@${linuxIP}" "sed -i 's/\r$//;s/[^[:print:]]//g' ${linuxPath}veeam_metrics.prom && chmod 644 ${linuxPath}veeam_metrics.prom" Write-Host "✅ [$((Get-Date).ToString('yyyy-MM-dd HH:mm:ss'))] 采集完成" Write-Host " - 总任务: $GlobalTotalJobs | 成功: $GlobalSuccessJobs | 失败: $GlobalFailedJobs | 运行中: $GlobalRunningJobs" Write-Host " - 今日总处理量: $([math]::Round($GlobalDailyTotalBackup, 2)) GB | 成功率: $GlobalSuccessRate%" Write-Host " - 已包含计划任务+最近$RestoreSessionHours小时恢复任务,等待60秒`n" -ForegroundColor Green } else { Write-Host "❌ 上传失败,请检查SSH免密配置" -ForegroundColor Red } Start-Sleep -Seconds 60 }