133133import com .cloud .dc .DataCenter ;
134134import com .cloud .dc .DataCenterVO ;
135135import com .cloud .dc .Pod ;
136+ import com .cloud .dc .dao .ClusterDao ;
136137import com .cloud .dc .dao .DataCenterDao ;
138+ import com .cloud .dc .dao .HostPodDao ;
137139import com .cloud .domain .Domain ;
138140import com .cloud .domain .dao .DomainDao ;
139141import com .cloud .event .ActionEvent ;
153155import com .cloud .hypervisor .HypervisorCapabilitiesVO ;
154156import com .cloud .hypervisor .dao .HypervisorCapabilitiesDao ;
155157import com .cloud .offering .DiskOffering ;
158+ import com .cloud .org .Cluster ;
156159import com .cloud .org .Grouping ;
157160import com .cloud .projects .Project ;
158161import com .cloud .projects .ProjectManager ;
@@ -323,6 +326,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
323326 @ Inject
324327 private VmWorkJobDao _workJobDao ;
325328 @ Inject
329+ ClusterDao clusterDao ;
330+ @ Inject
326331 private ClusterDetailsDao _clusterDetailsDao ;
327332 @ Inject
328333 private StorageManager storageMgr ;
@@ -346,6 +351,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
346351 protected ProjectManager projectManager ;
347352 @ Inject
348353 protected StoragePoolDetailsDao storagePoolDetailsDao ;
354+ @ Inject
355+ HostPodDao podDao ;
349356
350357
351358 protected Gson _gson ;
@@ -2380,25 +2387,18 @@ public Volume attachVolumeToVM(AttachVolumeCmd command) {
23802387 return attachVolumeToVM (command .getVirtualMachineId (), command .getId (), command .getDeviceId ());
23812388 }
23822389
2383- private Volume orchestrateAttachVolumeToVM (Long vmId , Long volumeId , Long deviceId ) {
2384- VolumeInfo volumeToAttach = volFactory .getVolume (volumeId );
2385-
2386- if (volumeToAttach .isAttachedVM ()) {
2387- throw new CloudRuntimeException ("This volume is already attached to a VM." );
2388- }
2389-
2390- UserVmVO vm = _userVmDao .findById (vmId );
2390+ protected VolumeVO getVmExistingVolumeForVolumeAttach (UserVmVO vm , VolumeInfo volumeToAttach ) {
23912391 VolumeVO existingVolumeOfVm = null ;
23922392 VMTemplateVO template = _templateDao .findById (vm .getTemplateId ());
2393- List <VolumeVO > rootVolumesOfVm = _volsDao .findByInstanceAndType (vmId , Volume .Type .ROOT );
2393+ List <VolumeVO > rootVolumesOfVm = _volsDao .findByInstanceAndType (vm . getId () , Volume .Type .ROOT );
23942394 if (rootVolumesOfVm .size () > 1 && template != null && !template .isDeployAsIs ()) {
23952395 throw new CloudRuntimeException ("The VM " + vm .getHostName () + " has more than one ROOT volume and is in an invalid state." );
23962396 } else {
23972397 if (!rootVolumesOfVm .isEmpty ()) {
23982398 existingVolumeOfVm = rootVolumesOfVm .get (0 );
23992399 } else {
24002400 // locate data volume of the vm
2401- List <VolumeVO > diskVolumesOfVm = _volsDao .findByInstanceAndType (vmId , Volume .Type .DATADISK );
2401+ List <VolumeVO > diskVolumesOfVm = _volsDao .findByInstanceAndType (vm . getId () , Volume .Type .DATADISK );
24022402 for (VolumeVO diskVolume : diskVolumesOfVm ) {
24032403 if (diskVolume .getState () != Volume .State .Allocated ) {
24042404 existingVolumeOfVm = diskVolume ;
@@ -2407,45 +2407,91 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
24072407 }
24082408 }
24092409 }
2410- if (s_logger .isTraceEnabled ()) {
2411- String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s" ;
2412- if (existingVolumeOfVm != null ) {
2413- s_logger .trace (String .format (msg ,
2414- volumeToAttach .getName (), volumeToAttach .getUuid (),
2410+ if (existingVolumeOfVm == null ) {
2411+ if (s_logger .isTraceEnabled ()) {
2412+ s_logger .trace (String .format ("No existing volume found for VM (%s/%s) to attach volume %s/%s" ,
24152413 vm .getName (), vm .getUuid (),
2416- existingVolumeOfVm .getName (), existingVolumeOfVm .getUuid (),
2417- existingVolumeOfVm .getPoolId ()));
2414+ volumeToAttach .getName (), volumeToAttach .getUuid ()));
24182415 }
2416+ return null ;
24192417 }
2420-
2421- HypervisorType rootDiskHyperType = vm .getHypervisorType ();
2422- HypervisorType volumeToAttachHyperType = _volsDao .getHypervisorType (volumeToAttach .getId ());
2423-
2418+ if (s_logger .isTraceEnabled ()) {
2419+ String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s" ;
2420+ s_logger .trace (String .format (msg ,
2421+ volumeToAttach .getName (), volumeToAttach .getUuid (),
2422+ vm .getName (), vm .getUuid (),
2423+ existingVolumeOfVm .getName (), existingVolumeOfVm .getUuid (),
2424+ existingVolumeOfVm .getPoolId ()));
2425+ }
2426+ return existingVolumeOfVm ;
2427+ }
2428+
2429+ protected StoragePool getSuitablePoolForAllocatedOrUploadedVolumeForAttach (final VolumeInfo volumeToAttach , final UserVmVO vm ) {
2430+ DataCenter zone = _dcDao .findById (vm .getDataCenterId ());
2431+ Pair <Long , Long > clusterHostId = virtualMachineManager .findClusterAndHostIdForVm (vm , false );
2432+ Long podId = vm .getPodIdToDeployIn ();
2433+ if (clusterHostId .first () != null ) {
2434+ Cluster cluster = clusterDao .findById (clusterHostId .first ());
2435+ podId = cluster .getPodId ();
2436+ }
2437+ Pod pod = podDao .findById (podId );
2438+ DiskOfferingVO offering = _diskOfferingDao .findById (volumeToAttach .getDiskOfferingId ());
2439+ DiskProfile diskProfile = new DiskProfile (volumeToAttach .getId (), volumeToAttach .getVolumeType (),
2440+ volumeToAttach .getName (), volumeToAttach .getId (), volumeToAttach .getSize (), offering .getTagsArray (),
2441+ offering .isUseLocalStorage (), offering .isRecreatable (),
2442+ volumeToAttach .getTemplateId ());
2443+ diskProfile .setHyperType (vm .getHypervisorType ());
2444+ return _volumeMgr .findStoragePool (diskProfile , zone , pod , clusterHostId .first (),
2445+ clusterHostId .second (), vm , Collections .emptySet ());
2446+ }
2447+
2448+ protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded (final VolumeInfo volumeToAttach , final UserVmVO vm , VolumeVO existingVolumeOfVm ) {
24242449 VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach ;
2425-
2450+ boolean volumeOnSecondary = volumeToAttach .getState () == Volume .State .Uploaded ;
2451+ if (!Arrays .asList (Volume .State .Allocated , Volume .State .Uploaded ).contains (volumeToAttach .getState ())) {
2452+ return newVolumeOnPrimaryStorage ;
2453+ }
24262454 //don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
2427- StoragePoolVO destPrimaryStorage = null ;
2455+ StoragePool destPrimaryStorage = null ;
24282456 if (existingVolumeOfVm != null && !existingVolumeOfVm .getState ().equals (Volume .State .Allocated )) {
24292457 destPrimaryStorage = _storagePoolDao .findById (existingVolumeOfVm .getPoolId ());
24302458 if (s_logger .isTraceEnabled () && destPrimaryStorage != null ) {
24312459 s_logger .trace (String .format ("decided on target storage: %s/%s" , destPrimaryStorage .getName (), destPrimaryStorage .getUuid ()));
24322460 }
24332461 }
2434-
2435- boolean volumeOnSecondary = volumeToAttach .getState () == Volume .State .Uploaded ;
2436-
2437- if (destPrimaryStorage != null && (volumeToAttach .getState () == Volume .State .Allocated || volumeOnSecondary )) {
2438- try {
2439- if (volumeOnSecondary && destPrimaryStorage .getPoolType () == Storage .StoragePoolType .PowerFlex ) {
2440- throw new InvalidParameterValueException ("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage .getPoolType ());
2462+ if (destPrimaryStorage == null ) {
2463+ destPrimaryStorage = getSuitablePoolForAllocatedOrUploadedVolumeForAttach (volumeToAttach , vm );
2464+ if (destPrimaryStorage == null ) {
2465+ if (Volume .State .Allocated .equals (volumeToAttach .getState ()) && State .Stopped .equals (vm .getState ())) {
2466+ return newVolumeOnPrimaryStorage ;
24412467 }
2442- newVolumeOnPrimaryStorage = _volumeMgr .createVolumeOnPrimaryStorage (vm , volumeToAttach , rootDiskHyperType , destPrimaryStorage );
2443- } catch (NoTransitionException e ) {
2444- s_logger .debug ("Failed to create volume on primary storage" , e );
2445- throw new CloudRuntimeException ("Failed to create volume on primary storage" , e );
2468+ throw new CloudRuntimeException (String .format ("Failed to find a primary storage for volume in state: %s" , volumeToAttach .getState ()));
2469+ }
2470+ }
2471+ try {
2472+ if (volumeOnSecondary && Storage .StoragePoolType .PowerFlex .equals (destPrimaryStorage .getPoolType ())) {
2473+ throw new InvalidParameterValueException ("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage .getPoolType ());
24462474 }
2475+ newVolumeOnPrimaryStorage = _volumeMgr .createVolumeOnPrimaryStorage (vm , volumeToAttach ,
2476+ vm .getHypervisorType (), destPrimaryStorage );
2477+ } catch (NoTransitionException e ) {
2478+ s_logger .debug ("Failed to create volume on primary storage" , e );
2479+ throw new CloudRuntimeException ("Failed to create volume on primary storage" , e );
2480+ }
2481+ return newVolumeOnPrimaryStorage ;
2482+ }
2483+
2484+ private Volume orchestrateAttachVolumeToVM (Long vmId , Long volumeId , Long deviceId ) {
2485+ VolumeInfo volumeToAttach = volFactory .getVolume (volumeId );
2486+
2487+ if (volumeToAttach .isAttachedVM ()) {
2488+ throw new CloudRuntimeException ("This volume is already attached to a VM." );
24472489 }
24482490
2491+ UserVmVO vm = _userVmDao .findById (vmId );
2492+ VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach (vm , volumeToAttach );
2493+ VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnPrimaryForAttachIfNeeded (volumeToAttach , vm , existingVolumeOfVm );
2494+
24492495 // reload the volume from db
24502496 newVolumeOnPrimaryStorage = volFactory .getVolume (newVolumeOnPrimaryStorage .getId ());
24512497 boolean moveVolumeNeeded = needMoveVolume (existingVolumeOfVm , newVolumeOnPrimaryStorage );
@@ -2463,19 +2509,17 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
24632509 StoragePoolVO vmRootVolumePool = _storagePoolDao .findById (existingVolumeOfVm .getPoolId ());
24642510
24652511 try {
2512+ HypervisorType volumeToAttachHyperType = _volsDao .getHypervisorType (volumeToAttach .getId ());
24662513 newVolumeOnPrimaryStorage = _volumeMgr .moveVolume (newVolumeOnPrimaryStorage , vmRootVolumePool .getDataCenterId (), vmRootVolumePool .getPodId (), vmRootVolumePool .getClusterId (),
24672514 volumeToAttachHyperType );
2468- } catch (ConcurrentOperationException e ) {
2469- s_logger .debug ("move volume failed" , e );
2470- throw new CloudRuntimeException ("move volume failed" , e );
2471- } catch (StorageUnavailableException e ) {
2515+ } catch (ConcurrentOperationException | StorageUnavailableException e ) {
24722516 s_logger .debug ("move volume failed" , e );
24732517 throw new CloudRuntimeException ("move volume failed" , e );
24742518 }
24752519 }
24762520 VolumeVO newVol = _volsDao .findById (newVolumeOnPrimaryStorage .getId ());
24772521 // Getting the fresh vm object in case of volume migration to check the current state of VM
2478- if (moveVolumeNeeded || volumeOnSecondary ) {
2522+ if (moveVolumeNeeded ) {
24792523 vm = _userVmDao .findById (vmId );
24802524 if (vm == null ) {
24812525 throw new InvalidParameterValueException ("VM not found." );
@@ -2659,9 +2703,6 @@ private void checkDeviceId(Long deviceId, VolumeInfo volumeToAttach, UserVmVO vm
26592703 if (!_volsDao .findByInstanceAndDeviceId (vm .getId (), 0 ).isEmpty ()) {
26602704 throw new InvalidParameterValueException ("Vm already has root volume attached to it" );
26612705 }
2662- if (volumeToAttach .getState () == Volume .State .Uploaded ) {
2663- throw new InvalidParameterValueException ("No support for Root volume attach in state " + Volume .State .Uploaded );
2664- }
26652706 }
26662707 }
26672708
0 commit comments