#
# This scripts measures the amount of disk change on VMs each time it is run.
# It measures all VM virtual disks for which CBT has been enabled.
#
# The first time it is run, it creatse a file containing baseline data (CBT change IDs and times).
# Each subsequent run measures changes since the baseline was set.
# It supports multiple virtual disks per VM, but not addition of new virtual disks after the baseline is established.
# Note that every run creates a short-lived snapshot on every VM that has CBT enabed.
#
# Time to run will vary in each environment. In my case, it took about 20 seconds per VM for the 1st run,
# and about half that long for subsequent runs.
#
# To reset the baseline, just delete (or move, or rename) the baseline file and re-run.
#
#
# # Inspired by the VMGuru.com blog posting of August 2011 -- "CBT Tracker Powershell Script - now with more zombie"
# # Re-imagined completely by Carlo.G, February 2016
# # Corrected to call QueryChangedDiskArea repeatedly, May 2016
#
#
# Important literals used in this script
#
$Folder = 'C:\Temp'
$Basefile = 'Baselines.csv'
$Datafile = 'Data.csv'
$vCenter = 'vcenter.domain.tld'
$DTformat = 'yyyy-MM-dd HH:mm:ss' # Chosen to import correctly into Excel
$Creds = Get-Credential 'domain\userid' -Message 'Provide userid\password with permissions on VCenter'
#
# Initialize PowerCLI and connect to vCenter
#
Set-Location "C:\Program Files (x86)\VMware\Infrastructure\vSphere PowerCLI\Scripts"
. .\Initialize-PowerCLIEnvironment.ps1 $true
Connect-VIServer $vcenter -Credential $creds
#
# Get list of VMs with Change Block Tracking (CBT) enabled
#
$Vms = Get-VM | ?{$_.PowerState -eq 'PoweredOn'} | ?{(Get-View $_).Config.ChangeTrackingEnabled}
# Override with a list of specific VM names for testing
# $Vms = @('TEST_VM_1','TEST_VM_2','TEST_VM_3') | % {Get-VM $_}
#
# Exclude VMs that are sensitive to snapshots
#
$Vms = $VMs | ?{$_.name -notmatch 'HATES_SNAPSHOTS' }
#
# Retrieve or generate baseline change ids (one for each disks) for each VM in list to be measured
# Generating a new baseline requires creating a snapshot (which is removed immediately)
#
# Note that there may be more than one disk per VM.
# and in some cases only some disks have CBT enabled.
#
$n = $VMs.count
$i = 0
Write-Host "Getting baselines for $n VMs"
$baselines = @()
Try {$baselines = Import-CSV (Join-Path $folder $basefile)}
Catch {Write-Host -f cyan "No $(Join-Path $folder $basefile) file found, so new one will be created" }
$more = $false
ForEach ($vm in $Vms) {
++$i
$b = $baselines | ?{$vm.name -eq $_.VMname} | select -first 1
If ($b -eq $null) {
Write-Host "Creating baseline for $($vm.name) ($i of $n)"
$more = $true
$TimeStamp = Get-Date -format $DTformat
$snapshot = New-Snapshot -VM $vm -Name 'Temp for CBT baseline - Delete immediately ' -Description "for Carlo's change block tracking script, $TimeStamp"
$snapview = Get-View $snapshot
$snapdisks = $snapview.Config.Hardware.Device | where {($_.GetType()).Name -eq "VirtualDisk"}
ForEach ($d in $snapdisks) {
[array]$baselines += New-Object 'PSObject' | select `
@{N='VmName';E={$vm.name}},
@{N='VmGBProv';E={$vm.ProvisionedSpaceGB}},
@{N='VmGBUsed';E={$vm.UsedSpaceGB}},
@{N='DiskSummary';E={$d.deviceinfo.summary}},
@{N='DiskName';E={$d.deviceinfo.label}},
@{N='DiskKey';E={$d.key}},
@{N='TimeStamp';E={$TimeStamp}},
@{N='ChangeId';E={$d.Backing.ChangeId}}
}
Remove-Snapshot $snapshot -Confirm:$false
}
}
If ($more) {$Baselines | Export-CSV (Join-Path $folder $basefile) -NoTypeInformation -Force}
#
# For each item in Baselines list, measure changes since baseline was set
# A snapshot (which is removed immediately) is needed for each VM measured
# Append results to existing file (if it exists)
#
# Note that there may be more than one disk per VM.
# and in some cases only some disks have CBT enabled.
#
$lastVMname = $null
$snapshot = $null
$data = @()
$Baselines = $BaseLines | Sort VMname, DiskKey | ? {$_.ChangeID -ne '' -and $_.ChangeId -ne $null}
$n = $Baselines.count
$i = 0
ForEach ($b in $Baselines) {
++$i
If ($b.VmName -ne $lastVMname) {
If ($snapshot -ne $null) {Remove-Snapshot $snapshot -Confirm:$false}
$LastVmName = $b.VmName
$vm = Get-VM $b.VmName
$vmview = Get-View $vm
$TimeStamp = Get-Date -format $DTformat
$Interval = (New-Timespan $b.TimeStamp $TimeStamp)
$snapshot = New-Snapshot -VM $vm -Name 'Temp for CBT polling - Delete immediately ' -Description "for Carlo's change block tracking script, $TimeStamp"
$snapview = Get-View $snapshot
$snapdisks = $snapview.Config.Hardware.Device | where {($_.GetType()).Name -eq "VirtualDisk"} | sort key
}
$disk = $snapdisks | ?{$_.key -eq $b.DiskKey}
Try {
$Offset = 0
$GBChanged = 0
Do {
$changes = $VmView.QueryChangedDiskAreas($SnapView.MoRef,$Disk.key,$Offset,$b.ChangeId)
$GBchanged += ($changes.ChangedArea | % {$_.length} | measure-object -sum).sum/1024/1024/1024
$LastChange = $changes.changedarea | Sort Start | select -last 1
$Offset = $LastChange.start + $LastChange.Length
}
While ($Disk.CapacityInBytes -gt $Offset -and $Changes.ChangeArea.Count -gt 0)
}
Catch {$GBchanged = 'error'}
Write-Host "$($b.Vmname) $($b.DiskName) $GBchanged GB changed since $($b.TimeStamp) ($i of $n)"
$data += New-Object 'PSObject' | select `
@{N='VmName';E={$vm.name}},
@{N='DiskName';E={$disk.deviceinfo.label}},
@{N='DiskSummary';E={$disk.deviceinfo.summary}},
@{N='BaseTime';E={$B.TimeStamp}},
@{N='ThisTime';E={$TimeStamp}},
@{N='Interval';E={$Interval}},
@{N='GBChanged';E={$GBChanged}},
@{N='VmGBProv';E={$vm.ProvisionedSpaceGB}},
@{N='VmGBUsed';E={$vm.UsedSpaceGB}}
}
Remove-Snapshot $snapshot -Confirm:$false -EA 0
$Data | Export-CSV (Join-Path $folder $datafile) -NoTypeInformation -Append
#
# Check for left-over CBT-related snapshots
#
Write-Host "Checking for leftover snapshots..."
$Snapshots = Get-VM | Get-Snapshot | ?{$_.name -match '\sCBT\s'}
If ($snapshots) {
Write-Host -f red "$(Snapshots.count) left-over CBT snapshots????"
$Snapshots | ft vm,created,name
}
Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.
Comments (1)
Commented: