diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index 784c0316e04..a4469f048de 100644 --- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -29,14 +29,19 @@ import java.util.ArrayList; 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; @@ -80,8 +85,10 @@ import com.vmware.vim25.VirtualMachineConfigOption; import com.vmware.vim25.VirtualMachineConfigSpec; import com.vmware.vim25.VirtualMachineConfigSummary; import com.vmware.vim25.VirtualMachineFileInfo; +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; @@ -98,12 +105,14 @@ 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; import java.util.Arrays; 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) { @@ -170,6 +179,10 @@ public class VirtualMachineMO extends BaseMO { 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) { @@ -1157,7 +1170,7 @@ public class VirtualMachineMO extends BaseMO { boolean connect, boolean connectAtBoot) throws Exception { if(s_logger.isTraceEnabled()) - s_logger.trace("vCenter API trace - detachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + s_logger.trace("vCenter API trace - attachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath + ", datastore: " + morDs.getValue() + ", connect: " + connect + ", connectAtBoot: " + connectAtBoot); assert(isoDatastorePath != null); @@ -1240,18 +1253,90 @@ public class VirtualMachineMO extends BaseMO { //deviceConfigSpecArray[0] = deviceConfigSpec; reConfigSpec.getDeviceChange().add(deviceConfigSpec); - ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec); - boolean result = _context.getVimClient().waitForTask(morTask); + ManagedObjectReference morTask = _context.getService().reconfigVMTask(_mor, reConfigSpec); - if(!result) { - if(s_logger.isTraceEnabled()) - s_logger.trace("vCenter API trace - detachIso() done(failed)"); - throw new Exception("Failed to detachIso due to " + TaskMO.getTaskFailureInfo(_context, morTask)); + // 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.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); } - _context.waitForTaskProgressDone(morTask); - - if(s_logger.isTraceEnabled()) - s_logger.trace("vCenter API trace - detachIso() done(successfully)"); } public Pair getVmdkFileInfo(String vmdkDatastorePath) throws Exception { @@ -2041,7 +2126,7 @@ public class VirtualMachineMO extends BaseMO { } } - return detachedDiskFiles; + return detachedDiskFiles; } public List getAllDeviceList() throws Exception { @@ -2066,7 +2151,7 @@ public class VirtualMachineMO extends BaseMO { for(VirtualDevice device : allDevices ) { if(device instanceof VirtualDisk) { VirtualDisk disk = (VirtualDisk)device; - String diskBusName = getDeviceBusName(allDevices, (VirtualDevice)disk); + String diskBusName = getDeviceBusName(allDevices, disk); if(busName.equalsIgnoreCase(diskBusName)) return disk; } @@ -2294,8 +2379,87 @@ public class VirtualMachineMO extends BaseMO { } public void unmountToolsInstaller() throws Exception { - _context.getService().unmountToolsInstaller(_mor); - } + int i = 1; + // 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.info("VM Question monitor stopped"); + } + }); + + try { + _context.getService().unmountToolsInstaller(_mor); + } finally { + flags[0] = true; + future.cancel(true); + } + } public void redoRegistration(ManagedObjectReference morHost) throws Exception { String vmName = getVmName();