mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	refactor attachvolumetoVM
This commit is contained in:
		
							parent
							
								
									87678b8525
								
							
						
					
					
						commit
						63c3634b79
					
				| @ -18,6 +18,7 @@ package com.cloud.storage.pool; | ||||
| 
 | ||||
| import java.net.UnknownHostException; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| import com.cloud.api.commands.CancelPrimaryStorageMaintenanceCmd; | ||||
| import com.cloud.api.commands.CreateStoragePoolCmd; | ||||
| @ -26,13 +27,18 @@ import com.cloud.api.commands.DeletePoolCmd; | ||||
| import com.cloud.api.commands.ListVolumesCmd; | ||||
| import com.cloud.api.commands.UpdateStoragePoolCmd; | ||||
| import com.cloud.api.commands.UploadVolumeCmd; | ||||
| import com.cloud.dc.DataCenterVO; | ||||
| import com.cloud.dc.HostPodVO; | ||||
| import com.cloud.exception.ConcurrentOperationException; | ||||
| import com.cloud.exception.InsufficientCapacityException; | ||||
| import com.cloud.exception.PermissionDeniedException; | ||||
| import com.cloud.exception.ResourceAllocationException; | ||||
| import com.cloud.exception.ResourceInUseException; | ||||
| import com.cloud.exception.ResourceUnavailableException; | ||||
| import com.cloud.storage.StoragePoolVO; | ||||
| import com.cloud.storage.volume.Volume; | ||||
| import com.cloud.vm.DiskProfile; | ||||
| import com.cloud.vm.VMInstanceVO; | ||||
| 
 | ||||
| public interface StoragePoolService { | ||||
|     /** | ||||
|  | ||||
| @ -147,4 +147,6 @@ public interface Volume extends ControlledEntity, BasedOn, StateObject<Volume.St | ||||
|     public void incrUpdatedCount(); | ||||
| 
 | ||||
|     public Date getUpdated(); | ||||
| 
 | ||||
| 	boolean isAttachedToVm(); | ||||
| } | ||||
|  | ||||
| @ -438,4 +438,9 @@ public class VolumeVO implements Volume, Identity { | ||||
|     public void setUuid(String uuid) { | ||||
|     	this.uuid = uuid; | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public boolean isAttachedToVm() { | ||||
|     	return this.getInstanceId() != null ? true : false;  | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -4,6 +4,7 @@ import java.util.List; | ||||
| 
 | ||||
| import com.cloud.agent.api.Answer; | ||||
| import com.cloud.agent.api.Command; | ||||
| import com.cloud.api.commands.AttachVolumeCmd; | ||||
| import com.cloud.api.commands.CreateVolumeCmd; | ||||
| import com.cloud.capacity.CapacityVO; | ||||
| import com.cloud.deploy.DeployDestination; | ||||
| @ -33,4 +34,5 @@ public interface StorageOrchestraEngine { | ||||
| 	VolumeVO createVolume(CreateVolumeCmd cmd); | ||||
| 	VolumeVO allocVolume(CreateVolumeCmd cmd) | ||||
| 			throws ResourceAllocationException; | ||||
| 	Volume attachVolumeToVM(AttachVolumeCmd command); | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.cloud.storage.orchestra; | ||||
| hpackage com.cloud.storage.orchestra; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| @ -12,6 +12,8 @@ import javax.naming.ConfigurationException; | ||||
| import org.apache.log4j.Logger; | ||||
| 
 | ||||
| import com.cloud.agent.api.Answer; | ||||
| import com.cloud.agent.api.AttachVolumeAnswer; | ||||
| import com.cloud.agent.api.AttachVolumeCommand; | ||||
| import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer; | ||||
| import com.cloud.agent.api.CreateVolumeFromSnapshotCommand; | ||||
| import com.cloud.agent.api.UpgradeSnapshotCommand; | ||||
| @ -21,7 +23,12 @@ import com.cloud.agent.api.storage.CreateAnswer; | ||||
| import com.cloud.agent.api.storage.CreateCommand; | ||||
| import com.cloud.agent.api.storage.DestroyCommand; | ||||
| import com.cloud.agent.api.to.VolumeTO; | ||||
| import com.cloud.api.BaseCmd; | ||||
| import com.cloud.api.commands.AttachVolumeCmd; | ||||
| import com.cloud.api.commands.CreateVolumeCmd; | ||||
| import com.cloud.async.AsyncJobExecutor; | ||||
| import com.cloud.async.AsyncJobVO; | ||||
| import com.cloud.async.BaseAsyncJobExecutor; | ||||
| import com.cloud.configuration.Config; | ||||
| import com.cloud.configuration.ConfigurationManager; | ||||
| import com.cloud.configuration.Resource.ResourceType; | ||||
| @ -52,6 +59,7 @@ import com.cloud.storage.VMTemplateHostVO; | ||||
| import com.cloud.storage.VMTemplateVO; | ||||
| import com.cloud.storage.VolumeHostVO; | ||||
| import com.cloud.storage.VolumeVO; | ||||
| import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; | ||||
| import com.cloud.storage.dao.DiskOfferingDao; | ||||
| import com.cloud.storage.dao.SnapshotDao; | ||||
| import com.cloud.storage.dao.StoragePoolDao; | ||||
| @ -91,6 +99,8 @@ import com.cloud.vm.UserVmVO; | ||||
| import com.cloud.vm.VMInstanceVO; | ||||
| import com.cloud.vm.VirtualMachine; | ||||
| import com.cloud.vm.VirtualMachineProfile; | ||||
| import com.cloud.vm.VirtualMachine.State; | ||||
| import com.cloud.vm.dao.UserVmDao; | ||||
| import com.cloud.vm.dao.VMInstanceDao; | ||||
| 
 | ||||
| public class StorageOrchestraEngineImpl implements StorageOrchestraEngine, Manager { | ||||
| @ -115,6 +125,8 @@ public class StorageOrchestraEngineImpl implements StorageOrchestraEngine, Manag | ||||
| 	protected StoragePoolDao _storagePoolDao; | ||||
| 	@Inject | ||||
| 	protected UsageEventDao _usageEventDao; | ||||
| 	@Inject | ||||
| 	protected UserVmDao _userVmDao; | ||||
| 	 | ||||
| 	 | ||||
| 	 | ||||
| @ -360,7 +372,8 @@ public class StorageOrchestraEngineImpl implements StorageOrchestraEngine, Manag | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void prepare(VirtualMachineProfile<? extends VirtualMachine> vm, DeployDestination dest, boolean recreate) throws StorageUnavailableException, InsufficientStorageCapacityException { | ||||
|     public void prepare(VirtualMachineProfile<? extends VirtualMachine> vm, DeployDestination dest, boolean recreate) throws StorageUnavailableException, InsufficientStorageCapacityException, | ||||
|     					ConcurrentOperationException { | ||||
|         if (dest == null) { | ||||
|             throw new CloudRuntimeException("Unable to prepare Volume for vm because DeployDestination is null, vm:" + vm); | ||||
|         } | ||||
| @ -377,55 +390,6 @@ public class StorageOrchestraEngineImpl implements StorageOrchestraEngine, Manag | ||||
|             vm.addDisk(new VolumeTO(created, destPool)); | ||||
|         } | ||||
|     } | ||||
| 	  | ||||
| 	@Override | ||||
|     @DB | ||||
|     public VolumeVO copyVolumeFromSecToPrimary(VolumeVO volume, VMInstanceVO vm, VMTemplateVO template, DataCenterVO dc, HostPodVO pod, Long clusterId, ServiceOfferingVO offering, DiskOfferingVO diskOffering, | ||||
|             List<StoragePoolVO> avoids, long size, HypervisorType hyperType) throws NoTransitionException { | ||||
|     	 | ||||
|     	final HashSet<StoragePool> avoidPools = new HashSet<StoragePool>(avoids); | ||||
|     	DiskProfile dskCh = createDiskCharacteristics(volume, template, dc, diskOffering); | ||||
|     	dskCh.setHyperType(vm.getHypervisorType()); | ||||
|     	// Find a suitable storage to create volume on  | ||||
|     	StoragePoolVO destPool = findStoragePool(dskCh, dc, pod, clusterId, vm, avoidPools); | ||||
|     	 | ||||
|     	// Copy the volume from secondary storage to the destination storage pool    	  	 | ||||
|     	processEvent(volume, Event.CopyRequested); | ||||
|     	VolumeHostVO volumeHostVO = _volumeHostDao.findByVolumeId(volume.getId()); | ||||
|     	HostVO secStorage = _hostDao.findById(volumeHostVO.getHostId()); | ||||
|     	String secondaryStorageURL = secStorage.getStorageUrl(); | ||||
|     	String[] volumePath = volumeHostVO.getInstallPath().split("/"); | ||||
|     	String volumeUUID = volumePath[volumePath.length - 1].split("\\.")[0]; | ||||
|     	 | ||||
|         CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(), volumeUUID, destPool, secondaryStorageURL, false, _copyvolumewait); | ||||
|         CopyVolumeAnswer cvAnswer; | ||||
| 		try { | ||||
|             cvAnswer = (CopyVolumeAnswer) sendToPool(destPool, cvCmd); | ||||
|         } catch (StorageUnavailableException e1) { | ||||
|         	processEvent(volume, Event.CopyFailed); | ||||
|             throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool."); | ||||
|         } | ||||
| 
 | ||||
|         if (cvAnswer == null || !cvAnswer.getResult()) { | ||||
|         	processEvent(volume, Event.CopyFailed); | ||||
|             throw new CloudRuntimeException("Failed to copy the volume from secondary storage to the destination primary storage pool."); | ||||
|         }         | ||||
|         Transaction txn = Transaction.currentTxn(); | ||||
|         txn.start();         | ||||
|         volume.setPath(cvAnswer.getVolumePath()); | ||||
|         volume.setFolder(destPool.getPath()); | ||||
|         volume.setPodId(destPool.getPodId()); | ||||
|         volume.setPoolId(destPool.getId());         | ||||
|         volume.setPodId(destPool.getPodId()); | ||||
|         processEvent(volume, Event.CopySucceeded);  | ||||
|         UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), null, volume.getSize()); | ||||
|         _usageEventDao.persist(usageEvent); | ||||
|         _volumeHostDao.remove(volumeHostVO.getId()); | ||||
|     	txn.commit(); | ||||
| 		return volume; | ||||
|     	 | ||||
|     } | ||||
| 	  | ||||
| 	 | ||||
| 	  | ||||
| 	 @Override | ||||
| @ -544,6 +508,27 @@ public class StorageOrchestraEngineImpl implements StorageOrchestraEngine, Manag | ||||
|         migrateVolumes(vols, destPool); | ||||
|         return vol; | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) | ||||
|     public Volume attachVolumeToVM(AttachVolumeCmd command) { | ||||
|         Account caller = UserContext.current().getCaller(); | ||||
| 		Long vmId = command.getVirtualMachineId(); | ||||
|         Long volumeId = command.getId(); | ||||
|         Long deviceId = command.getDeviceId(); | ||||
|         VolumeVO volume = _volumeDao.findById(volumeId); | ||||
| 
 | ||||
|         if (volume == null || volume.getVolumeType() != Volume.Type.DATADISK) { | ||||
|             throw new InvalidParameterValueException("Please specify a valid data volume."); | ||||
|         } | ||||
|          | ||||
|         UserVmVO vm = _userVmDao.findById(vmId); | ||||
|         if (vm == null || vm.getType() != VirtualMachine.Type.User) { | ||||
|             throw new InvalidParameterValueException("Please specify a valid User VM."); | ||||
|         } | ||||
|         _accountMgr.checkAccess(caller, null, true, volume, vm); | ||||
|         return _volumeMgr.attachVolumeToVM(volume, vm, deviceId); | ||||
|     } | ||||
| 
 | ||||
|     | ||||
| } | ||||
|  | ||||
| @ -18,6 +18,7 @@ package com.cloud.storage.pool; | ||||
| 
 | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| 
 | ||||
| import com.cloud.agent.api.Answer; | ||||
| import com.cloud.agent.api.Command; | ||||
| @ -154,4 +155,8 @@ public interface StoragePoolManager extends StoragePoolService, Manager { | ||||
| 			Account account, HashSet<StoragePool> poolsToAvoid); | ||||
| 
 | ||||
| 	boolean PrepareTemplateOnPool(VMTemplateVO template, StoragePool pool); | ||||
| 
 | ||||
| 	StoragePoolVO findStoragePool(DiskProfile dskCh, DataCenterVO dc, | ||||
| 			HostPodVO pod, Long clusterId, VMInstanceVO vm, | ||||
| 			Set<StoragePool> avoid); | ||||
| } | ||||
|  | ||||
| @ -438,7 +438,8 @@ public class StoragePoolManagerImpl implements StoragePoolManager, Manager, Clus | ||||
| 		return null; | ||||
|     } | ||||
| 
 | ||||
|     protected StoragePoolVO findStoragePool(DiskProfile dskCh, final DataCenterVO dc, HostPodVO pod, Long clusterId, VMInstanceVO vm, final Set<StoragePool> avoid) { | ||||
|     @Override | ||||
|     public StoragePoolVO findStoragePool(DiskProfile dskCh, final DataCenterVO dc, HostPodVO pod, Long clusterId, VMInstanceVO vm, final Set<StoragePool> avoid) { | ||||
| 
 | ||||
|         VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm); | ||||
|         Enumeration<StoragePoolAllocator> en = _storagePoolAllocators.enumeration(); | ||||
| @ -2292,4 +2293,7 @@ public class StoragePoolManagerImpl implements StoragePoolManager, Manager, Clus | ||||
|     public boolean PrepareTemplateOnPool(VMTemplateVO template, StoragePool pool) { | ||||
|     	 | ||||
|     } | ||||
|      | ||||
|     @O | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -3,6 +3,7 @@ package com.cloud.storage.volume; | ||||
| import java.util.List; | ||||
| 
 | ||||
| import com.cloud.agent.api.to.VolumeTO; | ||||
| import com.cloud.api.commands.AttachVolumeCmd; | ||||
| import com.cloud.api.commands.ListVolumesCmd; | ||||
| import com.cloud.dc.DataCenterVO; | ||||
| import com.cloud.dc.HostPodVO; | ||||
| @ -24,6 +25,7 @@ import com.cloud.user.Account; | ||||
| import com.cloud.utils.Pair; | ||||
| import com.cloud.utils.fsm.NoTransitionException; | ||||
| import com.cloud.vm.DiskProfile; | ||||
| import com.cloud.vm.UserVmVO; | ||||
| import com.cloud.vm.VMInstanceVO; | ||||
| import com.cloud.vm.VirtualMachine; | ||||
| import com.cloud.vm.VirtualMachineProfile; | ||||
| @ -61,9 +63,11 @@ public interface VolumeManager { | ||||
| 	 * @param size | ||||
| 	 * @param hyperType | ||||
| 	 * @return volume VO if success, null otherwise | ||||
| 	 * @throws StorageUnavailableException  | ||||
| 	 * @throws ConcurrentOperationException  | ||||
| 	 */ | ||||
| 	VolumeVO createVolume(VolumeVO volume, long VMTemplateId, DiskOfferingVO diskOffering, | ||||
|             HypervisorType hyperType, StoragePool assignedPool); | ||||
|             HypervisorType hyperType, StoragePool assignedPool) throws StorageUnavailableException, ConcurrentOperationException; | ||||
| 	 | ||||
| 	/** | ||||
| 	 * Marks the specified volume as destroyed in the management server database. The expunge thread will delete the volume from its storage pool. | ||||
| @ -133,4 +137,6 @@ public interface VolumeManager { | ||||
| 
 | ||||
| 	VolumeVO allocateDiskVolume(String volumeName, long zoneId, long ownerId, | ||||
| 			long domainId, long diskOfferingId, long size); | ||||
| 
 | ||||
| 	Volume attachVolumeToVM(VolumeVO volume, UserVmVO vm, Long deviceId); | ||||
| } | ||||
|  | ||||
| @ -10,6 +10,7 @@ import java.util.Date; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.UUID; | ||||
| 
 | ||||
| import javax.naming.ConfigurationException; | ||||
| 
 | ||||
| @ -17,6 +18,8 @@ import org.apache.log4j.Logger; | ||||
| 
 | ||||
| import com.cloud.agent.AgentManager; | ||||
| import com.cloud.agent.api.Answer; | ||||
| import com.cloud.agent.api.AttachVolumeAnswer; | ||||
| import com.cloud.agent.api.AttachVolumeCommand; | ||||
| import com.cloud.agent.api.storage.CopyVolumeAnswer; | ||||
| import com.cloud.agent.api.storage.CopyVolumeCommand; | ||||
| import com.cloud.agent.api.storage.CreateAnswer; | ||||
| @ -25,14 +28,23 @@ import com.cloud.agent.api.storage.DeleteVolumeCommand; | ||||
| import com.cloud.agent.api.storage.DestroyCommand; | ||||
| import com.cloud.agent.api.to.StorageFilerTO; | ||||
| import com.cloud.agent.api.to.VolumeTO; | ||||
| import com.cloud.api.BaseCmd; | ||||
| import com.cloud.api.commands.AttachVolumeCmd; | ||||
| import com.cloud.api.commands.CreateVolumeCmd; | ||||
| import com.cloud.api.commands.ListVolumesCmd; | ||||
| import com.cloud.async.AsyncJobExecutor; | ||||
| import com.cloud.async.AsyncJobVO; | ||||
| import com.cloud.async.BaseAsyncJobExecutor; | ||||
| import com.cloud.configuration.Config; | ||||
| import com.cloud.dc.DataCenterVO; | ||||
| import com.cloud.dc.HostPodVO; | ||||
| import com.cloud.dc.dao.DataCenterDao; | ||||
| import com.cloud.dc.dao.HostPodDao; | ||||
| import com.cloud.deploy.DeployDestination; | ||||
| import com.cloud.domain.Domain; | ||||
| import com.cloud.event.EventTypes; | ||||
| import com.cloud.event.UsageEventVO; | ||||
| import com.cloud.exception.AgentUnavailableException; | ||||
| import com.cloud.exception.ConcurrentOperationException; | ||||
| import com.cloud.exception.InsufficientStorageCapacityException; | ||||
| import com.cloud.exception.InvalidParameterValueException; | ||||
| @ -41,6 +53,7 @@ import com.cloud.exception.StorageUnavailableException; | ||||
| import com.cloud.host.HostVO; | ||||
| import com.cloud.host.dao.HostDao; | ||||
| import com.cloud.hypervisor.Hypervisor.HypervisorType; | ||||
| import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao; | ||||
| import com.cloud.server.ResourceTag.TaggedResourceType; | ||||
| import com.cloud.service.ServiceOfferingVO; | ||||
| import com.cloud.storage.DiskOfferingVO; | ||||
| @ -49,7 +62,9 @@ import com.cloud.storage.VMTemplateHostVO; | ||||
| import com.cloud.storage.VMTemplateVO; | ||||
| import com.cloud.storage.VolumeHostVO; | ||||
| import com.cloud.storage.VolumeVO; | ||||
| import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; | ||||
| import com.cloud.storage.dao.DiskOfferingDao; | ||||
| import com.cloud.storage.dao.StoragePoolDao; | ||||
| import com.cloud.storage.dao.VMTemplateDao; | ||||
| import com.cloud.storage.dao.VMTemplateHostDao; | ||||
| import com.cloud.storage.dao.VolumeDao; | ||||
| @ -64,6 +79,8 @@ import com.cloud.storage.snapshot.SnapshotManager; | ||||
| import com.cloud.storage.volume.Volume.Event; | ||||
| import com.cloud.storage.volume.Volume.Type; | ||||
| import com.cloud.user.Account; | ||||
| import com.cloud.user.UserContext; | ||||
| import com.cloud.utils.NumbersUtil; | ||||
| import com.cloud.utils.Pair; | ||||
| import com.cloud.utils.component.Inject; | ||||
| import com.cloud.utils.component.Manager; | ||||
| @ -77,11 +94,15 @@ import com.cloud.utils.exception.CloudRuntimeException; | ||||
| import com.cloud.utils.fsm.NoTransitionException; | ||||
| import com.cloud.utils.fsm.StateMachine2; | ||||
| import com.cloud.vm.DiskProfile; | ||||
| import com.cloud.vm.UserVmVO; | ||||
| import com.cloud.vm.VMInstanceVO; | ||||
| import com.cloud.vm.VirtualMachine; | ||||
| import com.cloud.vm.VirtualMachineProfile; | ||||
| import com.cloud.vm.VirtualMachine.State; | ||||
| import com.cloud.vm.dao.UserVmDao; | ||||
| import com.cloud.vm.dao.VMInstanceDao; | ||||
| import com.cloud.event.ActionEvent; | ||||
| import com.cloud.event.dao.UsageEventDao; | ||||
| import com.cloud.storage.VMTemplateStorageResourceAssoc; | ||||
| import com.cloud.tags.ResourceTagVO; | ||||
| import com.cloud.tags.dao.ResourceTagDao; | ||||
| @ -91,8 +112,10 @@ import com.cloud.template.VirtualMachineTemplate; | ||||
| public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|     private static final Logger s_logger = Logger.getLogger(VolumeManagerImpl.class); | ||||
|     protected int _retry = 2; | ||||
|     protected int _copyvolumewait; | ||||
|     private StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine; | ||||
|     protected SearchBuilder<VMTemplateHostVO> HostTemplateStatesSearch; | ||||
|      | ||||
|     @Inject | ||||
|     protected VolumeDao _volumeDao; | ||||
|     @Inject | ||||
| @ -121,6 +144,21 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|     protected VMInstanceDao _vmDao; | ||||
|     @Inject | ||||
|     protected VMTemplateDao _templateDao; | ||||
|     @Inject | ||||
|     protected UserVmDao _userVmDao; | ||||
|     @Inject | ||||
|     protected StoragePoolDao _storagePoolDao; | ||||
|     @Inject | ||||
|     protected VolumeHostDao _volumeHostDao; | ||||
|     @Inject | ||||
|     protected DataCenterDao _dcDao; | ||||
|     @Inject | ||||
|     protected HostPodDao _podDao; | ||||
|     @Inject | ||||
|     protected UsageEventDao _usageEventDao; | ||||
|      | ||||
|     @Inject | ||||
|     protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; | ||||
| 	 | ||||
|     | ||||
| 
 | ||||
| @ -183,15 +221,64 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|         return _volumeDao.persist(newVol); | ||||
|     } | ||||
|      | ||||
|     @DB | ||||
|     protected void copyVolumeFromSec(VolumeVO volume, StoragePool pool) throws ConcurrentOperationException, StorageUnavailableException { | ||||
|     	try { | ||||
|         	processEvent(volume, Volume.Event.CopyRequested); | ||||
|         } catch (NoTransitionException e) { | ||||
|         	throw new ConcurrentOperationException("Unable to request a copy operation on volume " + volume); | ||||
|         } | ||||
|          | ||||
|         boolean success = false; | ||||
|         try { | ||||
|         	VolumeHostVO volumeHostVO = _volumeHostDao.findByVolumeId(volume.getId()); | ||||
|         	HostVO secStorage = _hostDao.findById(volumeHostVO.getHostId()); | ||||
|         	String secondaryStorageURL = secStorage.getStorageUrl(); | ||||
|         	String[] volumePath = volumeHostVO.getInstallPath().split("/"); | ||||
|         	String volumeUUID = volumePath[volumePath.length - 1].split("\\.")[0]; | ||||
| 
 | ||||
|         	CopyVolumeCommand cvCmd = new CopyVolumeCommand(volume.getId(), volumeUUID, pool, secondaryStorageURL, false, _copyvolumewait); | ||||
|         	CopyVolumeAnswer cvAnswer; | ||||
| 
 | ||||
|         	cvAnswer = (CopyVolumeAnswer) _storagePoolMgr.sendToPool(pool, cvCmd); | ||||
|         	boolean result = (cvAnswer != null && cvAnswer.getResult()) ? true : false; | ||||
|         	if (result) { | ||||
|         		Transaction txn = Transaction.currentTxn(); | ||||
|         		txn.start();         | ||||
|         		volume.setPath(cvAnswer.getVolumePath()); | ||||
|         		volume.setFolder(pool.getPath()); | ||||
|         		volume.setPodId(pool.getPodId()); | ||||
|         		volume.setPoolId(pool.getId());         | ||||
|         		volume.setPodId(pool.getPodId()); | ||||
|         		processEvent(volume, Event.CopySucceeded);  | ||||
|         		UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), null, volume.getSize()); | ||||
|         		_usageEventDao.persist(usageEvent); | ||||
|         		_volumeHostDao.remove(volumeHostVO.getId()); | ||||
|         		txn.commit(); | ||||
|         	} | ||||
|         	 | ||||
|         	success = result; | ||||
|         } catch (StorageUnavailableException e) { | ||||
|         	processEvent(volume, Event.CopyFailed); | ||||
|         	throw e; | ||||
|         } finally { | ||||
|         	if (!success) { | ||||
|         		processEvent(volume, Event.CopyFailed); | ||||
|         		throw new ConcurrentOperationException("Failed to copy volume: " + volume); | ||||
|         	} | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     @DB | ||||
|     public VolumeVO createVolume(VolumeVO volume, long VMTemplateId, DiskOfferingVO diskOffering, | ||||
|              HypervisorType hyperType, StoragePool assignedPool) { | ||||
|              HypervisorType hyperType, StoragePool assignedPool) throws StorageUnavailableException, ConcurrentOperationException { | ||||
|          Long existingPoolId = null; | ||||
|     	 existingPoolId = volume.getPoolId(); | ||||
|           | ||||
|     	 if (existingPoolId == null && assignedPool == null) { | ||||
|     		 throw new StorageUnavailableException("Volume has no pool associate and also no storage pool assigned in DeployDestination, Unable to create " + volume, Volume.class, volume.getId()); | ||||
|     		 throw new StorageUnavailableException("Volume has no pool associate and also no storage pool assigned in DeployDestination, Unable to create.",  | ||||
|     				 								Volume.class, volume.getId()); | ||||
|     	 } | ||||
|     	  | ||||
|     	 if (assignedPool == null) { | ||||
| @ -205,9 +292,12 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|     	 boolean needToCreateVolume = false; | ||||
|     	 boolean needToRecreateVolume = false; | ||||
|     	 boolean needToMigrateVolume = false; | ||||
|     	 boolean needToCopyFromSec = false; | ||||
|     	 Volume.State state = volume.getState(); | ||||
|     	 if (state == Volume.State.Allocated || state == Volume.State.Creating) { | ||||
|         	 needToCreateVolume = true; | ||||
|          } else if (state == Volume.State.UploadOp) { | ||||
|         	 needToCopyFromSec = true; | ||||
|          } | ||||
| 
 | ||||
|     	 if (volume.isRecreatable()) { | ||||
| @ -244,16 +334,15 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|     		 } | ||||
|     		 newVol = createVolume(newVol, diskOffering, hyperType, assignedPool); | ||||
|     	 } else if (needToMigrateVolume) { | ||||
|     		 try { | ||||
|         		 List<Volume> volumesToMigrate = new ArrayList<Volume>(); | ||||
|         		 volumesToMigrate.add(volume); | ||||
|         		 migrateVolumes(volumesToMigrate, assignedPool); | ||||
|         		 newVol = _volumeDao.findById(volume.getId()); | ||||
|         	 } catch (ConcurrentOperationException e) { | ||||
|         		 throw new CloudRuntimeException("Migration of volume " + volume + " to storage pool " + assignedPool + " failed", e); | ||||
|         	 } | ||||
|     		 List<Volume> volumesToMigrate = new ArrayList<Volume>(); | ||||
|     		 volumesToMigrate.add(volume); | ||||
|     		 migrateVolumes(volumesToMigrate, assignedPool); | ||||
|     		  | ||||
|          } else if (needToCopyFromSec) { | ||||
|         	 copyVolumeFromSec(volume, assignedPool); | ||||
|          } | ||||
|     	  | ||||
|     	 newVol = _volumeDao.findById(volume.getId()); | ||||
|     	 return newVol; | ||||
|     } | ||||
|      | ||||
| @ -267,13 +356,13 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|      * @throws StorageUnavailableException | ||||
|      */ | ||||
|     private VolumeVO createVolume(VolumeVO toBeCreated, DiskOfferingVO offering, HypervisorType hypervisorType, | ||||
|             StoragePool sPool) throws StorageUnavailableException { | ||||
|             StoragePool sPool) throws StorageUnavailableException, ConcurrentOperationException { | ||||
|         if (sPool == null) { | ||||
|         	throw new CloudRuntimeException("can't create volume: " + toBeCreated + " on a empty storage"); | ||||
|         	throw new StorageUnavailableException("can't create volume: " + toBeCreated + " on a empty storage", Volume.class, toBeCreated.getId()); | ||||
|         } | ||||
|          | ||||
|         if (toBeCreated == null) { | ||||
|         	throw new CloudRuntimeException("volume can't be null"); | ||||
|         	throw new StorageUnavailableException("volume can't be null", Volume.class, toBeCreated.getId()); | ||||
|         } | ||||
|          | ||||
|         if (s_logger.isDebugEnabled()) { | ||||
| @ -286,7 +375,7 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|         try { | ||||
|         	processEvent(toBeCreated, Volume.Event.CreateRequested); | ||||
|         } catch (NoTransitionException e) { | ||||
|         	throw new CloudRuntimeException("Unable to request a create operation on volume " + toBeCreated); | ||||
|         	throw new ConcurrentOperationException("Unable to request a create operation on volume " + toBeCreated); | ||||
|         } | ||||
| 
 | ||||
|         boolean createdSuccessed = false; | ||||
| @ -317,8 +406,7 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|         			processEvent(toBeCreated, Volume.Event.OperationSucceeded); | ||||
|         			createdSuccessed = true; | ||||
|         		} catch (NoTransitionException e) { | ||||
|         			s_logger.debug("Unable to update volume state: " + e.toString()); | ||||
|         			return null; | ||||
|         			throw new ConcurrentOperationException("Unable to update volume state: " + e.toString()); | ||||
|         		} | ||||
|         	} | ||||
|         	return toBeCreated; | ||||
| @ -327,7 +415,7 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|         		try { | ||||
|         			processEvent(toBeCreated, Volume.Event.OperationFailed); | ||||
|         		} catch (NoTransitionException e1) { | ||||
|         			s_logger.debug("Unable to update volume state: " + e1.toString()); | ||||
|         			throw new ConcurrentOperationException("Unable to update volume state: " + e1.toString()); | ||||
|         		} | ||||
|         	} | ||||
|         } | ||||
| @ -614,18 +702,30 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|      | ||||
|     @Override | ||||
|     public boolean volumeOnSharedStoragePool(VolumeVO volume) { | ||||
|     	boolean result = false; | ||||
|     	if (volume.getState() != Volume.State.Ready) { | ||||
|     		//if it's not ready, we don't care | ||||
|     		result = true; | ||||
|     	} | ||||
|     	 | ||||
|         Long poolId = volume.getPoolId(); | ||||
|         if (poolId == null) { | ||||
|             return false; | ||||
|             result = false; | ||||
|         } else { | ||||
|             StoragePoolVO pool = _storagePoolDao.findById(poolId); | ||||
| 
 | ||||
|             if (pool == null) { | ||||
|                 return false; | ||||
|                 result = false; | ||||
|             } else { | ||||
|                 return pool.isShared(); | ||||
|                 result = pool.isShared(); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         if (!result) { | ||||
|         	throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool."); | ||||
|         } | ||||
|          | ||||
|         return result; | ||||
|     } | ||||
|      | ||||
|     public String getRandomVolumeName() { | ||||
| @ -1263,6 +1363,9 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
|         HostTemplateStatesSearch.join("host", HostSearch, HostSearch.entity().getId(), HostTemplateStatesSearch.entity().getHostId(), JoinBuilder.JoinType.INNER); | ||||
|         HostSearch.done(); | ||||
|         HostTemplateStatesSearch.done(); | ||||
|          | ||||
|         String value = configDao.getValue(Config.CopyVolumeWait.toString()); | ||||
|         _copyvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue())); | ||||
| 		return false; | ||||
| 	} | ||||
| 	 | ||||
| @ -1287,4 +1390,177 @@ public class VolumeManagerImpl implements VolumeManager, Manager { | ||||
| 		// TODO Auto-generated method stub | ||||
| 		return null; | ||||
| 	} | ||||
| 
 | ||||
| 	 | ||||
| 	private int getMaxDataVolumesSupported(UserVmVO vm) { | ||||
| 		Long hostId = vm.getHostId(); | ||||
| 		if (hostId == null) { | ||||
| 			hostId = vm.getLastHostId(); | ||||
| 		} | ||||
| 		HostVO host = _hostDao.findById(hostId); | ||||
| 		Integer maxDataVolumesSupported = null; | ||||
| 		if (host != null) { | ||||
| 			_hostDao.loadDetails(host); | ||||
| 			maxDataVolumesSupported = _hypervisorCapabilitiesDao.getMaxDataVolumesLimit(host.getHypervisorType(), host.getDetail("product_version")); | ||||
| 		} | ||||
| 		if (maxDataVolumesSupported == null) { | ||||
| 			maxDataVolumesSupported = 6;  // 6 data disks by default if nothing is specified in 'hypervisor_capabilities' table | ||||
| 		} | ||||
| 
 | ||||
| 		return maxDataVolumesSupported.intValue(); | ||||
| 	} | ||||
| 
 | ||||
| 	private boolean reachMaxDataDisks(VolumeVO volume, UserVmVO vm) { | ||||
| 		List<VolumeVO> existingDataVolumes = _volumeDao.findByInstanceAndType(vm.getId(), 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."); | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	private boolean isVolumeHypevisorMatchVm(VolumeVO volume, UserVmVO vm) { | ||||
| 		HypervisorType dataDiskHyperType = _volumeDao.getHypervisorType(volume.getId()); | ||||
| 		if (dataDiskHyperType != HypervisorType.None && vm.getHypervisorType() != dataDiskHyperType) { | ||||
| 			throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + vm.getHypervisorType() + " vm"); | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	private boolean isVolumeReadyToAttach(VolumeVO volume) { | ||||
| 		if (!volume.isAttachedToVm()) { | ||||
| 			throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM."); | ||||
| 		} | ||||
| 
 | ||||
| 		if ( !(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()) || Volume.State.UploadOp.equals(volume.getState())) ) { | ||||
| 			throw new InvalidParameterValueException("Volume state must be in Allocated, Ready or in Uploaded state"); | ||||
| 		} | ||||
| 		 | ||||
| 		if (Volume.State.UploadOp.equals(volume.getState())) { | ||||
| 			VolumeHostVO  volHostVO = _volumeHostDao.findByVolumeId(volume.getId()); | ||||
| 			if (volHostVO != null) { | ||||
| 				if( !(Status.DOWNLOADED.equals(volHostVO.getDownloadState())) ) { | ||||
| 					throw new InvalidParameterValueException("Volume is not uploaded yet. Please try this operation once the volume is uploaded"); | ||||
| 				} | ||||
| 	        } | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	private VolumeVO prepareDataDisk(VolumeVO volume, UserVmVO vm, Long clusterId) throws ConcurrentOperationException, StorageUnavailableException { | ||||
| 		DataCenterVO dc = _dcDao.findById(vm.getDataCenterIdToDeployIn()); | ||||
| 		HostPodVO pod = _podDao.findById(vm.getPodIdToDeployIn()); | ||||
| 		DiskOfferingVO diskVO = _diskOfferingDao.findById(volume.getDiskOfferingId()); | ||||
|         DiskProfile diskProfile = new DiskProfile(volume, diskVO, vm.getHypervisorType()); | ||||
|          | ||||
|         StoragePoolVO destStoragePool = _storagePoolMgr.findStoragePool(diskProfile, dc, pod, clusterId, (VMInstanceVO)vm, new HashSet<StoragePool>()); | ||||
|         if (destStoragePool == null) { | ||||
|         	throw new StorageUnavailableException("No available storage in dc: " + dc.getId() + ", pod: " + pod.getId() +  | ||||
|         			", cluster " + clusterId, Volume.class, volume.getId()); | ||||
|         } | ||||
|          | ||||
|         return createVolume(volume, vm.getTemplateId(), diskVO, vm.getHypervisorType(), destStoragePool); | ||||
| 	} | ||||
| 	 | ||||
| 	private long getDeviceId(VolumeVO volume, UserVmVO vm, Long assignedId) { | ||||
| 		//allocate deviceId | ||||
|         List<VolumeVO> vols = _volumeDao.findByInstance(vm.getId()); | ||||
|         if (assignedId != null) { | ||||
|             if (assignedId.longValue() > 15 || assignedId.longValue() == 0 || assignedId.longValue() == 3) { | ||||
|                 throw new InvalidParameterValueException("deviceId should be 1,2,4-15"); | ||||
|             } | ||||
|             for (VolumeVO vol : vols) { | ||||
|                 if (vol.getDeviceId().equals(assignedId)) { | ||||
|                     throw new InvalidParameterValueException("deviceId " + assignedId + " is used by VM " + vm.getHostName()); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             // allocate deviceId here | ||||
|             List<String> devIds = new ArrayList<String>(); | ||||
|             for (int i = 1; i < 15; i++) { | ||||
|                 devIds.add(String.valueOf(i)); | ||||
|             } | ||||
|             devIds.remove("3"); | ||||
|             for (VolumeVO vol : vols) { | ||||
|                 devIds.remove(vol.getDeviceId().toString().trim()); | ||||
|             } | ||||
|             assignedId = Long.parseLong(devIds.iterator().next()); | ||||
|         } | ||||
|         return assignedId.longValue(); | ||||
| 	} | ||||
| 	 | ||||
| 	private VolumeVO sendAttachCmd(UserVmVO vm, VolumeVO volume, Long devId) { | ||||
| 		String errorMsg = "Failed to attach volume: " + volume.getName() + " to VM: " + vm.getHostName(); | ||||
|         boolean sendCommand = (vm.getState() == State.Running); | ||||
|         AttachVolumeAnswer answer = null; | ||||
|         Long hostId = vm.getHostId(); | ||||
|         if (hostId == null) { | ||||
|             hostId = vm.getLastHostId(); | ||||
|             HostVO host = _hostDao.findById(hostId); | ||||
|             if (host != null && host.getHypervisorType() == HypervisorType.VMware) { | ||||
|                 sendCommand = true; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         if (!sendCommand) { | ||||
|         	_volumeDao.attachVolume(volume.getId(), vm.getId(), devId); | ||||
|         	return _volumeDao.findById(volume.getId()); | ||||
|         } | ||||
| 
 | ||||
|         StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); | ||||
|         AttachVolumeCommand cmd = new AttachVolumeCommand(true, vm.getInstanceName(), volume.getPoolType(),  | ||||
|         								volume.getFolder(), volume.getPath(), volume.getName(), devId, volume.getChainInfo()); | ||||
|         cmd.setPoolUuid(volumePool.getUuid()); | ||||
| 
 | ||||
|         answer = (AttachVolumeAnswer) _agentMgr.send(hostId, cmd); | ||||
| 
 | ||||
|         if (answer != null && answer.getResult()) { | ||||
|         	_volumeDao.attachVolume(volume.getId(), vm.getId(), answer.getDeviceId()); | ||||
|         	return _volumeDao.findById(volume.getId()); | ||||
|         } else { | ||||
|         	if (answer != null) { | ||||
|         		String details = answer.getDetails(); | ||||
|         		if (details != null && !details.isEmpty()) { | ||||
|         			errorMsg += "; " + details; | ||||
|         		} | ||||
|         	} | ||||
|         	throw new CloudRuntimeException(errorMsg); | ||||
|         } | ||||
| 	} | ||||
| 	 | ||||
| 	@Override | ||||
| 	public Volume attachVolumeToVM(VolumeVO volume, UserVmVO vm, Long deviceId) { | ||||
| 		if (vm.getDataCenterIdToDeployIn() != volume.getDataCenterId()) { | ||||
| 			throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume."); | ||||
| 		} | ||||
| 
 | ||||
|         if (vm.getState() != State.Running && vm.getState() != State.Stopped) { | ||||
|             throw new InvalidParameterValueException("Please specify a VM that is either running or stopped."); | ||||
|         } | ||||
|          | ||||
|         long devId = getDeviceId(volume, vm, deviceId); | ||||
|          | ||||
|         isVolumeReadyToAttach(volume); | ||||
| 
 | ||||
|         reachMaxDataDisks(volume, vm); | ||||
|          | ||||
|         isVolumeHypevisorMatchVm(volume, vm); | ||||
|          | ||||
|         volumeOnSharedStoragePool(volume); | ||||
|          | ||||
| 		VolumeVO rootVolumeOfVm = null; | ||||
| 		List<VolumeVO> rootVolumesOfVm = _volumeDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT); | ||||
| 
 | ||||
| 		rootVolumeOfVm = rootVolumesOfVm.get(0); | ||||
| 
 | ||||
| 		if (rootVolumeOfVm.getState() == Volume.State.Allocated) { | ||||
| 			return volume; | ||||
| 		} | ||||
| 		StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); | ||||
|         Long clusterId = rootDiskPool.getClusterId(); | ||||
| 		 | ||||
| 		 | ||||
|         volume = prepareDataDisk(volume, vm, clusterId); | ||||
| 
 | ||||
|         volume = sendAttachCmd(vm, volume, devId); | ||||
|         return volume; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -508,286 +508,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager | ||||
|             return status; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private int getMaxDataVolumesSupported(UserVmVO vm) { | ||||
|         Long hostId = vm.getHostId(); | ||||
|         if (hostId == null) { | ||||
|             hostId = vm.getLastHostId(); | ||||
|         } | ||||
|         HostVO host = _hostDao.findById(hostId); | ||||
|         Integer maxDataVolumesSupported = null; | ||||
|         if (host != null) { | ||||
|             _hostDao.loadDetails(host); | ||||
|             maxDataVolumesSupported = _hypervisorCapabilitiesDao.getMaxDataVolumesLimit(host.getHypervisorType(), host.getDetail("product_version")); | ||||
|         } | ||||
|         if (maxDataVolumesSupported == null) { | ||||
|             maxDataVolumesSupported = 6;  // 6 data disks by default if nothing is specified in 'hypervisor_capabilities' table | ||||
|         } | ||||
| 
 | ||||
|         return maxDataVolumesSupported.intValue(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) | ||||
|     public Volume attachVolumeToVM(AttachVolumeCmd command) { | ||||
|         Long vmId = command.getVirtualMachineId(); | ||||
|         Long volumeId = command.getId(); | ||||
|         Long deviceId = command.getDeviceId(); | ||||
|         Account caller = UserContext.current().getCaller(); | ||||
| 
 | ||||
|         // Check that the volume ID is valid | ||||
|         VolumeVO volume = _volsDao.findById(volumeId); | ||||
|         // Check that the volume is a data volume | ||||
|         if (volume == null || volume.getVolumeType() != Volume.Type.DATADISK) { | ||||
|             throw new InvalidParameterValueException("Please specify a valid data volume."); | ||||
|         } | ||||
| 
 | ||||
|         // Check that the volume is not currently attached to any VM | ||||
|         if (volume.getInstanceId() != null) { | ||||
|             throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM."); | ||||
|         } | ||||
| 
 | ||||
|         // Check that the volume is not destroyed | ||||
|         if (volume.getState() == Volume.State.Destroy) { | ||||
|             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 = _vmDao.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.getDataCenterIdToDeployIn() != volume.getDataCenterId()) { | ||||
|             throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume."); | ||||
|         } | ||||
| 
 | ||||
|         //permission check | ||||
|         _accountMgr.checkAccess(caller, null, true, volume, vm); | ||||
| 
 | ||||
|         //Check if volume is stored on secondary Storage. | ||||
|         boolean isVolumeOnSec = false; | ||||
|         VolumeHostVO  volHostVO = _volumeHostDao.findByVolumeId(volume.getId()); | ||||
|         if (volHostVO != null){ | ||||
|             isVolumeOnSec = true; | ||||
|             if( !(volHostVO.getDownloadState() == Status.DOWNLOADED) ){ | ||||
|                 throw new InvalidParameterValueException("Volume is not uploaded yet. Please try this operation once the volume is uploaded"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //If the volume is Ready, check that the volume is stored on shared storage | ||||
|         if (!(Volume.State.Allocated.equals(volume.getState()) || Volume.State.UploadOp.equals(volume.getState())) && !_storageMgr.volumeOnSharedStoragePool(volume)) { | ||||
|             throw new InvalidParameterValueException("Please specify a volume that has been created on a shared storage pool."); | ||||
|         } | ||||
| 
 | ||||
|         if ( !(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()) || Volume.State.UploadOp.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()); | ||||
|         if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) { | ||||
|             throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm"); | ||||
|         } | ||||
| 
 | ||||
|         //allocate deviceId | ||||
|         List<VolumeVO> vols = _volsDao.findByInstance(vmId); | ||||
|         if (deviceId != null) { | ||||
|             if (deviceId.longValue() > 15 || deviceId.longValue() == 0 || deviceId.longValue() == 3) { | ||||
|                 throw new RuntimeException("deviceId should be 1,2,4-15"); | ||||
|             } | ||||
|             for (VolumeVO vol : vols) { | ||||
|                 if (vol.getDeviceId().equals(deviceId)) { | ||||
|                     throw new RuntimeException("deviceId " + deviceId + " is used by VM " + vm.getHostName()); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             // allocate deviceId here | ||||
|             List<String> devIds = new ArrayList<String>(); | ||||
|             for (int i = 1; i < 15; i++) { | ||||
|                 devIds.add(String.valueOf(i)); | ||||
|             } | ||||
|             devIds.remove("3"); | ||||
|             for (VolumeVO vol : vols) { | ||||
|                 devIds.remove(vol.getDeviceId().toString().trim()); | ||||
|             } | ||||
|             deviceId = Long.parseLong(devIds.iterator().next()); | ||||
|         } | ||||
| 
 | ||||
|         boolean createVolumeOnBackend = true; | ||||
|         if (rootVolumeOfVm.getState() == Volume.State.Allocated) { | ||||
|             createVolumeOnBackend = false; | ||||
|             if(isVolumeOnSec){ | ||||
|                 throw new CloudRuntimeException("Cant attach uploaded volume to the vm which is not created. Please start it and then retry"); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         //create volume on the backend only when vm's root volume is allocated | ||||
|         if (createVolumeOnBackend) { | ||||
|             if (volume.getState().equals(Volume.State.Allocated) || isVolumeOnSec) { | ||||
|                 /* Need to create the volume */ | ||||
|                 VMTemplateVO rootDiskTmplt = _templateDao.findById(vm.getTemplateId()); | ||||
|                 DataCenterVO dcVO = _dcDao.findById(vm.getDataCenterIdToDeployIn()); | ||||
|                 HostPodVO pod = _podDao.findById(vm.getPodIdToDeployIn()); | ||||
|                 StoragePoolVO rootDiskPool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); | ||||
|                 ServiceOfferingVO svo = _serviceOfferingDao.findById(vm.getServiceOfferingId()); | ||||
|                 DiskOfferingVO diskVO = _diskOfferingDao.findById(volume.getDiskOfferingId()); | ||||
|                 Long clusterId = (rootDiskPool == null ? null : rootDiskPool.getClusterId()); | ||||
| 
 | ||||
|                 if (!isVolumeOnSec){ | ||||
|                     volume = _storageMgr.createVolume(volume, vm, rootDiskTmplt, dcVO, pod, clusterId, svo, diskVO, new ArrayList<StoragePoolVO>(), volume.getSize(), rootDiskHyperType); | ||||
|                 }else { | ||||
|                     try { | ||||
|                         // Format of data disk should be the same as root disk | ||||
|                         if( ! volHostVO.getFormat().getFileExtension().equals(_storageMgr.getSupportedImageFormatForCluster(rootDiskPool.getClusterId())) ){ | ||||
|                             throw new InvalidParameterValueException("Failed to attach volume to VM since volumes format " +volHostVO.getFormat().getFileExtension() + " is not compatible with the vm hypervisor type" ); | ||||
|                         } | ||||
| 
 | ||||
|                         // Check that there is some shared storage. | ||||
|                         StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId());                                            | ||||
|                         List<StoragePoolVO> sharedVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), null, true); | ||||
|                         if (sharedVMPools.size() == 0) { | ||||
|                             throw new CloudRuntimeException("Cannot attach volume since there are no shared storage pools in the VM's cluster to copy the uploaded volume to."); | ||||
|                         } | ||||
| 
 | ||||
|                         volume = _storageMgr.copyVolumeFromSecToPrimary(volume, vm, rootDiskTmplt, dcVO, pod, rootDiskPool.getClusterId(), svo, diskVO, new ArrayList<StoragePoolVO>(), volume.getSize(), rootDiskHyperType); | ||||
|                     } catch (NoTransitionException e) { | ||||
|                         throw new CloudRuntimeException("Unable to transition the volume ",e); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (volume == null) { | ||||
|                     throw new CloudRuntimeException("Failed to create volume when attaching it to VM: " + vm.getHostName()); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId()); | ||||
|             DiskOfferingVO volumeDiskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); | ||||
|             String[] volumeTags = volumeDiskOffering.getTagsArray(); | ||||
| 
 | ||||
|             StoragePoolVO sourcePool = _storagePoolDao.findById(volume.getPoolId()); | ||||
|             List<StoragePoolVO> sharedVMPools = _storagePoolDao.findPoolsByTags(vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), volumeTags, true); | ||||
|             boolean moveVolumeNeeded = true; | ||||
|             if (sharedVMPools.size() == 0) { | ||||
|                 String poolType; | ||||
|                 if (vmRootVolumePool.getClusterId() != null) { | ||||
|                     poolType = "cluster"; | ||||
|                 } else if (vmRootVolumePool.getPodId() != null) { | ||||
|                     poolType = "pod"; | ||||
|                 } else { | ||||
|                     poolType = "zone"; | ||||
|                 } | ||||
|                 throw new CloudRuntimeException("There are no storage pools in the VM's " + poolType + " with all of the volume's tags (" + volumeDiskOffering.getTags() + ")."); | ||||
|             } else { | ||||
|                 Long sourcePoolDcId = sourcePool.getDataCenterId(); | ||||
|                 Long sourcePoolPodId = sourcePool.getPodId(); | ||||
|                 Long sourcePoolClusterId = sourcePool.getClusterId(); | ||||
|                 for (StoragePoolVO vmPool : sharedVMPools) { | ||||
|                     Long vmPoolDcId = vmPool.getDataCenterId(); | ||||
|                     Long vmPoolPodId = vmPool.getPodId(); | ||||
|                     Long vmPoolClusterId = vmPool.getClusterId(); | ||||
| 
 | ||||
|                     if (sourcePoolDcId == vmPoolDcId && sourcePoolPodId == vmPoolPodId && sourcePoolClusterId == vmPoolClusterId) { | ||||
|                         moveVolumeNeeded = false; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (moveVolumeNeeded) { | ||||
|                 // Move the volume to a storage pool in the VM's zone, pod, or cluster | ||||
|                 try { | ||||
|                     volume = _storageMgr.moveVolume(volume, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(), dataDiskHyperType); | ||||
|                 } catch (ConcurrentOperationException e) { | ||||
|                     throw new CloudRuntimeException(e.toString()); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         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() + " progress status"); | ||||
|             } | ||||
| 
 | ||||
|             _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId); | ||||
|             _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId); | ||||
|         } | ||||
| 
 | ||||
|         String errorMsg = "Failed to attach volume: " + volume.getName() + " to VM: " + vm.getHostName(); | ||||
|         boolean sendCommand = (vm.getState() == State.Running); | ||||
|         AttachVolumeAnswer answer = null; | ||||
|         Long hostId = vm.getHostId(); | ||||
|         if (hostId == null) { | ||||
|             hostId = vm.getLastHostId(); | ||||
|             HostVO host = _hostDao.findById(hostId); | ||||
|             if (host != null && host.getHypervisorType() == HypervisorType.VMware) { | ||||
|                 sendCommand = true; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (sendCommand) { | ||||
|             StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId()); | ||||
|             AttachVolumeCommand cmd = new AttachVolumeCommand(true, vm.getInstanceName(), volume.getPoolType(), volume.getFolder(), volume.getPath(), volume.getName(), deviceId, volume.getChainInfo()); | ||||
|             cmd.setPoolUuid(volumePool.getUuid()); | ||||
| 
 | ||||
|             try { | ||||
|                 answer = (AttachVolumeAnswer) _agentMgr.send(hostId, cmd); | ||||
|             } catch (Exception e) { | ||||
|                 throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage()); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (!sendCommand || (answer != null && answer.getResult())) { | ||||
|             // Mark the volume as attached | ||||
|             if (sendCommand) { | ||||
|                 _volsDao.attachVolume(volume.getId(), vmId, answer.getDeviceId()); | ||||
|             } else { | ||||
|                 _volsDao.attachVolume(volume.getId(), vmId, deviceId); | ||||
|             } | ||||
|             return _volsDao.findById(volumeId); | ||||
|         } else { | ||||
|             if (answer != null) { | ||||
|                 String details = answer.getDetails(); | ||||
|                 if (details != null && !details.isEmpty()) { | ||||
|                     errorMsg += "; " + details; | ||||
|                 } | ||||
|             } | ||||
|             throw new CloudRuntimeException(errorMsg); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     @Override | ||||
|     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true) | ||||
|     public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user