Low Level Backup Procedures
This section describes low level details that may be helpful in coding a backup application. It is not the intent of this material to impose a design, but only to serve as a guideline with examples and exposition. The code samples provided below are not complete. They generally lack error handling and ignore critical details.
Communicating with the Server
Connections to the server machine require credentials: user name, password, and host name (or IP address). The following code connects to the server and extracts information useful for manipulating a service:
1
ManagedObjectReference svcRef = new ManagedObjectReference();
svcRef.setType("ServiceInstance");
svcRef.setValue("ServiceInstance");
2
VimServiceLocator locator = new VimServiceLocator();
locator.setMaintainSession(true);
VimPortType serviceConnection = locator.getVimPort("https://your_server/sdk");
3
ServiceInstanceContent serviceContent = serviceConnection.retrieveContent(svcRef);
ManagedObjectReference sessionManager = serviceInstance.getSessionManager();
UserSession us = serviceConnection.login(sessionManager, username, password, null);
The PropertyCollector
The PropertyCollector is used in this section to apply the above details to the backup task.
PropertyCollector Arguments
The PropertyCollector uses two relatively complicated argument structures. As was mentioned in PropertyCollector Data, these arguments are PropertySpec and ObjectSpec. PropertySpec is a list of the information desired, and ObjectSpec is a list of instructions indicating where to find the information. In theory, you could directly address an object using its moRef. In that case an ObjectSpec can be very simple. However, getting the initial moRef can be a challenge when a complicated ObjectSpec is required. To formulate a complex ObjectSpec, you need to understand the structure of the available data. This is complicated by the fact that an ObjectSpec can contain recursive elements.
Understanding an ObjectSpec
An ObjectSpec is a list of ObjectSpec elements, each specifying an object type, and giving a “selection spec” for the object. More About Managed Objects describes five types of managed objects: Folder, Datacenter, ComputeResource, ResourcePool, and VirtualMachine. VirtualApp (vApp) is a sixth type. You can “traverse” objects, because one managed object leads to another.
Folder – One of the items contained in the Folder is called childEntity, which is a list of moRefs that can contain one of the five managed object types. A Folder can be parent to any of these managed objects.
hostFolder – A moRef to a Folder containing a list of ComputeResources comprising a Datacenter.
vmFolder – A moRef to a Folder containing the VirtualMachines that are part of the Datacenter. If it is your objective to duplicate the display seen in a vSphere Client GUI, then this Folder is of limited use because it does not describe the ResourcePool that is the parent of a virtual machine.
VirtualMachine – A folder named vm contains a list of moRefs to child VirtualMachines.
resourcePool – A folder containing a list of moRefs pointing to child ResourcePools or VirtualApps.
resourcePool – A folder containing a list of moRefs pointing to child ResourcePools or VirtualApps.
vm – A list of moRefs to child VirtualMachines that employ the resources of the parent ResourcPool. A VirtualMachine always lists a ResourcePool as its parent.
The ObjectSpec does not have to lead you any farther than the moRef of a target object. You can gather information about the managed object itself using the moRef and the PropertySpec. This is described in detail in the section Understanding a PropertySpec.
A TraversalSpec extends SelectionSpec, a property of ObjectSpec, and contains the following elements:
Path – The element contained in the object that is used to steer traversal.
SelectSet – An array containing either SelectionSpec or TraversalSpec elements.
Skip – Whether or not to filter the object in the Path element.
Type – The type of object being referenced.
Name – Optional name you can use to reference the TraversalSpec, inherited from SelectionSpec.
SelectionSpec is a direct target for traversal, as is TraversalSpec (a class extending SelectionSpec). It is in the SelectSet that recursion can occur.
If you wish to traverse the entire configuration tree for a server, then you need only the “root node” moRef, which is always a Folder. This root folder moRef is available in the property rootFolder of the ObjectSpec service instance content. All of the above goes into this Java code sample.
// Traversal objects can use a symbolic name.
// First we define the TraversalSpec objects used to fill in the ObjectSpec.
//
// This TraversalSpec traverses Datacenter to vmFolder
TraversalSpec dc2vmFolder = new TraversalSpec();
dc2vmFolder.setType("Datacenter"); // Type of object for this spec
dc2vmFolder.setPath("vmFolder"); // Property name defining the next object
dc2vmFolder.setSelectSet(new SelectionSpec[] {"folderTSpec"});
//
// This TraversalSpec traverses Datacenter to hostFolder
TraversalSpec dc2hostFolder = new TraversalSpec();
dc2hostFolder.setType("Datacenter");
dc2hostFolder.setPath("hostFolder");
//
// We use the symbolic name "folderTSpec" which will be defined when we create the folderTSpec.
dc2vmFolder.setSelectSet(new SelectionSpec[] {"folderTSpec"});
//
// This TraversalSpec traverses ComputeResource to resourcePool
TraversalSpec cr2resourcePool = new TraversalSpec();
cr2resourcePool.setType("ComputeResource");
cr2resourcePool.setPath("resourcePool");
//
// This TraversalSpec traverses ComputeResource to host
TraversalSpec cr2host = new TraversalSpec();
cr2host.setType("ComputeResource");
cr2host.setPath("host");
//
// This TraversalSpec traverses ResourcePool to resourcePool
TraversalSpec rp2rp = new TraversalSpec();
rp2rp.setType("ResourcePool");
rp2rp.setPath("resourcePool");
//
// Finally, we tie it all together with the Folder TraversalSpec
TraversalSpec folderTS = new TraversalSpec();
folderTS.setName{"folderTSpec"); // Used for symbolic reference
folderTS.setType("Folder");
folderTS.setPath("childEntity");
folderTS.setSelectSet(new SelectionSpec[]{ "folderTSpec",
dc2vmFolder,
dc2hostFolder,
cr2resourcePool,
rp2rp});
ObjectSpec ospec = new ObjectSpec();
ospec.setObj(startingPoint); // This is where you supply the starting moRef (usually root folder)
ospec.setSkip(Boolean.FALSE);
ospec.setSelectSet(folderTS); // Attach the TraversalSpec we designed above
Understanding a PropertySpec
A PropertySpec is a list of individual properties that can be found at places identified by the ObjectSpec and its TraversalSpec. Once the PropertyCollector has a moRef, it can then return the properties associated with that moRef. This can include “nested” properties. Nested properties are properties that can be found inside of properties identified at the top level of the managed object. Nested properties are identified by a “dot” notation.
An example of nested properties can be drawn from the VirtualMachine managed object.A VirtualMachine has the property identified as summary, which identifies a VirtualMachineSummary data object. The VirtualMachineSummary contains property config, which identifies a VirtualMachineConfigSummary data object. The VirtualMachineConfigSummary has a property called name, which is a string containing the display name of the VirtualMachine. You can access this name property using the summary.config.name string value. To address all the properties of the VirtualMachineConfigSummary object, you would use the summary.config string value.
The PropertyCollector requires an array of PropertySpec elements. Each element includes:
Type – The type of object that contains the enclosed list of properties.
PathSet – An array of strings containing names of properties to be returned, including nested properties.
It is necessary to add an element for each type of object that you wish to query for properties. The following is a code sample of a PropertySpec:
// This code demonstrates how to specify a PropertySpec for several types of target objects:
PropertySpec folderSp = new PropertySpec();
folderSp.setType("Folder");
folderSp.setAll(Boolean.FALSE);
folderSp.setPathSet(new String [] {"parent", "name"});
PropertySpec dcSp = new PropertySpec();
dcSp.setType("Datacenter");
dcSp.setAll(Boolean.FALSE);
dcSp.setPathSet(new String [] {"parent","name"});
PropertySpec rpSp = new PropertySpec();
rpSp.setType("ResourcePool");
rpSp.setAll(Boolean.FALSE);
rpSp.setPathSet(new String [] {"parent","name","vm"});
PropertySpec crSp = new PropertySpec();
crSp.setType("ComputeResource");
crSp.setAll(Boolean.FALSE);
crSp.set:PathSet(new String [] {"parent","name"});
PropertySpec vmSp = new PropertySpec();
vmSp.setType("VirtualMachine");
vmSp.setAll(Boolean.FALSE);
vmSp.setPathSet(new String [] {"parent",
"name",
"summary.config",
"snapshot",
"config.hardware.device"});
// Tie it all together
PropertySpec [] pspec = new PropertySpec [] {folderSp,
dcSp,
rpSp,
crSp,
vmSp};
Getting the Data from the PropertyCollector
Now that we have defined ObjectSpec and PropertySpec (the where and what), we need to put them into a FilterSpec that combines the two. An array of FilterSpec elements is passed to the PropertyCollector (the minimum number of elements is one). Two mechanisms can retrieve data from PropertyCollector:
RetrieveProperties – A one-time request for all of the desired properties. This can involve a lot of data, and has no refresh option. RetrievePropertiesEx has an additional options parameter.
Update requests – PropertyCollector update requests take two forms: polling and waiting (see below).
Requesting Updates
The update method is the way to keep properties up to date. In either Polling or Waiting, it is first necessary to register your FilterSpec array object with the PropertyCollector. You do this using the CreateFilter method, which sends a copy of your FilterSpec to the server. Unlike the RetrieveProperties method, FilterSpec is retained after CreateFilter operation. The following code shows how to set FilterSpec:
// We already showed examples of creating pspec and ospec in the examples above.
// The PropertyCollector wants an array of FilterSpec objects, so:
PropertyFilterSpec fs = new PropertyFilterSpec();
fs.setPropSet(pspec);
fs.setObjectSet(ospec);
PropertyFilterSpec [] fsa = new PropertyFilterSpec [] {fs};
ManagedObjectReference pcRef = serviceContent.getPropertyCollector();
// This next statement sends the filter to the server for reference by the PropertyCollector
ManagedObjectReference pFilter = serviceConnection.CreateFilter(pcRef, fsa, Boolean.FALSE);
If you wish to begin polling, you may then call the function CheckForUpdates, which on the first try (when it must contain an empty string for the version number) returns a complete dump of all the requested properties from all the eligible objects, along with a version number. Subsequent calls to CheckForUpdates must contain this version number to indicate to the PropertyCollector that you seek any changes that deviate from this version. The result is either a partial list containing only the changes from the previous version (including a new version number), or a return code indicating no data has changed. The following code sample shows how to check for updates:
String updateVersion = ""; // Start with no version
UpdateSet changeData = serviceConnection.CheckForUpdates(pcRef, updateVersion);
if (changeData != nil) {
updateVersion = changeData.getVersion(); // Extract the version of the data set
}
// ...
// Get changes since the last version was sent.
UpdateSet latestData = serviceConnection.CheckForUpdates(pcRef, updateVersion);
If instead you wish to wait for updates to occur, you must create a task thread that blocks on the call WaitForUpdates. This task thread would return changes only as they occur and not at any other time. However if the request times out, you must renew it.
Extracting Information from the Change Data
The data returned from CheckForUpdates (or WaitForUpdates) is an array of PropertyFilterUpdate entries. Since a PropertyFilterUpdate entry is very generic, here is some code showing how to extract information from the PropertyFilterUpdate.
// Extract the PropertyFilterUpdate set from the changeData
PropertyFilterUpdate [] updateSet = changeData.getFilterSet();
// There is one entry in the updateSet for each filter you registered with the PropertyCollector.
// Since we currently have only one filter, the array length should be one.
PropertyFilterUpdate myUpdate = updateSet[0];
ObjectUpdate [] changes = myUpdate.getObjectSet();
for (a = 0; a < changes.length; a++) {
ObjectUpdate theObject = changes[a];
String objName = theObject.getObj().getMoType().getName();
// Must decide how to handle the value based on the name returned.
// The only names returned are names found in the PropertySpec lists.
// Get propertyName and value ...
}
Getting Specific Data
From time to time, you might need to get data that is relevant to a single item. In that case you can create a simple ObjectSpec including the moRef for the item of interest. The PropertySpec can then be set to obtain the properties you want, and you can use RetrieveProperties to get the data. Hopefully you can deduce moRef from a general examination of the properties, by searching for information from the rootFolder.
Identifying Virtual Disks for Backup and Restore
To back up a virtual machine, you first need to create a snapshot. Once the snapshot is created, you then need to identify the virtual disks associated with this snapshot. A virtual machine might have multiple snapshots associated with it. Each snapshot has a virtual “copy” of the virtual disks for the virtual machine. These copies are named with the base name of the disk, and a unique decimal number appended to the name. The format of the number is a hyphen character followed by a 6-digit zero-filled number. An example a disk copy name might be mydisk-NNNNNN.vmdk where NNNNNN would be some number like: 000032.
The vSphere API identifies virtual disk files by prefixing the datastore name onto the file system pathname and the filename: [storageN] myvmname/mydisk-NNNNNN.vmdk. The name in square brackets corresponds to the short name of the datastore that contains this virtual disk, while the remainder of the path string represents the location relative to the root of this datastore.
To get the name and characteristics of a virtual disk file, you use the PropertyCollector to select the property: config.hardware.device from a VirtualMachine managed object. This returns an array of virtual devices associated with a VirtualMachine or Snapshot. You must scan this list of devices to extract the list of virtual disks. All that is necessary is to see if each VirtualDevice entry extends to VirtualDisk. When you find such an entry, examine the BackingInfo property. You must extend the type of the backing property to one of the following, or a VirtualMachineSnapshot managed object:
It is important to know which backing type is in use in order to be able to re-create the Virtual Disk.It is also important to know that you cannot snapshot a disk of type VirtualDiskRawDiskMappingVer1BackingInfo, and therefore you cannot back up this type of Virtual Disk.
The properties of interest are the backing fileName and the VirtualDisk capacityInKB. Additionally, when change tracking is in place, you should also save the changeID.
Creating a Snapshot
Before performing a backup operation, you must create a snapshot of the target virtual machine. Both full and incremental backup rely on the snapshot in vSphere.
With SAN transport on VMFS volumes, the virtual machine should not have any pre-existing snapshots, so that reporting of in-use disk sectors will work. For details see About Changed Block Tracking.
As a best practice, you should search for and delete any pre-existing snapshots with the same name that you selected for the temporary snapshot. These snapshots are possibly remnants from failed backup attempts.
Within a specific snapshot, the names of virtual disk files (with extension .vmdk) can be modified with a zero-filled 6-digit decimal sequence number to ensure that the .vmdk files are uniquely named. Depending on whether or not the current virtual machine had a pre-existing snapshot, the disk name for a snapshot could have this format: <diskname>-<NNNNNN>.vmdk. This unique name is no longer valid after the snapshot is destroyed, so any data for a snapshot disk should be stored in the backup program under its base disk name.
The following code sample shows how to create a snapshot on a specific virtual machine:
// At this point we assume the virtual machine is identified as ManagedObjectReference vmMoRef.
String SnapshotName = "Backup";
String SnapshotDescription = "Temporary Snapshot for Backup";
boolean memory_files = false;
boolean quiesce_filesystem = true;
ManagedObjectReference taskRef = serviceConnection.getservice().CreateSnapshot_Task(vmMoRef,
SnapshotName, SnapshotDescription, memory_files, quiesce_filesystem);
You can use the taskRef return value as a moRef to track progress of the snapshot operation. After successful completion, taskRef.info.result contains the moRef of the snapshot.
Backing Up a Virtual Disk
This section describes how to get data from the Virtual Disk after you have identified it. In order to access a virtual disk, you must use the VixDiskLib. The following code shows how to initialize the VixDiskLib and use it for accessing a virtual disk. All operations require a VixDiskLib connection to access virtual disk data. At the present time VixDiskLib is not implemented for the Java language, so this code is C++ language:
VixDiskLibConnectParams connectParams;
VixDiskLibConnection srcConnection;
connectParams.serverName = strdup("TargetServer");
connectParams.creds.uid.userName = strdup("root");
connectParams.creds.uid.password = strdup("yourPasswd");
connectParams.port = 902;
VixError vixError = VixDiskLib_Init(1, 0, &logFunc, &warnFunc, &panicFunc, libDir);
vixError = VixDiskLib_Connect(&connectParams, &srcConnection);
This next section of code shows how to open and read a specific virtual disk:
VixDiskLibHandle diskHandle;
vixError = VixDiskLib_Open(srcConnection, diskPath, flags, &diskHandle);
uint8 mybuffer[some_multiple_of_512];
vixError = VixDiskLib_Read(diskHandle, startSector, numSectors, &mybuffer);
// Also getting the disk metadata:
size_t requiredLength = 1;
char *buf = new char [1];
// This next operation fails, but updates "requiredLength" with the proper buffer size
vixError = VixDiskLib_GetMetadataKeys(diskHandle, buf, requiredLength, &requiredLength);
delete [] buf;
buf = new char[requiredLength]; // Create a large enough buffer
vixError = VixDiskLib_GetMetadataKeys(diskHandle, buf, requiredLength, NULL);
// And finally, close the diskHandle:
vixError = VixDiskLib_Close(diskHandle);
// And if you are completely done with the VixDiskLib
VixDiskLib_Disconnect(srcConnection);
VixDiskLib_Exit();
Deleting a Snapshot
When you are done performing a backup, you need to delete the temporary snapshot. You can get the moRef for the snapshot from taskRef.info.result as describe above for the create snapshot operation. The following Java code demonstrates how to delete the snapshot:
ManagedObjectReference removeSnapshotTask;
ManagedObjectReference snapshot; // Already initialized.
removeSnapshotTask = serviceConnection.getservice().removeSnapshot_Task(snapshot, Boolean FALSE);
Changed Block Tracking on Virtual Disks
On ESXi hosts release 4.0 and later, virtual machines can keep track of disk sectors that have changed. This is called changed block tracking. Its method in the VMware vSphere API is QueryChangedDiskAreas, which takes the following parameters:
_this – Managed object reference to the virtual machine.
snapshot – Managed object reference to a Snapshot of the virtual machine.
deviceKey – Virtual disk for which to compute the changes.
startOffset – Byte offset where to start computing changes to virtual disk. The length of virtual disk sector(s) examined is returned in DiskChangeInfo.
changeId – An identifier for the state of a virtual disk at a specific point in time. A new ChangeId results every time someone creates a snapshot. You should retain this value with the version of change data that you extract (using QueryChangedDiskAreas) from the snapshot’s virtual disk.
When you back up a snapshot for the first time, ChangeId should be unset, or unsaved, indicating that a baseline (full) backup is required. If you have a saved ChangeId, it identifies the last time a backup was taken, and tells the changed block tracking logic to identify changes that have occurred since the time indicated by the saved ChangeId.
There are two ways to get this baseline backup:
1
2
Provide the special ChangeId "*" (star). The star indicates that QueryChangedDiskAreas should return only active portions of the virtual disk. For both thin provisioned (sparse) virtual disks and for ordinary virtual disks, this causes a substantial reduction in the amount of data to save.
To summarize, changeID is an identifier for a time in the past. It can be star "*" to identify all allocated areas of virtual disk, ignoring unallocated areas (of sparse disk), or it could be a changeId string saved at the time when a pre-backup snapshot was taken. It only makes sense to use the special ChangeId = "*" when no previous ChangeId exists. If a previous ChangeId does exist, then QueryChangedDiskAreas returns the disk sectors that changed since the new ChangeId was collected. Use of Change ID for Changed Block Tracking shows the algorithm.
The following restrictions are imposed on the "*" query when determining allocated areas of a virtual disk:
The virtual machine must have had no (zero) snapshots when changed block tracking was enabled.
Enabling Changed Block Tracking
This feature is disabled by default, because it reduces performance by a small but measurable amount. If you query the virtual machine configuration, you can determine if it is capable of changed block tracking. Use the property collector to retrieve the capability field from the VirtualMachineManagedObject. If the capability field contains the flag changeTrackingSupported, then you can proceed. The virtual machine version must be 7 or higher to support this. If the virtual machine version is lower than 7, upgrade the virtual hardware.
If supported, you enable changed block tracking using an abbreviated form of VirtualMachineConfigSpec, then use the ReconfigVM_Task method to reconfigure the virtual machine with changed block tracking:
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
configSpec.changeTrackingEnabled = new Boolean(true);
ManagedObjectReference taskMoRef =
serviceConnection.getService().ReconfigVm_Task(targetVM_MoRef, configSpec);
Powered-on virtual machines must go through a stun-unstun cycle (triggered either by power on, migrate, resume after suspend, or snapshot create/delete/revert) before the virtual machine reconfiguration takes effect.
To enable changed block tracking with the vSphere Client:
1
Select the virtual machine and ensure that Summary > VM Version says “7” or higher compatibility.
2
In the Summary tab, click Edit Settings > Options > Advanced > General.
3
4
In the new dialog box, locate or create a row with name ctkEnabled, and set its value to true not false. See above concerning the stun-unstun cycle.
To enable changed block tracking and back up with the VMware vSphere API:
1
configSpec.changeTrackingEnabled = new Boolean(true);
2
CreateSnapshot_Task(VMmoRef, SnapshotName, Description, memory_files, quiesce_filesystem);
3
Starting from the snapshot’s ConfigInfo, work your way to the BackingInfo of all virtual disks in the snapshot. This gives you the change IDs for all the disks of the virtual machine.
4
VixDiskLib_Read(snapshotDiskHandle, startSector, numSectors, &buffer); /* C not Java */
5
removeSnapshot_Task(SnapshotName, Boolean FALSE);
6
Next time you back up this virtual machine, create a snapshot and use QueryChangedDiskAreas with the change IDs from your previous backup to take advantage of changed block tracking.
changes = theVM.queryChangedDiskAreas(SnapshotMoRef, diskDeviceKey, startPosition, changeId);
Gathering Changed Block Information
Associated with changed block tracking is changeId, an identifier for versions of changed block data. Whenever a virtual machine snapshot is created, associated with that snapshot is a changeId that functions as a landmark to identify changes in virtual disk data. So it follows that when a snapshot is created for the purpose of creating an initial virtual disk backup, the changeId associated with that snapshot can be used to retrieve changes that have occurred since snapshot creation.
To obtain the changeId associated with any disk in a snapshot, you examine the “hardware” array from the snapshot. Any item in the devices table that is of type vim.vm.device.VirtualDevice.VirtualDisk encloses a class describing the “backing storage” (obtained using getBacking) that implements virtual disk. If backing storage is one of the following disk types, you can use the changeId property of the BackingInfo data object to obtain the changeId:
vim.vm.device.VirtualDevice.VirtualDiskFlatVer2BackingInfo
vim.vm.device.VirtualDevice.VirtualDiskSparseVer2BackingInfo
vim.vm.device.VirtualDevice.VirtualDiskRawDiskMappingVer1BackingInfo
vim.vm.device.VirtualDevice.VirtualDiskRawDiskVer2BackingInfo
Information returned by the QueryChangedDiskAreas method is a DiskChangeInfo data object containing an array of DiskChangeInfo.DiskChangeExtent items that enumerate the start offset and length of various disk areas that changed, and the length and start offset of the entire disk area covered by DiskChangeInfo.
When using QueryChangedDiskAreas to gather information about snapshots, enable change tracking before taking a snapshot. Attempts to collect information about changes that occurred before change tracking was enabled result in a FileFault error. Enabling change tracking provides the additional benefit of saving space because it enables backup of only information that has changed. If change tracking is not enabled, the entire virtual machine must be backed up each time, rather than incrementally.
Changed block tracking is supported whenever the I/O operations are processed by the ESXi storage stack:
When I/O operations are not processed by the ESXi storage stack, changed block tracking is not usable:
If the guest actually wrote to each block of a virtual disk (long format or secure erase), or if the virtual disk is thick and eager zeroed, or cloned thick disk, then the "*" query reports the entire disk as being in use.
To find change information, you can use the managed object browser at http://<ESXhost>/mob to follow path content > rootFolder > datacenter > datastore > vm > snapshot > config > hardware > virtualDisk > backing. Changed block tracking information (changeId) appears in the BackingInfo.
The following C++ code sample assumes that, in the past, you obtained a complete copy of the virtual disk, and at the time when the changeId associated with the snapshot was collected, you stored it for use at a later time, which is now. A new snapshot has been created, and the appropriate moRef is available:
String changeId;      // Already initialized: changeId, snapshotMoRef, theVM
ManagedObjectReference snapshotMoRef;
ManagedObjectReference theVM;
int diskDeviceKey;    // Identifies the virtual disk.
VirtualMachine.DiskChangeInfo changes;
long startPosition = 0;
do {
changes = theVM.queryChangedDiskAreas(snapshotMoRef, diskDeviceKey, startPosition, changeId);
for (int i = 0; i < changes.changedArea.length; i++) {
long length = changes.changedArea[i].length;
long offset = changes.changedArea[i].startOffset;
//
// Go get and save disk data here
}
startPosition = changes.startOffset + changes.length;
} while (startPosition < diskCapacity);
In the above code, QueryChangedDiskAreas is called repeatedly, as position moves through the virtual disk. This is because the number of entries in the ChangedDiskArea array could occupy a large amount of memory for describing changes to a large virtual disk. Some disk areas may have no changes for a given changeId.
The changeId (changed block ID) contains a sequence number in the form <UUID>/<nnn>. If <UUID> changes, it indicates that tracking information has become invalid, necessitating a full backup. Otherwise incremental backups can continue in the usual pattern.
Troubleshooting
If you reconfigure a virtual machine to set changeTrackingEnabled, but the property remains false, check that you have queried the virtual machine status with VirtualMachine->config() after reconfiguration with VirtualMachine->reconfigure() and not before. Also make sure that virtual machine compatibility is hardware version 7 or higher, and that it has undergone a stun-unstun cycle since reconfiguration.
Limitations on Changed Block Tracking
Changed block tracking does not work if the virtual hardware version is 6 or earlier, in physical compatibility RDM mode, or when the virtual disk is attached to a shared virtual SCSI bus. ESXi 3.5 supported only up to virtual hardware version 4.
Changed block tracking can be enabled on virtual machines that have disks like these, but when queried for their change ID, these disks always return an empty string. So if you have a virtual machine with a regular system disk and a pass-through RDM as a data disk, you can track changes only on the system disk.
Checking for Namespace
You can avoid using the queryChangedDiskAreas API on ESXi 3.5 based storage by parsing XML files for the namespace. For prepackaged methods that do this, see these SDK code samples:
Axis/java/com/vmware/samples/version/displaynewpropertieshost/DisplayNewPropertiesHostV25.java
Axis/java/com/vmware/samples/version/getvirtualdiskfiles/GetVirtualDiskFilesV25.java
DotNet/cs/DisplayNewProperties/DisplayNewPropertiesV25.cs
DotNet/cs/GetVirtualDiskFiles/GetVirtualDiskFilesV25.cs