Powershell cкрипт мониторинга репликации Hyper-V

Если в Вашей инфраструктуре настроена репликация виртуальных машин, то Вы наверняка не раз сталкивались с тем, что репликация останавливалась по разным причинам. Чтобы вовремя отреагировать на ошибки репликации был написан данный скрипт.

Зачем мониторить репликацию?

Остановившаяся репликация  может привезти к очень опасной ситуации для всех реплицируемых виртуальных машин, в тот момент, когда закончится место у стораджа на котором хранятся виртуалки. Так же существует всем известный закон подлости – когда репликация остановилась, через некоторое время (например сутки) что-то произошло с основным сервером и тогда Вы теряете не реплицированные данные.

Скрипт реагирует на регистрацию ошибок в журнале событий ОС, а так же с периодичностью 15 минут проверяет состояние репликации.
В случае ошибок репликации какой либо из VM, скрипт отправляет письмо с отчетом о состоянии репликации, три последние ошибки по каждой VM из системного журнала. Письмо отправляется с периодичностью в 1 час.

HV-ReplError

Если репликация восстановилась самостоятельно отправляется письмо:
HV-ReplSuccess
Настройка скрипта:
Создать каталог в котором будет работать скрипт, например:

C:Scripts

В эту папку копируем файлы:

  • OnReplCheck.xml
  • OnReplFailed.xml
  • VM-Repl-MSG.ps1

Необходимо заполнить секцию конфигурации скрипта своими данными:

##### Configuration Section Starts #####
$SMTPServer = "mail.Corp.com.ua"
$EMailMSGTo = "Me <Me@MyDomain.com.ua>"
# Send Mail using Gmail account
#$UseGmail = $false
# Set gmail Email address if $UseGmail = $true
$EMailMSGFrom = "Virtual HV-Repl <administrator@Corp.com.ua>"

$EMailMSGUseSSL = $false
$ScriptPath = Split-Path $MyInvocation.MyCommand.Path
$Date = get-date -Format d_M_yyyy_hh_mm_ss
# If $HTMLfile variable set, e-mail messages will be writed to file
$HTMLfile = $null
$HtLogFile = $ScriptPath + "log.txt"
$HtMailData = @{}
$HtLogWrite = @{}
$global:WriteToLogBool = $null

# Task Sheduler Data
$TaskUser = "CORPAdministrator"

Выполнить регистрацию скрипта в Task Scheduler:

.VM-Repl-MSG.ps1 -EventTask "Install" -TPwd "Pa$$w0rd"

Скрипт пингает удаленную сторону IPsec туннеля, поправьте IP в строке №174:

$Computer = "192.168.*.*"

Смотрите код… Скрипт по умолчанию отправляет почту при помощи внутреннего сервера, так же есть функция отправки почты при помощи Gmail.

Если Вы улучшите функциональность скрипта или найдете ошибки, пишите hmh@ittc.com.ua

Скачать VM-Repl-MSG-Source.zip

Чтобы быстро передернуть репликацию всех VM

Get-VMReplication | Reset-VMReplicationStatistics
Get-VMReplication | Resume-VMReplication
#========================================================================
# Created on:   14.05.2014 20:25
# Created by:   HmH
# Organization: ITTC
# Filename: VM-Repl-MSG.ps1
# Description: Monitor Hyper-V primary server replication
#========================================================================


Param(
    [Parameter(Mandatory = $true,
                    Position = 0,
                    ValueFromPipelineByPropertyName = $true)]
    [Alias('EventTask')] # This is the name of the parameter e.g. -Status Success
    [String]$EventT, # This is the value [Don't forget the comma at the end!]

    [Parameter(Mandatory = $false,
                    Position = 1,
                    ValueFromPipelineByPropertyName = $true)]
    [Alias('TPwd')]
    [String]$TaskPwd,

    [Parameter(Mandatory = $false,
                    Position = 2,
                    ValueFromPipelineByPropertyName = $true)]
    [Alias( 'GUser' )]
    [String]$GMailUser,

    [Parameter(Mandatory = $false,
                    Position = 3,
                    ValueFromPipelineByPropertyName = $true)]
    [Alias( 'GPwd' )]
    [String]$GMailPwd,

    [switch]$UseGmail

)


#If (!$args[0]) {
#write-host "no args"
#Exit}

 $Startdate = Get-Date
  ##### Configuration Section Starts #####
 $SMTPServer = "mail.Corp.com.ua"
 $EMailMSGTo = "Me "
 # Send Mail using Gmail account
 #$UseGmail = $false
 # Set gmail Email address if $UseGmail = $true
 $EMailMSGFrom = " HV-Repl "

 $EMailMSGUseSSL = $false
 $ScriptPath = Split-Path $MyInvocation.MyCommand.Path
 $Date = get-date -Format d_M_yyyy_hh_mm_ss
 # If $HTMLfile variable set, e-mail messages will be writed to file
 $HTMLfile = $null
 $HtLogFile = $ScriptPath + "log.txt"
 $HtMailData = @{}
 $HtLogWrite = @{}
 $global:WriteToLogBool = $null

 # Task Sheduler Data
 $TaskUser = "CORPAdministrator"


  ##### Configuration Section Ends#####

 $HtLog = Get-Content $HtLogFile | Select-Object -last 5 | ConvertFrom-StringData

  if ($EventT -eq "failed"){
    $HtLogWrite.Add("TaskEvent","Failed")
    write-host "failed"
 } elseif ($EventT -eq "check") {
    $HtLogWrite.Add("TaskEvent","Check")

 } elseif ($EventT -eq "install") {
    $FailedXMLPath = $ScriptPath + "OnReplFailed.xml"
    $CheckXMLPath = $ScriptPath + "OnReplCheck.xml"
    Register-ScheduledTask -Xml (get-content $FailedXMLPath | out-string) -TaskName "OnReplFailed" -User $TaskUser -Password $TaskPwd –Force
    Register-ScheduledTask -Xml (get-content $CheckXMLPath | out-string) -TaskName "OnReplCheck" -User $TaskUser -Password $TaskPwd –Force
    Write-Host "Tasks Registered"
    exit
 } else {exit}

Function CheckSendEMail {
    $Date = Get-Date
    $LogDate = Get-Date -Date $HtLog.LogDate
    $SendThreshold = Get-Date -Date "1:0:0" -UFormat %H:%M:%S
    $DateDate = $Date - $LogDate
    If ($HtLog.ReplStatus -ne $HtLogWrite.ReplStatus){
        $global:WriteToLogBool = $true
            Return $true
    } Elseif ($DateDate -gt $SendThreshold) {
        if ($HtLogWrite.ReplStatus -ne "Success"){
            $global:WriteToLogBool = $true
            Return $true
        } else {
            $global:WriteToLogBool = $false
            Return $false
        }
    } Else {
        If ($HtLog.TaskEvent -eq "Failed" -and $HtLogWrite.TaskEvent -eq "Check" ){
            $global:WriteToLogBool = $true
            Return $false
        } Else {
            $global:WriteToLogBool = $false
            Return $false
        }
    }
}


function WriteToLog ($ht) {
    $ht.GetEnumerator() | Sort-Object Name | ForEach-Object {"{0}={1}" -f $_.Name,$_.Value} | Add-Content $HtLogFile
}

 $VMListAll = Get-VM | where {$_.ReplicationState -ne "Disabled"}
 $HtLogWrite.add("ReplVMsCount",$VMListAll.Count)

 $VMList = get-vm | where {$_.ReplicationHealth -eq "Critical" -or $_.ReplicationHealth -eq "Warning"}
  If ($VMList){
    $HtLogWrite.Add("ReplStatus","Failed")
    If ($HtLogWrite.TaskEvent -eq "Failed"){
        $HtMailData.add("HeaderMSG","Comp Hyper-V Replication Failed!!! Event Failed")
    } else {
        $HtMailData.add("HeaderMSG","Comp Hyper-V Replication Failed!!! Event Check")
    }

 } Else {
    $HtLogWrite.Add("ReplStatus","Success")
    if ($HtLogWrite.TaskEvent -eq "Check"){
        $HtMailData.add("HeaderMSG","Server Hyper-V Replication Success!!! Event Check")
    } else {
        $HtMailData.add("HeaderMSG","Server Hyper-V Replication Success!!! Event Failed")
    }

 }

 # Check do we need to send notification E-Mail
 $SendEMail = CheckSendEMail

 $Date = Get-Date
 $HtLogWrite.Add("LogDate",$Date)
 $HtLogWrite.Add("SendMail",$SendEMail)

# Restor VM replication if VM in suspended state
# If ($VMListAll -ne $VMList){
#    Foreach ($Vm in $VMList) {
#        If ($Vm.ReplicationState -ne "Replicating") {
#            $Vm | Reset-VMReplicationStatistics
#            Start-Sleep -s 3
#            $Vm | Resume-VMReplication
#        }
#    }
# }


 # Exit script. Because we don't need to send notification e-mail
 If (!$SendEMail) {
    if ($WriteToLogBool){WriteToLog $HtLogWrite}
 exit
 }


Function CheckConnection($IPAddressCheck){
    $Ping = @{}
    $Ping = Get-WmiObject Win32_PingStatus -Filter "Address = '$Computer'" | Select @{Label="Status";Expression={ If ($_.StatusCode -ne 0) {"Failed"} Else {"Success"}}},ResponseTime

    return $ping
}

$HtmlMSGPingStatus = ""
$Computer = "192.168.*.*"
$PingRes = CheckConnection $Computer

if ($PingRes.Status -eq "Success") {
    $HtmlMSGPingStatus += "
  • IPSec Link ping: “+$PingRes.Status + ” ” + $PingRes.ResponseTime + ” ms” + “
"  +"`r`n"
} Else {
    $HtmlMSGPingStatus += "
  • IPSec Link ping: “+$PingRes.Status + “
"  +"`r`n"
}

$Computer = "8.8.8.8"
$PingRes = CheckConnection $Computer
if ($PingRes.Status -eq "Success") {
    $HtmlMSGPingStatus += "
  • Google DNS ping: “+$PingRes.Status + ” ” + $PingRes.ResponseTime + ” ms” + “
"  +"`r`n"
} Else {
    $HtmlMSGPingStatus += "
  • Google DNS ping: “+$PingRes.Status + “
"  +"`r`n"
    $HTMLfile = $ScriptPath + "MSGBody_"+$date+".html"
}
$HtMailData.add("PingStatus",$HtmlMSGPingStatus)

$RVMsStateTbl = ""
$i = 1
 Foreach ($VM in $VMListAll) {
    $VMReplState = $VM | Measure-VMReplication
    $i++
    if ($i % 2 -eq 1) {
        $RVMsStateTbl += "" +"`r`n"
    } else {
        $RVMsStateTbl += "" +"`r`n"
    }

    $RVMsStateTbl += "" + $VMReplState.Name + "" + "" + $VMReplState.State + "" + "" + $VMReplState.Health + "" + "" + $VMReplState.LReplTime + "" + "" + [math]::Round(($VMReplState.PReplSize/1073741824),2,1) + "" + "" + [math]::Round(($VMReplState.AvgReplSize/1073741824),2,1) + "" +"`r`n"
    $RVMsStateTbl += "" +"`r`n"

}
$HtMailData.Add("ReplVMsState",$RVMsStateTbl)

##
# TODO Start
##

 #Loop through each VM to get the corresponding events
 $i = 1
 ForEach ($VM in $VMList)
     {
         $VMReplStats = $VM | Measure-VMReplication


         #We should start getting events after last successful replication. Till then replication was happening.
         $FromDate = $VMReplStats.LastReplicationTime


         #This string will filter for events for the current VM only
         $FilterString = "*[UserData[VmlEventLog[(VmId='" + $VM.ID + "')]]]"


         $EventList = Get-WinEvent -FilterXML $FilterString  | Where {$_.TimeCreated -ge $FromDate -and $_.LevelDisplayName -eq "Error"} | Select -Last 3


         #Dump relevant information to the CSV file
         foreach ($Event in $EventList)
             {
             $i++
             if ($i % 2 -eq 1) {
                $MSGtbl += "" +"`r`n"
             } else {
                $MSGtbl += "" +"`r`n"
             }
                 If ($VM.ReplicationMode -eq "Primary")
                     {
                         $Server = $VMReplStats.PrimaryServerName
                     }
                 Else
                     {
                         $Server = $VMReplStats.ReplicaServerName
                     }

                 #$csv +=$VM.Name + "," + $Event.TimeCreated + "," + $Server + "," + $Event.Message +"`r`n"
                 $MSGtbl += "" + $VM.Name + "" + "" + $Event.TimeCreated + "" + "" + $Server + "" + "" + $Event.Message + "" +"`r`n"
                 $MSGtbl += "" +"`r`n"
             }

     }

$HtMailData.Add("ReplStatus",$MSGtbl)

##
# TODO End
##
Function GenerateEMailMSG ($HtMailData){
$HeaderReplVMsState = @"

Replication VMs Status:

“@
$BottomReplVMsState = @”

VM Name State Health LReplTime PReplSize(M) AvgReplSize(M)

"@

$HeaderPingStatus = @"

Ping status:

    “@
    $BottomPingStatus = @”

"@

$HeaderReplStatus = @"

Replication log details:

“@
$BottomReplStatus = @”

VM Name Date Server Message

"@


$EMailMSGToSend = @"

$(if ($HtMailData.PingStatus) { $HeaderPingStatus $HtMailData.Get_Item(“PingStatus”) $BottomPingStatus }) $(if ($HtMailData.ReplVMsState){ $HeaderReplVMsState $HtMailData.Get_Item(“ReplVMsState”) $BottomReplVMsState }) $(if ($HtMailData.ReplStatus) { $HeaderReplStatus $HtMailData.Get_Item(“ReplStatus”) $BottomReplStatus })

$(if ($HtMailData.HeaderMSG){$HtMailData.Get_Item(“HeaderMSG”)})

This is automated report on replication fail! Script total time: $( ((Get-Date) – $StartDate).ToString().SubString(0,8) )
"@

return $EMailMSGToSend
}

$HtmlMSGBody = ""
$HtmlMSGBody = GenerateEMailMSG $HtMailData

 If ($HTMLfile -ne $null) {
 $fso = new-object -comobject scripting.filesystemobject
 $file = $fso.CreateTextFile($HTMLfile,$true)
 $file.write($HtmlMSGBody)
 $file.close()
 }

 If ($HtLogWrite.ReplStatus -eq "Success") {
    $EmailMSGSubject = "[ATTENTION] Replication restored!"
 } Else {
    $EmailMSGSubject = "[ATTENTION] Replication requires your attention!"
 }
 #If there are VMs in critical health state, send an email to me
 If ($Gmail){
    $SMTPServer = "smtp.gmail.com"
    $SMTPClient = New-Object Net.Mail.SMTPClient( $SmtpServer, 587 )
    $SMTPClient.EnableSSL = $true
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential($GMailUser, $GMailPwd);

    $emailMessage = New-Object System.Net.Mail.MailMessage
    $emailMessage.SubjectEncoding = System.Text.Encoding.UTF8
    $emailMessage.BodyEncoding = System.Text.Encoding.UTF8
    $emailMessage.BodyTransferEncoding = System.Text.Encoding.UTF8
    $emailMessage.IsBodyHtml = $true
    $emailMessage.From = $EMailMSGFrom
    foreach ( $recipient in $EMailMSGTo )
    {
        $emailMessage.To.Add( $recipient )
    }
    $emailMessage.Subject = $EmailMSGSubject
    $emailMessage.Body = $HtmlMSGBody

    $SMTPClient.Send( $emailMessage )
 } else {

    send-mailmessage -to "$EMailMSGTo" -from "$EMailMSGFrom" -subject "$EmailMSGSubject" -Encoding "UTF8" -BodyAsHtml -body "$HtmlMSGBody" -smtpServer "$SMTPServer"
}
Write-host "Log Write next Line"
# Finaly write HashTable to log
WriteToLog $HtLogWrite

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.