From a70bbdb139b4b1b1c8dd8f9a258b4b37b5698290 Mon Sep 17 00:00:00 2001 From: Sateesh Chodapuneedi Date: Mon, 11 Nov 2013 08:48:12 +0530 Subject: [PATCH] CLOUDSTACK-4993 [VMware] When issuing detach ISO to Vcenter, MS should prevent Vcenter from posting Virtual Machine question dialog waiting for user response During ISO detach operation, answer question from vCenter by programmatically answering for VM question in detaching process. Signed-off-by: Sateesh Chodapuneedi --- .../vmware/mo/VirtualMachineMO.java | 194 ++++++++++++++++-- 1 file changed, 179 insertions(+), 15 deletions(-) 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();