Skip to content

Instantly share code, notes, and snippets.

@vmwarecode
Created June 27, 2016 20:52
Show Gist options
  • Save vmwarecode/30f133f5ab0350cdeaf4c26a009784f4 to your computer and use it in GitHub Desktop.
Save vmwarecode/30f133f5ab0350cdeaf4c26a009784f4 to your computer and use it in GitHub Desktop.
XVCvMotion
/*
* ****************************************************************************
* Copyright VMware, Inc. 2010-2016. All Rights Reserved.
* ****************************************************************************
*
* This software is made available for use under the terms of the BSD
* 3-Clause license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.vmware.vm;
import com.vmware.vim25.*;
import com.vmware.common.annotations.Action;
import com.vmware.common.annotations.Option;
import com.vmware.common.annotations.Sample;
import com.vmware.connection.BasicConnection;
import com.vmware.connection.ConnectedVimServiceBase;
import com.vmware.connection.Connection;
import com.vmware.connection.helpers.GetMOREF;
import java.util.*;
import javax.xml.ws.soap.SOAPFaultException;
/**
* <pre>
* XVCvMotion
*
* Used to Relocate VM from one VC to another.
*
* <b>Parameters:</b>
* url [required] : url of the web service
* username [required] : username for the authentication
* password [required] : password for the authentication
* vmname [required] : name of the virtual machine
* destcluster [required] : name of the cluster on target virtual center where to migrate the virtual machine.
* remoteurl [required] : url of web service for target virtual center
* ruser [required] : username for the authentication to the target virtual center.
* rpassword [required] : password for the authentication to the target virtual center.
* rthumbprint [required] : thumbprint of the target virtual center.
* targetfolder [optional] : folder on the target virtual center where to migrate the virtual machine.
*
* <b>Command Line:</b>
* run.bat com.vmware.vm.XVCvMotion --url [URLString] --username [User] --password [Password]
* --vmname [VMName] --destcluster [Target Cluster] --remoteurl [TargetVC URLString] --ruser [TargetVC User]
* --rpassword [TargetVC Password] --rthumbprint [TargetVC Thumbprint] --targetfolder [Target Folder]
* </pre>
*/
@Sample(name = "XVCvMotion", description = "Used to Relocate VM from one VC to another")
public class XVCvMotion extends ConnectedVimServiceBase {
/*
* Connection input parameters for the Source and target vCenter Servers.
*/
String vmName = null;
String destCluster = null;
String remoteurl = null;
String ruser = null;
String rpassword = null;
String rthumbprint = null;
String targetFolder = null; // default value for targetFolder is vm
private static VimPortType destVimPort = null; // VimPort for the target VC
// Connection
private static ServiceContent destServiceContent = null;
@Option(name = "vmname", description = "name of the virtual machine to be migrated")
public void setVmName(String name) {
this.vmName = name;
}
@Option(name = "clustername", description = "Target Cluster Name")
public void setCluster(String clusterName) {
this.destCluster = clusterName;
}
@Option(name = "remoteurl", description = "Target vCenter URL ")
public void setRemoteUrl(String remoteurl) {
this.remoteurl = remoteurl;
}
@Option(name = "ruser", description = "Target vCenter username")
public void setRuser(String ruser) {
this.ruser = ruser;
}
@Option(name = "rpassword", description = "Target vCenter Password")
public void setRpassword(String rpassword) {
this.rpassword = rpassword;
}
@Option(name = "rthumbprint", description = "Thumbprint of Target vCenter")
public void setThumbprint(String thumbprint) {
this.rthumbprint = thumbprint;
}
@Option(name = "targetfolder", description = "Target Folder", required = false)
public void setTargetFolder(String targetFolder) {
this.targetFolder = targetFolder;
}
/**
* This method returns Managed Object Reference to VM root Folder
*
* @param MOR
* for the starting Point of Filter
* @param Type
* OF Entity we are looking For.
* @return Managed Object Reference to VM root Folder
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
*/
ManagedObjectReference getVMFolderMor(ManagedObjectReference mor,
String type, String name) throws RuntimeFaultFaultMsg,
InvalidPropertyFaultMsg {
List<SelectionSpec> tSpec = buildVmFolderTraversal();
ManagedObjectReference retVal = null;
// Create PropertySpec
PropertySpec propertySpec = new PropertySpec();
propertySpec.setAll(Boolean.FALSE);
propertySpec.getPathSet().add("name");
propertySpec.setType(type);
// Now create ObjectSpec
ObjectSpec objectSpec = new ObjectSpec();
objectSpec.setObj(mor);
objectSpec.setSkip(Boolean.TRUE);
objectSpec.getSelectSet().addAll(tSpec);
// Create PropertyFilterSpec using the PropertySpec and ObjectSpec
PropertyFilterSpec propertyFilterSpec = new PropertyFilterSpec();
propertyFilterSpec.getPropSet().add(propertySpec);
propertyFilterSpec.getObjectSet().add(objectSpec);
List<PropertyFilterSpec> listpfs = new ArrayList<PropertyFilterSpec>(1);
listpfs.add(propertyFilterSpec);
List<ObjectContent> listobjcont = retrievePropertiesAllObjects(listpfs);
if (listobjcont != null) {
for (ObjectContent oc : listobjcont) {
ManagedObjectReference mr = oc.getObj();
String entityName = null;
List<DynamicProperty> listDynamicProps = oc.getPropSet();
DynamicProperty[] dps = listDynamicProps
.toArray(new DynamicProperty[listDynamicProps.size()]);
if (dps != null) {
for (DynamicProperty dp : dps) {
entityName = (String) dp.getVal();
}
}
if (entityName != null && entityName.equals(name)) {
retVal = mr;
break;
}
}
} else {
System.out.println("The Object Content is Null");
}
return retVal;
}
// RetrievePropertiesAllObjects
List<ObjectContent> retrievePropertiesAllObjects(
List<PropertyFilterSpec> listpfs) throws RuntimeFaultFaultMsg,
InvalidPropertyFaultMsg {
RetrieveOptions propObjectRetrieveOpts = new RetrieveOptions();
List<ObjectContent> listobjcontent = new ArrayList<ObjectContent>();
RetrieveResult rslts = destVimPort.retrievePropertiesEx(
destServiceContent.getPropertyCollector(), listpfs,
propObjectRetrieveOpts);
if (rslts != null && rslts.getObjects() != null
&& !rslts.getObjects().isEmpty()) {
listobjcontent.addAll(rslts.getObjects());
}
String token = null;
if (rslts != null && rslts.getToken() != null) {
token = rslts.getToken();
}
while (token != null && !token.isEmpty()) {
rslts = destVimPort.continueRetrievePropertiesEx(destServiceContent
.getPropertyCollector(), token);
token = null;
if (rslts != null) {
token = rslts.getToken();
if (rslts.getObjects() != null && !rslts.getObjects().isEmpty()) {
listobjcontent.addAll(rslts.getObjects());
}
}
}
return listobjcontent;
}
// buildVmFolderTraversal
// This to Get the MOR for the Folder where to place the VM.
//It Include traversal from CLuster to datacenter and then to VMFolder.
List<SelectionSpec> buildVmFolderTraversal() {
// DC -> VM Folder
TraversalSpec dcToVmf = new TraversalSpec();
dcToVmf.setType("Datacenter");
dcToVmf.setSkip(Boolean.FALSE);
dcToVmf.setPath("vmFolder");
dcToVmf.setName("dcToVmf");
dcToVmf.getSelectSet().add(getSelectionSpec("visitFolders"));
// CLUSTER -> Parent
TraversalSpec CrtoPr = new TraversalSpec();
CrtoPr.setType("ClusterComputeResource");
CrtoPr.setSkip(Boolean.FALSE);
CrtoPr.setPath("parent");
CrtoPr.setName("cctoparent");
CrtoPr.getSelectSet().add(getSelectionSpec("foldertoparent"));
// Folder->Parent
TraversalSpec FoltoPr = new TraversalSpec();
FoltoPr.setType("Folder");
FoltoPr.setSkip(Boolean.FALSE);
FoltoPr.setPath("parent");
FoltoPr.setName("foldertoparent");
FoltoPr.getSelectSet().add(getSelectionSpec("foldertoparent"));
FoltoPr.getSelectSet().add(getSelectionSpec("dcToVmf"));
// For Folder -> Folder recursion
TraversalSpec visitFolders = new TraversalSpec();
visitFolders.setType("Folder");
visitFolders.setPath("childEntity");
visitFolders.setSkip(Boolean.FALSE);
visitFolders.setName("visitFolders");
visitFolders.getSelectSet().add(getSelectionSpec("visitFolders"));
List<SelectionSpec> resultspec = new ArrayList<SelectionSpec>();
resultspec.add(visitFolders);
resultspec.add(dcToVmf);
resultspec.add(CrtoPr);
resultspec.add(FoltoPr);
return resultspec;
}
// GetSelectionSpec
SelectionSpec getSelectionSpec(String name) {
SelectionSpec genericSpec = new SelectionSpec();
genericSpec.setName(name);
return genericSpec;
}
/**
* This method returns a boolean value specifying whether the Task is
* succeeded or failed.
*
* @param task
* ManagedObjectReference representing the Task.
* @return boolean value representing the Task result.
* @throws InvalidCollectorVersionFaultMsg
*
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
*/
boolean getTaskResultAfterDone(ManagedObjectReference task)
throws InvalidPropertyFaultMsg, RuntimeFaultFaultMsg,
InvalidCollectorVersionFaultMsg {
boolean retVal = false;
// info has a property - state for state of the task
Object[] result = waitForValues.wait(task, new String[] { "info.state",
"info.error" }, new String[] { "state" },
new Object[][] { new Object[] { TaskInfoState.SUCCESS,
TaskInfoState.ERROR } });
if (result[0].equals(TaskInfoState.SUCCESS)) {
retVal = true;
}
if (result[1] instanceof LocalizedMethodFault) {
throw new RuntimeException(((LocalizedMethodFault) result[1])
.getLocalizedMessage());
}
return retVal;
}
@Action
public void placeVM() throws DuplicateNameFaultMsg, InvalidNameFaultMsg,
RuntimeFaultFaultMsg, InvalidPropertyFaultMsg, FileFaultFaultMsg,
InsufficientResourcesFaultFaultMsg, InvalidDatastoreFaultMsg,
InvalidStateFaultMsg, MigrationFaultFaultMsg, TimedoutFaultMsg,
VmConfigFaultFaultMsg, InvalidCollectorVersionFaultMsg,
InvalidLocaleFaultMsg, InvalidLoginFaultMsg {
PlacementResult placementResult;
ManagedObjectReference clusterMor = null;
PlacementSpec placementSpec = new PlacementSpec();
ManagedObjectReference vmMOR = null;
System.out.println("Searching for VM '" + vmName + "'..");
Map<String, ManagedObjectReference> vms = getMOREFs.inContainerByType(
serviceContent.getRootFolder(), "VirtualMachine");
if (vms.containsKey(vmName)) {
System.out.println("Found VM: " + vmName);
vmMOR = vms.get(vmName);
} else {
throw new IllegalStateException("No VM by the name of '" + vmName
+ "' found!");
}
Map<String, Object> vmProperties = getMOREFs.entityProps(vmMOR,
new String[] { "config.version", "config.cpuAllocation",
"config.memoryAllocation", "config.hardware.numCPU",
"config.hardware.memoryMB", "config.files",
"config.swapPlacement", "config.hardware.device",
"config.name" });
// Setting VirtualMachineConfigSpec properties
System.out.println("Setting VirtualMachineConfigSpec properties--");
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
configSpec.setVersion((String) vmProperties.get("config.version"));
configSpec.setCpuAllocation((ResourceAllocationInfo) vmProperties
.get("config.cpuAllocation"));
configSpec.setMemoryAllocation((ResourceAllocationInfo) vmProperties
.get("config.memoryAllocation"));
configSpec.setNumCPUs((Integer) vmProperties
.get("config.hardware.numCPU"));
Integer memoryinMBs = (Integer) vmProperties
.get("config.hardware.memoryMB");
configSpec.setMemoryMB(memoryinMBs.longValue());
configSpec.setFiles((VirtualMachineFileInfo) vmProperties
.get("config.files"));
configSpec.setSwapPlacement((String) vmProperties
.get("config.swapPlacement"));
configSpec.setName((String) vmProperties.get("config.name"));
List<VirtualDevice> listvd = ((ArrayOfVirtualDevice) vmProperties
.get("config.hardware.device")).getVirtualDevice();
for (VirtualDevice device : listvd) {
VirtualDeviceConfigSpec vdConfigSpec = new VirtualDeviceConfigSpec();
vdConfigSpec.setDevice(device);
configSpec.getDeviceChange().add(vdConfigSpec);
}
placementSpec.setConfigSpec(configSpec);
// Connection to Destination VC
System.out.println("Connecting to Destination vCenter - " + remoteurl);
DestConnect destConnect = new DestConnect();
Connection targetConnection = destConnect.getDestConnection(remoteurl,
ruser, rpassword);
destVimPort = targetConnection.getVimPort();
destServiceContent = targetConnection.getServiceContent();
// clusters will contain the list of Clusters available
System.out.println("Looking for the Cluster defined on Destination VC");
Map<String, ManagedObjectReference> clusters = destConnect.destGetMOREFs
.inContainerByType(destServiceContent.getRootFolder(),
"ClusterComputeResource");
if (clusters.containsKey(destCluster)) {
clusterMor = clusters.get(destCluster);
System.out.println("Found Cluster '" + destCluster
+ "'on Destination vCenter!");
} else {
throw new IllegalStateException("No Cluster by the name of '"
+ destCluster + "' found!");
}
System.out.println("Getting Recommendations from DRS for XVCvMotion--");
PlacementAction action = new PlacementAction();
try {
placementResult = destVimPort.placeVm(clusterMor, placementSpec);
action = getPlacementAction(placementResult);
} catch (SOAPFaultException e) {
if (e.getMessage().contains("vim.fault.InvalidState")) {
throw new IllegalStateException(
"Check the Cluster setting and make sure that DRS is enabled");
}
else {
throw new IllegalStateException(e.getMessage());
}
}
if (action != null) {
ManagedObjectReference vmfoldermor;
if (targetFolder == null) {
System.out
.println("Target Folder undefined Using Default VM Folder");
vmfoldermor = getVMFolderMor(clusterMor, "Folder", "vm");
} else {
System.out
.println("Setting Defined TargetFolder as VMFolder for XVCvMotion");
vmfoldermor = getVMFolderMor(clusterMor, "Folder", targetFolder);
}
if (vmfoldermor != null) {
ServiceLocatorNamePassword slNamePassowrd = new ServiceLocatorNamePassword();
slNamePassowrd.setPassword(rpassword);
slNamePassowrd.setUsername(ruser);
ServiceLocator locator = new ServiceLocator();
locator.setCredential(slNamePassowrd);
locator.setUrl(remoteurl);
locator.setInstanceUuid(destServiceContent.getAbout()
.getInstanceUuid());
locator.setSslThumbprint(rthumbprint);
VirtualMachineRelocateSpec relocateSpec = action
.getRelocateSpec();
relocateSpec.setService(locator);
// Manually set the folder else Exception would be thrown
relocateSpec.setFolder(vmfoldermor);
System.out.println("Relocation in Progress!");
ManagedObjectReference taskMOR = vimPort.relocateVMTask(vmMOR,
relocateSpec,
VirtualMachineMovePriority.DEFAULT_PRIORITY);
if (getTaskResultAfterDone(taskMOR)) {
System.out.println("Relocation done successfully");
} else {
System.out.println("Error:: Relocation failed");
}
} else {
throw new IllegalStateException("No Folder by the name of '"
+ targetFolder + "' found!");
}
} else {
System.out.println("Recommendations are not correct");
}
destConnect.disconnect();
}
/**
* This method is to return the first valid PlacementAction out of the DRS
* recommendations.
*
* @param placementResult
* @return PlacementAction
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
*/
PlacementAction getPlacementAction(PlacementResult placementResult)
throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg {
List<ClusterRecommendation> recommendations = placementResult
.getRecommendations();
PlacementAction placementAction = null;
int size = recommendations.size();
boolean actionOk = false;
if (size > 0) {
System.out.println("Total number of recommendations are " + size);
System.out
.println("Processing the xvcvmotion placement recommendations out of the recommendations received");
for (ClusterRecommendation clusterrecommend : recommendations) {
if (clusterrecommend.getReason().equalsIgnoreCase(
"xvmotionPlacement")) {
List<ClusterAction> actions = clusterrecommend.getAction();
for (ClusterAction action : actions) {
if (action instanceof PlacementAction) {
placementAction = (PlacementAction) action;
break;
}
}
if (placementAction != null) {
if (placementAction.getVm() == null
|| placementAction.getTargetHost() == null) {
System.out
.println("Placement Action doesnot have a vm and targethost set");
} else {
if (placementAction.getRelocateSpec() != null) {
actionOk = checkRelocateSpec(placementAction
.getRelocateSpec());
if (actionOk)
break;
else
placementAction = null;
}
}
} else {
System.out
.println("Recommendation doesnot have a placement action");
}
}
}
} else {
System.out.println("No recommendations by DRS");
}
return placementAction;
}
/**
* This method is to validate the RelocateSpec.
*
* @param relocateSpec
* @return boolean
* @throws RuntimeFaultFaultMsg
* @throws InvalidPropertyFaultMsg
*/
boolean checkRelocateSpec(VirtualMachineRelocateSpec relocateSpec)
throws RuntimeFaultFaultMsg, InvalidPropertyFaultMsg {
boolean check = false;
if (relocateSpec.getHost() != null) {
if (relocateSpec.getPool() != null) {
if (relocateSpec.getDatastore() != null) {
check = true;
} else {
System.out.println("RelocateSpec doesnot have a datastore");
}
} else {
System.out.println("RelocateSpec doesnot have a resource pool");
}
} else {
System.out.println("RelocateSpec doesnot have a host");
}
return check;
}
}
/**
* DestConnect Used to establish connection to the target Virtual Center.
*/
class DestConnect extends ConnectedVimServiceBase {
/**
* This method is to establish the connection to the Target vCenter.
*
* @param url
* , username, password
* @return Connection
*/
public GetMOREF destGetMOREFs;
Connection getDestConnection(String url, String username, String password) {
Connection connection = new BasicConnection();
connection.setPassword(password);
connection.setUrl(url);
connection.setUsername(username);
this.setHostConnection(true);
this.setConnection(connection);
Connection destConnection = this.connect();
this.destGetMOREFs = getMOREFs;
return destConnection;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment