102102import org .apache .cloudstack .storage .datastore .util .LinstorConfigurationManager ;
103103import org .apache .cloudstack .storage .datastore .util .LinstorUtil ;
104104import org .apache .cloudstack .storage .to .SnapshotObjectTO ;
105+ import org .apache .cloudstack .storage .to .VolumeObjectTO ;
105106import org .apache .cloudstack .storage .volume .VolumeObject ;
106107import org .apache .logging .log4j .Logger ;
107108import org .apache .logging .log4j .LogManager ;
@@ -798,6 +799,15 @@ private static boolean canCopyTemplateCond(DataObject srcData, DataObject dstDat
798799 || srcData .getDataStore ().getRole () == DataStoreRole .ImageCache );
799800 }
800801
802+ private static boolean canCopyVolumeCond (DataObject srcData , DataObject dstData ) {
803+ // Volume download from Linstor primary storage
804+ return srcData .getType () == DataObjectType .VOLUME
805+ && (dstData .getType () == DataObjectType .VOLUME || dstData .getType () == DataObjectType .TEMPLATE )
806+ && srcData .getDataStore ().getRole () == DataStoreRole .Primary
807+ && (dstData .getDataStore ().getRole () == DataStoreRole .Image
808+ || dstData .getDataStore ().getRole () == DataStoreRole .ImageCache );
809+ }
810+
801811 @ Override
802812 public boolean canCopy (DataObject srcData , DataObject dstData )
803813 {
@@ -814,6 +824,10 @@ public boolean canCopy(DataObject srcData, DataObject dstData)
814824 return storagePoolVO != null
815825 && storagePoolVO .getPoolType () == Storage .StoragePoolType .Linstor
816826 && tInfo .getSize () != null ;
827+ } else if (canCopyVolumeCond (srcData , dstData )) {
828+ VolumeInfo srcVolInfo = (VolumeInfo ) srcData ;
829+ StoragePoolVO storagePool = _storagePoolDao .findById (srcVolInfo .getPoolId ());
830+ return storagePool .getStorageProviderName ().equals (LinstorUtil .PROVIDER_NAME );
817831 }
818832 return false ;
819833 }
@@ -844,6 +858,9 @@ public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCal
844858 } else if (canCopyTemplateCond (srcData , dstData )) {
845859 Answer answer = copyTemplate (srcData , dstData );
846860 res = new CopyCommandResult (null , answer );
861+ } else if (canCopyVolumeCond (srcData , dstData )) {
862+ Answer answer = copyVolume (srcData , dstData );
863+ res = new CopyCommandResult (null , answer );
847864 } else {
848865 Answer answer = new Answer (null , false , "noimpl" );
849866 res = new CopyCommandResult (null , answer );
@@ -965,6 +982,43 @@ private Answer copyTemplate(DataObject srcData, DataObject dstData) {
965982 return answer ;
966983 }
967984
985+ private Answer copyVolume (DataObject srcData , DataObject dstData ) {
986+ VolumeInfo srcVolInfo = (VolumeInfo ) srcData ;
987+ final StoragePoolVO pool = _storagePoolDao .findById (srcVolInfo .getDataStore ().getId ());
988+ final DevelopersApi api = LinstorUtil .getLinstorAPI (pool .getHostAddress ());
989+ final String rscName = LinstorUtil .RSC_PREFIX + srcVolInfo .getUuid ();
990+
991+ VolumeObjectTO to = (VolumeObjectTO ) srcVolInfo .getTO ();
992+ // patch source format
993+ // Linstor volumes are stored as RAW, but we can't set the correct format as RAW (we use QCOW2)
994+ // otherwise create template from snapshot won't work, because this operation
995+ // uses the format of the base volume and we backup snapshots as QCOW2
996+ // https://github.com/apache/cloudstack/pull/8802#issuecomment-2024019927
997+ to .setFormat (Storage .ImageFormat .RAW );
998+ int nMaxExecutionSeconds = NumbersUtil .parseInt (
999+ _configDao .getValue (Config .CopyVolumeWait .key ()), 10800 );
1000+ CopyCommand cmd = new CopyCommand (
1001+ to ,
1002+ dstData .getTO (),
1003+ nMaxExecutionSeconds ,
1004+ VirtualMachineManager .ExecuteInSequence .value ());
1005+ Answer answer ;
1006+
1007+ try {
1008+ Optional <RemoteHostEndPoint > optEP = getLinstorEP (api , rscName );
1009+ if (optEP .isPresent ()) {
1010+ answer = optEP .get ().sendMessage (cmd );
1011+ }
1012+ else {
1013+ answer = new Answer (cmd , false , "Unable to get matching Linstor endpoint." );
1014+ }
1015+ } catch (ApiException exc ) {
1016+ logger .error ("copy volume failed: " , exc );
1017+ throw new CloudRuntimeException (exc .getBestMessage ());
1018+ }
1019+ return answer ;
1020+ }
1021+
9681022 /**
9691023 * Create a temporary resource from the snapshot to backup, so we can copy the data on a diskless agent
9701024 * @param api Linstor Developer api object
0 commit comments