diff --git a/api/src/main/java/com/cloud/template/TemplateApiService.java b/api/src/main/java/com/cloud/template/TemplateApiService.java index b62628560ae..ea818a55a0c 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 245840004fe..74a98beef9d 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 class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { return virtualMachineId; } + public Boolean isForced() { + return forced != null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -98,7 +106,7 @@ public class AttachIsoCmd extends BaseAsyncCmd implements UserCmd { @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 ae86e2fdd7f..4f4a0197ac7 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 class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { return virtualMachineId; } + public Boolean isForced() { + return forced != null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -87,7 +95,7 @@ public class DetachIsoCmd extends BaseAsyncCmd implements UserCmd { @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 d15a4e42da3..ae6ea1fa649 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 final class AttachCommand extends StorageSubSystemCommand { 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 1d805b582e9..eeeadaac42b 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 class DettachCommand extends StorageSubSystemCommand { 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 97a10e51f56..313660831dd 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 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa // // 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 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa } } + 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 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa "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 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa 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 a2aee6b53e1..b06c1bef69c 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 class VmwareStorageProcessor implements StorageProcessor { @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 @@ public class VmwareStorageProcessor implements StorageProcessor { 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 @@ public class VmwareStorageProcessor implements StorageProcessor { 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 @@ public class VmwareStorageProcessor implements StorageProcessor { 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 @@ public class VmwareStorageProcessor implements StorageProcessor { @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 e5c811878ce..1ca58a0dfa6 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 @@ public class KubernetesClusterActionWorker { } 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 @@ public class KubernetesClusterActionWorker { 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 deb5feb2e81..956c456c2ff 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 class TemplateManagerImpl extends ManagerBase implements TemplateManager, @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 class TemplateManagerImpl extends ManagerBase implements TemplateManager, 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 class TemplateManagerImpl extends ManagerBase implements TemplateManager, @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 class TemplateManagerImpl extends ManagerBase implements TemplateManager, 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 class TemplateManagerImpl extends ManagerBase implements TemplateManager, } } - 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 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, 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 53647ea74e6..04de128ac2b 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 class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir 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 69a70182218..9428725302b 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 efb591f341a..bc12c264f8c 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 e1ba6b0b87f..0d01931989b 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,117 +1498,11 @@ public class VirtualMachineMO extends BaseMO { 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 { - attachIso(isoDatastorePath, morDs, connect, connectAtBoot, null); - } - - // isoDatastorePath: [datastore name] isoFilePath - public void attachIso(String isoDatastorePath, ManagedObjectReference morDs, - boolean connect, boolean connectAtBoot, Integer key) 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; - if (key == null) { - cdRom = (VirtualCdrom) getIsoDevice(); - } else { - cdRom = (VirtualCdrom) getIsoDevice(key); - } - 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 - attachIso() 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 - attachIso() done(successfully)"); - } - - public int detachIso(String isoDatastorePath) throws Exception { - return detachIso(isoDatastorePath, false); - } - - public int detachIso(String isoDatastorePath, final boolean force) throws Exception { - if (s_logger.isTraceEnabled()) - s_logger.trace("vCenter API trace - detachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath); - - VirtualDevice device = getIsoDevice(isoDatastorePath); - 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; + 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(); @@ -1670,6 +1564,119 @@ public class VirtualMachineMO extends BaseMO { s_logger.info("VM Question monitor stopped"); } }); + return future; + } + // isoDatastorePath: [datastore name] isoFilePath + 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, boolean force) 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; + if (key == null) { + cdRom = (VirtualCdrom) getIsoDevice(); + } else { + cdRom = (VirtualCdrom) getIsoDevice(key); + } + 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); + + final Boolean[] flags = {false}; + final VirtualMachineMO vmMo = this; + Future future = answerVmwareQuestion(flags, vmMo, force); + try { + 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); + + 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 { + if (s_logger.isTraceEnabled()) + s_logger.trace("vCenter API trace - detachIso(). target MOR: " + _mor.getValue() + ", isoDatastorePath: " + isoDatastorePath); + + VirtualDevice device = getIsoDevice(isoDatastorePath); + 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 = answerVmwareQuestion(flags, vmMo, force); try { boolean result = _context.getVimClient().waitForTask(morTask); if (!result) {