135135import com .cloud .dc .DataCenter ;
136136import com .cloud .dc .DataCenterVO ;
137137import com .cloud .dc .Pod ;
138+ import com .cloud .dc .dao .ClusterDao ;
138139import com .cloud .dc .dao .DataCenterDao ;
140+ import com .cloud .dc .dao .HostPodDao ;
139141import com .cloud .domain .Domain ;
140142import com .cloud .domain .dao .DomainDao ;
141143import com .cloud .event .ActionEvent ;
155157import com .cloud .hypervisor .HypervisorCapabilitiesVO ;
156158import com .cloud .hypervisor .dao .HypervisorCapabilitiesDao ;
157159import com .cloud .offering .DiskOffering ;
160+ import com .cloud .org .Cluster ;
158161import com .cloud .org .Grouping ;
159162import com .cloud .projects .Project ;
160163import com .cloud .projects .ProjectManager ;
@@ -324,6 +327,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
324327 @ Inject
325328 private VmWorkJobDao _workJobDao ;
326329 @ Inject
330+ ClusterDao clusterDao ;
331+ @ Inject
327332 private ClusterDetailsDao _clusterDetailsDao ;
328333 @ Inject
329334 private StorageManager storageMgr ;
@@ -349,6 +354,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
349354 protected StoragePoolDetailsDao storagePoolDetailsDao ;
350355 @ Inject
351356 private BackupDao backupDao ;
357+ @ Inject
358+ HostPodDao podDao ;
352359
353360
354361 protected Gson _gson ;
@@ -2476,25 +2483,18 @@ public Volume attachVolumeToVM(AttachVolumeCmd command) {
24762483 return attachVolumeToVM (command .getVirtualMachineId (), command .getId (), command .getDeviceId (), false );
24772484 }
24782485
2479- private Volume orchestrateAttachVolumeToVM (Long vmId , Long volumeId , Long deviceId ) {
2480- VolumeInfo volumeToAttach = volFactory .getVolume (volumeId );
2481-
2482- if (volumeToAttach .isAttachedVM ()) {
2483- throw new CloudRuntimeException ("This volume is already attached to a VM." );
2484- }
2485-
2486- UserVmVO vm = _userVmDao .findById (vmId );
2486+ protected VolumeVO getVmExistingVolumeForVolumeAttach (UserVmVO vm , VolumeInfo volumeToAttach ) {
24872487 VolumeVO existingVolumeOfVm = null ;
24882488 VMTemplateVO template = _templateDao .findById (vm .getTemplateId ());
2489- List <VolumeVO > rootVolumesOfVm = _volsDao .findByInstanceAndType (vmId , Volume .Type .ROOT );
2489+ List <VolumeVO > rootVolumesOfVm = _volsDao .findByInstanceAndType (vm . getId () , Volume .Type .ROOT );
24902490 if (rootVolumesOfVm .size () > 1 && template != null && !template .isDeployAsIs ()) {
24912491 throw new CloudRuntimeException ("The VM " + vm .getHostName () + " has more than one ROOT volume and is in an invalid state." );
24922492 } else {
24932493 if (!rootVolumesOfVm .isEmpty ()) {
24942494 existingVolumeOfVm = rootVolumesOfVm .get (0 );
24952495 } else {
24962496 // locate data volume of the vm
2497- List <VolumeVO > diskVolumesOfVm = _volsDao .findByInstanceAndType (vmId , Volume .Type .DATADISK );
2497+ List <VolumeVO > diskVolumesOfVm = _volsDao .findByInstanceAndType (vm . getId () , Volume .Type .DATADISK );
24982498 for (VolumeVO diskVolume : diskVolumesOfVm ) {
24992499 if (diskVolume .getState () != Volume .State .Allocated ) {
25002500 existingVolumeOfVm = diskVolume ;
@@ -2503,40 +2503,94 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
25032503 }
25042504 }
25052505 }
2506- if (logger .isTraceEnabled ()) {
2507- if (existingVolumeOfVm != null ) {
2508- logger .trace ("attaching volume {} to a VM {} with an existing volume {} on primary storage {}" ,
2509- volumeToAttach , vm , existingVolumeOfVm , _storagePoolDao .findById (existingVolumeOfVm .getPoolId ()));
2506+ if (existingVolumeOfVm == null ) {
2507+ if (logger .isTraceEnabled ()) {
2508+ logger .trace (String .format ("No existing volume found for VM (%s/%s) to attach volume %s/%s" ,
2509+ vm .getName (), vm .getUuid (),
2510+ volumeToAttach .getName (), volumeToAttach .getUuid ()));
25102511 }
2512+ return null ;
25112513 }
2512-
2513- HypervisorType rootDiskHyperType = vm .getHypervisorType ();
2514- HypervisorType volumeToAttachHyperType = _volsDao .getHypervisorType (volumeToAttach .getId ());
2515-
2514+ if (logger .isTraceEnabled ()) {
2515+ String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s" ;
2516+ logger .trace (String .format (msg ,
2517+ volumeToAttach .getName (), volumeToAttach .getUuid (),
2518+ vm .getName (), vm .getUuid (),
2519+ existingVolumeOfVm .getName (), existingVolumeOfVm .getUuid (),
2520+ existingVolumeOfVm .getPoolId ()));
2521+ }
2522+ return existingVolumeOfVm ;
2523+ }
2524+
2525+ protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach (final VolumeInfo volumeToAttach , final UserVmVO vm ) {
2526+ DataCenter zone = _dcDao .findById (vm .getDataCenterId ());
2527+ Pair <Long , Long > clusterHostId = virtualMachineManager .findClusterAndHostIdForVm (vm , false );
2528+ long podId = vm .getPodIdToDeployIn ();
2529+ if (clusterHostId .first () != null ) {
2530+ Cluster cluster = clusterDao .findById (clusterHostId .first ());
2531+ podId = cluster .getPodId ();
2532+ }
2533+ Pod pod = podDao .findById (podId );
2534+ DiskOfferingVO offering = _diskOfferingDao .findById (volumeToAttach .getDiskOfferingId ());
2535+ DiskProfile diskProfile = new DiskProfile (volumeToAttach .getId (), volumeToAttach .getVolumeType (),
2536+ volumeToAttach .getName (), volumeToAttach .getId (), volumeToAttach .getSize (), offering .getTagsArray (),
2537+ offering .isUseLocalStorage (), offering .isRecreatable (),
2538+ volumeToAttach .getTemplateId ());
2539+ diskProfile .setHyperType (vm .getHypervisorType ());
2540+ StoragePool pool = _volumeMgr .findStoragePool (diskProfile , zone , pod , clusterHostId .first (),
2541+ clusterHostId .second (), vm , Collections .emptySet ());
2542+ if (pool == null ) {
2543+ throw new CloudRuntimeException (String .format ("Failed to find a primary storage for volume in state: %s" , volumeToAttach .getState ()));
2544+ }
2545+ return pool ;
2546+ }
2547+
2548+ protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded (final VolumeInfo volumeToAttach , final UserVmVO vm , VolumeVO existingVolumeOfVm ) {
25162549 VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach ;
2517-
2550+ boolean volumeOnSecondary = volumeToAttach .getState () == Volume .State .Uploaded ;
2551+ if (!Arrays .asList (Volume .State .Allocated , Volume .State .Uploaded ).contains (volumeToAttach .getState ())) {
2552+ return newVolumeOnPrimaryStorage ;
2553+ }
25182554 //don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
2519- StoragePoolVO destPrimaryStorage = null ;
2555+ StoragePool destPrimaryStorage = null ;
25202556 if (existingVolumeOfVm != null && !existingVolumeOfVm .getState ().equals (Volume .State .Allocated )) {
25212557 destPrimaryStorage = _storagePoolDao .findById (existingVolumeOfVm .getPoolId ());
25222558 if (logger .isTraceEnabled () && destPrimaryStorage != null ) {
25232559 logger .trace ("decided on target storage: {}" , destPrimaryStorage );
25242560 }
25252561 }
2526-
2527- boolean volumeOnSecondary = volumeToAttach .getState () == Volume .State .Uploaded ;
2528-
2529- if (destPrimaryStorage != null && (volumeToAttach .getState () == Volume .State .Allocated || volumeOnSecondary )) {
2530- try {
2531- if (volumeOnSecondary && destPrimaryStorage .getPoolType () == Storage .StoragePoolType .PowerFlex ) {
2532- throw new InvalidParameterValueException ("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage .getPoolType ());
2562+ if (destPrimaryStorage == null ) {
2563+ destPrimaryStorage = getPoolForAllocatedOrUploadedVolumeForAttach (volumeToAttach , vm );
2564+ if (destPrimaryStorage == null ) {
2565+ if (Volume .State .Allocated .equals (volumeToAttach .getState ()) && State .Stopped .equals (vm .getState ())) {
2566+ return newVolumeOnPrimaryStorage ;
25332567 }
2534- newVolumeOnPrimaryStorage = _volumeMgr .createVolumeOnPrimaryStorage (vm , volumeToAttach , rootDiskHyperType , destPrimaryStorage );
2535- } catch (NoTransitionException e ) {
2536- logger .debug ("Failed to create volume on primary storage" , e );
2537- throw new CloudRuntimeException ("Failed to create volume on primary storage" , e );
2568+ throw new CloudRuntimeException (String .format ("Failed to find a primary storage for volume in state: %s" , volumeToAttach .getState ()));
2569+ }
2570+ }
2571+ try {
2572+ if (volumeOnSecondary && Storage .StoragePoolType .PowerFlex .equals (destPrimaryStorage .getPoolType ())) {
2573+ throw new InvalidParameterValueException ("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage .getPoolType ());
25382574 }
2575+ newVolumeOnPrimaryStorage = _volumeMgr .createVolumeOnPrimaryStorage (vm , volumeToAttach ,
2576+ vm .getHypervisorType (), destPrimaryStorage );
2577+ } catch (NoTransitionException e ) {
2578+ s_logger .debug ("Failed to create volume on primary storage" , e );
2579+ throw new CloudRuntimeException ("Failed to create volume on primary storage" , e );
25392580 }
2581+ return newVolumeOnPrimaryStorage ;
2582+ }
2583+
2584+ private Volume orchestrateAttachVolumeToVM (Long vmId , Long volumeId , Long deviceId ) {
2585+ VolumeInfo volumeToAttach = volFactory .getVolume (volumeId );
2586+
2587+ if (volumeToAttach .isAttachedVM ()) {
2588+ throw new CloudRuntimeException ("This volume is already attached to a VM." );
2589+ }
2590+
2591+ UserVmVO vm = _userVmDao .findById (vmId );
2592+ VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach (vm , volumeToAttach );
2593+ VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnPrimaryForAttachIfNeeded (volumeToAttach , vm , existingVolumeOfVm );
25402594
25412595 // reload the volume from db
25422596 newVolumeOnPrimaryStorage = volFactory .getVolume (newVolumeOnPrimaryStorage .getId ());
@@ -2555,19 +2609,17 @@ private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long device
25552609 StoragePoolVO vmRootVolumePool = _storagePoolDao .findById (existingVolumeOfVm .getPoolId ());
25562610
25572611 try {
2612+ HypervisorType volumeToAttachHyperType = _volsDao .getHypervisorType (volumeToAttach .getId ());
25582613 newVolumeOnPrimaryStorage = _volumeMgr .moveVolume (newVolumeOnPrimaryStorage , vmRootVolumePool .getDataCenterId (), vmRootVolumePool .getPodId (), vmRootVolumePool .getClusterId (),
25592614 volumeToAttachHyperType );
2560- } catch (ConcurrentOperationException e ) {
2561- logger .debug ("move volume failed" , e );
2562- throw new CloudRuntimeException ("move volume failed" , e );
2563- } catch (StorageUnavailableException e ) {
2615+ } catch (ConcurrentOperationException | StorageUnavailableException e ) {
25642616 logger .debug ("move volume failed" , e );
25652617 throw new CloudRuntimeException ("move volume failed" , e );
25662618 }
25672619 }
25682620 VolumeVO newVol = _volsDao .findById (newVolumeOnPrimaryStorage .getId ());
25692621 // Getting the fresh vm object in case of volume migration to check the current state of VM
2570- if (moveVolumeNeeded || volumeOnSecondary ) {
2622+ if (moveVolumeNeeded ) {
25712623 vm = _userVmDao .findById (vmId );
25722624 if (vm == null ) {
25732625 throw new InvalidParameterValueException ("VM not found." );
@@ -2752,9 +2804,6 @@ private void checkDeviceId(Long deviceId, VolumeInfo volumeToAttach, UserVmVO vm
27522804 if (!_volsDao .findByInstanceAndDeviceId (vm .getId (), 0 ).isEmpty ()) {
27532805 throw new InvalidParameterValueException ("Vm already has root volume attached to it" );
27542806 }
2755- if (volumeToAttach .getState () == Volume .State .Uploaded ) {
2756- throw new InvalidParameterValueException ("No support for Root volume attach in state " + Volume .State .Uploaded );
2757- }
27582807 }
27592808 }
27602809
0 commit comments