How to Create Linked Virtual Machines with vSphere API?
More often than not, you may have several virtual machines based on same software stacks running on the same host. Although they are very much the same, they take as much space as multitude of what one virtual machine takes.
Since vSphere 4.0, things are different. You can significantly reduce the storage usage by a new feature called linked virtual machines. The idea is simple: sharing a common virtual disk among the similar virtual machines. The shared virtual disk serves as a base. On top of that, each virtual machine has its own delta disk. When a guest operating system writes to disk, the data persists to the delta disk. When it reads from disk, the delta disk is checked first before trying the base disk.
As a result, you only need to save one copy of the base disk no matter how many virtual machines you have (up to 8 virtual machines in a linked virtual machine group). One limitation is that you cannot use it with HA cluster.
How to create linked virtual machines? You have two approaches: clone a virtual machine either from a snapshot, or from its current running state.
You can automate this process by leveraging VMware vSphere API. Here is the tech note Linked Virtual Machines. Accompanied with this tech note, several samples are shipped in vSphere Web Service SDK. The following is a sample I rewrote using vSphere(VI) Java API.
package com.vmware.vim25.mo.samples.vm;
import java.net.URL;
import java.util.ArrayList;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.TaskInfoState;
import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer1BackingInfo;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualDiskRawDiskMappingVer1BackingInfo;
import com.vmware.vim25.VirtualDiskSparseVer1BackingInfo;
import com.vmware.vim25.VirtualDiskSparseVer2BackingInfo;
import com.vmware.vim25.VirtualMachineCloneSpec;
import com.vmware.vim25.VirtualMachineRelocateDiskMoveOptions;
import com.vmware.vim25.VirtualMachineRelocateSpec;
import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
import com.vmware.vim25.VirtualMachineSnapshotInfo;
import com.vmware.vim25.VirtualMachineSnapshotTree;
import com.vmware.vim25.mo.Datastore;
import com.vmware.vim25.mo.Folder;
import com.vmware.vim25.mo.InventoryNavigator;
import com.vmware.vim25.mo.ServiceInstance;
import com.vmware.vim25.mo.Task;
import com.vmware.vim25.mo.VirtualMachine;
/**
* Author: Steve Jin (sjin@vmware.com)
* This sample creates a linked clone from an existing snapshot. It works with vSphere 4 and not supported by the old VI SDK 2.* API
* Each independent disk needs a DiskLocator with diskMoveType as moveAllDiskBackingsAndDisallowSharing.
*/
public class VMLinkedClone
{
public static void main(String[] args) throws Exception
{
if (args.length != 6)
{
System.out.println("java VMLinkedClone <SdkUrl> <username> <password> <VmName> <SnapShotName> <CloneVmName>");
System.out.println("java VMLinkedClone https://8.8.8.8/sdk root password myVM1 snapBaseLine myCloneVM2");
return;
}
String vmName = args[3];
String snapShotName = args[4];
String cloneVmName = args[5];
ServiceInstance si = new ServiceInstance(new URL(args[0]), args[1], args[2], true);
VirtualMachine vm = (VirtualMachine) new InventoryNavigator(si.getRootFolder()).searchManagedEntity("VirtualMachine", vmName);
createLinkedClone(vm, snapShotName, cloneVmName);
si.getServerConnection().logout();
}
public static void createLinkedClone(VirtualMachine vm, String snapshotName, String cloneVmName) throws Exception
{
if(vm==null || snapshotName==null)
return;
ArrayList<Integer> diskKeys = getIndependenetVirtualDiskKeys(vm);
VirtualMachineRelocateSpec rSpec = new VirtualMachineRelocateSpec();
if(diskKeys.size() > 0)
{
Datastore[] dss = vm.getDatastores();
VirtualMachineRelocateSpecDiskLocator [] diskLocator = new VirtualMachineRelocateSpecDiskLocator[diskKeys.size()];
int count = 0;
for(Integer key : diskKeys)
{
diskLocator[count] = new VirtualMachineRelocateSpecDiskLocator();
diskLocator[count].setDatastore(dss[0].getMOR());
diskLocator[count].setDiskMoveType(VirtualMachineRelocateDiskMoveOptions.moveAllDiskBackingsAndDisallowSharing.toString());
diskLocator[count].setDiskId(key);
count = count + 1;
}
rSpec.setDiskMoveType(VirtualMachineRelocateDiskMoveOptions.createNewChildDiskBacking.toString());
rSpec.setDisk(diskLocator);
}
else
{
rSpec.setDiskMoveType(VirtualMachineRelocateDiskMoveOptions.createNewChildDiskBacking.toString());
}
VirtualMachineCloneSpec cloneSpec = new VirtualMachineCloneSpec();
cloneSpec.setPowerOn(false);
cloneSpec.setTemplate(false);
cloneSpec.setLocation(rSpec);
cloneSpec.setSnapshot(getSnapshotReference(vm, snapshotName));
try
{
Folder parent = (Folder) vm.getParent();
Task task = vm.cloneVM_Task(parent, cloneVmName, cloneSpec);
task.waitForTask();
if(task.getTaskInfo().getState() == TaskInfoState.error)
{
System.out.println("Failure: Virtual Machine cannot be cloned");
}
if(task.getTaskInfo().getState() == TaskInfoState.success)
{
System.out.println("Virtual Machine Cloned successfully.");
}
}
catch(Exception e)
{
System.out.println("Exception while cloning: " + e);
}
}
private static ArrayList<Integer> getIndependenetVirtualDiskKeys(VirtualMachine vm) throws Exception
{
ArrayList<Integer> diskKeys= new ArrayList<Integer>();
VirtualDevice[] devices = (VirtualDevice[]) vm.getPropertyByPath("config.hardware.device");
for(int i=0; i<devices.length; i++)
{
if(devices[i] instanceof VirtualDisk)
{
VirtualDisk vDisk = (VirtualDisk) devices[i];
String diskMode = "";
VirtualDeviceBackingInfo vdbi = vDisk.getBacking();
if(vdbi instanceof VirtualDiskFlatVer1BackingInfo)
{
diskMode = ((VirtualDiskFlatVer1BackingInfo) vdbi).getDiskMode();
}
else if(vdbi instanceof VirtualDiskFlatVer2BackingInfo)
{
diskMode = ((VirtualDiskFlatVer2BackingInfo)vdbi).getDiskMode();
}
else if(vdbi instanceof VirtualDiskRawDiskMappingVer1BackingInfo)
{
diskMode = ((VirtualDiskRawDiskMappingVer1BackingInfo)vdbi).getDiskMode();
}
else if(vdbi instanceof VirtualDiskSparseVer1BackingInfo)
{
diskMode = ((VirtualDiskSparseVer1BackingInfo)vdbi).getDiskMode();
}
else if(vdbi instanceof VirtualDiskSparseVer2BackingInfo)
{
diskMode = ((VirtualDiskSparseVer2BackingInfo)vdbi).getDiskMode();
}
if(diskMode.indexOf("independent") != -1)
{
diskKeys.add(vDisk.getKey());
}
}
}
return diskKeys;
}
private static ManagedObjectReference getSnapshotReference(VirtualMachine vm, String snapshotName) throws Exception
{
VirtualMachineSnapshotInfo snapInfo = vm.getSnapshot();
ManagedObjectReference snapmor = null;
if(snapInfo != null)
{
VirtualMachineSnapshotTree[] snapTree = snapInfo.getRootSnapshotList();
snapmor = traverseSnapshotInTree(snapTree, snapshotName);
}
return snapmor;
}
private static ManagedObjectReference traverseSnapshotInTree(VirtualMachineSnapshotTree[] snapTree, String snapshotName)
{
if(snapTree == null || snapshotName == null)
return null;
ManagedObjectReference snapmor = null;
for (int i=0; i < snapTree.length && snapmor == null; i++)
{
VirtualMachineSnapshotTree node = snapTree[i];
if (node.getName().equals(snapshotName))
{
snapmor = node.getSnapshot();
}
else
{
VirtualMachineSnapshotTree[] childTree = snapTree[i].getChildSnapshotList();
snapmor = traverseSnapshotInTree(childTree, snapshotName);
}
}
return snapmor;
}
}
Recent Comments