diff --git a/api/src/com/cloud/api/commands/PrepareTemplateCmd.java b/api/src/com/cloud/api/commands/PrepareTemplateCmd.java
new file mode 100644
index 00000000000..728f94fb666
--- /dev/null
+++ b/api/src/com/cloud/api/commands/PrepareTemplateCmd.java
@@ -0,0 +1,86 @@
+/* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
+ *
+ * This software is licensed under the GNU General Public License v3 or later.
+ *
+ * It is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or any later version.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+package com.cloud.api.commands;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiConstants;
+import com.cloud.api.BaseCmd;
+import com.cloud.api.Implementation;
+import com.cloud.api.Parameter;
+import com.cloud.api.response.ListResponse;
+import com.cloud.api.response.TemplateResponse;
+import com.cloud.template.VirtualMachineTemplate;
+import com.cloud.user.Account;
+
+@Implementation(responseObject=TemplateResponse.class, description="load template into primary storage")
+public class PrepareTemplateCmd extends BaseCmd {
+ public static final Logger s_logger = Logger.getLogger(PrepareTemplateCmd.class.getName());
+
+ private static final String s_name = "preparetemplateresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// API parameters /////////////////////
+ /////////////////////////////////////////////////////
+
+ @Parameter(name=ApiConstants.ZONE_ID, required=true, type=CommandType.LONG, description="zone ID of the template to be prepared in primary storage(s).")
+ private Long zoneId;
+
+ @Parameter(name=ApiConstants.TEMPLATE_ID, required=true, type=CommandType.LONG, description="template ID of the template to be prepared in primary storage(s).")
+ private Long templateId;
+
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public Long getZoneId() {
+ return zoneId;
+ }
+
+ public Long getTemplateId() {
+ return templateId;
+ }
+
+ /////////////////////////////////////////////////////
+ /////////////// API Implementation///////////////////
+ /////////////////////////////////////////////////////
+
+ @Override
+ public String getCommandName() {
+ return s_name;
+ }
+
+ @Override
+ public long getEntityOwnerId() {
+ return Account.ACCOUNT_ID_SYSTEM;
+ }
+
+ @Override
+ public void execute() {
+ ListResponse response = new ListResponse();
+
+ VirtualMachineTemplate vmTemplate = _templateService.prepareTemplate(this);
+ List templateResponses = _responseGenerator.createTemplateResponses(vmTemplate.getId(), zoneId, true);
+ response.setResponses(templateResponses);
+ response.setResponseName(getCommandName());
+ this.setResponseObject(response);
+ }
+}
+
diff --git a/api/src/com/cloud/template/TemplateService.java b/api/src/com/cloud/template/TemplateService.java
index 19bb1de9401..61206c28ad2 100755
--- a/api/src/com/cloud/template/TemplateService.java
+++ b/api/src/com/cloud/template/TemplateService.java
@@ -26,6 +26,7 @@ import com.cloud.api.commands.DeleteTemplateCmd;
import com.cloud.api.commands.DetachIsoCmd;
import com.cloud.api.commands.ExtractIsoCmd;
import com.cloud.api.commands.ExtractTemplateCmd;
+import com.cloud.api.commands.PrepareTemplateCmd;
import com.cloud.api.commands.RegisterIsoCmd;
import com.cloud.api.commands.RegisterTemplateCmd;
import com.cloud.exception.InternalErrorException;
@@ -39,6 +40,8 @@ public interface TemplateService {
VirtualMachineTemplate registerIso(RegisterIsoCmd cmd) throws IllegalArgumentException, ResourceAllocationException;
VirtualMachineTemplate copyTemplate(CopyTemplateCmd cmd) throws StorageUnavailableException, ResourceAllocationException;
+
+ VirtualMachineTemplate prepareTemplate(PrepareTemplateCmd cmd) ;
boolean detachIso(DetachIsoCmd cmd);
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index bb24ae65463..dcd03d5ec14 100755
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -58,6 +58,7 @@ createSnapshotPolicy=com.cloud.api.commands.CreateSnapshotPolicyCmd;15
deleteSnapshotPolicies=com.cloud.api.commands.DeleteSnapshotPoliciesCmd;15
listSnapshotPolicies=com.cloud.api.commands.ListSnapshotPoliciesCmd;15
+
#### template commands
createTemplate=com.cloud.api.commands.CreateTemplateCmd;15
registerTemplate=com.cloud.api.commands.RegisterTemplateCmd;15
@@ -68,6 +69,7 @@ listTemplates=com.cloud.api.commands.ListTemplatesCmd;15
updateTemplatePermissions=com.cloud.api.commands.UpdateTemplatePermissionsCmd;15
listTemplatePermissions=com.cloud.api.commands.ListTemplatePermissionsCmd;15
extractTemplate=com.cloud.api.commands.ExtractTemplateCmd;15
+prepareTemplate=com.cloud.api.commands.PrepareTemplateCmd;1
#### iso commands
attachIso=com.cloud.api.commands.AttachIsoCmd;15
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 541068d1ccf..f69d6d17862 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -54,6 +54,7 @@ public enum Config {
StorageStatsInterval("Storage", ManagementServer.class, String.class, "storage.stats.interval", "60000", "The interval (in milliseconds) when storage stats (per host) are retrieved from agents.", null),
MaxVolumeSize("Storage", ManagementServer.class, Integer.class, "storage.max.volume.size", "2000", "The maximum size for a volume (in GB).", null),
TotalRetries("Storage", AgentManager.class, Integer.class, "total.retries", "4", "The number of times each command sent to a host should be retried in case of failure.", null),
+ StoragePoolMaxWaitSeconds("Storage", ManagementServer.class, Integer.class, "storage.pool.max.waitseconds", "3600", "Timeout (in seconds) to synchronize storage pool operations.", null),
// Network
NetworkLBHaproxyStatsVisbility("Network", ManagementServer.class, String.class, "network.loadbalancer.haproxy.stats.visibility", "global", "Load Balancer(haproxy) stats visibilty, it can be global,guest-network,disabled", null),
diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java
index 804eb8c079b..c1ad5506cd0 100755
--- a/server/src/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/com/cloud/template/TemplateManagerImpl.java
@@ -25,6 +25,8 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import javax.ejb.Local;
import javax.naming.ConfigurationException;
@@ -43,10 +45,12 @@ import com.cloud.api.commands.DeleteTemplateCmd;
import com.cloud.api.commands.DetachIsoCmd;
import com.cloud.api.commands.ExtractIsoCmd;
import com.cloud.api.commands.ExtractTemplateCmd;
+import com.cloud.api.commands.PrepareTemplateCmd;
import com.cloud.api.commands.RegisterIsoCmd;
import com.cloud.api.commands.RegisterTemplateCmd;
import com.cloud.async.AsyncJobManager;
import com.cloud.async.AsyncJobVO;
+import com.cloud.configuration.Config;
import com.cloud.configuration.ResourceCount.ResourceType;
import com.cloud.configuration.dao.ConfigurationDao;
import com.cloud.dc.DataCenter;
@@ -74,6 +78,7 @@ import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
+import com.cloud.storage.StoragePoolStatus;
import com.cloud.storage.StoragePoolVO;
import com.cloud.storage.Upload;
import com.cloud.storage.Upload.Type;
@@ -111,6 +116,7 @@ import com.cloud.utils.component.Adapters;
import com.cloud.utils.component.ComponentLocator;
import com.cloud.utils.component.Inject;
import com.cloud.utils.component.Manager;
+import com.cloud.utils.concurrency.NamedThreadFactory;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder;
@@ -160,6 +166,9 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
@Inject HypervisorGuruManager _hvGuruMgr;
protected SearchBuilder HostTemplateStatesSearch;
+ int _storagePoolMaxWaitSeconds = 3600;
+ ExecutorService _preloadExecutor;
+
@Inject (adapter=TemplateAdapter.class)
protected Adapters _adapters;
@@ -220,6 +229,17 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
// FIXME: async job needs fixing
return extract(account, templateId, url, zoneId, mode, eventId, false, null, _asyncMgr);
}
+
+ @Override
+ public VirtualMachineTemplate prepareTemplate(PrepareTemplateCmd cmd) {
+
+ VMTemplateVO vmTemplate = _tmpltDao.findById(cmd.getTemplateId());
+ if(vmTemplate == null)
+ throw new InvalidParameterValueException("Unable to find template " + cmd.getTemplateId());
+
+ prepareTemplateInAllStoragePools(vmTemplate, cmd.getZoneId());
+ return vmTemplate;
+ }
private Long extract(Account account, Long templateId, String url, Long zoneId, String mode, Long eventId, boolean isISO, AsyncJobVO job, AsyncJobManager mgr) {
String desc = "template";
@@ -328,7 +348,33 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
}else{
return null;
}
- }
+ }
+
+ public void prepareTemplateInAllStoragePools(final VMTemplateVO template, long zoneId) {
+ List pools = _poolDao.listPoolsByStatus(StoragePoolStatus.Up);
+ for(final StoragePoolVO pool : pools) {
+ if(pool.getDataCenterId() == zoneId) {
+ s_logger.info("Schedule to preload template " + template.getId() + " into primary storage " + pool.getId());
+ this._preloadExecutor.execute(new Runnable() {
+ public void run() {
+ try {
+ reallyRun();
+ } catch(Throwable e) {
+ s_logger.warn("Unexpected exception ", e);
+ }
+ }
+
+ private void reallyRun() {
+ s_logger.info("Start to preload template " + template.getId() + " into primary storage " + pool.getId());
+ prepareTemplateForCreate(template, pool);
+ s_logger.info("End of preloading template " + template.getId() + " into primary storage " + pool.getId());
+ }
+ });
+ } else {
+ s_logger.info("Skip loading template " + template.getId() + " into primary storage " + pool.getId() + " as pool zone " + pool.getDataCenterId() + " is ");
+ }
+ }
+ }
@Override @DB
public VMTemplateStoragePoolVO prepareTemplateForCreate(VMTemplateVO template, StoragePool pool) {
@@ -391,7 +437,7 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
List vos = _poolHostDao.listByHostStatus(poolId, com.cloud.host.Status.Up);
- templateStoragePoolRef = _tmpltPoolDao.acquireInLockTable(templateStoragePoolRefId, 1200);
+ templateStoragePoolRef = _tmpltPoolDao.acquireInLockTable(templateStoragePoolRefId, _storagePoolMaxWaitSeconds);
if (templateStoragePoolRef == null) {
throw new CloudRuntimeException("Unable to acquire lock on VMTemplateStoragePool: " + templateStoragePoolRefId);
}
@@ -699,6 +745,8 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe
HostSearch.done();
HostTemplateStatesSearch.done();
+ _storagePoolMaxWaitSeconds = NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600);
+ _preloadExecutor = Executors.newFixedThreadPool(8, new NamedThreadFactory("Template-Preloader"));
return false;
}
diff --git a/setup/db/db/schema-229to2210.sql b/setup/db/db/schema-229to2210.sql
index c6f8b389036..0bfa781a99f 100644
--- a/setup/db/db/schema-229to2210.sql
+++ b/setup/db/db/schema-229to2210.sql
@@ -16,3 +16,4 @@ ALTER TABLE `cloud`.`cluster` ADD COLUMN `managed_state` varchar(32) NOT NULL D
ALTER TABLE `cloud`.`host` MODIFY `storage_ip_address` char(40);
INSERT IGNORE INTO configuration VALUES ('Network', 'DEFAULT', 'management-server', 'network.redundantrouter', 'false', 'enable/disable redundant virtual router');
+INSERT IGNORE INTO configuration VALUES ('Storage', 'DEFAULT', 'management-server', 'storage.pool.max.waitseconds', '3600', 'Timeout (in seconds) to synchronize storage pool operations.');