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 ;
@@ -569,10 +573,7 @@ public void handleTemplateSync(DataStore store) {
569573 }
570574
571575 if (availHypers .contains (tmplt .getHypervisorType ())) {
572- boolean copied = isCopyFromOtherStoragesEnabled (zoneId ) && tryCopyingTemplateToImageStore (tmplt , store );
573- if (!copied ) {
574- tryDownloadingTemplateToImageStore (tmplt , store );
575- }
576+ storageOrchestrator .orchestrateTemplateCopyFromSecondaryStores (tmplt .getId (), store );
576577 } else {
577578 logger .info ("Skip downloading template {} since current data center does not have hypervisor {}" , tmplt , tmplt .getHypervisorType ());
578579 }
@@ -619,6 +620,16 @@ public void handleTemplateSync(DataStore store) {
619620
620621 }
621622
623+ @ Override
624+ public void handleTemplateCopyFromSecondaryStores (long templateId , DataStore destStore ) {
625+ VMTemplateVO template = _templateDao .findById (templateId );
626+ long zoneId = destStore .getScope ().getScopeId ();
627+ boolean copied = imageStoreDetailsUtil .isCopyTemplatesFromOtherStoragesEnabled (destStore .getId (), zoneId ) && tryCopyingTemplateToImageStore (template , destStore );
628+ if (!copied ) {
629+ tryDownloadingTemplateToImageStore (template , destStore );
630+ }
631+ }
632+
622633 protected void tryDownloadingTemplateToImageStore (VMTemplateVO tmplt , DataStore destStore ) {
623634 if (tmplt .getUrl () == null ) {
624635 logger .info ("Not downloading template [{}] to image store [{}], as it has no URL." , tmplt .getUniqueName (),
@@ -636,28 +647,134 @@ protected void tryDownloadingTemplateToImageStore(VMTemplateVO tmplt, DataStore
636647 }
637648
638649 protected boolean tryCopyingTemplateToImageStore (VMTemplateVO tmplt , DataStore destStore ) {
639- Long zoneId = destStore .getScope ().getScopeId ();
640- List <DataStore > storesInZone = _storeMgr .getImageStoresByZoneIds (zoneId );
641- for (DataStore sourceStore : storesInZone ) {
642- Map <String , TemplateProp > existingTemplatesInSourceStore = listTemplate (sourceStore );
643- if (existingTemplatesInSourceStore == null || !existingTemplatesInSourceStore .containsKey (tmplt .getUniqueName ())) {
644- logger .debug ("Template [{}] does not exist on image store [{}]; searching on another one." ,
645- tmplt .getUniqueName (), sourceStore .getName ());
650+ if (searchAndCopyWithinZone (tmplt , destStore )) {
651+ return true ;
652+ }
653+
654+ Long destZoneId = destStore .getScope ().getScopeId ();
655+ logger .debug ("Template [{}] not found in any image store of zone [{}]. Checking other zones." ,
656+ tmplt .getUniqueName (), destZoneId );
657+
658+ return searchAndCopyAcrossZones (tmplt , destStore , destZoneId );
659+ }
660+
661+ private boolean searchAndCopyAcrossZones (VMTemplateVO tmplt , DataStore destStore , Long destZoneId ) {
662+ List <Long > allZoneIds = _dcDao .listAllIds ();
663+ for (Long otherZoneId : allZoneIds ) {
664+ if (otherZoneId .equals (destZoneId )) {
646665 continue ;
647666 }
648- TemplateObject sourceTmpl = (TemplateObject ) _templateFactory .getTemplate (tmplt .getId (), sourceStore );
649- if (sourceTmpl .getInstallPath () == null ) {
650- logger .warn ("Can not copy template [{}] from image store [{}], as it returned a null install path." , tmplt .getUniqueName (),
651- sourceStore .getName ());
667+
668+ List <DataStore > storesInOtherZone = _storeMgr .getImageStoresByZoneIds (otherZoneId );
669+ logger .debug ("Checking zone [{}] for template [{}]..." , otherZoneId , tmplt .getUniqueName ());
670+
671+ if (CollectionUtils .isEmpty (storesInOtherZone )) {
672+ logger .debug ("Zone [{}] has no image stores. Skipping." , otherZoneId );
652673 continue ;
653674 }
654- storageOrchestrator .orchestrateTemplateCopyToImageStore (sourceTmpl , destStore );
655- return true ;
675+
676+ TemplateObject sourceTmpl = findUsableTemplate (tmplt , storesInOtherZone );
677+ if (sourceTmpl == null ) {
678+ logger .debug ("Template [{}] not found with a valid install path in any image store of zone [{}]." ,
679+ tmplt .getUniqueName (), otherZoneId );
680+ continue ;
681+ }
682+
683+ logger .info ("Template [{}] found in zone [{}]. Initiating cross-zone copy to zone [{}]." ,
684+ tmplt .getUniqueName (), otherZoneId , destZoneId );
685+
686+ return copyTemplateAcrossZones (destStore , sourceTmpl );
656687 }
657- logger .debug ("Can't copy template [{}] from another image store." , tmplt .getUniqueName ());
688+
689+ logger .debug ("Template [{}] was not found in any zone. Cannot perform zone-to-zone copy." , tmplt .getUniqueName ());
658690 return false ;
659691 }
660692
693+ protected TemplateObject findUsableTemplate (VMTemplateVO tmplt , List <DataStore > imageStores ) {
694+ for (DataStore store : imageStores ) {
695+
696+ Map <String , TemplateProp > templates = listTemplate (store );
697+ if (templates == null || !templates .containsKey (tmplt .getUniqueName ())) {
698+ continue ;
699+ }
700+
701+ TemplateObject tmpl = (TemplateObject ) _templateFactory .getTemplate (tmplt .getId (), store );
702+ if (tmpl .getInstallPath () == null ) {
703+ logger .debug ("Template [{}] found in image store [{}] but install path is null. Skipping." ,
704+ tmplt .getUniqueName (), store .getName ());
705+ continue ;
706+ }
707+ return tmpl ;
708+ }
709+ return null ;
710+ }
711+
712+ private boolean searchAndCopyWithinZone (VMTemplateVO tmplt , DataStore destStore ) {
713+ Long destZoneId = destStore .getScope ().getScopeId ();
714+ List <DataStore > storesInSameZone = _storeMgr .getImageStoresByZoneIds (destZoneId );
715+
716+ TemplateObject sourceTmpl = findUsableTemplate (tmplt , storesInSameZone );
717+ if (sourceTmpl == null ) {
718+ return false ;
719+ }
720+
721+ TemplateApiResult result ;
722+ AsyncCallFuture <TemplateApiResult > future = copyTemplateToImageStore (sourceTmpl , destStore );
723+ try {
724+ result = future .get ();
725+ } catch (ExecutionException | InterruptedException e ) {
726+ logger .warn ("Exception while copying template [{}] from image store [{}] to image store [{}]: {}" ,
727+ sourceTmpl .getUniqueName (), sourceTmpl .getDataStore ().getName (), destStore .getName (), e .toString ());
728+ result = new TemplateApiResult (sourceTmpl );
729+ result .setResult (e .getMessage ());
730+ }
731+ return result .isSuccess ();
732+ }
733+
734+ private boolean copyTemplateAcrossZones (DataStore destStore , TemplateObject sourceTmpl ) {
735+ Long dstZoneId = destStore .getScope ().getScopeId ();
736+ DataCenterVO dstZone = _dcDao .findById (dstZoneId );
737+
738+ if (dstZone == null ) {
739+ logger .warn ("Destination zone [{}] not found for template [{}]." , dstZoneId , sourceTmpl .getUniqueName ());
740+ return false ;
741+ }
742+
743+ TemplateApiResult result ;
744+ try {
745+ VMTemplateVO template = _templateDao .findById (sourceTmpl .getId ());
746+ try {
747+ DataStore sourceStore = sourceTmpl .getDataStore ();
748+ long userId = CallContext .current ().getCallingUserId ();
749+ boolean success = _tmpltMgr .copy (userId , template , sourceStore , dstZone );
750+
751+ result = new TemplateApiResult (sourceTmpl );
752+ if (!success ) {
753+ result .setResult ("Cross-zone template copy failed" );
754+ }
755+ } catch (StorageUnavailableException | ResourceAllocationException e ) {
756+ logger .error ("Exception while copying template [{}] from zone [{}] to zone [{}]" ,
757+ template ,
758+ sourceTmpl .getDataStore ().getScope ().getScopeId (),
759+ dstZone .getId (),
760+ e );
761+ result = new TemplateApiResult (sourceTmpl );
762+ result .setResult (e .getMessage ());
763+ } finally {
764+ ThreadContext .clearAll ();
765+ }
766+ } catch (Exception e ) {
767+ logger .error ("Failed to copy template [{}] from zone [{}] to zone [{}]." ,
768+ sourceTmpl .getUniqueName (),
769+ sourceTmpl .getDataStore ().getScope ().getScopeId (),
770+ dstZoneId ,
771+ e );
772+ return false ;
773+ }
774+
775+ return result .isSuccess ();
776+ }
777+
661778 @ Override
662779 public AsyncCallFuture <TemplateApiResult > copyTemplateToImageStore (DataObject source , DataStore destStore ) {
663780 TemplateObject sourceTmpl = (TemplateObject ) source ;
@@ -701,10 +818,6 @@ protected Void copyTemplateToImageStoreCallback(AsyncCallbackDispatcher<Template
701818 return null ;
702819 }
703820
704- protected boolean isCopyFromOtherStoragesEnabled (Long zoneId ) {
705- return StorageManager .COPY_PUBLIC_TEMPLATES_FROM_OTHER_STORAGES .valueIn (zoneId );
706- }
707-
708821 protected void publishTemplateCreation (TemplateInfo tmplt ) {
709822 VMTemplateVO tmpltVo = _templateDao .findById (tmplt .getId ());
710823
0 commit comments