3131
3232import javax .inject .Inject ;
3333
34+ import com .cloud .exception .StorageUnavailableException ;
35+ import org .apache .cloudstack .context .CallContext ;
3436import org .apache .cloudstack .engine .orchestration .service .StorageOrchestrationService ;
3537import org .apache .cloudstack .engine .subsystem .api .storage .CopyCommandResult ;
3638import org .apache .cloudstack .engine .subsystem .api .storage .CreateCmdResult ;
6769import org .apache .cloudstack .storage .image .datastore .ImageStoreEntity ;
6870import org .apache .cloudstack .storage .image .store .TemplateObject ;
6971import org .apache .cloudstack .storage .to .TemplateObjectTO ;
72+ import org .apache .commons .collections .CollectionUtils ;
7073import org .apache .commons .lang3 .StringUtils ;
7174import org .apache .logging .log4j .Logger ;
7275import org .apache .logging .log4j .LogManager ;
76+ import org .apache .logging .log4j .ThreadContext ;
7377import org .springframework .stereotype .Component ;
7478
7579import com .cloud .agent .api .Answer ;
@@ -567,10 +571,7 @@ public void handleTemplateSync(DataStore store) {
567571 }
568572
569573 if (availHypers .contains (tmplt .getHypervisorType ())) {
570- boolean copied = isCopyFromOtherStoragesEnabled (zoneId ) && tryCopyingTemplateToImageStore (tmplt , store );
571- if (!copied ) {
572- tryDownloadingTemplateToImageStore (tmplt , store );
573- }
574+ storageOrchestrator .orchestrateTemplateCopyFromSecondaryStores (tmplt .getId (), store );
574575 } else {
575576 logger .info ("Skip downloading template {} since current data center does not have hypervisor {}" , tmplt , tmplt .getHypervisorType ());
576577 }
@@ -617,6 +618,16 @@ public void handleTemplateSync(DataStore store) {
617618
618619 }
619620
621+ @ Override
622+ public void handleTemplateCopyFromSecondaryStores (long templateId , DataStore destStore ) {
623+ VMTemplateVO template = _templateDao .findById (templateId );
624+ long zoneId = destStore .getScope ().getScopeId ();
625+ boolean copied = imageStoreDetailsUtil .isCopyTemplatesFromOtherStoragesEnabled (destStore .getId (), zoneId ) && tryCopyingTemplateToImageStore (template , destStore );
626+ if (!copied ) {
627+ tryDownloadingTemplateToImageStore (template , destStore );
628+ }
629+ }
630+
620631 protected void tryDownloadingTemplateToImageStore (VMTemplateVO tmplt , DataStore destStore ) {
621632 if (tmplt .getUrl () == null ) {
622633 logger .info ("Not downloading template [{}] to image store [{}], as it has no URL." , tmplt .getUniqueName (),
@@ -634,28 +645,134 @@ protected void tryDownloadingTemplateToImageStore(VMTemplateVO tmplt, DataStore
634645 }
635646
636647 protected boolean tryCopyingTemplateToImageStore (VMTemplateVO tmplt , DataStore destStore ) {
637- Long zoneId = destStore .getScope ().getScopeId ();
638- List <DataStore > storesInZone = _storeMgr .getImageStoresByZoneIds (zoneId );
639- for (DataStore sourceStore : storesInZone ) {
640- Map <String , TemplateProp > existingTemplatesInSourceStore = listTemplate (sourceStore );
641- if (existingTemplatesInSourceStore == null || !existingTemplatesInSourceStore .containsKey (tmplt .getUniqueName ())) {
642- logger .debug ("Template [{}] does not exist on image store [{}]; searching on another one." ,
643- tmplt .getUniqueName (), sourceStore .getName ());
648+ if (searchAndCopyWithinZone (tmplt , destStore )) {
649+ return true ;
650+ }
651+
652+ Long destZoneId = destStore .getScope ().getScopeId ();
653+ logger .debug ("Template [{}] not found in any image store of zone [{}]. Checking other zones." ,
654+ tmplt .getUniqueName (), destZoneId );
655+
656+ return searchAndCopyAcrossZones (tmplt , destStore , destZoneId );
657+ }
658+
659+ private boolean searchAndCopyAcrossZones (VMTemplateVO tmplt , DataStore destStore , Long destZoneId ) {
660+ List <Long > allZoneIds = _dcDao .listAllIds ();
661+ for (Long otherZoneId : allZoneIds ) {
662+ if (otherZoneId .equals (destZoneId )) {
644663 continue ;
645664 }
646- TemplateObject sourceTmpl = (TemplateObject ) _templateFactory .getTemplate (tmplt .getId (), sourceStore );
647- if (sourceTmpl .getInstallPath () == null ) {
648- logger .warn ("Can not copy template [{}] from image store [{}], as it returned a null install path." , tmplt .getUniqueName (),
649- sourceStore .getName ());
665+
666+ List <DataStore > storesInOtherZone = _storeMgr .getImageStoresByZoneIds (otherZoneId );
667+ logger .debug ("Checking zone [{}] for template [{}]..." , otherZoneId , tmplt .getUniqueName ());
668+
669+ if (CollectionUtils .isEmpty (storesInOtherZone )) {
670+ logger .debug ("Zone [{}] has no image stores. Skipping." , otherZoneId );
650671 continue ;
651672 }
652- storageOrchestrator .orchestrateTemplateCopyToImageStore (sourceTmpl , destStore );
653- return true ;
673+
674+ TemplateObject sourceTmpl = findUsableTemplate (tmplt , storesInOtherZone );
675+ if (sourceTmpl == null ) {
676+ logger .debug ("Template [{}] not found with a valid install path in any image store of zone [{}]." ,
677+ tmplt .getUniqueName (), otherZoneId );
678+ continue ;
679+ }
680+
681+ logger .info ("Template [{}] found in zone [{}]. Initiating cross-zone copy to zone [{}]." ,
682+ tmplt .getUniqueName (), otherZoneId , destZoneId );
683+
684+ return copyTemplateAcrossZones (destStore , sourceTmpl );
654685 }
655- logger .debug ("Can't copy template [{}] from another image store." , tmplt .getUniqueName ());
686+
687+ logger .debug ("Template [{}] was not found in any zone. Cannot perform zone-to-zone copy." , tmplt .getUniqueName ());
656688 return false ;
657689 }
658690
691+ protected TemplateObject findUsableTemplate (VMTemplateVO tmplt , List <DataStore > imageStores ) {
692+ for (DataStore store : imageStores ) {
693+
694+ Map <String , TemplateProp > templates = listTemplate (store );
695+ if (templates == null || !templates .containsKey (tmplt .getUniqueName ())) {
696+ continue ;
697+ }
698+
699+ TemplateObject tmpl = (TemplateObject ) _templateFactory .getTemplate (tmplt .getId (), store );
700+ if (tmpl .getInstallPath () == null ) {
701+ logger .debug ("Template [{}] found in image store [{}] but install path is null. Skipping." ,
702+ tmplt .getUniqueName (), store .getName ());
703+ continue ;
704+ }
705+ return tmpl ;
706+ }
707+ return null ;
708+ }
709+
710+ private boolean searchAndCopyWithinZone (VMTemplateVO tmplt , DataStore destStore ) {
711+ Long destZoneId = destStore .getScope ().getScopeId ();
712+ List <DataStore > storesInSameZone = _storeMgr .getImageStoresByZoneIds (destZoneId );
713+
714+ TemplateObject sourceTmpl = findUsableTemplate (tmplt , storesInSameZone );
715+ if (sourceTmpl == null ) {
716+ return false ;
717+ }
718+
719+ TemplateApiResult result ;
720+ AsyncCallFuture <TemplateApiResult > future = copyTemplateToImageStore (sourceTmpl , destStore );
721+ try {
722+ result = future .get ();
723+ } catch (ExecutionException | InterruptedException e ) {
724+ logger .warn ("Exception while copying template [{}] from image store [{}] to image store [{}]: {}" ,
725+ sourceTmpl .getUniqueName (), sourceTmpl .getDataStore ().getName (), destStore .getName (), e .toString ());
726+ result = new TemplateApiResult (sourceTmpl );
727+ result .setResult (e .getMessage ());
728+ }
729+ return result .isSuccess ();
730+ }
731+
732+ private boolean copyTemplateAcrossZones (DataStore destStore , TemplateObject sourceTmpl ) {
733+ Long dstZoneId = destStore .getScope ().getScopeId ();
734+ DataCenterVO dstZone = _dcDao .findById (dstZoneId );
735+
736+ if (dstZone == null ) {
737+ logger .warn ("Destination zone [{}] not found for template [{}]." , dstZoneId , sourceTmpl .getUniqueName ());
738+ return false ;
739+ }
740+
741+ TemplateApiResult result ;
742+ try {
743+ VMTemplateVO template = _templateDao .findById (sourceTmpl .getId ());
744+ try {
745+ DataStore sourceStore = sourceTmpl .getDataStore ();
746+ long userId = CallContext .current ().getCallingUserId ();
747+ boolean success = _tmpltMgr .copy (userId , template , sourceStore , dstZone );
748+
749+ result = new TemplateApiResult (sourceTmpl );
750+ if (!success ) {
751+ result .setResult ("Cross-zone template copy failed" );
752+ }
753+ } catch (StorageUnavailableException | ResourceAllocationException e ) {
754+ logger .error ("Exception while copying template [{}] from zone [{}] to zone [{}]" ,
755+ template ,
756+ sourceTmpl .getDataStore ().getScope ().getScopeId (),
757+ dstZone .getId (),
758+ e );
759+ result = new TemplateApiResult (sourceTmpl );
760+ result .setResult (e .getMessage ());
761+ } finally {
762+ ThreadContext .clearAll ();
763+ }
764+ } catch (Exception e ) {
765+ logger .error ("Failed to copy template [{}] from zone [{}] to zone [{}]." ,
766+ sourceTmpl .getUniqueName (),
767+ sourceTmpl .getDataStore ().getScope ().getScopeId (),
768+ dstZoneId ,
769+ e );
770+ return false ;
771+ }
772+
773+ return result .isSuccess ();
774+ }
775+
659776 @ Override
660777 public AsyncCallFuture <TemplateApiResult > copyTemplateToImageStore (DataObject source , DataStore destStore ) {
661778 TemplateObject sourceTmpl = (TemplateObject ) source ;
@@ -699,10 +816,6 @@ protected Void copyTemplateToImageStoreCallback(AsyncCallbackDispatcher<Template
699816 return null ;
700817 }
701818
702- protected boolean isCopyFromOtherStoragesEnabled (Long zoneId ) {
703- return StorageManager .COPY_PUBLIC_TEMPLATES_FROM_OTHER_STORAGES .valueIn (zoneId );
704- }
705-
706819 protected void publishTemplateCreation (TemplateInfo tmplt ) {
707820 VMTemplateVO tmpltVo = _templateDao .findById (tmplt .getId ());
708821
0 commit comments