Share via


Hyper-V: Troubleshoot Pending Snapshot Merges

This article is based on a guest blog post by Jeremy Hagan at: http://blogs.technet.com/b/tonyso/archive/2010/11/25/the-case-of-the-pending-vm-snapshot-merge.aspx#3371639. Please help improve it; is is the wiki way!

When a virtual machine stops responding, frequently the underlying cause is that the hard-disk has run out of space. If you are using fixed-size VHD files, (so that there should be nothing that should grow to fill up the disk), check to ensure that the machine is NOT actually running on a differencing disk (AVHD) that had been a snapshot in the past, which changes still waiting to merge into the parent VHD file.

When a snapshot is deleted, the virtual machine must be powered off, not merely rebooted, in order for the data in the differencing disk to merge into the parent VHD. The VM must remain powered off until the merge is complete. If the VM is booted back up again before the merge is completed, then the merge process stops until the machine is powered off again. The bigger the AVHD, the longer the merge takes. Although SCVMM and Hyper-V manager have an indication in the GUI that a snapshot exists, when a snapshot is deleted it is deleted from the GUI right away even though the snapshot hangs around until the merge is complete. So if your VM in this state there may be no visible indication of it.

Usually, this occurs because the VM had a deleted snapshot for an unknown time period, which grew until it filled the underlying disk. If you cannot free up disk space,  the merge can not take place even if the VM was turned off.

Many we maintain a (250 GB or so) LUN on each Hyper-V cluster that is used for staging and emergencies. You can use System Center Virtual Machine Manager (SCVMM) to migrate the (power off) VM over the network to this "holding" or "temporary" LUN and allow the merge to complete and then move it back to its home. Problem solved.

To prevent this, do not disable free disk space monitoring on LUNs hosting VMs.  Since the LUNs routinely run low on space then SCOM will be routinely raising Chicken Little alerts that many ignore. 

Here's a better way: alert on a pending merge.

To do this, examine the difference between virtual machines in three different states:

  • one without a snapshot
  • one with a snapshot
  • one with a deleted snapshot

Fix the issue from Virtual server: under IDE Controller in Settings window, you can remove the VHD file and then attach it again, this will not affect the virtual machine.

Start by downloading the PowerShell Management Library for Hyper-V. You can use configuration/global_settings/disk_merge_pending = true.

  • Use a VBScript  to create a temporary PowerShell script in the current directory. Because the VBScript is distributed by SCOM it will be on local disk,, allowing you to use RemoteSigned certificates and keep your servers safe.
  • Execute the PowerShell script by using the Windows Script Host Exec Method, which allows you to capture the output using the StdOut object of the Exec method
  • Return the state of the Hyper-V host (whether or not there was a pending merge) and the name of the VM with the pending merge to the monitor via the property bag API
  • Set the machine to a Warning state and raise an alert including the output of the script
  • Create a rule to run the script and a monitor to alert on the results. Jeremy modified the script and abandoned the property bag and just wrote the information to the local Application event log and the monitor would raise the alert based on that information. He configured the rule to run every 24 hours and for the alert to expire after 23 hours, so SCOM alerts him every day about the pending merge until resolves.

Of course you could customise this in your own implementation.

PowerShell

foreach ($file in Get-ChildItem "$Env:PROGRAMDATA\Microsoft\Windows\Hyper-V\Virtual Machines\.xml" {
    $file.CopyTo((Split-Path -parent $MyInvocation.MyCommand.Definition) + "\ + $file.Name) | out-null
    [xml]$ConfigFile = Get-Content ((Split-Path -parent $MyInvocation.MyCommand.Definition) + "\ + $file.Name)
    $VMName = $ConfigFile.Configuration.properties.name."#text"
    $MergePending = $ConfigFile.configuration.global_settings.disk_merge_pending."#text"
    if ($MergePending -eq "True") {Write-Host "$VMName has a pending merge."}
    del ((Split-Path -parent $MyInvocation.MyCommand.Definition) + "\ + $file.Name)
    Remove-Variable VMName
    Remove-Variable ConfigFile
    Remove-Variable MergePending
}

Copy the configuration files to a temporary location before parsing them just in case

VBScript with embedded PowerShell

Option Explicit

Dim objFSO : Set objFSO = CreateObject("Scripting.FileSystemObject")
Dim objShell : Set objShell = CreateObject("WScript.Shell")
Dim strParentPath : strParentPath = objFSO.GetParentFolderName(WScript.ScriptFullName)
Dim objPSFile : Set objPSFile = objFSO.CreateTextFile(strParentPath & "\PSTemp.ps1", True)

'Get the path to the PowerShell executable from the registry
Dim strPowerShell : strPowerShell = objShell.RegRead("HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell\Path")
Dim strCommand : strCommand = Chr(34) & strPowerShell & Chr(34) & " -NoProfile -NoLogo -File " & Chr(34) & strParentPath & "\PSTemp.ps1" & Chr(34)
Dim objExec
Dim strOutput : strOutput = ""
Dim i

objPSFile.WriteLine "foreach ($file in Get-ChildItem " & Chr(34) & "$Env:PROGRAMDATA\Microsoft\Windows\Hyper-V\Virtual Machines\.xml" & Chr(34) & ") {"
objPSFile.WriteLine "    $file.CopyTo((Split-Path -parent $MyInvocation.MyCommand.Definition) + " & Chr(34) & "\ & Chr(34) & " + $file.Name) | out-null"
objPSFile.WriteLine "    [xml]$ConfigFile = Get-Content ((Split-Path -parent $MyInvocation.MyCommand.Definition) + " & Chr(34) & "\ & Chr(34) & " + $file.Name)"
objPSFile.WriteLine "    $VMName = $ConfigFile.Configuration.properties.name." & Chr(34) & "#text" & Chr(34)
objPSFile.WriteLine "    $MergePending = $ConfigFile.configuration.global_settings.disk_merge_pending." & Chr(34) & "#text" & Chr(34)
objPSFile.WriteLine "    if ($MergePending -eq " & Chr(34) & "True" & Chr(34) & ") {Write-Host " & Chr(34) & "$VMName has a pending merge." & Chr(34) & "}"
objPSFile.WriteLine "    del ((Split-Path -parent $MyInvocation.MyCommand.Definition) + " & Chr(34) & "\ & Chr(34) & " + $file.Name)"
objPSFile.WriteLine "    Remove-Variable VMName"
objPSFile.WriteLine "    Remove-Variable ConfigFile"
objPSFile.WriteLine "    Remove-Variable MergePending"
objPSFile.WriteLine "}"
objPSFile.Close

Set objExec = objShell.Exec(strCommand)
objExec.StdIn.Close

'sanity check
Do While objExec.Status = 0
    WScript.Sleep 1000
    i = i + 1
    If i > 55 Then
        i = 0
        objExec.Terminate
    End If
Loop

Do While True
    If objExec.StdOut.AtEndOfStream Then
        Exit Do
    Else
        strOutput = strOutput & objExec.StdOut.Read(1)
    End If
Loop

If InStr(strOutput, "pending") Then objShell.LogEvent 2, strOutput

objFSO.DeleteFile strParentPath & "\PSTemp.ps1", True

Set objFSO = Nothing
Set objExec = Nothing
Set objPSFile = Nothing
Set objShell = Nothing

SCOM Rule

http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-45-60-metablogapi/7532.image_5F00_thumb_5F00_57E5640F.png

http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-45-60-metablogapi/5710.image_5F00_thumb_5F00_30CAA4CD.png

http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-45-60-metablogapi/6874.image_5F00_thumb_5F00_2002FA1F.png

SCOM Monitor

http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-45-60-metablogapi/8053.image_5F00_thumb_5F00_4AFAE824.png

http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-45-60-metablogapi/0363.image_5F00_thumb_5F00_7990F106.png

http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-45-60-metablogapi/5658.image_5F00_thumb_5F00_046DD24F.png

http://blogs.technet.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-00-45-60-metablogapi/3122.image_5F00_thumb_5F00_60F12DE9.png


See Also