diff --git a/api/src/main/java/com/cloud/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java index b62628560ae4..ea818a55a0cc 100644 --- a/api/src/main/java/com/cloud/template/TemplateApiService.java +++ b/api/src/main/java/com/cloud/template/TemplateApiService.java @@ -56,9 +56,9 @@ public interface TemplateApiService { VirtualMachineTemplate prepareTemplate(long templateId, long zoneId, Long storageId); - boolean detachIso(long vmId); + boolean detachIso(long vmId, boolean forced); - boolean attachIso(long isoId, long vmId); + boolean attachIso(long isoId, long vmId, boolean forced); /** * Deletes a template diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java index 245840004fe9..74a98beef9d6 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/AttachIsoCmd.java @@ -54,6 +54,10 @@ public class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { required = true, description = "the ID of the virtual machine") protected Long virtualMachineId; + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, + description = "If true, ejects existing ISO before attaching on VMware. Default: false", since = "4.15.1") + protected Boolean forced; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +70,10 @@ public Long getVirtualMachineId() { return virtualMachineId; } + public Boolean isForced() { + return forced != null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -98,7 +106,7 @@ public String getEventDescription() { @Override public void execute() { CallContext.current().setEventDetails("Vm Id: " + getVirtualMachineId() + " ISO ID: " + getId()); - boolean result = _templateService.attachIso(id, virtualMachineId); + boolean result = _templateService.attachIso(id, virtualMachineId, isForced()); if (result) { UserVm userVm = _responseGenerator.findUserVmById(virtualMachineId); if (userVm != null) { diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java index ae86e2fdd7fa..4f4a0197ac75 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/iso/DetachIsoCmd.java @@ -44,10 +44,14 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.VIRTUAL_MACHINE_ID, type=CommandType.UUID, entityType = UserVmResponse.class, - required=true, description="The ID of the virtual machine") + @Parameter(name = ApiConstants.VIRTUAL_MACHINE_ID, type = CommandType.UUID, entityType = UserVmResponse.class, + required = true, description = "The ID of the virtual machine") protected Long virtualMachineId; + @Parameter(name = ApiConstants.FORCED, type = CommandType.BOOLEAN, + description = "If true, ejects the ISO before detaching on VMware. Default: false", since = "4.15.1") + protected Boolean forced; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -56,6 +60,10 @@ public Long getVirtualMachineId() { return virtualMachineId; } + public Boolean isForced() { + return forced != null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -87,7 +95,7 @@ public String getEventDescription() { @Override public void execute() { - boolean result = _templateService.detachIso(virtualMachineId); + boolean result = _templateService.detachIso(virtualMachineId, isForced()); if (result) { UserVm userVm = _entityMgr.findById(UserVm.class, virtualMachineId); UserVmResponse response = _responseGenerator.createUserVmResponse(getResponseView(), "virtualmachine", userVm).get(0); diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java index d15a4e42da34..ae6ea1fa649b 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/AttachCommand.java @@ -27,6 +27,7 @@ public final class AttachCommand extends StorageSubSystemCommand { private DiskTO disk; private String vmName; private boolean inSeq = false; + private boolean forced = false; private Map controllerInfo; public AttachCommand(final DiskTO disk, final String vmName) { @@ -69,6 +70,14 @@ public void setVmName(final String vmName) { this.vmName = vmName; } + public boolean isForced() { + return forced; + } + + public void setForced(boolean forced) { + this.forced = forced; + } + @Override public void setExecuteInSequence(final boolean inSeq) { this.inSeq = inSeq; diff --git a/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java b/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java index 1d805b582e95..eeeadaac42bf 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java +++ b/core/src/main/java/org/apache/cloudstack/storage/command/DettachCommand.java @@ -31,6 +31,7 @@ public class DettachCommand extends StorageSubSystemCommand { private String _storageHost; private int _storagePort; private Map params; + private boolean forced; public DettachCommand(final DiskTO disk, final String vmName) { super(); @@ -106,6 +107,14 @@ public void setParams(Map params) { this.params = params; } + public boolean isForced() { + return forced; + } + + public void setForced(boolean forced) { + this.forced = forced; + } + @Override public void setExecuteInSequence(final boolean inSeq) { diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 97a10e51f568..313660831dd6 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -2019,35 +2019,7 @@ protected StartAnswer execute(StartCommand cmd) { if (volIso != null) { for (DiskTO vol : disks) { if (vol.getType() == Volume.Type.ISO) { - - TemplateObjectTO iso = (TemplateObjectTO) vol.getData(); - - if (iso.getPath() != null && !iso.getPath().isEmpty()) { - DataStoreTO imageStore = iso.getDataStore(); - if (!(imageStore instanceof NfsTO)) { - s_logger.debug("unsupported protocol"); - throw new Exception("unsupported protocol"); - } - NfsTO nfsImageStore = (NfsTO) imageStore; - String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath(); - Pair isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, isoPath); - assert (isoDatastoreInfo != null); - assert (isoDatastoreInfo.second() != null); - - deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); - Pair isoInfo = - VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, ideUnitNumber++, i + 1); - deviceConfigSpecArray[i].setDevice(isoInfo.first()); - if (isoInfo.second()) { - if (s_logger.isDebugEnabled()) - s_logger.debug("Prepare ISO volume at new device " + _gson.toJson(isoInfo.first())); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); - } else { - if (s_logger.isDebugEnabled()) - s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); - deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); - } - } + configureIso(hyperHost, vmMo, vol, deviceConfigSpecArray, ideUnitNumber++, i); i++; } } @@ -2075,8 +2047,16 @@ protected StartAnswer execute(StartCommand cmd) { // // Setup ROOT/DATA disk devices // + if (multipleIsosAtached(sortedDisks) && deployAsIs) { + sortedDisks = getDisks(sortedDisks); + } + for (DiskTO vol : sortedDisks) { if (vol.getType() == Volume.Type.ISO) { + if (deployAsIs) { + configureIso(hyperHost, vmMo, vol, deviceConfigSpecArray, ideUnitNumber++, i); + i++; + } continue; } @@ -2457,6 +2437,46 @@ protected StartAnswer execute(StartCommand cmd) { } } + private boolean multipleIsosAtached(DiskTO[] sortedDisks) { + return Arrays.stream(sortedDisks).filter(disk -> disk.getType() == Volume.Type.ISO).count() > 1; + } + + private DiskTO[] getDisks(DiskTO[] sortedDisks) { + return Arrays.stream(sortedDisks).filter(vol -> ((vol.getPath() != null && + vol.getPath().contains("configdrive"))) || (vol.getType() != Volume.Type.ISO)).toArray(DiskTO[]::new); + } + private void configureIso(VmwareHypervisorHost hyperHost, VirtualMachineMO vmMo, DiskTO vol, + VirtualDeviceConfigSpec[] deviceConfigSpecArray, int ideUnitNumber, int i) throws Exception { + TemplateObjectTO iso = (TemplateObjectTO) vol.getData(); + + if (iso.getPath() != null && !iso.getPath().isEmpty()) { + DataStoreTO imageStore = iso.getDataStore(); + if (!(imageStore instanceof NfsTO)) { + s_logger.debug("unsupported protocol"); + throw new Exception("unsupported protocol"); + } + NfsTO nfsImageStore = (NfsTO) imageStore; + String isoPath = nfsImageStore.getUrl() + File.separator + iso.getPath(); + Pair isoDatastoreInfo = getIsoDatastoreInfo(hyperHost, isoPath); + assert (isoDatastoreInfo != null); + assert (isoDatastoreInfo.second() != null); + + deviceConfigSpecArray[i] = new VirtualDeviceConfigSpec(); + Pair isoInfo = + VmwareHelper.prepareIsoDevice(vmMo, isoDatastoreInfo.first(), isoDatastoreInfo.second(), true, true, ideUnitNumber, i + 1); + deviceConfigSpecArray[i].setDevice(isoInfo.first()); + if (isoInfo.second()) { + if (s_logger.isDebugEnabled()) + s_logger.debug("Prepare ISO volume at new device " + _gson.toJson(isoInfo.first())); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.ADD); + } else { + if (s_logger.isDebugEnabled()) + s_logger.debug("Prepare ISO volume at existing device " + _gson.toJson(isoInfo.first())); + deviceConfigSpecArray[i].setOperation(VirtualDeviceConfigSpecOperation.EDIT); + } + } + } + private String mapAdapterType(String adapterStringFromOVF) { if (StringUtils.isBlank(adapterStringFromOVF) || adapterStringFromOVF.equalsIgnoreCase(VirtualEthernetCardType.E1000.toString())) { return VirtualEthernetCardType.E1000.toString(); @@ -5382,7 +5402,7 @@ protected AttachIsoAnswer execute(AttachIsoCommand cmd) { "Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try."); } } catch (Throwable e) { - vmMo.detachIso(null); + vmMo.detachIso(null, cmd.isForce()); } } @@ -5411,7 +5431,7 @@ protected AttachIsoAnswer execute(AttachIsoCommand cmd) { String isoDatastorePath = String.format("[%s] %s%s", storeName, isoStorePathFromRoot, isoFileName); if (cmd.isAttach()) { - vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false, cmd.getDeviceKey()); + vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false, cmd.getDeviceKey(), cmd.isForce()); return new AttachIsoAnswer(cmd); } else { int key = vmMo.detachIso(isoDatastorePath, cmd.isForce()); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java index 1584bd003dc4..d11814800b0e 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/storage/resource/VmwareStorageProcessor.java @@ -2045,7 +2045,7 @@ public Answer backupSnapshot(CopyCommand cmd) { @Override public Answer attachIso(AttachCommand cmd) { - return this.attachIso(cmd.getDisk(), true, cmd.getVmName()); + return this.attachIso(cmd.getDisk(), true, cmd.getVmName(), cmd.isForced()); } @Override @@ -2411,7 +2411,7 @@ private synchronized ManagedObjectReference prepareSecondaryDatastoreOnHost(Stri return morDatastore; } - private Answer attachIso(DiskTO disk, boolean isAttach, String vmName) { + private Answer attachIso(DiskTO disk, boolean isAttach, String vmName, boolean force) { try { VmwareContext context = hostService.getServiceContext(null); VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, null); @@ -2441,7 +2441,7 @@ private Answer attachIso(DiskTO disk, boolean isAttach, String vmName) { return new AttachAnswer("Failed to unmount vmware-tools installer ISO as the corresponding CDROM device is locked by VM. Please unmount the CDROM device inside the VM and ret-try."); } } catch(Throwable e){ - vmMo.detachIso(null); + vmMo.detachIso(null, force); } } @@ -2469,9 +2469,9 @@ private Answer attachIso(DiskTO disk, boolean isAttach, String vmName) { String isoDatastorePath = String.format("[%s] %s/%s", storeName, isoStorePathFromRoot, isoFileName); if (isAttach) { - vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false); + vmMo.attachIso(isoDatastorePath, morSecondaryDs, true, false, force); } else { - vmMo.detachIso(isoDatastorePath); + vmMo.detachIso(isoDatastorePath, force); } return new AttachAnswer(disk); @@ -2497,7 +2497,7 @@ private Answer attachIso(DiskTO disk, boolean isAttach, String vmName) { @Override public Answer dettachIso(DettachCommand cmd) { - return this.attachIso(cmd.getDisk(), false, cmd.getVmName()); + return this.attachIso(cmd.getDisk(), false, cmd.getVmName(), cmd.isForced()); } @Override diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java index e5c811878ced..1ca58a0dfa68 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterActionWorker.java @@ -319,7 +319,7 @@ protected void attachIsoKubernetesVMs(List clusterVMs, final KubernetesS } for (UserVm vm : clusterVMs) { try { - templateService.attachIso(iso.getId(), vm.getId()); + templateService.attachIso(iso.getId(), vm.getId(), true); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Attached binaries ISO for VM : %s in cluster: %s", vm.getDisplayName(), kubernetesCluster.getName())); } @@ -337,7 +337,7 @@ protected void detachIsoKubernetesVMs(List clusterVMs) { for (UserVm vm : clusterVMs) { boolean result = false; try { - result = templateService.detachIso(vm.getId()); + result = templateService.detachIso(vm.getId(), true); } catch (CloudRuntimeException ex) { LOGGER.warn(String.format("Failed to detach binaries ISO from VM : %s in the Kubernetes cluster : %s ", vm.getDisplayName(), kubernetesCluster.getName()), ex); } diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java index 57a8fdfe10c2..b345bd39b775 100755 --- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java @@ -1150,7 +1150,7 @@ public boolean templateIsDeleteable(long templateId) { @Override @ActionEvent(eventType = EventTypes.EVENT_ISO_DETACH, eventDescription = "detaching ISO", async = true) - public boolean detachIso(long vmId) { + public boolean detachIso(long vmId, boolean forced) { Account caller = CallContext.current().getCallingAccount(); Long userId = CallContext.current().getCallingUserId(); @@ -1178,7 +1178,7 @@ public boolean detachIso(long vmId) { throw new InvalidParameterValueException("Please specify a VM that is either Stopped or Running."); } - boolean result = attachISOToVM(vmId, userId, isoId, false); // attach=false + boolean result = attachISOToVM(vmId, userId, isoId, false, forced); // attach=false // => detach if (result) { return result; @@ -1189,7 +1189,7 @@ public boolean detachIso(long vmId) { @Override @ActionEvent(eventType = EventTypes.EVENT_ISO_ATTACH, eventDescription = "attaching ISO", async = true) - public boolean attachIso(long isoId, long vmId) { + public boolean attachIso(long isoId, long vmId, boolean forced) { Account caller = CallContext.current().getCallingAccount(); Long userId = CallContext.current().getCallingUserId(); @@ -1231,7 +1231,7 @@ public boolean attachIso(long isoId, long vmId) { if ("vmware-tools.iso".equals(iso.getName()) && vm.getHypervisorType() != Hypervisor.HypervisorType.VMware) { throw new InvalidParameterValueException("Cannot attach VMware tools drivers to incompatible hypervisor " + vm.getHypervisorType()); } - boolean result = attachISOToVM(vmId, userId, isoId, true); + boolean result = attachISOToVM(vmId, userId, isoId, true, forced); if (result) { return result; } else { @@ -1270,7 +1270,7 @@ public TemplateInfo prepareIso(long isoId, long dcId, Long hostId, Long poolId) } } - private boolean attachISOToVM(long vmId, long isoId, boolean attach) { + private boolean attachISOToVM(long vmId, long isoId, boolean attach, boolean forced) { UserVmVO vm = _userVmDao.findById(vmId); if (vm == null) { @@ -1302,19 +1302,21 @@ private boolean attachISOToVM(long vmId, long isoId, boolean attach) { Command cmd = null; if (attach) { - cmd = new AttachCommand(disk, vmName, vmTO.getDetails()); + cmd = new AttachCommand(disk, vmName); + ((AttachCommand)cmd).setForced(forced); } else { - cmd = new DettachCommand(disk, vmName, vmTO.getDetails()); + cmd = new DettachCommand(disk, vmName); + ((DettachCommand)cmd).setForced(forced); } Answer a = _agentMgr.easySend(vm.getHostId(), cmd); return (a != null && a.getResult()); } - private boolean attachISOToVM(long vmId, long userId, long isoId, boolean attach) { + private boolean attachISOToVM(long vmId, long userId, long isoId, boolean attach, boolean forced) { UserVmVO vm = _userVmDao.findById(vmId); VMTemplateVO iso = _tmpltDao.findById(isoId); - boolean success = attachISOToVM(vmId, isoId, attach); + boolean success = attachISOToVM(vmId, isoId, attach, forced); if (success && attach) { vm.setIsoId(iso.getId()); _userVmDao.update(vmId, vm); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 6f6685167444..160db8f7b5b2 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -5350,7 +5350,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE for (VMTemplateVO tmpl: child_templates){ if (tmpl.getFormat() == Storage.ImageFormat.ISO){ s_logger.info("MDOV trying to attach disk to the VM " + tmpl.getId() + " vmid=" + vm.getId()); - _tmplService.attachIso(tmpl.getId(), vm.getId()); + _tmplService.attachIso(tmpl.getId(), vm.getId(), true); } } diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js index 39271c44d8a5..574c57a7a503 100644 --- a/ui/src/config/section/compute.js +++ b/ui/src/config/section/compute.js @@ -253,7 +253,13 @@ export default { label: 'label.action.detach.iso', message: 'message.detach.iso.confirm', dataView: true, - args: ['virtualmachineid'], + args: (record, store) => { + var args = ['virtualmachineid'] + if (record && record.hypervisor && record.hypervisor === 'VMware') { + args.push('forced') + } + return args + }, show: (record) => { return ['Running', 'Stopped'].includes(record.state) && 'isoid' in record && record.isoid }, mapping: { virtualmachineid: { diff --git a/ui/src/views/compute/AttachIso.vue b/ui/src/views/compute/AttachIso.vue index efb591f341aa..bc12c264f8c1 100644 --- a/ui/src/views/compute/AttachIso.vue +++ b/ui/src/views/compute/AttachIso.vue @@ -33,6 +33,9 @@ + + +
{{ this.$t('label.cancel') }} @@ -116,6 +119,11 @@ export default { id: values.id, virtualmachineid: this.resource.id } + + if (values.forced) { + params.forced = values.forced + } + this.loading = true const title = this.$t('label.action.attach.iso') api('attachIso', params).then(json => { diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index e1ba6b0b87f2..0d01931989bf 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -1498,14 +1498,82 @@ public void detachAllDisks() throws Exception { s_logger.trace("vCenter API trace - detachAllDisk() done(successfully)"); } + private Future answerVmwareQuestion(Boolean[] flags, VirtualMachineMO vmMo, boolean force) { + 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(), force ? ANSWER_YES : ANSWER_NO); + 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(), force ? ANSWER_YES : ANSWER_NO); + } + } + + 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"); + } + }); + return future; + } // isoDatastorePath: [datastore name] isoFilePath - public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, boolean connect, boolean connectAtBoot) throws Exception { - attachIso(isoDatastorePath, morDs, connect, connectAtBoot, null); + public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, boolean connect, boolean connectAtBoot, boolean forced) throws Exception { + attachIso(isoDatastorePath, morDs, connect, connectAtBoot, null, forced); } // isoDatastorePath: [datastore name] isoFilePath public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, - boolean connect, boolean connectAtBoot, Integer key) throws Exception { + boolean connect, boolean connectAtBoot, Integer key, boolean force) throws Exception { if (s_logger.isTraceEnabled()) s_logger.trace("vCenter API trace - attachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath + ", datastore: " + @@ -1556,22 +1624,26 @@ public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, 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 - attachIso() done(failed)"); - throw new Exception("Failed to attach ISO due to " + TaskMO.getTaskFailureInfo(_context, morTask)); - } - - _context.waitForTaskProgressDone(morTask); + final Boolean[] flags = {false}; + final VirtualMachineMO vmMo = this; + Future future = answerVmwareQuestion(flags, vmMo, force); + try { + boolean result = _context.getVimClient().waitForTask(morTask); - if (s_logger.isTraceEnabled()) - s_logger.trace("vCenter API trace - attachIso() done(successfully)"); - } + if (!result) { + if (s_logger.isTraceEnabled()) + s_logger.trace("vCenter API trace - attachIso() done(failed)"); + throw new Exception("Failed to attach ISO due to " + TaskMO.getTaskFailureInfo(_context, morTask)); + } + _context.waitForTaskProgressDone(morTask); - public int detachIso(String isoDatastorePath) throws Exception { - return detachIso(isoDatastorePath, false); + if (s_logger.isTraceEnabled()) + s_logger.trace("vCenter API trace - attachIso() done(successfully)"); + } finally { + flags[0] = true; + future.cancel(true); + } } public int detachIso(String isoDatastorePath, final boolean force) throws Exception { @@ -1604,72 +1676,7 @@ public int detachIso(String isoDatastorePath, final boolean force) throws Except // 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(), force ? ANSWER_YES : ANSWER_NO); - 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(), force ? ANSWER_YES : ANSWER_NO); - } - } - - 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"); - } - }); + Future future = answerVmwareQuestion(flags, vmMo, force); try { boolean result = _context.getVimClient().waitForTask(morTask); if (!result) {