winterhazel commented on code in PR #12296:
URL: https://github.com/apache/cloudstack/pull/12296#discussion_r2651290650


##########
api/src/main/java/org/apache/cloudstack/api/command/admin/host/AddSecondaryStorageCmd.java:
##########
@@ -44,6 +49,9 @@ public class AddSecondaryStorageCmd extends BaseCmd {
     @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, 
entityType = ZoneResponse.class, description = "the Zone ID for the secondary 
storage")
     protected Long zoneId;
 
+    @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, 
description = "Details in key/value pairs using format 
details[i].keyname=keyvalue. Example: 
details[0].copytemplatesfromothersecondarystorages=true")

Review Comment:
   I usually prefer having parameters instead of passing things as a detail, so 
that it is automatically added to the API's documentation, but I'm ok if  you 
keep it like this seeing that there's already documentation and a UI change for 
it



##########
engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/StorageOrchestrator.java:
##########
@@ -653,4 +674,51 @@ public TemplateApiResult call() {
             return result;
         }
     }
+
+    private class CrossZoneCopyTemplateTask implements 
Callable<TemplateApiResult> {
+        private final long userId;
+        private final TemplateInfo sourceTmpl;
+        private final DataStore sourceStore;
+        private final DataCenterVO dstZone;
+        private final String logid;
+
+        CrossZoneCopyTemplateTask(long userId,
+                                  TemplateInfo sourceTmpl,
+                                  DataStore sourceStore,
+                                  DataCenterVO dstZone) {
+            this.userId = userId;
+            this.sourceTmpl = sourceTmpl;
+            this.sourceStore = sourceStore;
+            this.dstZone = dstZone;
+            this.logid = ThreadContext.get(LOGCONTEXTID);
+        }
+
+        @Override
+        public TemplateApiResult call() {
+            ThreadContext.put(LOGCONTEXTID, logid);
+            TemplateApiResult result;
+            VMTemplateVO template = templateDao.findById(sourceTmpl.getId());
+            try {
+                boolean success = templateManager.copy(userId, template, 
sourceStore, dstZone);
+
+                result = new TemplateApiResult(sourceTmpl);
+                if (!success) {
+                    result.setResult("Cross-zone template copy failed");
+                }
+            } catch (Exception e) {
+                logger.error("Exception while copying template [{}] from zone 
[{}] to zone [{}]",
+                        template,
+                        sourceStore.getScope().getScopeId(),
+                        dstZone.getId(),
+                        e);
+                result = new TemplateApiResult(sourceTmpl);
+                result.setResult(e.getMessage());
+            } finally {
+                tryCleaningUpExecutor(dstZone.getId());
+                ThreadContext.clearAll();
+            }

Review Comment:
   I would place these 2 lines outside the finally so that there's less idented 
code



##########
engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/StorageOrchestrator.java:
##########
@@ -308,6 +321,14 @@ public Future<TemplateApiResult> 
orchestrateTemplateCopyToImageStore(TemplateInf
         return submit(destStore.getScope().getScopeId(), new 
CopyTemplateTask(source, destStore));
     }
 
+    @Override
+    public Future<TemplateApiResult> 
orchestrateTemplateCopyAcrossZones(TemplateInfo templateInfo, DataStore 
sourceStore, DataStore destStore) {

Review Comment:
   You can reduce the number of parameters in this method. There's no need to 
pass the `sourceStore`, you can get it via `templateInfo.getDataStore()`



##########
engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java:
##########
@@ -615,28 +615,118 @@ protected void 
tryDownloadingTemplateToImageStore(VMTemplateVO tmplt, DataStore
     }
 
     protected boolean tryCopyingTemplateToImageStore(VMTemplateVO tmplt, 
DataStore destStore) {
-        Long zoneId = destStore.getScope().getScopeId();
-        List<DataStore> storesInZone = 
_storeMgr.getImageStoresByZoneIds(zoneId);
-        for (DataStore sourceStore : storesInZone) {
+        Long destZoneId = destStore.getScope().getScopeId();
+
+        List<DataStore> storesInSameZone = 
_storeMgr.getImageStoresByZoneIds(destZoneId);
+        if (searchAndCopyWithinZone(tmplt, destStore, storesInSameZone)) {
+            return true;
+        }
+
+        logger.debug("Template [{}] not found in any image store of zone [{}]. 
Checking other zones.",
+                tmplt.getUniqueName(), destZoneId);
+
+        return searchAndCopyAcrossZones(tmplt, destStore, destZoneId);
+    }
+
+    private boolean searchAndCopyAcrossZones(VMTemplateVO tmplt, DataStore 
destStore, Long destZoneId) {
+        List<Long> allZoneIds = _dcDao.listAllIds();
+        for (Long otherZoneId : allZoneIds) {
+            if (otherZoneId.equals(destZoneId)) {
+                continue;
+            }
+
+            List<DataStore> storesInOtherZone = 
_storeMgr.getImageStoresByZoneIds(otherZoneId);
+            logger.debug("Checking zone [{}] for template [{}]...", 
otherZoneId, tmplt.getUniqueName());
+
+            if (storesInOtherZone == null || storesInOtherZone.isEmpty()) {
+                logger.debug("Zone [{}] has no image stores. Skipping.", 
otherZoneId);
+                continue;
+            }
+
+            DataStore sourceStore = findImageStorageHavingTemplate(tmplt, 
storesInOtherZone);
+            if (sourceStore == null) {
+                logger.debug("Template [{}] not found in any image store of 
zone [{}].",
+                        tmplt.getUniqueName(), otherZoneId);
+                continue;
+            }
+
+            TemplateObject sourceTmpl = (TemplateObject) 
_templateFactory.getTemplate(tmplt.getId(), sourceStore);
+            if (sourceTmpl.getInstallPath() == null) {
+                logger.warn("Cannot copy template [{}] from image store [{}]; 
install path is null.",
+                        tmplt.getUniqueName(), sourceStore.getName());
+                continue;
+            }

Review Comment:
   In the following situation:
   - 2 Zones: A, B
   - ACS is attempting to copy from A to B
   - A has 2 image stores: A1, A2
   - A1 has an inconsistent entry for the template with a null install path, A2 
has a consistent entry
   
   If `findImageStorageHavingTemplate` returns A1, ACS will not copy the 
template from image store A2 to zone B, even though it is possible.
   
   It is better to loop through the secondary storages of zone A, search for a 
template entry with a install path that is not null, and return it. This way, 
you can also deduplicate the code in `searchAndCopyWithinZone`.



##########
engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java:
##########
@@ -615,28 +615,118 @@ protected void 
tryDownloadingTemplateToImageStore(VMTemplateVO tmplt, DataStore
     }
 
     protected boolean tryCopyingTemplateToImageStore(VMTemplateVO tmplt, 
DataStore destStore) {
-        Long zoneId = destStore.getScope().getScopeId();
-        List<DataStore> storesInZone = 
_storeMgr.getImageStoresByZoneIds(zoneId);
-        for (DataStore sourceStore : storesInZone) {
+        Long destZoneId = destStore.getScope().getScopeId();
+
+        List<DataStore> storesInSameZone = 
_storeMgr.getImageStoresByZoneIds(destZoneId);

Review Comment:
   Move this to inside `searchAndCopyWithinZone`?



##########
engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/StorageOrchestrator.java:
##########
@@ -653,4 +674,51 @@ public TemplateApiResult call() {
             return result;
         }
     }
+
+    private class CrossZoneCopyTemplateTask implements 
Callable<TemplateApiResult> {
+        private final long userId;
+        private final TemplateInfo sourceTmpl;
+        private final DataStore sourceStore;
+        private final DataCenterVO dstZone;
+        private final String logid;
+
+        CrossZoneCopyTemplateTask(long userId,
+                                  TemplateInfo sourceTmpl,
+                                  DataStore sourceStore,
+                                  DataCenterVO dstZone) {
+            this.userId = userId;
+            this.sourceTmpl = sourceTmpl;
+            this.sourceStore = sourceStore;
+            this.dstZone = dstZone;
+            this.logid = ThreadContext.get(LOGCONTEXTID);
+        }
+
+        @Override
+        public TemplateApiResult call() {
+            ThreadContext.put(LOGCONTEXTID, logid);
+            TemplateApiResult result;
+            VMTemplateVO template = templateDao.findById(sourceTmpl.getId());
+            try {
+                boolean success = templateManager.copy(userId, template, 
sourceStore, dstZone);
+
+                result = new TemplateApiResult(sourceTmpl);
+                if (!success) {
+                    result.setResult("Cross-zone template copy failed");
+                }
+            } catch (Exception e) {

Review Comment:
   Catch only the relevant exceptions (`StorageUnavailableException` and 
`ResourceAllocationException`) instead?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to