Sateesh Chodapuneedi b0535d770d CLOUDSTACK-9676 Start instance fails after reverting to a VM snapshot, when there are child VM snapshots
Issue
====
Start instance fails after reverting to a VM snapshot, when there is 1 or more child VM snapshots in the snapshot tree of the VM.
Per the code that detects the presence of a snapshot, we are checking for only current snapshot instead of checking presence of any snapshot in the snapshot tree.
The failure to detect all snapshots means ACP reconfigures the VM in wrong way assuming there are no snapshots for the VM.
This results in start failure.

Fix
===
Ensure correct detection of VM snapshots in the VM snapshot tree

This closes #1828

Signed-off-by: Sateesh Chodapuneedi <sateesh.chodapuneedi@accelerite.com>
(cherry picked from commit 673bb25b5936d1c54e9210781280e9ddc507c830)
Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
2016-12-22 13:29:07 +05:30

3204 lines
149 KiB
Java

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.hypervisor.vmware.mo;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.log4j.Logger;
import com.google.gson.Gson;
import com.vmware.vim25.ArrayOfManagedObjectReference;
import com.vmware.vim25.ChoiceOption;
import com.vmware.vim25.CustomFieldStringValue;
import com.vmware.vim25.DistributedVirtualSwitchPortConnection;
import com.vmware.vim25.DynamicProperty;
import com.vmware.vim25.ElementDescription;
import com.vmware.vim25.GuestInfo;
import com.vmware.vim25.GuestOsDescriptor;
import com.vmware.vim25.HttpNfcLeaseDeviceUrl;
import com.vmware.vim25.HttpNfcLeaseInfo;
import com.vmware.vim25.HttpNfcLeaseState;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.ObjectSpec;
import com.vmware.vim25.OptionValue;
import com.vmware.vim25.OvfCreateDescriptorParams;
import com.vmware.vim25.OvfCreateDescriptorResult;
import com.vmware.vim25.OvfFile;
import com.vmware.vim25.ParaVirtualSCSIController;
import com.vmware.vim25.PropertyFilterSpec;
import com.vmware.vim25.PropertySpec;
import com.vmware.vim25.TraversalSpec;
import com.vmware.vim25.VirtualBusLogicController;
import com.vmware.vim25.VirtualCdrom;
import com.vmware.vim25.VirtualCdromIsoBackingInfo;
import com.vmware.vim25.VirtualCdromRemotePassthroughBackingInfo;
import com.vmware.vim25.VirtualController;
import com.vmware.vim25.VirtualDevice;
import com.vmware.vim25.VirtualDeviceBackingInfo;
import com.vmware.vim25.VirtualDeviceConfigSpec;
import com.vmware.vim25.VirtualDeviceConfigSpecFileOperation;
import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
import com.vmware.vim25.VirtualDeviceConnectInfo;
import com.vmware.vim25.VirtualDisk;
import com.vmware.vim25.VirtualDiskFlatVer1BackingInfo;
import com.vmware.vim25.VirtualDiskFlatVer2BackingInfo;
import com.vmware.vim25.VirtualDiskMode;
import com.vmware.vim25.VirtualDiskRawDiskMappingVer1BackingInfo;
import com.vmware.vim25.VirtualDiskSparseVer1BackingInfo;
import com.vmware.vim25.VirtualDiskSparseVer2BackingInfo;
import com.vmware.vim25.VirtualDiskType;
import com.vmware.vim25.VirtualEthernetCard;
import com.vmware.vim25.VirtualEthernetCardDistributedVirtualPortBackingInfo;
import com.vmware.vim25.VirtualHardwareOption;
import com.vmware.vim25.VirtualIDEController;
import com.vmware.vim25.VirtualLsiLogicController;
import com.vmware.vim25.VirtualLsiLogicSASController;
import com.vmware.vim25.VirtualMachineCloneSpec;
import com.vmware.vim25.VirtualMachineConfigInfo;
import com.vmware.vim25.VirtualMachineConfigOption;
import com.vmware.vim25.VirtualMachineConfigSpec;
import com.vmware.vim25.VirtualMachineConfigSummary;
import com.vmware.vim25.VirtualMachineFileInfo;
import com.vmware.vim25.VirtualMachineFileLayoutEx;
import com.vmware.vim25.VirtualMachineMessage;
import com.vmware.vim25.VirtualMachineMovePriority;
import com.vmware.vim25.VirtualMachinePowerState;
import com.vmware.vim25.VirtualMachineQuestionInfo;
import com.vmware.vim25.VirtualMachineRelocateDiskMoveOptions;
import com.vmware.vim25.VirtualMachineRelocateSpec;
import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
import com.vmware.vim25.VirtualMachineRuntimeInfo;
import com.vmware.vim25.VirtualMachineSnapshotInfo;
import com.vmware.vim25.VirtualMachineSnapshotTree;
import com.vmware.vim25.VirtualSCSIController;
import com.vmware.vim25.VirtualSCSISharing;
import com.cloud.hypervisor.vmware.mo.SnapshotDescriptor.SnapshotInfo;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.cloud.hypervisor.vmware.util.VmwareHelper;
import com.cloud.utils.ActionDelegate;
import com.cloud.utils.Pair;
import com.cloud.utils.Ternary;
import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.script.Script;
public class VirtualMachineMO extends BaseMO {
private static final Logger s_logger = Logger.getLogger(VirtualMachineMO.class);
private static final ExecutorService MonitorServiceExecutor = Executors.newCachedThreadPool(new NamedThreadFactory("VM-Question-Monitor"));
private ManagedObjectReference _vmEnvironmentBrowser = null;
public VirtualMachineMO(VmwareContext context, ManagedObjectReference morVm) {
super(context, morVm);
}
public VirtualMachineMO(VmwareContext context, String morType, String morValue) {
super(context, morType, morValue);
}
public Pair<DatacenterMO, String> getOwnerDatacenter() throws Exception {
return DatacenterMO.getOwnerDatacenter(getContext(), getMor());
}
public Pair<DatastoreMO, String> getOwnerDatastore(String dsFullPath) throws Exception {
String dsName = DatastoreFile.getDatastoreNameFromPath(dsFullPath);
PropertySpec pSpec = new PropertySpec();
pSpec.setType("Datastore");
pSpec.getPathSet().add("name");
TraversalSpec vmDatastoreTraversal = new TraversalSpec();
vmDatastoreTraversal.setType("VirtualMachine");
vmDatastoreTraversal.setPath("datastore");
vmDatastoreTraversal.setName("vmDatastoreTraversal");
ObjectSpec oSpec = new ObjectSpec();
oSpec.setObj(_mor);
oSpec.setSkip(Boolean.TRUE);
oSpec.getSelectSet().add(vmDatastoreTraversal);
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
if (ocs != null) {
for (ObjectContent oc : ocs) {
DynamicProperty prop = oc.getPropSet().get(0);
if (prop.getVal().toString().equals(dsName)) {
return new Pair<DatastoreMO, String>(new DatastoreMO(_context, oc.getObj()), dsName);
}
}
}
return null;
}
public HostMO getRunningHost() throws Exception {
VirtualMachineRuntimeInfo runtimeInfo = getRuntimeInfo();
return new HostMO(_context, runtimeInfo.getHost());
}
public String getVmName() throws Exception {
return (String)getContext().getVimClient().getDynamicProperty(_mor, "name");
}
public GuestInfo getVmGuestInfo() throws Exception {
return (GuestInfo)getContext().getVimClient().getDynamicProperty(_mor, "guest");
}
public void answerVM(String questionId, String choice) throws Exception {
getContext().getService().answerVM(_mor, questionId, choice);
}
public boolean isVMwareToolsRunning() throws Exception {
GuestInfo guestInfo = getVmGuestInfo();
if (guestInfo != null) {
if ("guestToolsRunning".equalsIgnoreCase(guestInfo.getToolsRunningStatus()))
return true;
}
return false;
}
public boolean powerOn() throws Exception {
if (getResetSafePowerState() == VirtualMachinePowerState.POWERED_ON)
return true;
ManagedObjectReference morTask = _context.getService().powerOnVMTask(_mor, null);
// Monitor VM questions
final Boolean[] flags = {false};
final VirtualMachineMO vmMo = this;
Future<?> future = MonitorServiceExecutor.submit(new Runnable() {
@Override
public void run() {
s_logger.info("VM Question monitor started...");
while (!flags[0]) {
try {
VirtualMachineRuntimeInfo runtimeInfo = vmMo.getRuntimeInfo();
VirtualMachineQuestionInfo question = runtimeInfo.getQuestion();
if (question != null) {
s_logger.info("Question id: " + question.getId());
s_logger.info("Question text: " + question.getText());
if (question.getMessage() != null) {
for (VirtualMachineMessage msg : question.getMessage()) {
if (s_logger.isInfoEnabled()) {
s_logger.info("msg id: " + msg.getId());
s_logger.info("msg text: " + msg.getText());
}
if ("msg.uuid.altered".equalsIgnoreCase(msg.getId())) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + msg.getId()
+ ", we will automatically answer as 'moved it' to address out of band HA for the VM");
vmMo.answerVM(question.getId(), "1");
break;
}
}
}
if (s_logger.isTraceEnabled())
s_logger.trace("These are the choices we can have just in case");
ChoiceOption choice = question.getChoice();
if (choice != null) {
for (ElementDescription info : choice.getChoiceInfo()) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Choice option key: " + info.getKey());
s_logger.trace("Choice option label: " + info.getLabel());
}
}
}
}
} catch (Throwable e) {
s_logger.error("Unexpected exception: ", e);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
s_logger.debug("[ignored] interupted while dealing with vm questions.");
}
}
s_logger.info("VM Question monitor stopped");
}
});
try {
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware powerOnVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
} finally {
// make sure to let VM question monitor exit
flags[0] = true;
}
return false;
}
public boolean powerOff() throws Exception {
if (getResetSafePowerState() == VirtualMachinePowerState.POWERED_OFF)
return true;
return powerOffNoCheck();
}
public boolean safePowerOff(int shutdownWaitMs) throws Exception {
if (getResetSafePowerState() == VirtualMachinePowerState.POWERED_OFF)
return true;
if (isVMwareToolsRunning()) {
try {
String vmName = getName();
s_logger.info("Try gracefully shut down VM " + vmName);
shutdown();
long startTick = System.currentTimeMillis();
while (getResetSafePowerState() != VirtualMachinePowerState.POWERED_OFF && System.currentTimeMillis() - startTick < shutdownWaitMs) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
s_logger.debug("[ignored] interupted while powering of vm.");
}
}
if (getResetSafePowerState() != VirtualMachinePowerState.POWERED_OFF) {
s_logger.info("can not gracefully shutdown VM within " + (shutdownWaitMs / 1000) + " seconds, we will perform force power off on VM " + vmName);
return powerOffNoCheck();
}
return true;
} catch (Exception e) {
s_logger.warn("Failed to do guest-os graceful shutdown due to " + VmwareHelper.getExceptionMessage(e));
}
}
return powerOffNoCheck();
}
private boolean powerOffNoCheck() throws Exception {
ManagedObjectReference morTask = _context.getService().powerOffVMTask(_mor);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
// It seems that even if a power-off task is returned done, VM state may still not be marked,
// wait up to 5 seconds to make sure to avoid race conditioning for immediate following on operations
// that relies on a powered-off VM
long startTick = System.currentTimeMillis();
while (getResetSafePowerState() != VirtualMachinePowerState.POWERED_OFF && System.currentTimeMillis() - startTick < 5000) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
s_logger.debug("[ignored] interupted while powering of vm unconditionaly.");
}
}
return true;
} else {
if (getResetSafePowerState() == VirtualMachinePowerState.POWERED_OFF) {
// to help deal with possible race-condition
s_logger.info("Current power-off task failed. However, VM has been switched to the state we are expecting for");
return true;
}
s_logger.error("VMware powerOffVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public VirtualMachinePowerState getResetSafePowerState() throws Exception {
VirtualMachinePowerState powerState = VirtualMachinePowerState.POWERED_OFF;
// This is really ugly, there is a case that when windows guest VM is doing sysprep, the temporary
// rebooting process may let us pick up a "poweredOff" state during VMsync process, this can trigger
// a series actions. Unfortunately, from VMware API we can not distinguish power state into such details.
// We hope by giving it 3 second to re-read the state can cover this as a short-term solution.
//
// In the future, VMsync should not kick off CloudStack action (this is not a HA case) based on VM
// state report, until then we can remove this hacking fix
for (int i = 0; i < 3; i++) {
powerState = (VirtualMachinePowerState)getContext().getVimClient().getDynamicProperty(_mor, "runtime.powerState");
if (powerState == VirtualMachinePowerState.POWERED_OFF) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
s_logger.debug("[ignored] interupted while pausing after power off.");
}
} else {
break;
}
}
return powerState;
}
public VirtualMachinePowerState getPowerState() throws Exception {
return (VirtualMachinePowerState)getContext().getVimClient().getDynamicProperty(_mor, "runtime.powerState");
}
public boolean reset() throws Exception {
ManagedObjectReference morTask = _context.getService().resetVMTask(_mor);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware resetVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public void shutdown() throws Exception {
_context.getService().shutdownGuest(_mor);
}
public void rebootGuest() throws Exception {
_context.getService().rebootGuest(_mor);
}
public void markAsTemplate() throws Exception {
_context.getService().markAsTemplate(_mor);
}
public boolean isTemplate() throws Exception {
VirtualMachineConfigInfo configInfo = getConfigInfo();
return configInfo.isTemplate();
}
public boolean migrate(ManagedObjectReference morRp, ManagedObjectReference morTargetHost) throws Exception {
ManagedObjectReference morTask = _context.getService().migrateVMTask(_mor, morRp, morTargetHost, VirtualMachineMovePriority.DEFAULT_PRIORITY, null);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware migrateVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean changeDatastore(VirtualMachineRelocateSpec relocateSpec) throws Exception {
ManagedObjectReference morTask = _context.getVimClient().getService().relocateVMTask(_mor, relocateSpec, VirtualMachineMovePriority.DEFAULT_PRIORITY);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware RelocateVM_Task to change datastore failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean changeHost(VirtualMachineRelocateSpec relocateSpec) throws Exception {
ManagedObjectReference morTask = _context.getService().relocateVMTask(_mor, relocateSpec, VirtualMachineMovePriority.DEFAULT_PRIORITY);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware RelocateVM_Task to change host failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean relocate(ManagedObjectReference morTargetHost) throws Exception {
VirtualMachineRelocateSpec relocateSpec = new VirtualMachineRelocateSpec();
relocateSpec.setHost(morTargetHost);
ManagedObjectReference morTask = _context.getService().relocateVMTask(_mor, relocateSpec, null);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware relocateVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public VirtualMachineSnapshotInfo getSnapshotInfo() throws Exception {
return (VirtualMachineSnapshotInfo)_context.getVimClient().getDynamicProperty(_mor, "snapshot");
}
public boolean createSnapshot(String snapshotName, String snapshotDescription, boolean dumpMemory, boolean quiesce) throws Exception {
long apiTimeout = _context.getVimClient().getVcenterSessionTimeout();
ManagedObjectReference morTask = _context.getService().createSnapshotTask(_mor, snapshotName, snapshotDescription, dumpMemory, quiesce);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
ManagedObjectReference morSnapshot = null;
// We still need to wait until the object appear in vCenter
long startTick = System.currentTimeMillis();
while (System.currentTimeMillis() - startTick < apiTimeout) {
morSnapshot = getSnapshotMor(snapshotName);
if (morSnapshot != null) {
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
s_logger.debug("[ignored] interupted while waiting for snapshot to be done.");
}
}
if (morSnapshot == null) {
s_logger.error("We've been waiting for over " + apiTimeout + " milli seconds for snapshot MOR to be appearing in vCenter after CreateSnapshot task is done, but it is still not there?!");
return false;
}
s_logger.debug("Waited for " + (System.currentTimeMillis() - startTick) + " seconds for snapshot object [" + snapshotName + "] to appear in vCenter.");
return true;
} else {
s_logger.error("VMware createSnapshot_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean removeSnapshot(String snapshotName, boolean removeChildren) throws Exception {
ManagedObjectReference morSnapshot = getSnapshotMor(snapshotName);
if (morSnapshot == null) {
s_logger.warn("Unable to find snapshot: " + snapshotName);
return false;
}
ManagedObjectReference morTask = _context.getService().removeSnapshotTask(morSnapshot, removeChildren, true);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware removeSnapshot_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean revertToSnapshot(String snapshotName) throws Exception {
ManagedObjectReference morSnapshot = getSnapshotMor(snapshotName);
if (morSnapshot == null) {
s_logger.warn("Unable to find snapshot: " + snapshotName);
return false;
}
ManagedObjectReference morTask = _context.getService().revertToSnapshotTask(morSnapshot, _mor, null);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware revert to snapshot failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean removeAllSnapshots() throws Exception {
VirtualMachineSnapshotInfo snapshotInfo = getSnapshotInfo();
if (snapshotInfo != null && snapshotInfo.getRootSnapshotList() != null) {
List<VirtualMachineSnapshotTree> tree = snapshotInfo.getRootSnapshotList();
for (VirtualMachineSnapshotTree treeNode : tree) {
ManagedObjectReference morTask = _context.getService().removeSnapshotTask(treeNode.getSnapshot(), true, true);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
} else {
s_logger.error("VMware removeSnapshot_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
return false;
}
}
}
return true;
}
public String
getSnapshotDiskFileDatastorePath(VirtualMachineFileInfo vmFileInfo, List<Pair<ManagedObjectReference, String>> datastoreMounts, String snapshotDiskFile)
throws Exception {
// if file path start with "/", need to search all datastore mounts on the host in order
// to form fully qualified datastore path
if (snapshotDiskFile.startsWith("/")) {
for (Pair<ManagedObjectReference, String> mount : datastoreMounts) {
if (snapshotDiskFile.startsWith(mount.second())) {
DatastoreMO dsMo = new DatastoreMO(_context, mount.first());
String dsFullPath = String.format("[%s] %s", dsMo.getName(), snapshotDiskFile.substring(mount.second().length() + 1));
s_logger.info("Convert snapshot disk file name to datastore path. " + snapshotDiskFile + "->" + dsFullPath);
return dsFullPath;
}
}
s_logger.info("Convert snapshot disk file name to datastore path. " + snapshotDiskFile + "->" + snapshotDiskFile);
return snapshotDiskFile;
} else {
// snapshot directory string from VirtualMachineFileInfo ends with /
String dsFullPath = vmFileInfo.getSnapshotDirectory() + snapshotDiskFile;
s_logger.info("Convert snapshot disk file name to datastore path. " + snapshotDiskFile + "->" + dsFullPath);
return dsFullPath;
}
}
public SnapshotDescriptor getSnapshotDescriptor() throws Exception {
Pair<DatacenterMO, String> dcPair = getOwnerDatacenter();
String dsPath = getSnapshotDescriptorDatastorePath();
assert (dsPath != null);
String url = getContext().composeDatastoreBrowseUrl(dcPair.second(), dsPath);
byte[] content = getContext().getResourceContent(url);
if (content == null || content.length < 1) {
s_logger.warn("Snapshot descriptor file (vsd) does not exist anymore?");
}
SnapshotDescriptor descriptor = new SnapshotDescriptor();
descriptor.parse(content);
return descriptor;
}
public String getSnapshotDescriptorDatastorePath() throws Exception {
PropertySpec pSpec = new PropertySpec();
pSpec.setType("VirtualMachine");
pSpec.getPathSet().add("name");
pSpec.getPathSet().add("config.files");
ObjectSpec oSpec = new ObjectSpec();
oSpec.setObj(_mor);
oSpec.setSkip(Boolean.FALSE);
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
assert (ocs != null);
String vmName = null;
VirtualMachineFileInfo fileInfo = null;
assert (ocs.size() == 1);
for (ObjectContent oc : ocs) {
List<DynamicProperty> props = oc.getPropSet();
if (props != null) {
assert (props.size() == 2);
for (DynamicProperty prop : props) {
if (prop.getName().equals("name")) {
vmName = prop.getVal().toString();
} else {
fileInfo = (VirtualMachineFileInfo)prop.getVal();
}
}
}
}
assert (vmName != null);
assert (fileInfo != null);
// .vmsd file exists at the same directory of .vmx file
DatastoreFile vmxFile = new DatastoreFile(fileInfo.getVmPathName());
return vmxFile.getCompanionPath(vmName + ".vmsd");
}
public ManagedObjectReference getSnapshotMor(String snapshotName) throws Exception {
VirtualMachineSnapshotInfo info = getSnapshotInfo();
if (info != null) {
List<VirtualMachineSnapshotTree> snapTree = info.getRootSnapshotList();
return VmwareHelper.findSnapshotInTree(snapTree, snapshotName);
}
return null;
}
public boolean hasSnapshot() throws Exception {
VirtualMachineSnapshotInfo info = getSnapshotInfo();
if (info != null) {
ManagedObjectReference currentSnapshot = info.getCurrentSnapshot();
if (currentSnapshot != null) {
return true;
}
List<VirtualMachineSnapshotTree> rootSnapshotList = info.getRootSnapshotList();
if (rootSnapshotList != null && rootSnapshotList.size() > 0) {
return true;
}
}
return false;
}
public boolean createFullClone(String cloneName, ManagedObjectReference morFolder, ManagedObjectReference morResourcePool, ManagedObjectReference morDs)
throws Exception {
VirtualMachineCloneSpec cloneSpec = new VirtualMachineCloneSpec();
VirtualMachineRelocateSpec relocSpec = new VirtualMachineRelocateSpec();
cloneSpec.setLocation(relocSpec);
cloneSpec.setPowerOn(false);
cloneSpec.setTemplate(false);
relocSpec.setDatastore(morDs);
relocSpec.setPool(morResourcePool);
ManagedObjectReference morTask = _context.getService().cloneVMTask(_mor, morFolder, cloneName, cloneSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware cloneVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean createLinkedClone(String cloneName, ManagedObjectReference morBaseSnapshot, ManagedObjectReference morFolder, ManagedObjectReference morResourcePool,
ManagedObjectReference morDs) throws Exception {
assert (morBaseSnapshot != null);
assert (morFolder != null);
assert (morResourcePool != null);
assert (morDs != null);
VirtualDisk[] independentDisks = getAllIndependentDiskDevice();
VirtualMachineRelocateSpec rSpec = new VirtualMachineRelocateSpec();
if (independentDisks.length > 0) {
List<VirtualMachineRelocateSpecDiskLocator> diskLocator = new ArrayList<VirtualMachineRelocateSpecDiskLocator>(independentDisks.length);
for (int i = 0; i < independentDisks.length; i++) {
VirtualMachineRelocateSpecDiskLocator loc = new VirtualMachineRelocateSpecDiskLocator();
loc.setDatastore(morDs);
loc.setDiskId(independentDisks[i].getKey());
loc.setDiskMoveType(VirtualMachineRelocateDiskMoveOptions.MOVE_ALL_DISK_BACKINGS_AND_DISALLOW_SHARING.value());
diskLocator.add(loc);
}
rSpec.setDiskMoveType(VirtualMachineRelocateDiskMoveOptions.CREATE_NEW_CHILD_DISK_BACKING.value());
rSpec.getDisk().addAll(diskLocator);
} else {
rSpec.setDiskMoveType(VirtualMachineRelocateDiskMoveOptions.CREATE_NEW_CHILD_DISK_BACKING.value());
}
rSpec.setPool(morResourcePool);
VirtualMachineCloneSpec cloneSpec = new VirtualMachineCloneSpec();
cloneSpec.setPowerOn(false);
cloneSpec.setTemplate(false);
cloneSpec.setLocation(rSpec);
cloneSpec.setSnapshot(morBaseSnapshot);
ManagedObjectReference morTask = _context.getService().cloneVMTask(_mor, morFolder, cloneName, cloneSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware cloneVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public VirtualMachineRuntimeInfo getRuntimeInfo() throws Exception {
return (VirtualMachineRuntimeInfo)_context.getVimClient().getDynamicProperty(_mor, "runtime");
}
public VirtualMachineConfigInfo getConfigInfo() throws Exception {
return (VirtualMachineConfigInfo)_context.getVimClient().getDynamicProperty(_mor, "config");
}
public VirtualMachineConfigSummary getConfigSummary() throws Exception {
return (VirtualMachineConfigSummary)_context.getVimClient().getDynamicProperty(_mor, "summary.config");
}
public VirtualMachineFileInfo getFileInfo() throws Exception {
return (VirtualMachineFileInfo)_context.getVimClient().getDynamicProperty(_mor, "config.files");
}
public VirtualMachineFileLayoutEx getFileLayout() throws Exception {
VirtualMachineFileLayoutEx fileLayout = null;
PropertySpec pSpec = new PropertySpec();
pSpec.setType("VirtualMachine");
pSpec.getPathSet().add("layoutEx");
ObjectSpec oSpec = new ObjectSpec();
oSpec.setObj(_mor);
oSpec.setSkip(Boolean.FALSE);
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
if (ocs != null) {
for (ObjectContent oc : ocs) {
List<DynamicProperty> props = oc.getPropSet();
if (props != null) {
for (DynamicProperty prop : props) {
if (prop.getName().equals("layoutEx")) {
fileLayout = (VirtualMachineFileLayoutEx)prop.getVal();
break;
}
}
}
}
}
return fileLayout;
}
@Override
public ManagedObjectReference getParentMor() throws Exception {
return (ManagedObjectReference)_context.getVimClient().getDynamicProperty(_mor, "parent");
}
public String[] getNetworks() throws Exception {
PropertySpec pSpec = new PropertySpec();
pSpec.setType("Network");
pSpec.getPathSet().add("name");
TraversalSpec vm2NetworkTraversal = new TraversalSpec();
vm2NetworkTraversal.setType("VirtualMachine");
vm2NetworkTraversal.setPath("network");
vm2NetworkTraversal.setName("vm2NetworkTraversal");
ObjectSpec oSpec = new ObjectSpec();
oSpec.setObj(_mor);
oSpec.setSkip(Boolean.TRUE);
oSpec.getSelectSet().add(vm2NetworkTraversal);
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
List<String> networks = new ArrayList<String>();
if (ocs != null && ocs.size() > 0) {
for (ObjectContent oc : ocs) {
networks.add(oc.getPropSet().get(0).getVal().toString());
}
}
return networks.toArray(new String[0]);
}
public List<NetworkDetails> getNetworksWithDetails() throws Exception {
List<NetworkDetails> networks = new ArrayList<NetworkDetails>();
int gcTagKey = getCustomFieldKey("Network", CustomFieldConstants.CLOUD_GC);
if (gcTagKey == 0) {
gcTagKey = getCustomFieldKey("DistributedVirtualPortgroup", CustomFieldConstants.CLOUD_GC_DVP);
s_logger.debug("The custom key for dvPortGroup is : " + gcTagKey);
}
PropertySpec pSpec = new PropertySpec();
pSpec.setType("Network");
pSpec.getPathSet().add("name");
pSpec.getPathSet().add("vm");
pSpec.getPathSet().add(String.format("value[%d]", gcTagKey));
TraversalSpec vm2NetworkTraversal = new TraversalSpec();
vm2NetworkTraversal.setType("VirtualMachine");
vm2NetworkTraversal.setPath("network");
vm2NetworkTraversal.setName("vm2NetworkTraversal");
ObjectSpec oSpec = new ObjectSpec();
oSpec.setObj(_mor);
oSpec.setSkip(Boolean.TRUE);
oSpec.getSelectSet().add(vm2NetworkTraversal);
PropertyFilterSpec pfSpec = new PropertyFilterSpec();
pfSpec.getPropSet().add(pSpec);
pfSpec.getObjectSet().add(oSpec);
List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
pfSpecArr.add(pfSpec);
List<ObjectContent> ocs = _context.getService().retrieveProperties(_context.getPropertyCollector(), pfSpecArr);
if (ocs != null && ocs.size() > 0) {
for (ObjectContent oc : ocs) {
ArrayOfManagedObjectReference morVms = null;
String gcTagValue = null;
String name = null;
for (DynamicProperty prop : oc.getPropSet()) {
if (prop.getName().equals("name"))
name = prop.getVal().toString();
else if (prop.getName().equals("vm"))
morVms = (ArrayOfManagedObjectReference)prop.getVal();
else if (prop.getName().startsWith("value[")) {
CustomFieldStringValue val = (CustomFieldStringValue)prop.getVal();
if (val != null)
gcTagValue = val.getValue();
}
}
NetworkDetails details =
new NetworkDetails(name, oc.getObj(), (morVms != null ? morVms.getManagedObjectReference().toArray(
new ManagedObjectReference[morVms.getManagedObjectReference().size()]) : null), gcTagValue);
networks.add(details);
}
s_logger.debug("Retrieved " + networks.size() + " networks with key : " + gcTagKey);
}
return networks;
}
/**
* Retrieve path info to access VM files via vSphere web interface
* @return [0] vm-name, [1] data-center-name, [2] datastore-name
* @throws Exception
*/
public String[] getHttpAccessPathInfo() throws Exception {
String[] pathInfo = new String[3];
Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter();
VirtualMachineFileInfo fileInfo = getFileInfo();
String vmxFilePath = fileInfo.getVmPathName();
String vmxPathTokens[] = vmxFilePath.split("\\[|\\]|/");
assert (vmxPathTokens.length == 4);
pathInfo[1] = vmxPathTokens[1].trim(); // vSphere vm name
pathInfo[2] = dcInfo.second(); // vSphere datacenter name
pathInfo[3] = vmxPathTokens[0].trim(); // vSphere datastore name
return pathInfo;
}
public String getVmxHttpAccessUrl() throws Exception {
Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter();
VirtualMachineFileInfo fileInfo = getFileInfo();
String vmxFilePath = fileInfo.getVmPathName();
String vmxPathTokens[] = vmxFilePath.split("\\[|\\]|/");
StringBuffer sb = new StringBuffer("https://" + _context.getServerAddress() + "/folder/");
sb.append(URLEncoder.encode(vmxPathTokens[2].trim(), "UTF-8"));
sb.append("/");
sb.append(URLEncoder.encode(vmxPathTokens[3].trim(), "UTF-8"));
sb.append("?dcPath=");
sb.append(URLEncoder.encode(dcInfo.second(), "UTF-8"));
sb.append("&dsName=");
sb.append(URLEncoder.encode(vmxPathTokens[1].trim(), "UTF-8"));
return sb.toString();
}
public boolean setVncConfigInfo(boolean enableVnc, String vncPassword, int vncPort, String keyboard) throws Exception {
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
OptionValue[] vncOptions = VmwareHelper.composeVncOptions(null, enableVnc, vncPassword, vncPort, keyboard);
vmConfigSpec.getExtraConfig().addAll(Arrays.asList(vncOptions));
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, vmConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware reconfigVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean configureVm(VirtualMachineConfigSpec vmConfigSpec) throws Exception {
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, vmConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware reconfigVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public boolean configureVm(Ternary<VirtualDevice, VirtualDeviceConfigSpecOperation, VirtualDeviceConfigSpecFileOperation>[] devices) throws Exception {
assert (devices != null);
VirtualMachineConfigSpec configSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[devices.length];
int i = 0;
for (Ternary<VirtualDevice, VirtualDeviceConfigSpecOperation, VirtualDeviceConfigSpecFileOperation> deviceTernary : devices) {
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(deviceTernary.first());
deviceConfigSpec.setOperation(deviceTernary.second());
deviceConfigSpec.setFileOperation(deviceTernary.third());
deviceConfigSpecArray[i++] = deviceConfigSpec;
}
configSpec.getDeviceChange().addAll(Arrays.asList(deviceConfigSpecArray));
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, configSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware reconfigVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
public Pair<String, Integer> getVncPort(String hostNetworkName) throws Exception {
HostMO hostMo = getRunningHost();
VmwareHypervisorHostNetworkSummary summary = hostMo.getHyperHostNetworkSummary(hostNetworkName);
VirtualMachineConfigInfo configInfo = getConfigInfo();
List<OptionValue> values = configInfo.getExtraConfig();
if (values != null) {
for (OptionValue option : values) {
if (option.getKey().equals("RemoteDisplay.vnc.port")) {
String value = (String)option.getValue();
if (value != null) {
return new Pair<String, Integer>(summary.getHostIp(), Integer.parseInt(value));
}
}
}
}
return new Pair<String, Integer>(summary.getHostIp(), 0);
}
// vmdkDatastorePath: [datastore name] vmdkFilePath
public void createDisk(String vmdkDatastorePath, int sizeInMb, ManagedObjectReference morDs, int controllerKey) throws Exception {
createDisk(vmdkDatastorePath, VirtualDiskType.THIN, VirtualDiskMode.PERSISTENT, null, sizeInMb, morDs, controllerKey);
}
// vmdkDatastorePath: [datastore name] vmdkFilePath
public void createDisk(String vmdkDatastorePath, VirtualDiskType diskType, VirtualDiskMode diskMode, String rdmDeviceName, int sizeInMb,
ManagedObjectReference morDs, int controllerKey) throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - createDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " + vmdkDatastorePath + ", sizeInMb: " + sizeInMb +
", diskType: " + diskType + ", diskMode: " + diskMode + ", rdmDeviceName: " + rdmDeviceName + ", datastore: " + morDs.getValue() + ", controllerKey: " +
controllerKey);
assert (vmdkDatastorePath != null);
assert (morDs != null);
int ideControllerKey = getIDEDeviceControllerKey();
if (controllerKey < 0) {
controllerKey = ideControllerKey;
}
VirtualDisk newDisk = new VirtualDisk();
if (diskType == VirtualDiskType.THIN || diskType == VirtualDiskType.PREALLOCATED || diskType == VirtualDiskType.EAGER_ZEROED_THICK) {
VirtualDiskFlatVer2BackingInfo backingInfo = new VirtualDiskFlatVer2BackingInfo();
backingInfo.setDiskMode(VirtualDiskMode.PERSISTENT.value());
if (diskType == VirtualDiskType.THIN) {
backingInfo.setThinProvisioned(true);
} else {
backingInfo.setThinProvisioned(false);
}
if (diskType == VirtualDiskType.EAGER_ZEROED_THICK) {
backingInfo.setEagerlyScrub(true);
} else {
backingInfo.setEagerlyScrub(false);
}
backingInfo.setDatastore(morDs);
backingInfo.setFileName(vmdkDatastorePath);
newDisk.setBacking(backingInfo);
} else if (diskType == VirtualDiskType.RDM || diskType == VirtualDiskType.RDMP) {
VirtualDiskRawDiskMappingVer1BackingInfo backingInfo = new VirtualDiskRawDiskMappingVer1BackingInfo();
if (diskType == VirtualDiskType.RDM) {
backingInfo.setCompatibilityMode("virtualMode");
} else {
backingInfo.setCompatibilityMode("physicalMode");
}
backingInfo.setDeviceName(rdmDeviceName);
if (diskType == VirtualDiskType.RDM) {
backingInfo.setDiskMode(VirtualDiskMode.PERSISTENT.value());
}
backingInfo.setDatastore(morDs);
backingInfo.setFileName(vmdkDatastorePath);
newDisk.setBacking(backingInfo);
}
int deviceNumber = getNextDeviceNumber(controllerKey);
newDisk.setControllerKey(controllerKey);
newDisk.setKey(-deviceNumber);
newDisk.setUnitNumber(deviceNumber);
newDisk.setCapacityInKB(sizeInMb * 1024);
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(newDisk);
deviceConfigSpec.setFileOperation(VirtualDeviceConfigSpecFileOperation.CREATE);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - createDisk() done(failed)");
throw new Exception("Unable to create disk " + vmdkDatastorePath + " due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - createDisk() done(successfully)");
}
public void updateVmdkAdapter(String vmdkFileName, String newAdapterType) throws Exception {
Pair<VmdkFileDescriptor, byte[]> vmdkInfo = getVmdkFileInfo(vmdkFileName);
VmdkFileDescriptor vmdkFileDescriptor = vmdkInfo.first();
boolean isVmfsSparseFile = vmdkFileDescriptor.isVmfsSparseFile();
if (!isVmfsSparseFile) {
String currentAdapterType = vmdkFileDescriptor.getAdapterType();
if (!currentAdapterType.equalsIgnoreCase(newAdapterType)) {
s_logger.info("Updating adapter type to " + newAdapterType + " for VMDK file " + vmdkFileName);
Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter();
byte[] newVmdkContent = vmdkFileDescriptor.changeVmdkAdapterType(vmdkInfo.second(), newAdapterType);
String vmdkUploadUrl = getContext().composeDatastoreBrowseUrl(dcInfo.first().getName(), vmdkFileName);
getContext().uploadResourceContent(vmdkUploadUrl, newVmdkContent);
s_logger.info("Updated VMDK file " + vmdkFileName);
}
}
}
public void updateAdapterTypeIfRequired(String vmdkFileName) throws Exception {
// Validate existing adapter type of VMDK file. Update it with a supported adapter type if validation fails.
Pair<VmdkFileDescriptor, byte[]> vmdkInfo = getVmdkFileInfo(vmdkFileName);
VmdkFileDescriptor vmdkFileDescriptor = vmdkInfo.first();
boolean isVmfsSparseFile = vmdkFileDescriptor.isVmfsSparseFile();
if (!isVmfsSparseFile) {
String currentAdapterTypeStr = vmdkFileDescriptor.getAdapterType();
if (s_logger.isTraceEnabled()) {
s_logger.trace("Detected adapter type " + currentAdapterTypeStr + " for VMDK file " + vmdkFileName);
}
VmdkAdapterType currentAdapterType = VmdkAdapterType.getType(currentAdapterTypeStr);
if (currentAdapterType == VmdkAdapterType.none) {
// Value of currentAdapterType can be VmdkAdapterType.none only if adapter type of vmdk is set to either
// lsisas1068 (SAS controller) or pvscsi (Vmware Paravirtual) only. Valid adapter type for those controllers is lsilogic.
// Hence use adapter type lsilogic. Other adapter types ide, lsilogic, buslogic are valid and does not need to be modified.
VmdkAdapterType newAdapterType = VmdkAdapterType.lsilogic;
s_logger.debug("Updating adapter type to " + newAdapterType + " from " + currentAdapterTypeStr + " for VMDK file " + vmdkFileName);
Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter();
byte[] newVmdkContent = vmdkFileDescriptor.changeVmdkAdapterType(vmdkInfo.second(), newAdapterType.toString());
String vmdkUploadUrl = getContext().composeDatastoreBrowseUrl(dcInfo.first().getName(), vmdkFileName);
getContext().uploadResourceContent(vmdkUploadUrl, newVmdkContent);
s_logger.debug("Updated VMDK file " + vmdkFileName);
}
}
}
public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs, String diskController) throws Exception {
if(s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: "
+ new Gson().toJson(vmdkDatastorePathChain) + ", datastore: " + morDs.getValue());
int controllerKey = 0;
int unitNumber = 0;
if (DiskControllerType.getType(diskController) == DiskControllerType.ide) {
// IDE virtual disk cannot be added if VM is running
if (getPowerState() == VirtualMachinePowerState.POWERED_ON) {
throw new Exception("Adding a virtual disk over IDE controller is not supported while VM is running in VMware hypervisor. Please re-try when VM is not running.");
}
// Get next available unit number and controller key
int ideDeviceCount = getNumberOfIDEDevices();
if (ideDeviceCount >= VmwareHelper.MAX_IDE_CONTROLLER_COUNT * VmwareHelper.MAX_ALLOWED_DEVICES_IDE_CONTROLLER) {
throw new Exception("Maximum limit of devices supported on IDE controllers [" + VmwareHelper.MAX_IDE_CONTROLLER_COUNT
* VmwareHelper.MAX_ALLOWED_DEVICES_IDE_CONTROLLER + "] is reached.");
}
controllerKey = getIDEControllerKey(ideDeviceCount);
unitNumber = getFreeUnitNumberOnIDEController(controllerKey);
} else {
controllerKey = getScsiDiskControllerKey(diskController);
unitNumber = -1;
}
synchronized (_mor.getValue().intern()) {
VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, null, controllerKey, vmdkDatastorePathChain, morDs, unitNumber, 1);
controllerKey = newDisk.getControllerKey();
unitNumber = newDisk.getUnitNumber();
VirtualDiskFlatVer2BackingInfo backingInfo = (VirtualDiskFlatVer2BackingInfo)newDisk.getBacking();
String vmdkFileName = backingInfo.getFileName();
DiskControllerType diskControllerType = DiskControllerType.getType(diskController);
VmdkAdapterType vmdkAdapterType = VmdkAdapterType.getAdapterType(diskControllerType);
if (vmdkAdapterType == VmdkAdapterType.none) {
String message = "Failed to attach disk due to invalid vmdk adapter type for vmdk file [" +
vmdkFileName + "] with controller : " + diskControllerType;
s_logger.debug(message);
throw new Exception(message);
}
updateVmdkAdapter(vmdkFileName, vmdkAdapterType.toString());
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(newDisk);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk() done(failed)");
throw new Exception("Failed to attach disk due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
}
if(s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk() done(successfully)");
}
private int getControllerBusNumber(int controllerKey) throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualController && device.getKey() == controllerKey) {
return ((VirtualController)device).getBusNumber();
}
}
}
throw new Exception("SCSI Controller with key " + controllerKey + " is Not Found");
}
public void attachDisk(String[] vmdkDatastorePathChain, ManagedObjectReference morDs) throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " + new Gson().toJson(vmdkDatastorePathChain) +
", datastore: " + morDs.getValue());
synchronized (_mor.getValue().intern()) {
VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, null, getScsiDeviceControllerKey(), vmdkDatastorePathChain, morDs, -1, 1);
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(newDisk);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk() done(failed)");
throw new Exception("Failed to attach disk due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
}
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk() done(successfully)");
}
public void attachDisk(Pair<String, ManagedObjectReference>[] vmdkDatastorePathChain, int controllerKey) throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " + new Gson().toJson(vmdkDatastorePathChain));
synchronized (_mor.getValue().intern()) {
VirtualDevice newDisk = VmwareHelper.prepareDiskDevice(this, controllerKey, vmdkDatastorePathChain, -1, 1);
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(newDisk);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk() done(failed)");
throw new Exception("Failed to attach disk due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
}
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachDisk() done(successfully)");
}
// vmdkDatastorePath: [datastore name] vmdkFilePath
public List<Pair<String, ManagedObjectReference>> detachDisk(String vmdkDatastorePath, boolean deleteBackingFile) throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachDisk(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " + vmdkDatastorePath + ", deleteBacking: " +
deleteBackingFile);
// Note: if VM has been taken snapshot, original backing file will be renamed, therefore, when we try to find the matching
// VirtualDisk, we only perform prefix matching
Pair<VirtualDisk, String> deviceInfo = getDiskDevice(vmdkDatastorePath);
if (deviceInfo == null) {
s_logger.warn("vCenter API trace - detachDisk() done (failed)");
throw new Exception("No such disk device: " + vmdkDatastorePath);
}
// IDE virtual disk cannot be detached if VM is running
if (deviceInfo.second() != null && deviceInfo.second().contains("ide")) {
if (getPowerState() == VirtualMachinePowerState.POWERED_ON) {
throw new Exception("Removing a virtual disk over IDE controller is not supported while VM is running in VMware hypervisor. " +
"Please re-try when VM is not running.");
}
}
List<Pair<String, ManagedObjectReference>> chain = getDiskDatastorePathChain(deviceInfo.first(), true);
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(deviceInfo.first());
if (deleteBackingFile) {
deviceConfigSpec.setFileOperation(VirtualDeviceConfigSpecFileOperation.DESTROY);
}
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.REMOVE);
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachDisk() done (failed)");
throw new Exception("Failed to detach disk due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
// VMware does not update snapshot references to the detached disk, we have to work around it
SnapshotDescriptor snapshotDescriptor = null;
try {
snapshotDescriptor = getSnapshotDescriptor();
} catch (Exception e) {
s_logger.info("Unable to retrieve snapshot descriptor, will skip updating snapshot reference");
}
if (snapshotDescriptor != null) {
for (Pair<String, ManagedObjectReference> pair : chain) {
DatastoreFile dsFile = new DatastoreFile(pair.first());
snapshotDescriptor.removeDiskReferenceFromSnapshot(dsFile.getFileName());
}
Pair<DatacenterMO, String> dcPair = getOwnerDatacenter();
String dsPath = getSnapshotDescriptorDatastorePath();
assert (dsPath != null);
String url = getContext().composeDatastoreBrowseUrl(dcPair.second(), dsPath);
getContext().uploadResourceContent(url, snapshotDescriptor.getVmsdContent());
}
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachDisk() done (successfully)");
return chain;
}
public void detachAllDisks() throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachAllDisk(). target MOR: " + _mor.getValue());
VirtualDisk[] disks = getAllDiskDevice();
if (disks.length > 0) {
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[disks.length];
for (int i = 0; i < disks.length; i++) {
deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec();
deviceConfigSpecArray[i].setDevice(disks[i]);
deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.REMOVE);
}
reConfigSpec.getDeviceChange().addAll(Arrays.asList(deviceConfigSpecArray));
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachAllDisk() done(failed)");
throw new Exception("Failed to detach disk due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
}
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachAllDisk() done(successfully)");
}
// isoDatastorePath: [datastore name] isoFilePath
public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, boolean connect, boolean connectAtBoot) throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - attachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath + ", datastore: " +
morDs.getValue() + ", connect: " + connect + ", connectAtBoot: " + connectAtBoot);
assert (isoDatastorePath != null);
assert (morDs != null);
boolean newCdRom = false;
VirtualCdrom cdRom = (VirtualCdrom)getIsoDevice();
if (cdRom == null) {
newCdRom = true;
cdRom = new VirtualCdrom();
cdRom.setControllerKey(getIDEDeviceControllerKey());
int deviceNumber = getNextIDEDeviceNumber();
cdRom.setUnitNumber(deviceNumber);
cdRom.setKey(-deviceNumber);
}
VirtualDeviceConnectInfo cInfo = new VirtualDeviceConnectInfo();
cInfo.setConnected(connect);
cInfo.setStartConnected(connectAtBoot);
cdRom.setConnectable(cInfo);
VirtualCdromIsoBackingInfo backingInfo = new VirtualCdromIsoBackingInfo();
backingInfo.setFileName(isoDatastorePath);
backingInfo.setDatastore(morDs);
cdRom.setBacking(backingInfo);
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
//VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1];
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(cdRom);
if (newCdRom) {
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
} else {
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.EDIT);
}
//deviceConfigSpecArray[0] = deviceConfigSpec;
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachIso() done(failed)");
throw new Exception("Failed to attach ISO due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachIso() done(successfully)");
}
public void detachIso(String isoDatastorePath) throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath);
VirtualDevice device = getIsoDevice();
if (device == null) {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - detachIso() done(failed)");
throw new Exception("Unable to find a CDROM device");
}
VirtualCdromRemotePassthroughBackingInfo backingInfo = new VirtualCdromRemotePassthroughBackingInfo();
backingInfo.setDeviceName("");
device.setBacking(backingInfo);
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
//VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[1];
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(device);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.EDIT);
//deviceConfigSpecArray[0] = deviceConfigSpec;
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
// Monitor VM questions
final Boolean[] flags = {false};
final VirtualMachineMO vmMo = this;
Future<?> future = MonitorServiceExecutor.submit(new Runnable() {
@Override
public void run() {
s_logger.info("VM Question monitor started...");
while (!flags[0]) {
try {
VirtualMachineRuntimeInfo runtimeInfo = vmMo.getRuntimeInfo();
VirtualMachineQuestionInfo question = runtimeInfo.getQuestion();
if (question != null) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Question id: " + question.getId());
s_logger.trace("Question text: " + question.getText());
}
if (question.getMessage() != null) {
for (VirtualMachineMessage msg : question.getMessage()) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("msg id: " + msg.getId());
s_logger.trace("msg text: " + msg.getText());
}
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msg.getId())) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + msg.getId() +
", for safe operation we will automatically decline it");
vmMo.answerVM(question.getId(), "1");
break;
}
}
} else if (question.getText() != null) {
String text = question.getText();
String msgId;
String msgText;
if (s_logger.isDebugEnabled()) {
s_logger.debug("question text : " + text);
}
String[] tokens = text.split(":");
msgId = tokens[0];
msgText = tokens[1];
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msgId)) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + question.getId() +
". Message id : " + msgId + ". Message text : " + msgText + ", for safe operation we will automatically decline it.");
vmMo.answerVM(question.getId(), "1");
}
}
ChoiceOption choice = question.getChoice();
if (choice != null) {
for (ElementDescription info : choice.getChoiceInfo()) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Choice option key: " + info.getKey());
s_logger.trace("Choice option label: " + info.getLabel());
}
}
}
}
} catch (Throwable e) {
s_logger.error("Unexpected exception: ", e);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
s_logger.debug("[ignored] interupted while handling vm question about iso detach.");
}
}
s_logger.info("VM Question monitor stopped");
}
});
try {
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
if (s_logger.isDebugEnabled())
s_logger.trace("vCenter API trace - detachIso() done(failed)");
throw new Exception("Failed to detachIso due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
_context.waitForTaskProgressDone(morTask);
s_logger.trace("vCenter API trace - detachIso() done(successfully)");
} finally {
flags[0] = true;
future.cancel(true);
}
}
public Pair<VmdkFileDescriptor, byte[]> getVmdkFileInfo(String vmdkDatastorePath) throws Exception {
if (s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - getVmdkFileInfo(). target MOR: " + _mor.getValue() + ", vmdkDatastorePath: " + vmdkDatastorePath);
Pair<DatacenterMO, String> dcPair = getOwnerDatacenter();
String url = getContext().composeDatastoreBrowseUrl(dcPair.second(), vmdkDatastorePath);
byte[] content = getContext().getResourceContent(url);
VmdkFileDescriptor descriptor = new VmdkFileDescriptor();
descriptor.parse(content);
Pair<VmdkFileDescriptor, byte[]> result = new Pair<VmdkFileDescriptor, byte[]>(descriptor, content);
if (s_logger.isTraceEnabled()) {
s_logger.trace("vCenter API trace - getVmdkFileInfo() done");
s_logger.trace("VMDK file descriptor: " + new Gson().toJson(result.first()));
}
return result;
}
public void exportVm(String exportDir, String exportName, boolean packToOva, boolean leaveOvaFileOnly) throws Exception {
ManagedObjectReference morOvf = _context.getServiceContent().getOvfManager();
VirtualMachineRuntimeInfo runtimeInfo = getRuntimeInfo();
HostMO hostMo = new HostMO(_context, runtimeInfo.getHost());
String hostName = hostMo.getHostName();
String vmName = getVmName();
DatacenterMO dcMo = new DatacenterMO(_context, hostMo.getHyperHostDatacenter());
if (runtimeInfo.getPowerState() != VirtualMachinePowerState.POWERED_OFF) {
String msg = "Unable to export VM because it is not at powerdOff state. vmName: " + vmName + ", host: " + hostName;
s_logger.error(msg);
throw new Exception(msg);
}
ManagedObjectReference morLease = _context.getService().exportVm(getMor());
if (morLease == null) {
s_logger.error("exportVm() failed");
throw new Exception("exportVm() failed");
}
HttpNfcLeaseMO leaseMo = new HttpNfcLeaseMO(_context, morLease);
HttpNfcLeaseState state = leaseMo.waitState(new HttpNfcLeaseState[] {HttpNfcLeaseState.READY, HttpNfcLeaseState.ERROR});
try {
if (state == HttpNfcLeaseState.READY) {
final HttpNfcLeaseMO.ProgressReporter progressReporter = leaseMo.createProgressReporter();
boolean success = false;
List<String> fileNames = new ArrayList<String>();
try {
HttpNfcLeaseInfo leaseInfo = leaseMo.getLeaseInfo();
final long totalBytes = leaseInfo.getTotalDiskCapacityInKB() * 1024;
long totalBytesDownloaded = 0;
List<HttpNfcLeaseDeviceUrl> deviceUrls = leaseInfo.getDeviceUrl();
s_logger.info("volss: copy vmdk and ovf file starts " + System.currentTimeMillis());
if (deviceUrls != null) {
OvfFile[] ovfFiles = new OvfFile[deviceUrls.size()];
for (int i = 0; i < deviceUrls.size(); i++) {
String deviceId = deviceUrls.get(i).getKey();
String deviceUrlStr = deviceUrls.get(i).getUrl();
String orgDiskFileName = deviceUrlStr.substring(deviceUrlStr.lastIndexOf("/") + 1);
String diskFileName = String.format("%s-disk%d%s", exportName, i, VmwareHelper.getFileExtension(orgDiskFileName, ".vmdk"));
String diskUrlStr = deviceUrlStr.replace("*", hostName);
diskUrlStr = HypervisorHostHelper.resolveHostNameInUrl(dcMo, diskUrlStr);
String diskLocalPath = exportDir + File.separator + diskFileName;
fileNames.add(diskLocalPath);
if (s_logger.isInfoEnabled()) {
s_logger.info("Download VMDK file for export. url: " + deviceUrlStr);
}
long lengthOfDiskFile = _context.downloadVmdkFile(diskUrlStr, diskLocalPath, totalBytesDownloaded, new ActionDelegate<Long>() {
@Override
public void action(Long param) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Download progress " + param + "/" + totalBytes);
}
progressReporter.reportProgress((int)(param * 100 / totalBytes));
}
});
totalBytesDownloaded += lengthOfDiskFile;
OvfFile ovfFile = new OvfFile();
ovfFile.setPath(diskFileName);
ovfFile.setDeviceId(deviceId);
ovfFile.setSize(lengthOfDiskFile);
ovfFiles[i] = ovfFile;
}
// write OVF descriptor file
OvfCreateDescriptorParams ovfDescParams = new OvfCreateDescriptorParams();
ovfDescParams.getOvfFiles().addAll(Arrays.asList(ovfFiles));
OvfCreateDescriptorResult ovfCreateDescriptorResult = _context.getService().createDescriptor(morOvf, getMor(), ovfDescParams);
String ovfPath = exportDir + File.separator + exportName + ".ovf";
fileNames.add(ovfPath);
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream(ovfPath),"UTF-8");
out.write(ovfCreateDescriptorResult.getOvfDescriptor());
out.close();
// tar files into OVA
if (packToOva) {
// Important! we need to sync file system before we can safely use tar to work around a linux kernal bug(or feature)
s_logger.info("Sync file system before we package OVA...");
Script commandSync = new Script(true, "sync", 0, s_logger);
commandSync.execute();
Script command = new Script(false, "tar", 0, s_logger);
command.setWorkDir(exportDir);
command.add("-cf", exportName + ".ova");
command.add(exportName + ".ovf"); // OVF file should be the first file in OVA archive
for (String name : fileNames) {
command.add((new File(name).getName()));
}
s_logger.info("Package OVA with commmand: " + command.toString());
command.execute();
// to be safe, physically test existence of the target OVA file
if ((new File(exportDir + File.separator + exportName + ".ova")).exists()) {
success = true;
} else {
s_logger.error(exportDir + File.separator + exportName + ".ova is not created as expected");
}
} else {
success = true;
}
}
s_logger.info("volss: copy vmdk and ovf file finishes " + System.currentTimeMillis());
} catch (Throwable e) {
s_logger.error("Unexpected exception ", e);
} finally {
progressReporter.close();
if (leaveOvaFileOnly) {
for (String name : fileNames) {
new File(name).delete();
}
}
if (!success)
throw new Exception("Unable to finish the whole process to package as a OVA file");
}
}
} finally {
leaseMo.updateLeaseProgress(100);
leaseMo.completeLease();
}
}
// snapshot directory in format of: /vmfs/volumes/<datastore name>/<path>
@Deprecated
public void setSnapshotDirectory(String snapshotDir) throws Exception {
VirtualMachineFileInfo fileInfo = getFileInfo();
Pair<DatacenterMO, String> dcInfo = getOwnerDatacenter();
String vmxUrl = _context.composeDatastoreBrowseUrl(dcInfo.second(), fileInfo.getVmPathName());
byte[] vmxContent = _context.getResourceContent(vmxUrl);
BufferedReader in = null;
BufferedWriter out = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
boolean replaced = false;
try {
in = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(vmxContent),"UTF-8"));
out = new BufferedWriter(new OutputStreamWriter(bos,"UTF-8"));
String line;
while ((line = in.readLine()) != null) {
if (line.startsWith("workingDir")) {
replaced = true;
out.write(String.format("workingDir=\"%s\"", snapshotDir));
out.newLine();
} else {
out.write(line);
out.newLine();
}
}
if (!replaced) {
out.newLine();
out.write(String.format("workingDir=\"%s\"", snapshotDir));
out.newLine();
}
} finally {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
_context.uploadResourceContent(vmxUrl, bos.toByteArray());
// It seems that I don't need to do re-registration. VMware has bug in writing the correct snapshot's VMDK path to
// its disk backing info anyway.
// redoRegistration();
}
// destName does not contain extension name
public void backupCurrentSnapshot(String deviceName, ManagedObjectReference morDestDs, String destDsDirectory, String destName, boolean includeBase) throws Exception {
SnapshotDescriptor descriptor = getSnapshotDescriptor();
SnapshotInfo[] snapshotInfo = descriptor.getCurrentDiskChain();
if (snapshotInfo.length == 0) {
String msg = "No snapshot found in this VM";
throw new Exception(msg);
}
HostMO hostMo = getRunningHost();
DatacenterMO dcMo = getOwnerDatacenter().first();
List<Pair<ManagedObjectReference, String>> mounts = hostMo.getDatastoreMountsOnHost();
VirtualMachineFileInfo vmFileInfo = getFileInfo();
List<Ternary<String, String, String>> backupInfo = new ArrayList<Ternary<String, String, String>>();
for (int i = 0; i < snapshotInfo.length; i++) {
if (!includeBase && i == snapshotInfo.length - 1) {
break;
}
SnapshotDescriptor.DiskInfo[] disks = snapshotInfo[i].getDisks();
if (disks != null) {
String destBaseFileName;
String destFileName;
String destParentFileName;
for (SnapshotDescriptor.DiskInfo disk : disks) {
if (deviceName == null || deviceName.equals(disk.getDeviceName())) {
String srcVmdkFullDsPath = getSnapshotDiskFileDatastorePath(vmFileInfo, mounts, disk.getDiskFileName());
Pair<DatastoreMO, String> srcDsInfo = getOwnerDatastore(srcVmdkFullDsPath);
Pair<VmdkFileDescriptor, byte[]> vmdkInfo = getVmdkFileInfo(srcVmdkFullDsPath);
String srcVmdkBaseFilePath = DatastoreFile.getCompanionDatastorePath(srcVmdkFullDsPath, vmdkInfo.first().getBaseFileName());
destFileName = destName + (snapshotInfo.length - i - 1) + ".vmdk";
if (vmdkInfo.first().getParentFileName() != null) {
destBaseFileName = destName + (snapshotInfo.length - i - 1) + "-delta.vmdk";
destParentFileName = destName + (snapshotInfo.length - i - 2) + ".vmdk";
} else {
destBaseFileName = destName + (snapshotInfo.length - i - 1) + "-flat.vmdk";
destParentFileName = null;
}
s_logger.info("Copy VMDK base file " + srcVmdkBaseFilePath + " to " + destDsDirectory + "/" + destBaseFileName);
srcDsInfo.first().copyDatastoreFile(srcVmdkBaseFilePath, dcMo.getMor(), morDestDs, destDsDirectory + "/" + destBaseFileName, dcMo.getMor(), true);
byte[] newVmdkContent = VmdkFileDescriptor.changeVmdkContentBaseInfo(vmdkInfo.second(), destBaseFileName, destParentFileName);
String vmdkUploadUrl = getContext().composeDatastoreBrowseUrl(dcMo.getName(), destDsDirectory + "/" + destFileName);
s_logger.info("Upload VMDK content file to " + destDsDirectory + "/" + destFileName);
getContext().uploadResourceContent(vmdkUploadUrl, newVmdkContent);
backupInfo.add(new Ternary<String, String, String>(destFileName, destBaseFileName, destParentFileName));
}
}
}
}
byte[] vdiskInfo = VmwareHelper.composeDiskInfo(backupInfo, snapshotInfo.length, includeBase);
String vdiskUploadUrl = getContext().composeDatastoreBrowseUrl(dcMo.getName(), destDsDirectory + "/" + destName + ".vdisk");
getContext().uploadResourceContent(vdiskUploadUrl, vdiskInfo);
}
public String[] getCurrentSnapshotDiskChainDatastorePaths(String diskDevice) throws Exception {
HostMO hostMo = getRunningHost();
List<Pair<ManagedObjectReference, String>> mounts = hostMo.getDatastoreMountsOnHost();
VirtualMachineFileInfo vmFileInfo = getFileInfo();
SnapshotDescriptor descriptor = getSnapshotDescriptor();
SnapshotInfo[] snapshotInfo = descriptor.getCurrentDiskChain();
List<String> diskDsFullPaths = new ArrayList<String>();
for (int i = 0; i < snapshotInfo.length; i++) {
SnapshotDescriptor.DiskInfo[] disks = snapshotInfo[i].getDisks();
if (disks != null) {
for (SnapshotDescriptor.DiskInfo disk : disks) {
String deviceNameInDisk = disk.getDeviceName();
if (diskDevice == null || diskDevice.equalsIgnoreCase(deviceNameInDisk)) {
String vmdkFullDsPath = getSnapshotDiskFileDatastorePath(vmFileInfo, mounts, disk.getDiskFileName());
diskDsFullPaths.add(vmdkFullDsPath);
}
}
}
}
return diskDsFullPaths.toArray(new String[0]);
}
// return the disk chain (VMDK datastore paths) for cloned snapshot
public Pair<VirtualMachineMO, String[]> cloneFromCurrentSnapshot(String clonedVmName, int cpuSpeedMHz, int memoryMb, String diskDevice, ManagedObjectReference morDs)
throws Exception {
assert (morDs != null);
String[] disks = getCurrentSnapshotDiskChainDatastorePaths(diskDevice);
VirtualMachineMO clonedVm = cloneFromDiskChain(clonedVmName, cpuSpeedMHz, memoryMb, disks, morDs);
return new Pair<VirtualMachineMO, String[]>(clonedVm, disks);
}
public VirtualMachineMO cloneFromDiskChain(String clonedVmName, int cpuSpeedMHz, int memoryMb, String[] disks, ManagedObjectReference morDs) throws Exception {
assert (disks != null);
assert (disks.length >= 1);
HostMO hostMo = getRunningHost();
VirtualMachineMO clonedVmMo = HypervisorHostHelper.createWorkerVM(hostMo, new DatastoreMO(hostMo.getContext(), morDs), clonedVmName);
if (clonedVmMo == null)
throw new Exception("Unable to find just-created blank VM");
boolean bSuccess = false;
try {
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
VirtualDevice device = VmwareHelper.prepareDiskDevice(clonedVmMo, null, -1, disks, morDs, -1, 1);
deviceConfigSpec.setDevice(device);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
clonedVmMo.configureVm(vmConfigSpec);
bSuccess = true;
return clonedVmMo;
} finally {
if (!bSuccess) {
clonedVmMo.detachAllDisks();
clonedVmMo.destroy();
}
}
}
public GuestOsDescriptor getGuestOsDescriptor(String guestOsId) throws Exception {
GuestOsDescriptor guestOsDescriptor = null;
String guestId = guestOsId;
if (guestId == null) {
guestId = getGuestId();
}
ManagedObjectReference vmEnvironmentBrowser = _context.getVimClient().getMoRefProp(_mor, "environmentBrowser");
VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(vmEnvironmentBrowser, null, null);
List<GuestOsDescriptor> guestDescriptors = vmConfigOption.getGuestOSDescriptor();
for (GuestOsDescriptor descriptor : guestDescriptors) {
if (guestId != null && guestId.equalsIgnoreCase(descriptor.getId())) {
guestOsDescriptor = descriptor;
break;
}
}
return guestOsDescriptor;
}
public void plugDevice(VirtualDevice device) throws Exception {
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(device);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
if (!configureVm(vmConfigSpec)) {
throw new Exception("Failed to add devices");
}
}
public void tearDownDevice(VirtualDevice device) throws Exception {
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
deviceConfigSpec.setDevice(device);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.REMOVE);
vmConfigSpec.getDeviceChange().add(deviceConfigSpec);
if (!configureVm(vmConfigSpec)) {
throw new Exception("Failed to detach devices");
}
}
public void tearDownDevices(Class<?>[] deviceClasses) throws Exception {
VirtualDevice[] devices = getMatchedDevices(deviceClasses);
if (devices.length > 0) {
VirtualMachineConfigSpec vmConfigSpec = new VirtualMachineConfigSpec();
VirtualDeviceConfigSpec[] deviceConfigSpecArray = new VirtualDeviceConfigSpec[devices.length];
for (int i = 0; i < devices.length; i++) {
deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec();
deviceConfigSpecArray[i].setDevice(devices[i]);
deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.REMOVE);
vmConfigSpec.getDeviceChange().add(deviceConfigSpecArray[i]);
}
if (!configureVm(vmConfigSpec)) {
throw new Exception("Failed to detach devices");
}
}
}
public void copyAllVmDiskFiles(DatastoreMO destDsMo, String destDsDir, boolean followDiskChain) throws Exception {
VirtualDevice[] disks = getAllDiskDevice();
DatacenterMO dcMo = getOwnerDatacenter().first();
if (disks != null) {
for (VirtualDevice disk : disks) {
List<Pair<String, ManagedObjectReference>> vmdkFiles = getDiskDatastorePathChain((VirtualDisk)disk, followDiskChain);
for (Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
DatastoreMO srcDsMo = new DatastoreMO(_context, fileItem.second());
DatastoreFile srcFile = new DatastoreFile(fileItem.first());
DatastoreFile destFile = new DatastoreFile(destDsMo.getName(), destDsDir, srcFile.getFileName());
Pair<VmdkFileDescriptor, byte[]> vmdkDescriptor = null;
vmdkDescriptor = getVmdkFileInfo(fileItem.first());
s_logger.info("Copy VM disk file " + srcFile.getPath() + " to " + destFile.getPath());
srcDsMo.copyDatastoreFile(fileItem.first(), dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
if (vmdkDescriptor != null) {
String vmdkBaseFileName = vmdkDescriptor.first().getBaseFileName();
String baseFilePath = srcFile.getCompanionPath(vmdkBaseFileName);
destFile = new DatastoreFile(destDsMo.getName(), destDsDir, vmdkBaseFileName);
s_logger.info("Copy VM disk file " + baseFilePath + " to " + destFile.getPath());
srcDsMo.copyDatastoreFile(baseFilePath, dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
}
}
}
}
}
public List<String> getVmdkFileBaseNames() throws Exception {
List<String> vmdkFileBaseNames = new ArrayList<String>();
VirtualDevice[] devices = getAllDiskDevice();
for(VirtualDevice device : devices) {
if(device instanceof VirtualDisk) {
vmdkFileBaseNames.add(getVmdkFileBaseName((VirtualDisk)device));
}
}
return vmdkFileBaseNames;
}
public String getVmdkFileBaseName(VirtualDisk disk) throws Exception {
String vmdkFileBaseName = null;
VirtualDeviceBackingInfo backingInfo = disk.getBacking();
if(backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
DatastoreFile dsBackingFile = new DatastoreFile(diskBackingInfo.getFileName());
vmdkFileBaseName = dsBackingFile.getFileBaseName();
}
return vmdkFileBaseName;
}
// this method relies on un-offical VMware API
@Deprecated
public void moveAllVmDiskFiles(DatastoreMO destDsMo, String destDsDir, boolean followDiskChain) throws Exception {
VirtualDevice[] disks = getAllDiskDevice();
DatacenterMO dcMo = getOwnerDatacenter().first();
if (disks != null) {
for (VirtualDevice disk : disks) {
List<Pair<String, ManagedObjectReference>> vmdkFiles = getDiskDatastorePathChain((VirtualDisk)disk, followDiskChain);
for (Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
DatastoreMO srcDsMo = new DatastoreMO(_context, fileItem.second());
DatastoreFile srcFile = new DatastoreFile(fileItem.first());
DatastoreFile destFile = new DatastoreFile(destDsMo.getName(), destDsDir, srcFile.getFileName());
Pair<VmdkFileDescriptor, byte[]> vmdkDescriptor = null;
vmdkDescriptor = getVmdkFileInfo(fileItem.first());
s_logger.info("Move VM disk file " + srcFile.getPath() + " to " + destFile.getPath());
srcDsMo.moveDatastoreFile(fileItem.first(), dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
if (vmdkDescriptor != null) {
String vmdkBaseFileName = vmdkDescriptor.first().getBaseFileName();
String baseFilePath = srcFile.getCompanionPath(vmdkBaseFileName);
destFile = new DatastoreFile(destDsMo.getName(), destDsDir, vmdkBaseFileName);
s_logger.info("Move VM disk file " + baseFilePath + " to " + destFile.getPath());
srcDsMo.moveDatastoreFile(baseFilePath, dcMo.getMor(), destDsMo.getMor(), destFile.getPath(), dcMo.getMor(), true);
}
}
}
}
}
public int getPvScsiDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof ParaVirtualSCSIController) {
return device.getKey();
}
}
}
return -1;
}
public int getPvScsiDeviceControllerKey() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof ParaVirtualSCSIController) {
return device.getKey();
}
}
}
assert (false);
throw new Exception("VMware Paravirtual SCSI Controller Not Found");
}
public void ensurePvScsiDeviceController(int requiredNumScsiControllers, int availableBusNum) throws Exception {
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
int busNum = availableBusNum;
while (busNum < requiredNumScsiControllers) {
ParaVirtualSCSIController scsiController = new ParaVirtualSCSIController();
scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING);
scsiController.setBusNumber(busNum);
scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfig.getDeviceChange().add(scsiControllerSpec);
busNum++;
}
if (configureVm(vmConfig)) {
throw new Exception("Unable to add Scsi controllers to the VM " + getName());
} else {
s_logger.info("Successfully added " + requiredNumScsiControllers + " SCSI controllers.");
}
}
public String getRecommendedDiskController(String guestOsId) throws Exception {
String recommendedController;
GuestOsDescriptor guestOsDescriptor = getGuestOsDescriptor(guestOsId);
recommendedController = VmwareHelper.getRecommendedDiskControllerFromDescriptor(guestOsDescriptor);
return recommendedController;
}
public boolean isPvScsiSupported() throws Exception {
int virtualHardwareVersion;
virtualHardwareVersion = getVirtualHardwareVersion();
// Check if virtual machine is using hardware version 7 or later.
if (virtualHardwareVersion < 7) {
s_logger.error("The virtual hardware version of the VM is " + virtualHardwareVersion
+ ", which doesn't support PV SCSI controller type for virtual harddisks. Please upgrade this VM's virtual hardware version to 7 or later.");
return false;
}
return true;
}
// Would be useful if there exists multiple sub types of SCSI controllers per VM are supported in CloudStack f
public int getScsiDiskControllerKey(String diskController) throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if ((DiskControllerType.getType(diskController) == DiskControllerType.lsilogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof VirtualLsiLogicController) {
return ((VirtualLsiLogicController)device).getKey();
} else if ((DiskControllerType.getType(diskController) == DiskControllerType.lsisas1068 || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof VirtualLsiLogicSASController) {
return ((VirtualLsiLogicSASController)device).getKey();
} else if ((DiskControllerType.getType(diskController) == DiskControllerType.pvscsi || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof ParaVirtualSCSIController) {
return ((ParaVirtualSCSIController)device).getKey();
} else if ((DiskControllerType.getType(diskController) == DiskControllerType.buslogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof VirtualBusLogicController) {
return ((VirtualBusLogicController)device).getKey();
}
}
}
assert (false);
throw new IllegalStateException("Scsi disk controller of type " + diskController + " not found among configured devices.");
}
public int getScsiDiskControllerKeyNoException(String diskController) throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if ((DiskControllerType.getType(diskController) == DiskControllerType.lsilogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof VirtualLsiLogicController) {
return ((VirtualLsiLogicController)device).getKey();
} else if ((DiskControllerType.getType(diskController) == DiskControllerType.lsisas1068 || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof VirtualLsiLogicSASController) {
return ((VirtualLsiLogicSASController)device).getKey();
} else if ((DiskControllerType.getType(diskController) == DiskControllerType.pvscsi || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof ParaVirtualSCSIController) {
return ((ParaVirtualSCSIController)device).getKey();
} else if ((DiskControllerType.getType(diskController) == DiskControllerType.buslogic || DiskControllerType.getType(diskController) == DiskControllerType.scsi)
&& device instanceof VirtualBusLogicController) {
return ((VirtualBusLogicController)device).getKey();
}
}
}
return -1;
}
public int getNextScsiDiskDeviceNumber() throws Exception {
int scsiControllerKey = getScsiDeviceControllerKey();
int deviceNumber = getNextDeviceNumber(scsiControllerKey);
return deviceNumber;
}
public int getScsiDeviceControllerKey() throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualSCSIController) {
return device.getKey();
}
}
}
assert (false);
throw new Exception("SCSI Controller Not Found");
}
public int getGenericScsiDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualSCSIController) {
return device.getKey();
}
}
}
return -1;
}
public int getScsiDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if(devices != null && devices.size() > 0) {
for(VirtualDevice device : devices) {
if(device instanceof VirtualLsiLogicController) {
return device.getKey();
}
}
}
return -1;
}
public void ensureScsiDeviceController() throws Exception {
int scsiControllerKey = getScsiDeviceControllerKeyNoException();
if (scsiControllerKey < 0) {
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
// Scsi controller
VirtualLsiLogicController scsiController = new VirtualLsiLogicController();
scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING);
scsiController.setBusNumber(0);
scsiController.setKey(1);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfig.getDeviceChange().add(scsiControllerSpec);
if (configureVm(vmConfig)) {
throw new Exception("Unable to add Scsi controller");
}
}
}
public void ensureScsiDeviceControllers(int count, int availableBusNum) throws Exception {
int scsiControllerKey = getScsiDeviceControllerKeyNoException();
if (scsiControllerKey < 0) {
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
int busNum = availableBusNum;
while (busNum < count) {
VirtualLsiLogicController scsiController = new VirtualLsiLogicController();
scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING);
scsiController.setBusNumber(busNum);
scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfig.getDeviceChange().add(scsiControllerSpec);
busNum++;
}
if (configureVm(vmConfig)) {
throw new Exception("Unable to add Scsi controllers to the VM " + getName());
} else {
s_logger.info("Successfully added " + count + " SCSI controllers.");
}
}
}
// return pair of VirtualDisk and disk device bus name(ide0:0, etc)
public Pair<VirtualDisk, String> getDiskDevice(String vmdkDatastorePath) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
ArrayList<Pair<VirtualDisk, String>> partialMatchingDiskDevices = new ArrayList<Pair<VirtualDisk, String>>();
DatastoreFile dsSrcFile = new DatastoreFile(vmdkDatastorePath);
String srcBaseName = dsSrcFile.getFileBaseName();
String trimmedSrcBaseName = VmwareHelper.trimSnapshotDeltaPostfix(srcBaseName);
s_logger.info("Look for disk device info for volume : " + vmdkDatastorePath + " with base name: " + srcBaseName);
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
s_logger.info("Test against disk device, controller key: " + device.getControllerKey() + ", unit number: " + device.getUnitNumber());
VirtualDeviceBackingInfo backingInfo = ((VirtualDisk)device).getBacking();
if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
do {
s_logger.info("Test against disk backing : " + diskBackingInfo.getFileName());
DatastoreFile dsBackingFile = new DatastoreFile(diskBackingInfo.getFileName());
String backingBaseName = dsBackingFile.getFileBaseName();
if (backingBaseName.equalsIgnoreCase(srcBaseName)) {
String deviceNumbering = getDeviceBusName(devices, device);
s_logger.info("Disk backing : " + diskBackingInfo.getFileName() + " matches ==> " + deviceNumbering);
return new Pair<VirtualDisk, String>((VirtualDisk)device, deviceNumbering);
}
if (backingBaseName.contains(trimmedSrcBaseName)) {
String deviceNumbering = getDeviceBusName(devices, device);
partialMatchingDiskDevices.add(new Pair<VirtualDisk, String>((VirtualDisk)device, deviceNumbering));
}
diskBackingInfo = diskBackingInfo.getParent();
} while (diskBackingInfo != null);
}
}
}
}
// No disk device was found with an exact match for the volume path, hence look for disk device that matches the trimmed name.
s_logger.info("No disk device with an exact match found for volume : " + vmdkDatastorePath + ". Look for disk device info against trimmed base name: " + srcBaseName);
if (partialMatchingDiskDevices != null) {
if (partialMatchingDiskDevices.size() == 1) {
VirtualDiskFlatVer2BackingInfo matchingDiskBackingInfo = (VirtualDiskFlatVer2BackingInfo)partialMatchingDiskDevices.get(0).first().getBacking();
s_logger.info("Disk backing : " + matchingDiskBackingInfo.getFileName() + " matches ==> " + partialMatchingDiskDevices.get(0).second());
return partialMatchingDiskDevices.get(0);
} else if (partialMatchingDiskDevices.size() > 1) {
s_logger.warn("Disk device info lookup for volume: " + vmdkDatastorePath + " failed as multiple disk devices were found to match" +
" volume's trimmed base name: " + trimmedSrcBaseName);
return null;
}
}
s_logger.warn("Disk device info lookup for volume: " + vmdkDatastorePath + " failed as no matching disk device found");
return null;
}
public String getDiskCurrentTopBackingFileInChain(String deviceBusName) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
s_logger.info("Test against disk device, controller key: " + device.getControllerKey() + ", unit number: " + device.getUnitNumber());
VirtualDeviceBackingInfo backingInfo = ((VirtualDisk)device).getBacking();
if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
String deviceNumbering = getDeviceBusName(devices, device);
if (deviceNumbering.equals(deviceBusName))
return diskBackingInfo.getFileName();
}
}
}
}
return null;
}
public VirtualDisk getDiskDeviceByDeviceBusName(String deviceBusName) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
String deviceNumbering = getDeviceBusName(devices, device);
if (deviceNumbering.equals(deviceBusName))
return (VirtualDisk)device;
}
}
}
return null;
}
public VirtualMachineDiskInfoBuilder getDiskInfoBuilder() throws Exception {
VirtualMachineDiskInfoBuilder builder = new VirtualMachineDiskInfoBuilder();
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
VirtualDeviceBackingInfo backingInfo = ((VirtualDisk)device).getBacking();
if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
while (diskBackingInfo != null) {
String deviceBusName = getDeviceBusName(devices, device);
builder.addDisk(deviceBusName, diskBackingInfo.getFileName());
diskBackingInfo = diskBackingInfo.getParent();
}
}
}
}
}
return builder;
}
public List<Pair<Integer, ManagedObjectReference>> getAllDiskDatastores() throws Exception {
List<Pair<Integer, ManagedObjectReference>> disks = new ArrayList<Pair<Integer, ManagedObjectReference>>();
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
VirtualDeviceBackingInfo backingInfo = ((VirtualDisk)device).getBacking();
if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo) {
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
disks.add(new Pair<Integer, ManagedObjectReference>(new Integer(device.getKey()), diskBackingInfo.getDatastore()));
}
}
}
}
return disks;
}
@Deprecated
public List<Pair<String, ManagedObjectReference>> getDiskDatastorePathChain(VirtualDisk disk, boolean followChain) throws Exception {
VirtualDeviceBackingInfo backingInfo = disk.getBacking();
if (!(backingInfo instanceof VirtualDiskFlatVer2BackingInfo)) {
throw new Exception("Unsupported VirtualDeviceBackingInfo");
}
List<Pair<String, ManagedObjectReference>> pathList = new ArrayList<Pair<String, ManagedObjectReference>>();
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)backingInfo;
if (!followChain) {
pathList.add(new Pair<String, ManagedObjectReference>(diskBackingInfo.getFileName(), diskBackingInfo.getDatastore()));
return pathList;
}
Pair<DatacenterMO, String> dcPair = getOwnerDatacenter();
VirtualMachineFileInfo vmFilesInfo = getFileInfo();
DatastoreFile snapshotDirFile = new DatastoreFile(vmFilesInfo.getSnapshotDirectory());
DatastoreFile vmxDirFile = new DatastoreFile(vmFilesInfo.getVmPathName());
do {
if (diskBackingInfo.getParent() != null) {
pathList.add(new Pair<String, ManagedObjectReference>(diskBackingInfo.getFileName(), diskBackingInfo.getDatastore()));
diskBackingInfo = diskBackingInfo.getParent();
} else {
// try getting parent info from VMDK file itself
byte[] content = null;
try {
String url = getContext().composeDatastoreBrowseUrl(dcPair.second(), diskBackingInfo.getFileName());
content = getContext().getResourceContent(url);
if (content == null || content.length == 0) {
break;
}
pathList.add(new Pair<String, ManagedObjectReference>(diskBackingInfo.getFileName(), diskBackingInfo.getDatastore()));
} catch (Exception e) {
// if snapshot directory has been changed to place other than default. VMware has a bug
// that its corresponding disk backing info is not updated correctly. therefore, we will try search
// in snapshot directory one more time
DatastoreFile currentFile = new DatastoreFile(diskBackingInfo.getFileName());
String vmdkFullDsPath = snapshotDirFile.getCompanionPath(currentFile.getFileName());
String url = getContext().composeDatastoreBrowseUrl(dcPair.second(), vmdkFullDsPath);
content = getContext().getResourceContent(url);
if (content == null || content.length == 0) {
break;
}
pathList.add(new Pair<String, ManagedObjectReference>(vmdkFullDsPath, diskBackingInfo.getDatastore()));
}
VmdkFileDescriptor descriptor = new VmdkFileDescriptor();
descriptor.parse(content);
if (descriptor.getParentFileName() != null && !descriptor.getParentFileName().isEmpty()) {
// create a fake one
VirtualDiskFlatVer2BackingInfo parentDiskBackingInfo = new VirtualDiskFlatVer2BackingInfo();
parentDiskBackingInfo.setDatastore(diskBackingInfo.getDatastore());
String parentFileName = descriptor.getParentFileName();
if (parentFileName.startsWith("/")) {
int fileNameStartPos = parentFileName.lastIndexOf("/");
parentFileName = parentFileName.substring(fileNameStartPos + 1);
parentDiskBackingInfo.setFileName(vmxDirFile.getCompanionPath(parentFileName));
} else {
parentDiskBackingInfo.setFileName(snapshotDirFile.getCompanionPath(parentFileName));
}
diskBackingInfo = parentDiskBackingInfo;
} else {
break;
}
}
} while (diskBackingInfo != null);
return pathList;
}
private String getDeviceBusName(List<VirtualDevice> allDevices, VirtualDevice theDevice) throws Exception {
for (VirtualDevice device : allDevices) {
if (device.getKey() == theDevice.getControllerKey().intValue()) {
if (device instanceof VirtualIDEController) {
return String.format("ide%d:%d", ((VirtualIDEController)device).getBusNumber(), theDevice.getUnitNumber());
} else if (device instanceof VirtualSCSIController) {
return String.format("scsi%d:%d", ((VirtualSCSIController)device).getBusNumber(), theDevice.getUnitNumber());
} else {
throw new Exception("Device controller is not supported yet");
}
}
}
throw new Exception("Unable to find device controller");
}
public List<VirtualDisk> getVirtualDisks() throws Exception {
List<VirtualDisk> virtualDisks = new ArrayList<VirtualDisk>();
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
virtualDisks.add((VirtualDisk)device);
}
}
return virtualDisks;
}
public List<String> detachAllDisksExcept(String vmdkBaseName, String deviceBusName) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
VirtualMachineConfigSpec reConfigSpec = new VirtualMachineConfigSpec();
List<String> detachedDiskFiles = new ArrayList<String>();
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
VirtualDeviceConfigSpec deviceConfigSpec = new VirtualDeviceConfigSpec();
VirtualDiskFlatVer2BackingInfo diskBackingInfo = (VirtualDiskFlatVer2BackingInfo)device.getBacking();
DatastoreFile dsBackingFile = new DatastoreFile(diskBackingInfo.getFileName());
String backingBaseName = dsBackingFile.getFileBaseName();
String deviceNumbering = getDeviceBusName(devices, device);
if (backingBaseName.equalsIgnoreCase(vmdkBaseName) || (deviceBusName != null && deviceBusName.equals(deviceNumbering))) {
continue;
} else {
s_logger.info("Detach " + diskBackingInfo.getFileName() + " from " + getName());
detachedDiskFiles.add(diskBackingInfo.getFileName());
deviceConfigSpec.setDevice(device);
deviceConfigSpec.setOperation(VirtualDeviceConfigSpecOperation.REMOVE);
reConfigSpec.getDeviceChange().add(deviceConfigSpec);
}
}
}
if (detachedDiskFiles.size() > 0) {
ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
} else {
s_logger.warn("Unable to reconfigure the VM to detach disks");
throw new Exception("Unable to reconfigure the VM to detach disks");
}
}
return detachedDiskFiles;
}
public List<VirtualDevice> getAllDeviceList() throws Exception {
return _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
}
public VirtualDisk[] getAllDiskDevice() throws Exception {
List<VirtualDisk> deviceList = new ArrayList<VirtualDisk>();
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
deviceList.add((VirtualDisk)device);
}
}
}
return deviceList.toArray(new VirtualDisk[0]);
}
public VirtualDisk getDiskDeviceByBusName(List<VirtualDevice> allDevices, String busName) throws Exception {
for (VirtualDevice device : allDevices) {
if (device instanceof VirtualDisk) {
VirtualDisk disk = (VirtualDisk)device;
String diskBusName = getDeviceBusName(allDevices, disk);
if (busName.equalsIgnoreCase(diskBusName))
return disk;
}
}
return null;
}
public VirtualDisk[] getAllIndependentDiskDevice() throws Exception {
List<VirtualDisk> independentDisks = new ArrayList<VirtualDisk>();
VirtualDisk[] allDisks = getAllDiskDevice();
if (allDisks.length > 0) {
for (VirtualDisk disk : allDisks) {
String diskMode = "";
if (disk.getBacking() instanceof VirtualDiskFlatVer1BackingInfo) {
diskMode = ((VirtualDiskFlatVer1BackingInfo)disk.getBacking()).getDiskMode();
} else if (disk.getBacking() instanceof VirtualDiskFlatVer2BackingInfo) {
diskMode = ((VirtualDiskFlatVer2BackingInfo)disk.getBacking()).getDiskMode();
} else if (disk.getBacking() instanceof VirtualDiskRawDiskMappingVer1BackingInfo) {
diskMode = ((VirtualDiskRawDiskMappingVer1BackingInfo)disk.getBacking()).getDiskMode();
} else if (disk.getBacking() instanceof VirtualDiskSparseVer1BackingInfo) {
diskMode = ((VirtualDiskSparseVer1BackingInfo)disk.getBacking()).getDiskMode();
} else if (disk.getBacking() instanceof VirtualDiskSparseVer2BackingInfo) {
diskMode = ((VirtualDiskSparseVer2BackingInfo)disk.getBacking()).getDiskMode();
}
if (diskMode.indexOf("independent") != -1) {
independentDisks.add(disk);
}
}
}
return independentDisks.toArray(new VirtualDisk[0]);
}
public int tryGetIDEDeviceControllerKey() throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualIDEController) {
return ((VirtualIDEController)device).getKey();
}
}
}
return -1;
}
public int getIDEDeviceControllerKey() throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualIDEController) {
return ((VirtualIDEController)device).getKey();
}
}
}
assert (false);
throw new Exception("IDE Controller Not Found");
}
public int getIDEControllerKey(int ideUnitNumber) throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
int requiredIdeController = ideUnitNumber / VmwareHelper.MAX_IDE_CONTROLLER_COUNT;
int ideControllerCount = 0;
if(devices != null && devices.size() > 0) {
for(VirtualDevice device : devices) {
if(device instanceof VirtualIDEController) {
if (ideControllerCount == requiredIdeController) {
return ((VirtualIDEController)device).getKey();
}
ideControllerCount++;
}
}
}
assert(false);
throw new Exception("IDE Controller Not Found");
}
public int getNumberOfIDEDevices() throws Exception {
int ideDeviceCount = 0;
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualIDEController) {
ideDeviceCount += ((VirtualIDEController)device).getDevice().size();
}
}
}
return ideDeviceCount;
}
public int getFreeUnitNumberOnIDEController(int controllerKey) throws Exception {
int freeUnitNumber = 0;
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
int deviceCount = 0;
int ideDeviceUnitNumber = -1;
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk && (controllerKey == device.getControllerKey())) {
deviceCount++;
ideDeviceUnitNumber = device.getUnitNumber();
}
}
}
if (deviceCount == 1) {
if (ideDeviceUnitNumber == 0) {
freeUnitNumber = 1;
} // else freeUnitNumber is already initialized to 0
} else if (deviceCount == 2) {
throw new Exception("IDE controller with key [" + controllerKey + "] already has 2 device attached. Cannot attach more than the limit of 2.");
}
return freeUnitNumber;
}
public int getNextIDEDeviceNumber() throws Exception {
int controllerKey = getIDEDeviceControllerKey();
return getNextDeviceNumber(controllerKey);
}
public VirtualDevice getIsoDevice() throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualCdrom) {
return device;
}
}
}
return null;
}
public int getNextDeviceNumber(int controllerKey) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
List<Integer> existingUnitNumbers = new ArrayList<Integer>();
int deviceNumber = 0;
int scsiControllerKey = getGenericScsiDeviceControllerKeyNoException();
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device.getControllerKey() != null && device.getControllerKey().intValue() == controllerKey) {
existingUnitNumbers.add(device.getUnitNumber());
}
}
}
while (true) {
// Next device number should be the lowest device number on the key that is not in use and is not reserved.
if (!existingUnitNumbers.contains(Integer.valueOf(deviceNumber))) {
if (controllerKey != scsiControllerKey || !VmwareHelper.isReservedScsiDeviceNumber(deviceNumber))
break;
}
++deviceNumber;
}
return deviceNumber;
}
private List<VirtualDevice> getNicDevices(boolean sorted) throws Exception {
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
List<VirtualDevice> nics = new ArrayList<VirtualDevice>();
if (devices != null) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualEthernetCard) {
nics.add(device);
}
}
}
if (sorted) {
Collections.sort(nics, new Comparator<VirtualDevice>() {
@Override
public int compare(VirtualDevice arg0, VirtualDevice arg1) {
int unitNumber0 = arg0.getUnitNumber() != null ? arg0.getUnitNumber().intValue() : -1;
int unitNumber1 = arg1.getUnitNumber() != null ? arg1.getUnitNumber().intValue() : -1;
if (unitNumber0 < unitNumber1)
return -1;
else if (unitNumber0 > unitNumber1)
return 1;
return 0;
}
});
}
return nics;
}
public VirtualDevice[] getNicDevices() throws Exception {
return getNicDevices(false).toArray(new VirtualDevice[0]);
}
public VirtualDevice getNicDeviceByIndex(int index) throws Exception {
List<VirtualDevice> nics = getNicDevices(true);
try {
return nics.get(index);
} catch (IndexOutOfBoundsException e) {
// Not found
return null;
}
}
public Pair<Integer, VirtualDevice> getNicDeviceIndex(String networkNamePrefix) throws Exception {
List<VirtualDevice> nics = getNicDevices(true);
int index = 0;
String attachedNetworkSummary;
String dvPortGroupName;
for (VirtualDevice nic : nics) {
attachedNetworkSummary = ((VirtualEthernetCard)nic).getDeviceInfo().getSummary();
if (attachedNetworkSummary.startsWith(networkNamePrefix)) {
return new Pair<Integer, VirtualDevice>(new Integer(index), nic);
} else if (attachedNetworkSummary.endsWith("DistributedVirtualPortBackingInfo.summary") || attachedNetworkSummary.startsWith("DVSwitch")) {
dvPortGroupName = getDvPortGroupName((VirtualEthernetCard)nic);
if (dvPortGroupName != null && dvPortGroupName.startsWith(networkNamePrefix)) {
s_logger.debug("Found a dvPortGroup already associated with public NIC.");
return new Pair<Integer, VirtualDevice>(new Integer(index), nic);
}
}
index++;
}
return new Pair<Integer, VirtualDevice>(new Integer(-1), null);
}
public String getDvPortGroupName(VirtualEthernetCard nic) throws Exception {
VirtualEthernetCardDistributedVirtualPortBackingInfo dvpBackingInfo = (VirtualEthernetCardDistributedVirtualPortBackingInfo)nic.getBacking();
DistributedVirtualSwitchPortConnection dvsPort = dvpBackingInfo.getPort();
String dvPortGroupKey = dvsPort.getPortgroupKey();
ManagedObjectReference dvPortGroupMor = new ManagedObjectReference();
dvPortGroupMor.setValue(dvPortGroupKey);
dvPortGroupMor.setType("DistributedVirtualPortgroup");
return (String)_context.getVimClient().getDynamicProperty(dvPortGroupMor, "name");
}
public VirtualDevice[] getMatchedDevices(Class<?>[] deviceClasses) throws Exception {
assert (deviceClasses != null);
List<VirtualDevice> returnList = new ArrayList<VirtualDevice>();
List<VirtualDevice> devices = _context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null) {
for (VirtualDevice device : devices) {
for (Class<?> clz : deviceClasses) {
if (clz.isInstance(device)) {
returnList.add(device);
break;
}
}
}
}
return returnList.toArray(new VirtualDevice[0]);
}
public void mountToolsInstaller() throws Exception {
_context.getService().mountToolsInstaller(_mor);
}
public boolean unmountToolsInstaller() throws Exception {
// Monitor VM questions
final Boolean[] flags = {false};
final VirtualMachineMO vmMo = this;
final boolean[] encounterQuestion = new boolean[1];
encounterQuestion[0] = false;
Future<?> future = MonitorServiceExecutor.submit(new Runnable() {
@Override
public void run() {
s_logger.info("VM Question monitor started...");
while (!flags[0]) {
try {
VirtualMachineRuntimeInfo runtimeInfo = vmMo.getRuntimeInfo();
VirtualMachineQuestionInfo question = runtimeInfo.getQuestion();
if (question != null) {
encounterQuestion[0] = true;
if (s_logger.isTraceEnabled()) {
s_logger.trace("Question id: " + question.getId());
s_logger.trace("Question text: " + question.getText());
}
if (question.getMessage() != null) {
for (VirtualMachineMessage msg : question.getMessage()) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("msg id: " + msg.getId());
s_logger.trace("msg text: " + msg.getText());
}
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msg.getId())) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + msg.getId() +
", for safe operation we will automatically decline it");
vmMo.answerVM(question.getId(), "1");
break;
}
}
} else if (question.getText() != null) {
String text = question.getText();
String msgId;
String msgText;
if (s_logger.isDebugEnabled()) {
s_logger.debug("question text : " + text);
}
String[] tokens = text.split(":");
msgId = tokens[0];
msgText = tokens[1];
if ("msg.cdromdisconnect.locked".equalsIgnoreCase(msgId)) {
s_logger.info("Found that VM has a pending question that we need to answer programmatically, question id: " + question.getId() +
". Message id : " + msgId + ". Message text : " + msgText + ", for safe operation we will automatically decline it.");
vmMo.answerVM(question.getId(), "1");
}
}
ChoiceOption choice = question.getChoice();
if (choice != null) {
for (ElementDescription info : choice.getChoiceInfo()) {
if (s_logger.isTraceEnabled()) {
s_logger.trace("Choice option key: " + info.getKey());
s_logger.trace("Choice option label: " + info.getLabel());
}
}
}
}
} catch (Throwable e) {
s_logger.error("Unexpected exception: ", e);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
s_logger.debug("[ignored] interupted while handling vm question about umount tools install.");
}
}
s_logger.info("VM Question monitor stopped");
}
});
try {
_context.getService().unmountToolsInstaller(_mor);
} finally {
flags[0] = true;
future.cancel(true);
}
if (encounterQuestion[0]) {
s_logger.warn("cdrom is locked by VM. Failed to detach the ISO.");
return false;
} else {
s_logger.info("Successfully unmounted tools installer from VM.");
return true;
}
}
public void redoRegistration(ManagedObjectReference morHost) throws Exception {
String vmName = getVmName();
VirtualMachineFileInfo vmFileInfo = getFileInfo();
boolean isTemplate = isTemplate();
HostMO hostMo;
if (morHost != null)
hostMo = new HostMO(getContext(), morHost);
else
hostMo = getRunningHost();
ManagedObjectReference morFolder = getParentMor();
ManagedObjectReference morPool = hostMo.getHyperHostOwnerResourcePool();
_context.getService().unregisterVM(_mor);
ManagedObjectReference morTask = _context.getService().registerVMTask(morFolder, vmFileInfo.getVmPathName(), vmName, false, morPool, hostMo.getMor());
boolean result = _context.getVimClient().waitForTask(morTask);
if (!result) {
throw new Exception("Unable to register template due to " + TaskMO.getTaskFailureInfo(_context, morTask));
} else {
_context.waitForTaskProgressDone(morTask);
if (isTemplate) {
VirtualMachineMO vmNewRegistration = hostMo.findVmOnHyperHost(vmName);
assert (vmNewRegistration != null);
vmNewRegistration.markAsTemplate();
}
}
}
public long getHotAddMemoryIncrementSizeInMb() throws Exception {
return (Long)_context.getVimClient().getDynamicProperty(_mor, "config.hotPlugMemoryIncrementSize");
}
public long getHotAddMemoryLimitInMb() throws Exception {
return (Long)_context.getVimClient().getDynamicProperty(_mor, "config.hotPlugMemoryLimit");
}
public String getGuestId() throws Exception {
return (String)_context.getVimClient().getDynamicProperty(_mor, "config.guestId");
}
public int getCoresPerSocket() throws Exception {
// number of cores per socket is 1 in case of ESXi. It's not defined explicitly and the property is support since vSphere API 5.0.
String apiVersion = HypervisorHostHelper.getVcenterApiVersion(_context);
if (apiVersion.compareTo("5.0") < 0) {
return 1;
}
Integer coresPerSocket = (Integer)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.numCoresPerSocket");
return coresPerSocket != null ? coresPerSocket : 1;
}
public int getVirtualHardwareVersion() throws Exception {
VirtualHardwareOption vhOption = getVirtualHardwareOption();
return vhOption.getHwVersion();
}
public VirtualHardwareOption getVirtualHardwareOption() throws Exception {
VirtualMachineConfigOption vmConfigOption = _context.getService().queryConfigOption(getEnvironmentBrowser(), null, null);
return vmConfigOption.getHardwareOptions();
}
private ManagedObjectReference getEnvironmentBrowser() throws Exception {
if (_vmEnvironmentBrowser == null) {
_vmEnvironmentBrowser = _context.getVimClient().getMoRefProp(_mor, "environmentBrowser");
}
return _vmEnvironmentBrowser;
}
public boolean isCpuHotAddSupported(String guestOsId) throws Exception {
boolean guestOsSupportsCpuHotAdd = false;
boolean virtualHardwareSupportsCpuHotAdd = false;
GuestOsDescriptor guestOsDescriptor;
int virtualHardwareVersion;
int numCoresPerSocket;
guestOsDescriptor = getGuestOsDescriptor(guestOsId);
virtualHardwareVersion = getVirtualHardwareVersion();
// Check if guest operating system supports cpu hotadd
if (guestOsDescriptor.isSupportsCpuHotAdd()) {
guestOsSupportsCpuHotAdd = true;
}
// Check if virtual machine is using hardware version 8 or later.
// If hardware version is 7, then only 1 core per socket is supported. Hot adding multi-core vcpus is not allowed if hardware version is 7.
if (virtualHardwareVersion >= 8) {
virtualHardwareSupportsCpuHotAdd = true;
} else if (virtualHardwareVersion == 7) {
// Check if virtual machine has only 1 core per socket.
numCoresPerSocket = getCoresPerSocket();
if (numCoresPerSocket == 1) {
virtualHardwareSupportsCpuHotAdd = true;
}
}
return guestOsSupportsCpuHotAdd && virtualHardwareSupportsCpuHotAdd;
}
public boolean isMemoryHotAddSupported(String guestOsId) throws Exception {
boolean guestOsSupportsMemoryHotAdd = false;
boolean virtualHardwareSupportsMemoryHotAdd = false;
GuestOsDescriptor guestOsDescriptor;
int virtualHardwareVersion;
guestOsDescriptor = getGuestOsDescriptor(guestOsId);
virtualHardwareVersion = getVirtualHardwareVersion();
// Check if guest operating system supports memory hotadd
if (guestOsDescriptor.isSupportsMemoryHotAdd()) {
guestOsSupportsMemoryHotAdd = true;
}
// Check if virtual machine is using hardware version 7 or later.
if (virtualHardwareVersion >= 7) {
virtualHardwareSupportsMemoryHotAdd = true;
}
return guestOsSupportsMemoryHotAdd && virtualHardwareSupportsMemoryHotAdd;
}
public void ensureLsiLogicSasDeviceControllers(int count, int availableBusNum) throws Exception {
int scsiControllerKey = getLsiLogicSasDeviceControllerKeyNoException();
if (scsiControllerKey < 0) {
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
int busNum = availableBusNum;
while (busNum < count) {
VirtualLsiLogicSASController scsiController = new VirtualLsiLogicSASController();
scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING);
scsiController.setBusNumber(busNum);
scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfig.getDeviceChange().add(scsiControllerSpec);
busNum++;
}
if (configureVm(vmConfig)) {
throw new Exception("Unable to add Scsi controller of type LsiLogic SAS.");
}
}
}
private int getLsiLogicSasDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualLsiLogicSASController) {
return device.getKey();
}
}
}
return -1;
}
public void ensureBusLogicDeviceControllers(int count, int availableBusNum) throws Exception {
int scsiControllerKey = getBusLogicDeviceControllerKeyNoException();
if (scsiControllerKey < 0) {
VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
int busNum = availableBusNum;
while (busNum < count) {
VirtualBusLogicController scsiController = new VirtualBusLogicController();
scsiController.setSharedBus(VirtualSCSISharing.NO_SHARING);
scsiController.setBusNumber(busNum);
scsiController.setKey(busNum - VmwareHelper.MAX_SCSI_CONTROLLER_COUNT);
VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
scsiControllerSpec.setDevice(scsiController);
scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.ADD);
vmConfig.getDeviceChange().add(scsiControllerSpec);
busNum++;
}
if (configureVm(vmConfig)) {
throw new Exception("Unable to add Scsi BusLogic controllers to the VM " + getName());
} else {
s_logger.info("Successfully added " + count + " SCSI BusLogic controllers.");
}
}
}
private int getBusLogicDeviceControllerKeyNoException() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualBusLogicController) {
return device.getKey();
}
}
}
return -1;
}
public Ternary<Integer, Integer, DiskControllerType> getScsiControllerInfo() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().
getDynamicProperty(_mor, "config.hardware.device");
int scsiControllerCount = 0;
int busNum = -1;
DiskControllerType controllerType = DiskControllerType.lsilogic;
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualSCSIController) {
scsiControllerCount++;
int deviceBus = ((VirtualSCSIController)device).getBusNumber();
if (busNum < deviceBus) {
busNum = deviceBus;
}
if (device instanceof VirtualLsiLogicController) {
controllerType = DiskControllerType.lsilogic;
} else if (device instanceof VirtualLsiLogicSASController) {
controllerType = DiskControllerType.lsisas1068;
} else if (device instanceof VirtualBusLogicController) {
controllerType = DiskControllerType.buslogic;
} else if (device instanceof ParaVirtualSCSIController) {
controllerType = DiskControllerType.pvscsi;
}
}
}
}
return new Ternary<Integer, Integer, DiskControllerType>(scsiControllerCount, busNum, controllerType);
}
public int getNumberOfVirtualDisks() throws Exception {
List<VirtualDevice> devices = (List<VirtualDevice>)_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
s_logger.info("Counting disk devices attached to VM " + getVmName());
int count = 0;
if (devices != null && devices.size() > 0) {
for (VirtualDevice device : devices) {
if (device instanceof VirtualDisk) {
count++;
}
}
}
return count;
}
public boolean consolidateVmDisks() throws Exception {
ManagedObjectReference morTask = _context.getService().consolidateVMDisksTask(_mor);
boolean result = _context.getVimClient().waitForTask(morTask);
if (result) {
_context.waitForTaskProgressDone(morTask);
return true;
} else {
s_logger.error("VMware ConsolidateVMDisks_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask));
}
return false;
}
}