public void handleTemplateSync()

in engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java [309:606]


    public void handleTemplateSync(DataStore store) {
        if (store == null) {
            logger.warn("Huh? image store is null");
            return;
        }
        long storeId = store.getId();

        // add lock to make template sync for a data store only be done once
        String lockString = "templatesync.storeId:" + storeId;
        GlobalLock syncLock = GlobalLock.getInternLock(lockString);
        try {
            if (syncLock.lock(3)) {
                try {
                    Long zoneId = store.getScope().getScopeId();

                    Map<String, TemplateProp> templateInfos = listTemplate(store);
                    if (templateInfos == null) {
                        return;
                    }

                    Set<VMTemplateVO> toBeDownloaded = new HashSet<VMTemplateVO>();
                    List<VMTemplateVO> allTemplates = null;
                    if (zoneId == null) {
                        // region wide store
                        allTemplates = _templateDao.listByState(VirtualMachineTemplate.State.Active, VirtualMachineTemplate.State.NotUploaded, VirtualMachineTemplate.State.UploadInProgress);
                    } else {
                        // zone wide store
                        allTemplates = _templateDao.listInZoneByState(zoneId, VirtualMachineTemplate.State.Active, VirtualMachineTemplate.State.NotUploaded, VirtualMachineTemplate.State.UploadInProgress);
                    }
                    List<VMTemplateVO> rtngTmplts = _templateDao.listAllSystemVMTemplates();
                    List<VMTemplateVO> defaultBuiltin = _templateDao.listDefaultBuiltinTemplates();

                    if (rtngTmplts != null) {
                        for (VMTemplateVO rtngTmplt : rtngTmplts) {
                            if (!allTemplates.contains(rtngTmplt)) {
                                allTemplates.add(rtngTmplt);
                            }
                        }
                    }

                    if (defaultBuiltin != null) {
                        for (VMTemplateVO builtinTmplt : defaultBuiltin) {
                            if (!allTemplates.contains(builtinTmplt)) {
                                allTemplates.add(builtinTmplt);
                            }
                        }
                    }

                    for (Iterator<VMTemplateVO> iter = allTemplates.listIterator(); iter.hasNext();) {
                        VMTemplateVO child_template = iter.next();
                        if (child_template.getParentTemplateId() != null) {
                            String uniqueName = child_template.getUniqueName();
                            if (templateInfos.containsKey(uniqueName)) {
                                templateInfos.remove(uniqueName);
                            }
                            iter.remove();
                        }
                    }

                    toBeDownloaded.addAll(allTemplates);

                    final StateMachine2<VirtualMachineTemplate.State, VirtualMachineTemplate.Event, VirtualMachineTemplate> stateMachine = VirtualMachineTemplate.State.getStateMachine();
                    Boolean followRedirect = StorageManager.DataStoreDownloadFollowRedirects.value();
                    for (VMTemplateVO tmplt : allTemplates) {
                        String uniqueName = tmplt.getUniqueName();
                        TemplateDataStoreVO tmpltStore = _vmTemplateStoreDao.findByStoreTemplate(storeId, tmplt.getId());
                        if (templateInfos.containsKey(uniqueName)) {
                            TemplateProp tmpltInfo = templateInfos.remove(uniqueName);
                            toBeDownloaded.remove(tmplt);
                            if (tmpltStore != null) {
                                logger.info("Template Sync found {} already in the image store", tmplt);
                                if (tmpltStore.getDownloadState() != Status.DOWNLOADED) {
                                    tmpltStore.setErrorString("");
                                }
                                if (tmpltInfo.isCorrupted()) {
                                    tmpltStore.setDownloadState(Status.DOWNLOAD_ERROR);
                                    String msg = String.format("Template %s is corrupted on secondary storage %s", tmplt, store);
                                    tmpltStore.setErrorString(msg);
                                    logger.info(msg);
                                    _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_UPLOAD_FAILED, zoneId, null, msg, msg);
                                    if (tmplt.getState() == VirtualMachineTemplate.State.NotUploaded || tmplt.getState() == VirtualMachineTemplate.State.UploadInProgress) {
                                        logger.info("Template Sync found {} on image store {} uploaded using SSVM as corrupted, marking it as failed", tmplt, store);
                                        tmpltStore.setState(State.Failed);
                                        try {
                                            stateMachine.transitTo(tmplt, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao);
                                        } catch (NoTransitionException e) {
                                            logger.error("Unexpected state transition exception for template {}. Details: {}", tmplt, e.getMessage());
                                        }
                                    } else if (tmplt.getUrl() == null) {
                                        msg = String.format("Private template (%s) with install path %s is corrupted, please check in image store: %s", tmplt, tmpltInfo.getInstallPath(), store);
                                        logger.warn(msg);
                                    } else {
                                        logger.info("Removing template_store_ref entry for corrupted template {}", tmplt);
                                        _vmTemplateStoreDao.remove(tmpltStore.getId());
                                        toBeDownloaded.add(tmplt);
                                    }
                                } else {
                                    if(tmpltStore.getDownloadState() != Status.DOWNLOADED) {
                                        String etype = EventTypes.EVENT_TEMPLATE_CREATE;
                                        if (tmplt.getFormat() == ImageFormat.ISO) {
                                            etype = EventTypes.EVENT_ISO_CREATE;
                                        }

                                        if (zoneId != null) {
                                            UsageEventUtils.publishUsageEvent(etype, tmplt.getAccountId(), zoneId, tmplt.getId(), tmplt.getName(), null, null,
                                                    tmpltInfo.getPhysicalSize(), tmpltInfo.getSize(), VirtualMachineTemplate.class.getName(), tmplt.getUuid());
                                        }
                                    }

                                    tmpltStore.setDownloadPercent(100);
                                    tmpltStore.setDownloadState(Status.DOWNLOADED);
                                    tmpltStore.setState(ObjectInDataStoreStateMachine.State.Ready);
                                    tmpltStore.setInstallPath(tmpltInfo.getInstallPath());
                                    tmpltStore.setSize(tmpltInfo.getSize());
                                    tmpltStore.setPhysicalSize(tmpltInfo.getPhysicalSize());
                                    tmpltStore.setLastUpdated(new Date());
                                    // update size in vm_template table
                                    VMTemplateVO tmlpt = _templateDao.findById(tmplt.getId());
                                    tmlpt.setSize(tmpltInfo.getSize());
                                    _templateDao.update(tmplt.getId(), tmlpt);

                                    if (tmplt.getState() == VirtualMachineTemplate.State.NotUploaded || tmplt.getState() == VirtualMachineTemplate.State.UploadInProgress) {
                                        VirtualMachineTemplate.Event event = VirtualMachineTemplate.Event.OperationSucceeded;
                                        // For multi-disk OVA, check and create data disk templates
                                        if (tmplt.getFormat().equals(ImageFormat.OVA)) {
                                            if (!createOvaDataDiskTemplates(_templateFactory.getTemplate(tmlpt.getId(), store), tmplt.isDeployAsIs())) {
                                                event = VirtualMachineTemplate.Event.OperationFailed;
                                            }
                                        }
                                        try {
                                            stateMachine.transitTo(tmplt, event, null, _templateDao);
                                        } catch (NoTransitionException e) {
                                            logger.error("Unexpected state transition exception for template {}. Details: {}", tmplt, e.getMessage());
                                        }
                                    }

                                    // Skipping limit checks for SYSTEM Account and for the templates created from volumes or snapshots
                                    // which already got checked and incremented during createTemplate API call.
                                    if (tmpltInfo.getSize() > 0 && tmplt.getAccountId() != Account.ACCOUNT_ID_SYSTEM && tmplt.getUrl() != null) {
                                        long accountId = tmplt.getAccountId();
                                        try {
                                            _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(accountId),
                                                    com.cloud.configuration.Resource.ResourceType.secondary_storage,
                                                    tmpltInfo.getSize() - UriUtils.getRemoteSize(tmplt.getUrl(),
                                                            followRedirect));
                                        } catch (ResourceAllocationException e) {
                                            logger.warn(e.getMessage());
                                            _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED, zoneId, null, e.getMessage(), e.getMessage());
                                        } finally {
                                            _resourceLimitMgr.recalculateResourceCount(accountId, _accountMgr.getAccount(accountId).getDomainId(),
                                                    com.cloud.configuration.Resource.ResourceType.secondary_storage.getOrdinal());
                                        }
                                    }
                                }
                                _vmTemplateStoreDao.update(tmpltStore.getId(), tmpltStore);
                            } else {
                                tmpltStore = new TemplateDataStoreVO(storeId, tmplt.getId(), new Date(), 100, Status.DOWNLOADED, null, null, null, tmpltInfo.getInstallPath(), tmplt.getUrl());
                                tmpltStore.setSize(tmpltInfo.getSize());
                                tmpltStore.setPhysicalSize(tmpltInfo.getPhysicalSize());
                                tmpltStore.setDataStoreRole(store.getRole());
                                _vmTemplateStoreDao.persist(tmpltStore);

                                // update size in vm_template table
                                VMTemplateVO tmlpt = _templateDao.findById(tmplt.getId());
                                tmlpt.setSize(tmpltInfo.getSize());
                                _templateDao.update(tmplt.getId(), tmlpt);
                                associateTemplateToZone(tmplt.getId(), zoneId);

                                String etype = EventTypes.EVENT_TEMPLATE_CREATE;
                                if (tmplt.getFormat() == ImageFormat.ISO) {
                                    etype = EventTypes.EVENT_ISO_CREATE;
                                }

                                UsageEventUtils.publishUsageEvent(etype, tmplt.getAccountId(), zoneId, tmplt.getId(), tmplt.getName(), null, null,
                                        tmpltInfo.getPhysicalSize(), tmpltInfo.getSize(), VirtualMachineTemplate.class.getName(), tmplt.getUuid());
                            }
                        } else if (tmplt.getState() == VirtualMachineTemplate.State.NotUploaded || tmplt.getState() == VirtualMachineTemplate.State.UploadInProgress) {
                            logger.info("Template Sync did not find {} on image store {} uploaded using SSVM, marking it as failed", tmplt, store);
                            toBeDownloaded.remove(tmplt);
                            tmpltStore.setDownloadState(Status.DOWNLOAD_ERROR);
                            String msg = String.format("Template %s is corrupted on secondary storage %s", tmplt, store);
                            tmpltStore.setErrorString(msg);
                            tmpltStore.setState(State.Failed);
                            _vmTemplateStoreDao.update(tmpltStore.getId(), tmpltStore);
                            try {
                                stateMachine.transitTo(tmplt, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao);
                            } catch (NoTransitionException e) {
                                logger.error("Unexpected state transition exception for template {}. Details: {}", tmplt, e.getMessage());
                            }
                        } else if (tmplt.isDirectDownload()) {
                            logger.info("Template {} is marked for direct download, discarding it for download on image stores", tmplt);
                            toBeDownloaded.remove(tmplt);
                        } else {
                            logger.info("Template Sync did not find {} on image store {}, may request download based on available hypervisor types", tmplt, store);
                            if (tmpltStore != null) {
                                if (_storeMgr.isRegionStore(store) && tmpltStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED
                                        && tmpltStore.getState() == State.Ready
                                        && tmpltStore.getInstallPath() == null) {
                                    logger.info("Keep fake entry in template store table for migration of previous NFS to object store");
                                } else {
                                    logger.info("Removing leftover template {} entry from template store table", tmplt);
                                    // remove those leftover entries
                                    _vmTemplateStoreDao.remove(tmpltStore.getId());
                                }
                            }
                        }
                    }

                    if (toBeDownloaded.size() > 0) {
                        /* Only download templates whose hypervirsor type is in the zone */
                        List<HypervisorType> availHypers = _clusterDao.getAvailableHypervisorInZone(zoneId);
                        if (availHypers.isEmpty()) {
                            /*
                             * This is for cloudzone, local secondary storage resource
                             * started before cluster created
                             */
                            availHypers.add(HypervisorType.KVM);
                        }
                        /* Baremetal need not to download any template */
                        availHypers.remove(HypervisorType.BareMetal);
                        availHypers.add(HypervisorType.None); // bug 9809: resume ISO
                        // download.
                        for (VMTemplateVO tmplt : toBeDownloaded) {
                            if (tmplt.getUrl() == null) { // If url is null, skip downloading
                                logger.info("Skip downloading template {} since no url is specified.", tmplt);
                                continue;
                            }
                            // if this is private template, skip sync to a new image store
                            if (isSkipTemplateStoreDownload(tmplt, zoneId)) {
                                logger.info("Skip sync downloading private template {} to a new image store", tmplt);
                                continue;
                            }

                            // if this is a region store, and there is already an DOWNLOADED entry there without install_path information, which
                            // means that this is a duplicate entry from migration of previous NFS to staging.
                            if (_storeMgr.isRegionStore(store)) {
                                TemplateDataStoreVO tmpltStore = _vmTemplateStoreDao.findByStoreTemplate(storeId, tmplt.getId());
                                if (tmpltStore != null && tmpltStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOADED && tmpltStore.getState() == State.Ready
                                        && tmpltStore.getInstallPath() == null) {
                                    logger.info("Skip sync template for migration of previous NFS to object store");
                                    continue;
                                }
                            }

                            if (availHypers.contains(tmplt.getHypervisorType())) {
                                logger.info("Downloading template {} to image store {}", tmplt, store);
                                associateTemplateToZone(tmplt.getId(), zoneId);
                                TemplateInfo tmpl = _templateFactory.getTemplate(tmplt.getId(), store);
                                TemplateOpContext<TemplateApiResult> context = new TemplateOpContext<>(null,(TemplateObject)tmpl, null);
                                AsyncCallbackDispatcher<TemplateServiceImpl, TemplateApiResult> caller = AsyncCallbackDispatcher.create(this);
                                caller.setCallback(caller.getTarget().createTemplateAsyncCallBack(null, null));
                                caller.setContext(context);
                                createTemplateAsync(tmpl, store, caller);
                            } else {
                                logger.info("Skip downloading template {} since current data center does not have hypervisor {}", tmplt, tmplt.getHypervisorType());
                            }
                        }
                    }

                    for (String uniqueName : templateInfos.keySet()) {
                        TemplateProp tInfo = templateInfos.get(uniqueName);
                        if (_tmpltMgr.templateIsDeleteable(tInfo.getId())) {
                            // we cannot directly call deleteTemplateSync here to reuse delete logic since in this case db does not have this template at all.
                            TemplateObjectTO tmplTO = new TemplateObjectTO();
                            tmplTO.setDataStore(store.getTO());
                            tmplTO.setPath(tInfo.getInstallPath());
                            tmplTO.setId(tInfo.getId());
                            DeleteCommand dtCommand = new DeleteCommand(tmplTO);
                            EndPoint ep = _epSelector.select(store);
                            Answer answer = null;
                            if (ep == null) {
                                String errMsg = "No remote endpoint to send command, check if host or ssvm is down?";
                                logger.error(errMsg);
                                answer = new Answer(dtCommand, false, errMsg);
                            } else {
                                answer = ep.sendMessage(dtCommand);
                            }
                            if (answer == null || !answer.getResult()) {
                                logger.info("Failed to deleted template at store: {}", store);

                            } else {
                                String description = String.format("Deleted template %s on secondary storage %s", tInfo.getTemplateName(), store);
                                logger.info(description);
                            }

                        }
                    }
                } finally {
                    syncLock.unlock();
                }
            } else {
                logger.info("Couldn't get global lock on {}, another thread may be doing template sync on data store {} now.", lockString, store);
            }
        } finally {
            syncLock.releaseRef();
        }

    }