VolumeApiServiceImpl extends ManagerBase implements VolumeApiService, VmWorkJobHandler { @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) { final Account caller = CallContext.current().getCallingAccount(); if (cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null || cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null) || cmmd.getId() == null && (cmmd.getDeviceId() == null || cmmd.getVirtualMachineId() == null)) { throw new InvalidParameterValueException("Please provide either a volume id, or a tuple(device id, instance id)"); } final Long volumeId = cmmd.getId(); VolumeVO volume = null; if (volumeId != null) { volume = _volsDao.findById(volumeId); } else { volume = _volsDao.findByInstanceAndDeviceId(cmmd.getVirtualMachineId(), cmmd.getDeviceId()).get(0); } if (volume == null) { throw new InvalidParameterValueException("Unable to find volume with ID: " + volumeId); } Long vmId = null; if (cmmd.getVirtualMachineId() == null) { vmId = volume.getInstanceId(); } else { vmId = cmmd.getVirtualMachineId(); } _accountMgr.checkAccess(caller, null, true, volume); if (vmId == null) { throw new InvalidParameterValueException("The specified volume is not attached to a VM."); } final UserVmVO vm = _userVmDao.findById(vmId); if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) { throw new InvalidParameterValueException("Please specify a VM that is either running or stopped."); } if (!(volume.getVolumeType() == Volume.Type.ROOT || volume.getVolumeType() == Volume.Type.DATADISK)) { throw new InvalidParameterValueException("Please specify volume of type " + Volume.Type.DATADISK.toString() + " or " + Volume.Type.ROOT.toString()); } if (volume.getVolumeType() == Volume.Type.ROOT) { validateRootVolumeDetachAttach(volume, vm); } final List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId); if (vmSnapshots.size() > 0) { throw new InvalidParameterValueException("Unable to detach volume, please specify a VM that does not have VM snapshots"); } final AsyncJobExecutionContext asyncExecutionContext = AsyncJobExecutionContext.getCurrentExecutionContext(); if (asyncExecutionContext != null) { final AsyncJob job = asyncExecutionContext.getJob(); if (s_logger.isInfoEnabled()) { s_logger.info("Trying to attaching volume " + volumeId + "to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status"); } _jobMgr.updateAsyncJobAttachment(job.getId(), "Volume", volumeId); } final AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { VmWorkJobVO placeHolder = null; placeHolder = createPlaceHolderWork(vmId); try { return orchestrateDetachVolumeFromVM(vmId, volumeId); } finally { _workJobDao.expunge(placeHolder.getId()); } } else { final Outcome<Volume> outcome = detachVolumeFromVmThroughJobQueue(vmId, volumeId); Volume vol = null; try { outcome.get(); } catch (final InterruptedException e) { throw new RuntimeException("Operation is interrupted", e); } catch (final java.util.concurrent.ExecutionException e) { throw new RuntimeException("Execution excetion", e); } final Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); if (jobResult != null) { if (jobResult instanceof ConcurrentOperationException) { throw (ConcurrentOperationException)jobResult; } else if (jobResult instanceof RuntimeException) { throw (RuntimeException)jobResult; } else if (jobResult instanceof Throwable) { throw new RuntimeException("Unexpected exception", (Throwable)jobResult); } else if (jobResult instanceof Long) { vol = _volsDao.findById((Long) jobResult); } } return vol; } } protected  VolumeApiServiceImpl(); @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPLOAD, eventDescription = "uploading volume", async = true) VolumeVO uploadVolume(UploadVolumeCmd cmd); @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPLOAD, eventDescription = "uploading volume for post upload", async = true) GetUploadParamsResponse uploadVolume(final GetUploadParamsForVolumeCmd cmd); String getRandomVolumeName(); String getVolumeNameFromCommand(CreateVolumeCmd cmd); @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true) VolumeVO allocVolume(CreateVolumeCmd cmd); boolean validateVolumeSizeRange(long size); @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", async = true) VolumeVO createVolume(CreateVolumeCmd cmd); @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true) VolumeVO resizeVolume(ResizeVolumeCmd cmd); @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DELETE, eventDescription = "deleting volume") boolean deleteVolume(long volumeId, Account caller); @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) Volume attachVolumeToVM(AttachVolumeCmd command); Volume attachVolumeToVM(Long vmId, Long volumeId, Long deviceId); @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPDATE, eventDescription = "updating volume", async = true) Volume updateVolume(long volumeId, String path, String state, Long storageId, Boolean displayVolume, String customId, long entityOwnerId, String chainInfo); @Override void updateDisplay(Volume volume, Boolean displayVolume); @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) Volume detachVolumeFromVM(DetachVolumeCmd cmmd); @DB @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_MIGRATE, eventDescription = "migrating volume", async = true) Volume migrateVolume(MigrateVolumeCmd cmd); @Override @ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "taking snapshot", async = true) Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm); @Override @ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "allocating snapshot", create = true) Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName); @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_EXTRACT, eventDescription = "extracting volume", async = true) String extractVolume(ExtractVolumeCmd cmd); @Override boolean isDisplayResourceEnabled(Long id); @Override boolean configure(String name, Map<String, Object> params); List<StoragePoolAllocator> getStoragePoolAllocators(); @Inject void setStoragePoolAllocators(List<StoragePoolAllocator> storagePoolAllocators); Outcome<Volume> attachVolumeToVmThroughJobQueue(final Long vmId, final Long volumeId, final Long deviceId); Outcome<Volume> detachVolumeFromVmThroughJobQueue(final Long vmId, final Long volumeId); Outcome<Volume> resizeVolumeThroughJobQueue(final Long vmId, final long volumeId,
      final long currentSize, final long newSize, final Long newMinIops, final Long newMaxIops, final Long newServiceOfferingId, final boolean shrinkOk); Outcome<String> extractVolumeThroughJobQueue(final Long vmId, final long volumeId,
      final long zoneId); Outcome<Volume> migrateVolumeThroughJobQueue(final Long vmId, final long volumeId,
      final long destPoolId, final boolean liveMigrate); Outcome<Snapshot> takeVolumeSnapshotThroughJobQueue(final Long vmId, final Long volumeId,
      final Long policyId, final Long snapshotId, final Long accountId, final boolean quiesceVm); @Override Pair<JobInfo.Status, String> handleVmWorkJob(VmWork work); static final String VM_WORK_JOB_HANDLER; }
@Test(expected = InvalidParameterValueException.class) public void testDetachVolumeFromRunningVm() throws NoSuchFieldException, IllegalAccessException { Field dedicateIdField = _detachCmdClass.getDeclaredField("id"); dedicateIdField.setAccessible(true); dedicateIdField.set(detachCmd, 1L); _svc.detachVolumeFromVM(detachCmd); }
@Test(expected = InvalidParameterValueException.class) public void testDetachVolumeOfManagedDataStore() throws NoSuchFieldException, IllegalAccessException { Field dedicateIdField = _detachCmdClass.getDeclaredField("id"); dedicateIdField.setAccessible(true); dedicateIdField.set(detachCmd, 4L); _svc.detachVolumeFromVM(detachCmd); }
@Test public void testDetachVolumeFromStoppedXenVm() throws NoSuchFieldException, IllegalAccessException { thrown.expect(NullPointerException.class); Field dedicateIdField = _detachCmdClass.getDeclaredField("id"); dedicateIdField.setAccessible(true); dedicateIdField.set(detachCmd, 2L); _svc.detachVolumeFromVM(detachCmd); }