diff --git a/zabbix_vbr_job.ps1 b/zabbix_vbr_job.ps1 new file mode 100644 index 0000000..1516eaf --- /dev/null +++ b/zabbix_vbr_job.ps1 @@ -0,0 +1,255 @@ +[CmdletBinding()] +param( + [Parameter(Position = 0, Mandatory = $false)] + [ValidateSet("RepoInfo", "JobsInfo", "TotalJob")] + [System.String]$Operation +) + +# Загрузка библиотеки Npgsql +Add-Type -Path "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Npgsql\v4.0_4.0.12.0__5d8b90d52f46fda7\Npgsql.dll" + +########### Adjust the following variables to match your configuration ########### +$veeamserver = 'localhost' # Machine name where Veeam is installed +$SQLServer = 'localhost' # Database server where Veeam database is located +$SQLIntegratedSecurity = $false # Use Windows integrated security? +$SQLuid = 'zabbixveeam' # SQL Username when using SQL Authentication - ignored if using Integrated security +$SQLpwd = 'CHANGEME' # SQL user password +$SQLveeamdb = 'VeeamBackup' # Name of Veeam database. VeeamBackup is the default + +$typeNames = @{ + 0 = "Job"; + 1 = "Replication"; + 2 = "File"; + 28 = "Tape"; + 51 = "Sync"; + 63 = "Copy"; + 4030 = "RMAN"; + 12002 = "Agent backup policy"; + 12003 = "Agent backup job"; + 13000 = "NAS"; + 14000 = "Proxmox"; +} + +# $jobtypes is used in SQL queries. Built automatically from the enumeration above. +$jobTypes = "($(($typeNames.Keys | Sort-Object) -join ", "))" + +########### DO NOT MODIFY BELOW ########### +function Get-ConnectionString() { + $builder = New-Object Npgsql.NpgsqlConnectionStringBuilder + $builder.Host = $SQLServer + $builder.IntegratedSecurity = $SQLIntegratedSecurity + $builder.Database = $SQLveeamdb + $builder.Username = $SQLuid + $builder.Password = $SQLpwd + Write-Debug "Connection String: $($builder.ConnectionString)" + return $builder.ConnectionString +} + +function Start-Connection() { + $connectionString = Get-ConnectionString + + # Create a connection to PostgreSQL + Write-Debug "Opening SQL connection" + $connection = New-Object Npgsql.NpgsqlConnection + $connection.ConnectionString = $connectionString + $connection.Open() + if ($connection.State -notmatch "Open") { + # Connection open failed. Wait and retry connection + Start-Sleep -Seconds 5 + $connection = New-Object Npgsql.NpgsqlConnection + $connection.ConnectionString = $connectionString + $connection.Open() + } + Write-Debug "SQL connection state: $($connection.State)" + return $connection +} + +function Get-SqlCommand { + [CmdletBinding()] + param ( + [Parameter(Position = 0, Mandatory = $true)] + [System.String]$Command + ) + + $Connection = $null + # Use try-catch to avoid exceptions if connection to SQL cannot be opened or data cannot be read + # It either returns the data read or $null on failure + try { + $Connection = Start-Connection + $SqlCmd = New-Object Npgsql.NpgsqlCommand + $SqlCmd.CommandText = $Command + $SqlCmd.Connection = $Connection + $SqlCmd.CommandTimeout = 0 + $SqlAdapter = New-Object Npgsql.NpgsqlDataAdapter + Write-Debug "Executing SQL query: ##$Command##" + $SqlAdapter.SelectCommand = $SqlCmd + $DataSet = New-Object System.Data.DataSet + $SqlAdapter.Fill($DataSet) + $retval = $DataSet.Tables[0] + } + catch { + $retval = $null + # We output the error message. This gets sent to Zabbix. + Write-Output $_.Exception.Message + } + finally { + # Make sure the connection is closed + if ($null -ne $Connection) { + $Connection.Close() + } + } + return $retval +} + +function ConvertTo-Unixtimestamp { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [System.DateTime]$date + ) + # Unix epoch + [System.DateTime]$unixepoch = (get-date -date "01/01/1970 00:00:00Z") + + # Handle empty dates + # We make this one second less than $unixepoch. + # This makes the time calculation below return -1 to Zabbix, making the item "unsupported" while the job is running (or before it ran for the first time) + if ($null -eq $date -or $date -lt $unixepoch) { + $date = $unixepoch.AddSeconds(-1); + } + + # Return the seconds elapsed between the reference date and the epoch + return [int]((New-TimeSpan -Start $unixepoch -end $date).TotalSeconds) +} + +function Get-SessionInfo { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [System.Object]$BackupSession + ) + + # Return $null if there is no session data + if (!$BackupSession) { + return $null + } + + # Get reason for the job failure/warning + # We get all jobs reasons from both table column and log_xml + $Log = (([Xml]$BackupSession.log_xml).Root.Log | Where-Object { $_.Status -eq 'EFailed' }).Title + $reason = $BackupSession.reason + foreach ($logreason in $Log) { + $reason += "`r`n$logreason" + } + + # Build the output object + Write-Debug "Building object for job: $($BackupSession.job_name)" + $Object = [PSCustomObject]@{ + JOBID = $BackupSession.job_id + JOBTYPEID = $BackupSession.job_type + JOBTYPENAME = $typeNames[$BackupSession.job_type] + JOBNAME = ([System.Net.WebUtility]::HtmlEncode($BackupSession.job_name)) + JOBRESULT = $BackupSession.result + JOBRETRY = $BackupSession.is_retry + JOBREASON = ([System.Net.WebUtility]::HtmlEncode($reason)) + JOBPROGRESS = $BackupSession.progress + JOBSTART = (ConvertTo-Unixtimestamp $BackupSession.creation_time.ToUniversalTime()) + JOBEND = (ConvertTo-Unixtimestamp $BackupSession.end_time.ToUniversalTime()) + } + return $Object +} + +function Get-JobInfo() { + Write-Debug "Entering Get-JobInfo()" + + # Get all active jobs + $BackupJobs = Get-SqlCommand "SELECT id, name, options FROM ""bjobs"" WHERE schedule_enabled = 'true' AND type IN $jobTypes ORDER BY type, name" + Write-Debug "Job count: $($BackupJobs.Count)" + $return = @() + # Get information for each active job + foreach ($job in $BackupJobs) { + if (([Xml]$job.options).JobOptionsRoot.RunManually -eq "False") { + Write-Debug "Getting data for job: $($job.name)" + # Get backup jobs session information + $LastJobSessions = Get-SqlCommand "SELECT job_id, job_type, job_name, result, is_retry, progress, creation_time, end_time, log_xml, reason + FROM ""backup.model.jobsessions"" + INNER JOIN ""backup.model.backupjobsessions"" + ON ""backup.model.jobsessions"".id = ""backup.model.backupjobsessions"".id + WHERE job_id='$($job.id)' + ORDER BY creation_time DESC + LIMIT 2" + $LastJobSession = $LastJobSessions | Sort-Object end_time -Descending | Select-Object -First 1 + # Exception BackupSync continuous state + if ($LastJobSession.job_type -like '51' -and $LastJobSession.state -like '9') { + $LastJobSession = $LastJobSessions | Sort-Object end_time -Descending | Select-Object -Last 1 + } + $sessionInfo = Get-SessionInfo $LastJobSession + $return += $sessionInfo + } + } + Write-Verbose "Got job information. Number of jobs: $($return.Count)" + # Convert data to JSON + $return = ConvertTo-Json -Compress -InputObject @($return) + Write-Output $return +} + +function Get-RepoInfo() { + Write-Debug "Entering Get-RepoInfo()" + Write-Debug "Veeam server: $veeamserver" + # Get data from WIM class + $repoinfo = Get-CimInstance -Class Repository -ComputerName $veeamserver -Namespace ROOT\VeeamBS + + $return = @() + # Build the output object + foreach ($item in $repoinfo) { + Write-Debug "Repository $($item.NAME)" + $Object = [PSCustomObject]@{ + REPONAME = ([System.Net.WebUtility]::HtmlEncode($item.NAME)) + REPOCAPACITY = $item.Capacity + REPOFREE = $item.FreeSpace + REPOOUTOFDATE = $item.OutOfDate + } + $return += $Object + } + Write-Debug "Repository count: $($return.Count)" + + # Convert data to JSON + $return = ConvertTo-Json -Compress -InputObject @($return) + Write-Output $return +} + +function Get-Totaljob() { + $BackupJobs = Get-SqlCommand "SELECT COUNT(jobs.name) as JobCount + FROM JobsView jobs + WHERE Schedule_Enabled = 'true' AND type IN $jobTypes" + Write-Debug $BackupJobs.ToString() + if ($null -ne $BackupJobs) { + Write-Output $BackupJobs.JobCount + } + else { + Write-Output "-- ERROR -- : No data available. Check configuration" + } +} + +If ($PSBoundParameters['Debug']) { + $DebugPreference = 'Continue' +} + +Write-Debug "Job types: $jobTypes" +Write-Debug "Veeam server: $veeamserver" +Write-Debug "SQL server: $SQLServer" +switch ($Operation) { + "RepoInfo" { + Get-RepoInfo + } + "JobsInfo" { + Get-JobInfo + } + "TotalJob" { + Get-Totaljob + } + default { + Write-Output "-- ERROR -- : Need an option !" + Write-Output "Valid options are: RepoInfo, JobsInfo or TotalJob" + Write-Output "This script is not intended to be run directly but called by Zabbix." + } +}