CBT Tracker PowerShell Script - Now With More Zombie

PDFPrintE-mail

Friday, August 26 2011 15:18 Written by VMGuru

It appears that when I did my site re-design a while back, I left out a few key blog posts.  I was kindly reminded today, that my CBT Tracker post no longer exists, so I've decided to bring it back from the dead, as I was under the impression many people found high value in understanding data growth patterns of individual VMs in their environment.

I took it as a personal challenge this week to leverage PowerShell, PowerCLI and the vSphere API to track the amount of data that changes in a VM over a regular interval window. It turns out VMware does make it quite simple to query what blocks in a VMDK file have changed and what the length of data is as long as you know how to structure the API call.

The following script can be run as a scheduled task in Windows on a regular interval such as every 15 or 60 minutes, or even just once a day (depending on how granular you want your data) against a single VM. This is perfect for profiling the amount of data a particular VM will need to send (uncompressed) over a network connection for backup or replication purposes.

There are a few quick things to note about this script:

  1. It requires vSphere and CBT to be enabled for the selected virtual machine. This also means the VM Hardware version must be set to 7. There is an optional flag of "-enableCBT $true" that can be set as a script parameter to enable CBT for the specified VM. This executes an additional function in the script to turn CBT on. If CBT is already enabled, the function simply exits cleanly, no harm, no foul.
  2. I don't recommend running this script against a VM if there is potential overlap with other regular processes such as backup or replication that add snapshots to virtual machines. I don't have advanced snapshot debugging in here and simply don't know how all scenarios of snapshots being added/removed from multiple programs against a single VM will play out.
  3. I have it limited so a single instance of the script can be run against a single VM only. I started off with this being a simple proof-of-concept script, and haven't built in proper multi-VM intelligence yet. If there is enough demand, I'll definitely consider it.

You should create a standalone directory somewhere on an available hard drive of a Windows system. Create a new PS1 file using the built in script editor in PowerGUI. Copy and paste the following code block and save the file as "CBT_Tracker.ps1" in the newly created directory:

There is one optional parameter, and that is setting "-enableCBT = $true" in the batch file inside the quotes.  What this will do is attempt to enable CBT on the specified VM.  Once CBT is enabled, the flag is no longer needed.  It also will not do any specific harm if CBT is already enabled, and will just exit the function and continue on its way.

CBT_Tracker.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
Param (
    [string] $vmName,
    [string] $vimHost,
    [string] $vimUser,
    [string] $vimPass,
    [Boolean] $enableCBT = $false
 
)
#Use the optional $enableCBT paramater at input to enable CBT on
#a compatible virtual machine (HW Version 7). In order for CBT flags to take 
#effect, a Snapshot must be added and removed.
function Add-CBTFlag {
    if ($vmv.Config.ChangeTrackingEnabled){
        "CBT is already enabled on $vm"
        return
    }
    $vmConfigSpec = New-Object VMware.Vim.VirtualMachineConfigSpec
    $vmConfigSpec.changeTrackingEnabled = $true
    $vmv.ReconfigVM($vmConfigSpec)
    sleep 3
    $vm | New-Snapshot -Name "Enable CBT"
    sleep 5
    $vm | Get-Snapshot -Name "Enable CBT" | Remove-Snapshot -Confirm:$false
    "Enabled CBT for $vm"
}
 
#Add a snapshot specific to the purposes of this script
function Add-CBTSnap {
    $newSnap = $vm | New-Snapshot -Name "CBT Change Tracker"
    return $newSnap
}
 
#Remove the snapshot created for the purpose of this script
function Remove-CBTSnap {
    param (
        $vmSnap
    )
    $vmSnap | Remove-Snapshot -Confirm:$false
}
 
#Captures the ChangeID of the snapshot, which is required for comparisson 
#tracking purposes.  The current run of the script will always compare
#current state against the previously logged ChangeID for differences.
function Get-ChangeID {
    param (
        $vmSnap
    )
    $vmSnapv.Config.Hardware.Device | where {($_.GetType()).Name -eq "VirtualDisk"} | ForEach-Object {
        $vzCBT = New-Object PSObject
        $vzCBT.PSObject.TypeNames.Clear()
        $vzCBT.PSObject.TypeNames.Insert(0,"vzCBTTracker")
        $vzCBT `
            | Add-Member -MemberType NoteProperty -Name VMName -Value $vm.Name -PassThru `
            | Add-Member -MemberType NoteProperty -Name DeviceID -Value $_.Key -PassThru `
            | Add-Member -MemberType NoteProperty -Name ChangeID -Value $_.Backing.Get_ChangeID()
        [array] $vzCBTColl += $vzCBT
    }
    return $vzCBTColl
}
 
#Writes the historic ChangeID and VMDK device information to a tracking file
#This file is read on each iteration of the script to capture the previos
#iterations ChangeID inforation.
function Write-TrackerLog {
    $output = $null
    $cid | ForEach-Object {
        $output += $_.VMName + "," + $_.DeviceID + "," + $_.ChangeID + "`n"
    }
    $output | Out-File "$vm-CBTTracker.cfg"
}
 
#Reads previous state information of the VM from the Tracker.cfg file
#Captures the list of changed blocks for the specified VM for each VMDK file
#calculates the total size in bytes of changed block data across all VMDK
#files associated with the VM.
function Get-ChangedBlockSize {
    $chSize = 0
    Get-Content "$vm-CBTTracker.cfg" | ForEach-Object {
        if ($_ -ne "") {
            $TrackerRef = $_.split(",")
            $changes = $vmv.QueryChangedDiskAreas($vmSnapv.MoRef,$TrackerRef[1],0,$TrackerRef[2])
            $changes.ChangedArea | ForEach-Object {
                $chSize += $_.Length
            }
        }
    }
    $chSize = $chSize/1024/1024
    return $chSize
}
 
#Writes the actual iterations change data out to a CSV file
function Write-TrackerCSV {
    $longDate = Get-Date
    $shortDate = $longDate.toShortDateString() + " - " + $longdate.toShortTimeString()
    Add-Content "$vm-CBTTracker.csv" "$vm,$shortDate,$amtChanged"
}
 
#Loads VMware's PowerCLI PowerShell Cmdlets if necessary
[void](Get-PSSnapin VMWare.VimAutomation.Core -ErrorVariable getVmwareSnapinErr 2> $null)
if ($getVmwareSnapinErr.Count -gt 0) {    Add-PSSnapin VMware.VimAutomation.Core }
 
#Establish a connection to the VI Server based on input parameters
Connect-VIServer $vimHost -User $vimUser -Password $vimPass
 
#Load some VM variables
$vm = Get-VM $vmName
$vmv = $vm | Get-View
 
#We can only run against a single VM at this point in time.  This makes sure
#the object being passed is a single VirtualMachineImpl object, and not a 
#collection.
if (($vm.GetType()).Name -ne "VirtualMachineImpl"){
    "Script can only run against a single VM at a time"
    return
}
 
#Checks to make sure VM Hardware version is 7.  Terminates if not.
if ($vmv.Config.Version -ne "vmx-07" -and $vmv.Config.Version -ne "vmx-08"){
    "The Virtual Machine $vm must be VM Hardware Version 7 to support CBT"
    return
}
 
#If the "-enableCBT $true" flag is set as an input parameter, enables CBT 
#for specified VM if necessary
if ($enableCBT){
    Add-CBTFlag
}
 
#If all other checks fail, this is a final failsafe to ensure CBT is enabled
#on the VM before continuing the script.
if ($vmv.Config.ChangeTrackingEnabled -eq $false){
    "CBT is not enabled on $vm.  Script cannot proceed."
    return
}
 
#Load up snapshot variables
$vmSnap = Add-CBTSnap
$vmSnapv = $vmSnap | Get-View
$amtChanged = 0
 
#Checks to see if the script has previous written out a CBTTracker.cfg file. This
#would indicate file deletion, or first script run.  If cfg file doesn't exist,
#initializes CSV headings.  If this is an iterative run, Gets the amount of
#data that has changed since previous iteration.  Writes to CSV file.
if (Test-Path "$vm-CBTTracker.cfg") {
    $amtChanged = Get-ChangedBlockSize
    Write-TrackerCSV
}
else {
    Add-Content "$vm-CBTTracker.csv" "VMName,Time,ChangedMB"
}
 
#Capsture the current ChangeID to use against next iteration, Remove Snapshot
#Write cfg data.
$cid = Get-ChangeID ($vmSnap)
Remove-CBTSnap ($vmSnap)
Write-TrackerLog
 

With the script file created, use notepad or other simple text editor to create a new CMD file called "CBT_Tracker.cmd". This is what you will execute within Windows Task Scheduler. Paste the following line in the CMD file and edit with the proper parameters for your environment. Make sure you copy exactly, including the quotes.

1
powershell -command "& '.\CBT_Tracker.ps1' -VMName VMName -vimHost vCenter.domain.com -vimuser domain\user -vimpass Passw0rd"

You can now fire up Windows Task Scheduler and point to the CMD file. After the script runs for the first time, you will see 2 new files per VM that you set this to run against. $VMName-CBT_Tracker.cfg is used for tracking previous snapshot history for reference in the current script iteration. $VMName-CBT_Tracker.csv is the actual data file that contains the VMName, Timestamp, and Amount of data changed.

I've also attached a ZIP file containing the PS1 script for those that don't want to mess around with script editors and copy/paste actions.

Attachments:
Download this file (CBT_Tracker.ps1.zip)CBT_Tracker.ps1.zip[Zip File containing the CBT Tracker script PS1 File]2 Kb
Trackback(0)
Comments (14)Add Comment
ChadO
September 23, 2011
75.145.175.109
Votes: +0
...

How are we to interpret the ChangedMB information? I've been running this script against one of my DC's. I'm finding that every 15 minutes, there is roughly 30-75 "ChangedMB". If there are three intervals of 30 MB changes, does that mean that I would need to transfer 90 MB of new data uncompressed to my target location?

VMGuru
September 24, 2011
98.228.173.173
Votes: +0
...

Every replication product uses different levels and types of compression. If you are not using compression in a replication technology, then what the results indicate are that if you wanted to use a 15 minute replication window, you'd need to be able to transfer 30-75MB over the network within that same time period. For example, 30 MB = 240 Mb, and there are 900 Seconds in 15 Minutes. 240/900 = .2667 Mb/Sec. This means you'd need approximately 273 Kb/Sec bandwidth to replicate 30 MB of uncompressed over a 15 Minute Interval safely.

joluinfante
November 30, 2011
190.228.31.238
Votes: +0
...

Hi.
I'm working with esxi 4.1u1 std version.
What are the minimum software (and version) requirements to run this script?

Thanks

Robyn Richardson
December 19, 2011
64.20.204.194
Votes: +0
...

where does the CSV file get saved? i'm seeing snapshots created and removed, but not seeing the files that are supposed to be created. maybe i'm missing some background information on how this all works. VM ver = 7, vmware powerCLI installed, running as administrator. i am getting a certificate error, could that be it?

Christian Kelly
April 25, 2012
76.126.153.133
Votes: +0
...

VM ver = 7

Christian Kelly
April 25, 2012
76.126.153.133
Votes: +0
...

This is a GREAT tool. Currently it's working with VM ver = 7 but not with 8. Is there any way to make it work with VM ver = 8?

Thanks,

Christian Kelly
April 25, 2012
24.23.241.180
Votes: +0
...

Also what happens if you have more than 1 VMDK on VM? Does it give you the total for both drives or just 1?

Thanks,

VMGuru
April 26, 2012
98.228.173.173
Votes: +0
...

Christian,

I have fixed line 118 so it contains the proper value of:

if ($vmv.Config.Version -ne "vmx-07" -and $vmv.Config.Version -ne "vmx-08"){

This will ensure it runs for both V7 and V8 HW.

If you have multiple VMDK files attached to a VM, the script will provide the collective total change across the VM, but will not directly display the results for each individual VMDK. That would require some rework of the function starting in line 76 to not do a collective total.

Christian Kelly
April 26, 2012
12.167.100.35
Votes: +0
...

Thanks so much. Haveing the total change across the VMs is what I needed so that's great. One thing to note there seems to be an issue now on line 48 where it's erroring out it seems like a 3 got added somehow.

Thanks,

Gerg
May 10, 2012
195.82.39.2
Votes: +0
...

Thanks for this VM Guru, exactly what I was looking for:-)

I had some issues with the certificate error which I resolved by running the following command in PowerCLI...
Set-PowerCLIConfiguration -InvalidCertificateAction "Ignore" -Confirm:$false

The other thing that had me lost for a while was the task, I had to put the name of the CMD file in as the program and then the directory in the 'Star in (optional):' box.

With that done I'm in business and happily monitoring a couple of test Windows 7 boxes every 15 mins from the vCentre VM. Both are thin provisioned, not being used for anything, one has Symantec Endpoint installed, no pending updates, nobody logged onto them, no active apps, basically doing nothing at all.

I've noticed that the 'Changed MB' on both VM's ranges from 5 to 98! Any thoughts on why the changed MB would be so high?

Cheers
Greg

VMGuru
May 11, 2012
98.228.173.173
Votes: +0
...

Christian - Fixed all of the instances where 3 was showing up after $_ characters. Seems to be a bug in my PoSH parser for the blog.

Gerg - Thanks for the tips. I don't know that I ever tested this on Windows 7. I was a VERY slow convert from XP, and this script and post was originally written before i changed over. Regarding the disk changes, what CBT tracks actually sits underneath any OS File System layer. NTFS/FAT and to a Lesser extent EXT partitions are just noisy. Even the slightest change automatically makes a change that is sized based on the block size that is set for the VMFS. Even Symantec Endpoint Checks for updates, writes logs, etc. Each slight change is 1-8MB at the VMDK level! Even more-so if your VMFS is not aligned properly as well: http://www.vmware.com/pdf/esx3_partition_align.pdf

Gerg
May 11, 2012
195.82.39.2
Votes: +0
...

Hi VM Guru,

I'll have a look at the VMFS alignment.

When I had a closer look at the CSV file I realised that the big changes all seem to happen hourly, I was running a 15 min cycle! It could be something to do with a Windows 7 tasks buried away in the task scheduler or as you say SEP could be doing something. Strange that it also happens on the VM that doesn't have SEP though.

I was wondering where you would recommend running the tasks from? In DEV I'm running them from the VM that has vCentre installed but is that best practice?

One final noob question......
I have a server that creates a lot of temp files when reports are ran. If I replicated the server daily at 23:00 and had a script in place that deleted the temp files before the replication would all the writes and deletes of those temp files be seen as changes in blocks and need to be replicated?

Many thanks
Greg

Troy
May 15, 2012
203.202.106.99
Votes: +0
...

Hi VMGuru,

Only on some of my VM's I getting "Script can only run against a single VM at a time".
Any reason why this is happening on some VM's?

Troy
May 15, 2012
203.202.106.99
Votes: +0
...

Sorry VMGuru also getting this

Exception calling "QueryChangedDiskAreas" with "4" argument(s): "A specified pa
rameter was not correct.
changeId"
At C:CBT_Tracker.ps1:81 char:50
+ $changes = $vmv.QueryChangedDiskAreas

Write comment
You must be logged in to post a comment. Please register if you do not have an account yet.

busy