From 02e64f90ded96ee55f8799d40e5790e7dc7b2ffe Mon Sep 17 00:00:00 2001 From: Kelven Yang Date: Mon, 8 Aug 2011 18:36:46 -0700 Subject: [PATCH] Add prepareTemplate command(admin only) to allow pre-loading template into primary storage --- .../api/commands/PrepareTemplateCmd.java | 86 +++++++++++++++++++ .../com/cloud/template/TemplateService.java | 3 + client/tomcatconf/commands.properties.in | 2 + .../src/com/cloud/configuration/Config.java | 1 + .../cloud/template/TemplateManagerImpl.java | 52 ++++++++++- setup/db/db/schema-229to2210.sql | 1 + 6 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 api/src/com/cloud/api/commands/PrepareTemplateCmd.java 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.');