throw new InvalidParameterValueException(
"Please specify a volume that is not destroyed.");
}
// Check that the virtual machine ID is valid and it's a user vm
UserVmVO vm = _userVmDao.findById(vmId);
if (vm == null || vm.getType() != VirtualMachine.Type.User) {
throw new InvalidParameterValueException(
"Please specify a valid User VM.");
}
// Check that the VM is in the correct state
if (vm.getState() != State.Running && vm.getState() != State.Stopped) {
throw new InvalidParameterValueException(
"Please specify a VM that is either running or stopped.");
}
// Check that the device ID is valid
if (deviceId != null) {
if (deviceId.longValue() == 0) {
throw new InvalidParameterValueException(
"deviceId can't be 0, which is used by Root device");
}
}
// Check that the number of data volumes attached to VM is less than
// that supported by hypervisor
List<VolumeVO> existingDataVolumes = _volsDao.findByInstanceAndType(
vmId, Volume.Type.DATADISK);
int maxDataVolumesSupported = getMaxDataVolumesSupported(vm);
if (existingDataVolumes.size() >= maxDataVolumesSupported) {
throw new InvalidParameterValueException(
"The specified VM already has the maximum number of data disks ("
+ maxDataVolumesSupported
+ "). Please specify another VM.");
}
// Check that the VM and the volume are in the same zone
if (vm.getDataCenterId() != volume.getDataCenterId()) {
throw new InvalidParameterValueException(
"Please specify a VM that is in the same zone as the volume.");
}
// If local storage is disabled then attaching a volume with local disk
// offering not allowed
DataCenterVO dataCenter = _dcDao.findById(volume.getDataCenterId());
if (!dataCenter.isLocalStorageEnabled()) {
DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume
.getDiskOfferingId());
if (diskOffering.getUseLocalStorage()) {
throw new InvalidParameterValueException(
"Zone is not configured to use local storage but volume's disk offering "
+ diskOffering.getName() + " uses it");
}
}
// if target VM has associated VM snapshots
List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
if(vmSnapshots.size() > 0){
throw new InvalidParameterValueException(
"Unable to attach volume, please specify a VM that does not have VM snapshots");
}
// permission check
_accountMgr.checkAccess(caller, null, true, volume, vm);
if (!(Volume.State.Allocated.equals(volume.getState())
|| Volume.State.Ready.equals(volume.getState()) || Volume.State.Uploaded
.equals(volume.getState()))) {
throw new InvalidParameterValueException(
"Volume state must be in Allocated, Ready or in Uploaded state");
}
VolumeVO rootVolumeOfVm = null;
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId,
Volume.Type.ROOT);
if (rootVolumesOfVm.size() != 1) {
throw new CloudRuntimeException(
"The VM "
+ vm.getHostName()
+ " has more than one ROOT volume and is in an invalid state.");
} else {
rootVolumeOfVm = rootVolumesOfVm.get(0);
}
HypervisorType rootDiskHyperType = vm.getHypervisorType();
HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume
.getId());
VolumeVO dataDiskVol = _volsDao.findById(volume.getId());
StoragePoolVO dataDiskStoragePool = _storagePoolDao.findById(dataDiskVol.getPoolId());
// managed storage can be used for different types of hypervisors
// only perform this check if the volume's storage pool is not null and not managed
if (dataDiskStoragePool != null && !dataDiskStoragePool.isManaged()) {
if (dataDiskHyperType != HypervisorType.None
&& rootDiskHyperType != dataDiskHyperType) {
throw new InvalidParameterValueException(
"Can't attach a volume created by: " + dataDiskHyperType
+ " to a " + rootDiskHyperType + " vm");
}
}
deviceId = getDeviceId(vmId, deviceId);
VolumeInfo volumeOnPrimaryStorage = volume;
// Check if volume is stored on secondary storage
boolean isVolumeOnSec = false;
VolumeInfo volOnSecondary = volFactory.getVolume(volume.getId(), DataStoreRole.Image);
if (volOnSecondary != null) {
isVolumeOnSec = true;
}
boolean createVolumeOnBackend = true;
if (rootVolumeOfVm.getState() == Volume.State.Allocated) {
createVolumeOnBackend = false;
}
// Create volume on the backend only when VM's root volume is allocated
if (createVolumeOnBackend) {
if (volume.getState().equals(Volume.State.Allocated)
|| volume.getState() == Volume.State.Uploaded) {
try {
volumeOnPrimaryStorage = createVolumeOnPrimaryStorage(vm, rootVolumeOfVm, volume, rootDiskHyperType);
} catch (NoTransitionException e) {
s_logger.debug("Failed to create volume on primary storage", e);
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
}
}
// reload the volume from db
volumeOnPrimaryStorage = volFactory.getVolume(volumeOnPrimaryStorage.getId());
boolean moveVolumeNeeded = needMoveVolume(rootVolumeOfVm, volumeOnPrimaryStorage);
if (moveVolumeNeeded) {
PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)volumeOnPrimaryStorage.getDataStore();
if (primaryStore.isLocal()) {
throw new CloudRuntimeException(
"Failed to attach local data volume "
+ volume.getName()
+ " to VM "
+ vm.getDisplayName()
+ " as migration of local data volume is not allowed");
}
StoragePoolVO vmRootVolumePool = _storagePoolDao
.findById(rootVolumeOfVm.getPoolId());
try {
volumeOnPrimaryStorage = moveVolume(volumeOnPrimaryStorage,
vmRootVolumePool.getDataCenterId(),
vmRootVolumePool.getPodId(),
vmRootVolumePool.getClusterId(),
dataDiskHyperType);
} catch (ConcurrentOperationException e) {
s_logger.debug("move volume failed", e);
throw new CloudRuntimeException("move volume failed", e);
} catch (StorageUnavailableException e) {
s_logger.debug("move volume failed", e);
throw new CloudRuntimeException("move volume failed", e);
}
}
}
AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor
.getCurrentExecutor();
if (asyncExecutor != null) {
AsyncJobVO job = asyncExecutor.getJob();
if (s_logger.isInfoEnabled()) {
s_logger.info("Trying to attaching volume " + volumeId
+ " to vm instance:" + vm.getId()
+ ", update async job-" + job.getId() + " = [ " + job.getUuid()
+ " ] progress status");
}
_asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);