You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by bf...@apache.org on 2013/04/10 19:45:42 UTC
[02/50] [abbrv] QuickCloud: Enable secondary storage daemon to run
outside the system vm
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManagerImpl.java
----------------------------------------------------------------------
diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManagerImpl.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManagerImpl.java
new file mode 100755
index 0000000..a9d23cb
--- /dev/null
+++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManagerImpl.java
@@ -0,0 +1,1074 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.storage.template;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.cloudstack.storage.resource.SecondaryStorageResource;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.storage.DownloadAnswer;
+import com.cloud.agent.api.storage.DownloadCommand;
+import com.cloud.agent.api.storage.DownloadCommand.Proxy;
+import com.cloud.agent.api.storage.DownloadCommand.ResourceType;
+import com.cloud.agent.api.storage.DownloadProgressCommand;
+import com.cloud.agent.api.storage.DownloadProgressCommand.RequestType;
+import com.cloud.exception.InternalErrorException;
+import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.StorageLayer;
+import com.cloud.storage.VMTemplateHostVO;
+import com.cloud.storage.VMTemplateStorageResourceAssoc;
+import com.cloud.storage.template.HttpTemplateDownloader;
+import com.cloud.storage.template.IsoProcessor;
+import com.cloud.storage.template.LocalTemplateDownloader;
+import com.cloud.storage.template.Processor;
+import com.cloud.storage.template.Processor.FormatInfo;
+import com.cloud.storage.template.QCOW2Processor;
+import com.cloud.storage.template.RawImageProcessor;
+import com.cloud.storage.template.ScpTemplateDownloader;
+import com.cloud.storage.template.TemplateConstants;
+import com.cloud.storage.template.TemplateDownloader;
+import com.cloud.storage.template.TemplateDownloader.DownloadCompleteCallback;
+import com.cloud.storage.template.TemplateDownloader.Status;
+import com.cloud.storage.template.TemplateInfo;
+import com.cloud.storage.template.TemplateLocation;
+import com.cloud.storage.template.VhdProcessor;
+import com.cloud.storage.template.VmdkProcessor;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.OutputInterpreter;
+import com.cloud.utils.script.Script;
+
+@Local(value = DownloadManager.class)
+public class DownloadManagerImpl extends ManagerBase implements DownloadManager {
+ private String _name;
+ StorageLayer _storage;
+ Map<String, Processor> _processors;
+
+ public class Completion implements DownloadCompleteCallback {
+ private final String jobId;
+
+ public Completion(String jobId) {
+ this.jobId = jobId;
+ }
+
+ @Override
+ public void downloadComplete(Status status) {
+ setDownloadStatus(jobId, status);
+ }
+ }
+
+ private static class DownloadJob {
+ private final TemplateDownloader td;
+ private final String jobId;
+ private final String tmpltName;
+ private final boolean hvm;
+ private final ImageFormat format;
+ private String tmpltPath;
+ private final String description;
+ private String checksum;
+ private final Long accountId;
+ private final String installPathPrefix;
+ private long templatesize;
+ private long templatePhysicalSize;
+ private final long id;
+ private final ResourceType resourceType;
+
+ public DownloadJob(TemplateDownloader td, String jobId, long id, String tmpltName, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix, ResourceType resourceType) {
+ super();
+ this.td = td;
+ this.jobId = jobId;
+ this.tmpltName = tmpltName;
+ this.format = format;
+ this.hvm = hvm;
+ this.accountId = accountId;
+ this.description = descr;
+ this.checksum = cksum;
+ this.installPathPrefix = installPathPrefix;
+ this.templatesize = 0;
+ this.id = id;
+ this.resourceType = resourceType;
+ }
+
+ public TemplateDownloader getTd() {
+ return td;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getChecksum() {
+ return checksum;
+ }
+
+ public TemplateDownloader getTemplateDownloader() {
+ return td;
+ }
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public String getTmpltName() {
+ return tmpltName;
+ }
+
+ public ImageFormat getFormat() {
+ return format;
+ }
+
+ public boolean isHvm() {
+ return hvm;
+ }
+
+ public Long getAccountId() {
+ return accountId;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public ResourceType getResourceType() {
+ return resourceType;
+ }
+
+ public void setTmpltPath(String tmpltPath) {
+ this.tmpltPath = tmpltPath;
+ }
+
+ public String getTmpltPath() {
+ return tmpltPath;
+ }
+
+ public String getInstallPathPrefix() {
+ return installPathPrefix;
+ }
+
+ public void cleanup() {
+ if (td != null) {
+ String dnldPath = td.getDownloadLocalPath();
+ if (dnldPath != null) {
+ File f = new File(dnldPath);
+ File dir = f.getParentFile();
+ f.delete();
+ if (dir != null) {
+ dir.delete();
+ }
+ }
+ }
+
+ }
+
+ public void setTemplatesize(long templatesize) {
+ this.templatesize = templatesize;
+ }
+
+ public long getTemplatesize() {
+ return templatesize;
+ }
+
+ public void setTemplatePhysicalSize(long templatePhysicalSize) {
+ this.templatePhysicalSize = templatePhysicalSize;
+ }
+
+ public long getTemplatePhysicalSize() {
+ return templatePhysicalSize;
+ }
+
+ public void setCheckSum(String checksum) {
+ this.checksum = checksum;
+ }
+ }
+
+ public static final Logger s_logger = Logger.getLogger(DownloadManagerImpl.class);
+ private String _templateDir;
+ private String _volumeDir;
+ private String createTmpltScr;
+ private String createVolScr;
+ private List<Processor> processors;
+
+ private ExecutorService threadPool;
+
+ private final Map<String, DownloadJob> jobs = new ConcurrentHashMap<String, DownloadJob>();
+ private String listTmpltScr;
+ private String listVolScr;
+ private int installTimeoutPerGig = 180 * 60 * 1000;
+ private boolean _sslCopy;
+
+ /**
+ * Get notified of change of job status. Executed in context of downloader thread
+ *
+ * @param jobId
+ * the id of the job
+ * @param status
+ * the status of the job
+ */
+ public void setDownloadStatus(String jobId, Status status) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj == null) {
+ s_logger.warn("setDownloadStatus for jobId: " + jobId + ", status=" + status + " no job found");
+ return;
+ }
+ TemplateDownloader td = dj.getTemplateDownloader();
+ s_logger.info("Download Completion for jobId: " + jobId + ", status=" + status);
+ s_logger.info("local: " + td.getDownloadLocalPath() + ", bytes=" + td.getDownloadedBytes() + ", error=" + td.getDownloadError() + ", pct=" + td.getDownloadPercent());
+
+ switch (status) {
+ case ABORTED:
+ case NOT_STARTED:
+ case UNRECOVERABLE_ERROR:
+ // TODO
+ dj.cleanup();
+ break;
+ case UNKNOWN:
+ return;
+ case IN_PROGRESS:
+ s_logger.info("Resuming jobId: " + jobId + ", status=" + status);
+ td.setResume(true);
+ threadPool.execute(td);
+ break;
+ case RECOVERABLE_ERROR:
+ threadPool.execute(td);
+ break;
+ case DOWNLOAD_FINISHED:
+ td.setDownloadError("Download success, starting install ");
+ String result = postDownload(jobId);
+ if (result != null) {
+ s_logger.error("Failed post download script: " + result);
+ td.setStatus(Status.UNRECOVERABLE_ERROR);
+ td.setDownloadError("Failed post download script: " + result);
+ } else {
+ td.setStatus(Status.POST_DOWNLOAD_FINISHED);
+ td.setDownloadError("Install completed successfully at " + new SimpleDateFormat().format(new Date()));
+ }
+ dj.cleanup();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private String computeCheckSum(File f) {
+ byte[] buffer = new byte[8192];
+ int read = 0;
+ MessageDigest digest;
+ String checksum = null;
+ InputStream is = null;
+ try {
+ digest = MessageDigest.getInstance("MD5");
+ is = new FileInputStream(f);
+ while( (read = is.read(buffer)) > 0) {
+ digest.update(buffer, 0, read);
+ }
+ byte[] md5sum = digest.digest();
+ BigInteger bigInt = new BigInteger(1, md5sum);
+ checksum = String.format("%032x",bigInt);
+ return checksum;
+ }catch(IOException e) {
+ return null;
+ }catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+ finally {
+ try {
+ if(is != null)
+ is.close();
+ } catch (IOException e) {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Post download activity (install and cleanup). Executed in context of downloader thread
+ *
+ * @throws IOException
+ */
+ private String postDownload(String jobId) {
+ DownloadJob dnld = jobs.get(jobId);
+ TemplateDownloader td = dnld.getTemplateDownloader();
+ String resourcePath = null;
+ ResourceType resourceType = dnld.getResourceType();
+
+ // once template path is set, remove the parent dir so that the template is installed with a relative path
+ String finalResourcePath = "";
+ if (resourceType == ResourceType.TEMPLATE){
+ finalResourcePath += _templateDir + File.separator + dnld.getAccountId() + File.separator + dnld.getId() + File.separator;
+ resourcePath = dnld.getInstallPathPrefix() + dnld.getAccountId() + File.separator + dnld.getId() + File.separator;// dnld.getTmpltName();
+ }else {
+ finalResourcePath += _volumeDir + File.separator + dnld.getId() + File.separator;
+ resourcePath = dnld.getInstallPathPrefix() + dnld.getId() + File.separator;// dnld.getTmpltName();
+ }
+
+ _storage.mkdirs(resourcePath);
+ dnld.setTmpltPath(finalResourcePath);
+
+ File originalTemplate = new File(td.getDownloadLocalPath());
+ String checkSum = computeCheckSum(originalTemplate);
+ if (checkSum == null) {
+ s_logger.warn("Something wrong happened when try to calculate the checksum of downloaded template!");
+ }
+ dnld.setCheckSum(checkSum);
+
+ int imgSizeGigs = (int) Math.ceil(_storage.getSize(td.getDownloadLocalPath()) * 1.0d / (1024 * 1024 * 1024));
+ imgSizeGigs++; // add one just in case
+ long timeout = imgSizeGigs * installTimeoutPerGig;
+ Script scr = null;
+ String script = resourceType == ResourceType.TEMPLATE ? createTmpltScr : createVolScr;
+ scr = new Script(script, timeout, s_logger);
+ scr.add("-s", Integer.toString(imgSizeGigs));
+ scr.add("-S", Long.toString(td.getMaxTemplateSizeInBytes()));
+ if (dnld.getDescription() != null && dnld.getDescription().length() > 1) {
+ scr.add("-d", dnld.getDescription());
+ }
+ if (dnld.isHvm()) {
+ scr.add("-h");
+ }
+
+ // add options common to ISO and template
+ String extension = dnld.getFormat().getFileExtension();
+ String templateName = "";
+ if( extension.equals("iso")) {
+ templateName = jobs.get(jobId).getTmpltName().trim().replace(" ", "_");
+ } else {
+ templateName = java.util.UUID.nameUUIDFromBytes((jobs.get(jobId).getTmpltName() + System.currentTimeMillis()).getBytes()).toString();
+ }
+
+ String templateFilename = templateName + "." + extension;
+ dnld.setTmpltPath(finalResourcePath + "/" + templateFilename);
+ scr.add("-n", templateFilename);
+
+ scr.add("-t", resourcePath);
+ scr.add("-f", td.getDownloadLocalPath());
+ if (dnld.getChecksum() != null && dnld.getChecksum().length() > 1) {
+ scr.add("-c", dnld.getChecksum());
+ }
+ scr.add("-u"); // cleanup
+ String result;
+ result = scr.execute();
+
+ if (result != null) {
+ return result;
+ }
+
+ // Set permissions for the downloaded template
+ File downloadedTemplate = new File(resourcePath + "/" + templateFilename);
+ _storage.setWorldReadableAndWriteable(downloadedTemplate);
+
+ // Set permissions for template/volume.properties
+ String propertiesFile = resourcePath;
+ if (resourceType == ResourceType.TEMPLATE){
+ propertiesFile += "/template.properties";
+ }else{
+ propertiesFile += "/volume.properties";
+ }
+ File templateProperties = new File(propertiesFile);
+ _storage.setWorldReadableAndWriteable(templateProperties);
+
+ TemplateLocation loc = new TemplateLocation(_storage, resourcePath);
+ try {
+ loc.create(dnld.getId(), true, dnld.getTmpltName());
+ } catch (IOException e) {
+ s_logger.warn("Something is wrong with template location " + resourcePath, e);
+ loc.purge();
+ return "Unable to download due to " + e.getMessage();
+ }
+
+ Iterator<Processor> en = _processors.values().iterator();
+ while (en.hasNext()) {
+ Processor processor = en.next();
+
+ FormatInfo info = null;
+ try {
+ info = processor.process(resourcePath, null, templateName);
+ } catch (InternalErrorException e) {
+ s_logger.error("Template process exception ", e);
+ return e.toString();
+ }
+ if (info != null) {
+ loc.addFormat(info);
+ dnld.setTemplatesize(info.virtualSize);
+ dnld.setTemplatePhysicalSize(info.size);
+ break;
+ }
+ }
+
+ if (!loc.save()) {
+ s_logger.warn("Cleaning up because we're unable to save the formats");
+ loc.purge();
+ }
+
+ return null;
+ }
+
+ @Override
+ public Status getDownloadStatus(String jobId) {
+ DownloadJob job = jobs.get(jobId);
+ if (job != null) {
+ TemplateDownloader td = job.getTemplateDownloader();
+ if (td != null) {
+ return td.getStatus();
+ }
+ }
+ return Status.UNKNOWN;
+ }
+
+ @Override
+ public String downloadPublicTemplate(long id, String url, String name, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix, String user, String password, long maxTemplateSizeInBytes, Proxy proxy, ResourceType resourceType) {
+ UUID uuid = UUID.randomUUID();
+ String jobId = uuid.toString();
+ String tmpDir = "";
+ if(resourceType == ResourceType.TEMPLATE){
+ tmpDir = installPathPrefix + File.separator + accountId + File.separator + id;
+ }else {
+ tmpDir = installPathPrefix + File.separator + id;
+ }
+
+ try {
+
+ if (!_storage.mkdirs(tmpDir)) {
+ s_logger.warn("Unable to create " + tmpDir);
+ return "Unable to create " + tmpDir;
+ }
+ // TO DO - define constant for volume properties.
+ File file = ResourceType.TEMPLATE == resourceType ? _storage.getFile(tmpDir + File.separator + TemplateLocation.Filename) :
+ _storage.getFile(tmpDir + File.separator + "volume.properties");
+ if ( file.exists() ) {
+ file.delete();
+ }
+
+ if (!file.createNewFile()) {
+ s_logger.warn("Unable to create new file: " + file.getAbsolutePath());
+ return "Unable to create new file: " + file.getAbsolutePath();
+ }
+
+ URI uri;
+ try {
+ uri = new URI(url);
+ } catch (URISyntaxException e) {
+ throw new CloudRuntimeException("URI is incorrect: " + url);
+ }
+ TemplateDownloader td;
+ if ((uri != null) && (uri.getScheme() != null)) {
+ if (uri.getScheme().equalsIgnoreCase("http") || uri.getScheme().equalsIgnoreCase("https")) {
+ td = new HttpTemplateDownloader(_storage, url, tmpDir, new Completion(jobId), maxTemplateSizeInBytes, user, password, proxy, resourceType);
+ } else if (uri.getScheme().equalsIgnoreCase("file")) {
+ td = new LocalTemplateDownloader(_storage, url, tmpDir, maxTemplateSizeInBytes, new Completion(jobId));
+ } else if (uri.getScheme().equalsIgnoreCase("scp")) {
+ td = new ScpTemplateDownloader(_storage, url, tmpDir, maxTemplateSizeInBytes, new Completion(jobId));
+ } else if (uri.getScheme().equalsIgnoreCase("nfs")) {
+ td = null;
+ // TODO: implement this.
+ throw new CloudRuntimeException("Scheme is not supported " + url);
+ } else {
+ throw new CloudRuntimeException("Scheme is not supported " + url);
+ }
+ } else {
+ throw new CloudRuntimeException("Unable to download from URL: " + url);
+ }
+ DownloadJob dj = new DownloadJob(td, jobId, id, name, format, hvm, accountId, descr, cksum, installPathPrefix, resourceType);
+ jobs.put(jobId, dj);
+ threadPool.execute(td);
+
+ return jobId;
+ } catch (IOException e) {
+ s_logger.warn("Unable to download to " + tmpDir, e);
+ return null;
+ }
+ }
+
+ @Override
+ public String getDownloadError(String jobId) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj != null) {
+ return dj.getTemplateDownloader().getDownloadError();
+ }
+ return null;
+ }
+
+ public long getDownloadTemplateSize(String jobId) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj != null) {
+ return dj.getTemplatesize();
+ }
+ return 0;
+ }
+
+ public String getDownloadCheckSum(String jobId) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj != null) {
+ return dj.getChecksum();
+ }
+ return null;
+ }
+
+ public long getDownloadTemplatePhysicalSize(String jobId) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj != null) {
+ return dj.getTemplatePhysicalSize();
+ }
+ return 0;
+ }
+
+ // @Override
+ public String getDownloadLocalPath(String jobId) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj != null) {
+ return dj.getTemplateDownloader().getDownloadLocalPath();
+ }
+ return null;
+ }
+
+ @Override
+ public int getDownloadPct(String jobId) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj != null) {
+ return dj.getTemplateDownloader().getDownloadPercent();
+ }
+ return 0;
+ }
+
+ public static VMTemplateHostVO.Status convertStatus(Status tds) {
+ switch (tds) {
+ case ABORTED:
+ return VMTemplateHostVO.Status.NOT_DOWNLOADED;
+ case DOWNLOAD_FINISHED:
+ return VMTemplateHostVO.Status.DOWNLOAD_IN_PROGRESS;
+ case IN_PROGRESS:
+ return VMTemplateHostVO.Status.DOWNLOAD_IN_PROGRESS;
+ case NOT_STARTED:
+ return VMTemplateHostVO.Status.NOT_DOWNLOADED;
+ case RECOVERABLE_ERROR:
+ return VMTemplateHostVO.Status.NOT_DOWNLOADED;
+ case UNKNOWN:
+ return VMTemplateHostVO.Status.UNKNOWN;
+ case UNRECOVERABLE_ERROR:
+ return VMTemplateHostVO.Status.DOWNLOAD_ERROR;
+ case POST_DOWNLOAD_FINISHED:
+ return VMTemplateHostVO.Status.DOWNLOADED;
+ default:
+ return VMTemplateHostVO.Status.UNKNOWN;
+ }
+ }
+
+ @Override
+ public com.cloud.storage.VMTemplateHostVO.Status getDownloadStatus2(String jobId) {
+ return convertStatus(getDownloadStatus(jobId));
+ }
+
+ @Override
+ public DownloadAnswer handleDownloadCommand(SecondaryStorageResource resource, DownloadCommand cmd) {
+ ResourceType resourceType = cmd.getResourceType();
+ if (cmd instanceof DownloadProgressCommand) {
+ return handleDownloadProgressCmd( resource, (DownloadProgressCommand) cmd);
+ }
+
+ if (cmd.getUrl() == null) {
+ return new DownloadAnswer(resourceType.toString() + " is corrupted on storage due to an invalid url , cannot download", VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR);
+ }
+
+ if (cmd.getName() == null) {
+ return new DownloadAnswer("Invalid Name", VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR);
+ }
+
+ String installPathPrefix = null;
+ if (ResourceType.TEMPLATE == resourceType){
+ installPathPrefix = resource.getRootDir(cmd) + File.separator + _templateDir;
+ }else {
+ installPathPrefix = resource.getRootDir(cmd) + File.separator + _volumeDir;
+ }
+
+ String user = null;
+ String password = null;
+ if (cmd.getAuth() != null) {
+ user = cmd.getAuth().getUserName();
+ password = new String(cmd.getAuth().getPassword());
+ }
+ //TO DO - Define Volume max size as well
+ long maxDownloadSizeInBytes = (cmd.getMaxDownloadSizeInBytes() == null) ? TemplateDownloader.DEFAULT_MAX_TEMPLATE_SIZE_IN_BYTES : (cmd.getMaxDownloadSizeInBytes());
+ String jobId = downloadPublicTemplate(cmd.getId(), cmd.getUrl(), cmd.getName(), cmd.getFormat(), cmd.isHvm(), cmd.getAccountId(), cmd.getDescription(), cmd.getChecksum(), installPathPrefix, user, password, maxDownloadSizeInBytes, cmd.getProxy(), resourceType);
+ sleep();
+ if (jobId == null) {
+ return new DownloadAnswer("Internal Error", VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR);
+ }
+ return new DownloadAnswer(jobId, getDownloadPct(jobId), getDownloadError(jobId), getDownloadStatus2(jobId), getDownloadLocalPath(jobId), getInstallPath(jobId),
+ getDownloadTemplateSize(jobId), getDownloadTemplateSize(jobId), getDownloadCheckSum(jobId));
+ }
+
+ private void sleep() {
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ private DownloadAnswer handleDownloadProgressCmd(SecondaryStorageResource resource, DownloadProgressCommand cmd) {
+ String jobId = cmd.getJobId();
+ DownloadAnswer answer;
+ DownloadJob dj = null;
+ if (jobId != null) {
+ dj = jobs.get(jobId);
+ }
+ if (dj == null) {
+ if (cmd.getRequest() == RequestType.GET_OR_RESTART) {
+ DownloadCommand dcmd = new DownloadCommand(cmd);
+ return handleDownloadCommand(resource, dcmd);
+ } else {
+ return new DownloadAnswer("Cannot find job", VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR.UNKNOWN);
+ }
+ }
+ TemplateDownloader td = dj.getTemplateDownloader();
+ switch (cmd.getRequest()) {
+ case GET_STATUS:
+ break;
+ case ABORT:
+ td.stopDownload();
+ sleep();
+ break;
+ case RESTART:
+ td.stopDownload();
+ sleep();
+ threadPool.execute(td);
+ break;
+ case PURGE:
+ td.stopDownload();
+ answer = new DownloadAnswer(jobId, getDownloadPct(jobId), getDownloadError(jobId), getDownloadStatus2(jobId), getDownloadLocalPath(jobId),
+ getInstallPath(jobId), getDownloadTemplateSize(jobId), getDownloadTemplatePhysicalSize(jobId), getDownloadCheckSum(jobId));
+ jobs.remove(jobId);
+ return answer;
+ default:
+ break; // TODO
+ }
+ return new DownloadAnswer(jobId, getDownloadPct(jobId), getDownloadError(jobId), getDownloadStatus2(jobId), getDownloadLocalPath(jobId),
+ getInstallPath(jobId), getDownloadTemplateSize(jobId), getDownloadTemplatePhysicalSize(jobId), getDownloadCheckSum(jobId));
+ }
+
+ private String getInstallPath(String jobId) {
+ DownloadJob dj = jobs.get(jobId);
+ if (dj != null) {
+ return dj.getTmpltPath();
+ }
+ return null;
+ }
+
+ private String createTempDir(File rootDir, String name) throws IOException {
+
+ File f = File.createTempFile(name, "", rootDir);
+ f.delete();
+ f.mkdir();
+ _storage.setWorldReadableAndWriteable(f);
+ return f.getAbsolutePath();
+
+ }
+
+
+ private List<String> listVolumes(String rootdir) {
+ List<String> result = new ArrayList<String>();
+
+ Script script = new Script(listVolScr, s_logger);
+ script.add("-r", rootdir);
+ ZfsPathParser zpp = new ZfsPathParser(rootdir);
+ script.execute(zpp);
+ result.addAll(zpp.getPaths());
+ s_logger.info("found " + zpp.getPaths().size() + " volumes" + zpp.getPaths());
+ return result;
+ }
+
+
+
+ private List<String> listTemplates(String rootdir) {
+ List<String> result = new ArrayList<String>();
+
+ Script script = new Script(listTmpltScr, s_logger);
+ script.add("-r", rootdir);
+ ZfsPathParser zpp = new ZfsPathParser(rootdir);
+ script.execute(zpp);
+ result.addAll(zpp.getPaths());
+ s_logger.info("found " + zpp.getPaths().size() + " templates" + zpp.getPaths());
+ return result;
+ }
+
+ @Override
+ public Map<String, TemplateInfo> gatherTemplateInfo(String rootDir) {
+ Map<String, TemplateInfo> result = new HashMap<String, TemplateInfo>();
+ String templateDir = rootDir + File.separator + _templateDir;
+
+ if (! _storage.exists(templateDir)) {
+ _storage.mkdirs(templateDir);
+ }
+
+ List<String> publicTmplts = listTemplates(templateDir);
+ for (String tmplt : publicTmplts) {
+ String path = tmplt.substring(0, tmplt.lastIndexOf(File.separator));
+ TemplateLocation loc = new TemplateLocation(_storage, path);
+ try {
+ if (!loc.load()) {
+ s_logger.warn("Post download installation was not completed for " + path);
+ //loc.purge();
+ _storage.cleanup(path, templateDir);
+ continue;
+ }
+ } catch (IOException e) {
+ s_logger.warn("Unable to load template location " + path, e);
+ continue;
+ }
+
+ TemplateInfo tInfo = loc.getTemplateInfo();
+
+ if ((tInfo.getSize() == tInfo.getPhysicalSize())
+ && (tInfo.getInstallPath().endsWith(ImageFormat.OVA.getFileExtension()))) {
+ try {
+ Processor processor = _processors.get("VMDK Processor");
+ VmdkProcessor vmdkProcessor = (VmdkProcessor)processor;
+ long vSize =
+ vmdkProcessor.getTemplateVirtualSize(
+ path,
+ tInfo.getInstallPath().substring(
+ tInfo.getInstallPath().lastIndexOf(File.separator) + 1));
+ tInfo.setSize(vSize);
+ loc.updateVirtualSize(vSize);
+ loc.save();
+ } catch (Exception e) {
+ s_logger.error("Unable to get the virtual size of the template: " + tInfo.getInstallPath()
+ + " due to " + e.getMessage());
+ }
+ }
+
+ result.put(tInfo.getTemplateName(), tInfo);
+ s_logger.debug("Added template name: " + tInfo.getTemplateName() + ", path: " + tmplt);
+ }
+ /*
+ for (String tmplt : isoTmplts) {
+ String tmp[];
+ tmp = tmplt.split("/");
+ String tmpltName = tmp[tmp.length - 2];
+ tmplt = tmplt.substring(tmplt.lastIndexOf("iso/"));
+ TemplateInfo tInfo = new TemplateInfo(tmpltName, tmplt, false);
+ s_logger.debug("Added iso template name: " + tmpltName + ", path: " + tmplt);
+ result.put(tmpltName, tInfo);
+ }
+ */
+ return result;
+ }
+
+ @Override
+ public Map<Long, TemplateInfo> gatherVolumeInfo(String rootDir) {
+ Map<Long, TemplateInfo> result = new HashMap<Long, TemplateInfo>();
+ String volumeDir = rootDir + File.separator + _volumeDir;
+
+ if (! _storage.exists(volumeDir)) {
+ _storage.mkdirs(volumeDir);
+ }
+
+ List<String> vols = listVolumes(volumeDir);
+ for (String vol : vols) {
+ String path = vol.substring(0, vol.lastIndexOf(File.separator));
+ TemplateLocation loc = new TemplateLocation(_storage, path);
+ try {
+ if (!loc.load()) {
+ s_logger.warn("Post download installation was not completed for " + path);
+ //loc.purge();
+ _storage.cleanup(path, volumeDir);
+ continue;
+ }
+ } catch (IOException e) {
+ s_logger.warn("Unable to load volume location " + path, e);
+ continue;
+ }
+
+ TemplateInfo vInfo = loc.getTemplateInfo();
+
+ if ((vInfo.getSize() == vInfo.getPhysicalSize())
+ && (vInfo.getInstallPath().endsWith(ImageFormat.OVA.getFileExtension()))) {
+ try {
+ Processor processor = _processors.get("VMDK Processor");
+ VmdkProcessor vmdkProcessor = (VmdkProcessor)processor;
+ long vSize =
+ vmdkProcessor.getTemplateVirtualSize(
+ path,
+ vInfo.getInstallPath().substring(
+ vInfo.getInstallPath().lastIndexOf(File.separator) + 1));
+ vInfo.setSize(vSize);
+ loc.updateVirtualSize(vSize);
+ loc.save();
+ } catch (Exception e) {
+ s_logger.error("Unable to get the virtual size of the volume: " + vInfo.getInstallPath()
+ + " due to " + e.getMessage());
+ }
+ }
+
+ result.put(vInfo.getId(), vInfo);
+ s_logger.debug("Added volume name: " + vInfo.getTemplateName() + ", path: " + vol);
+ }
+ return result;
+ }
+
+ private int deleteDownloadDirectories(File downloadPath, int deleted) {
+ try {
+ if (downloadPath.exists()) {
+ File[] files = downloadPath.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ if (files[i].isDirectory()) {
+ deleteDownloadDirectories(files[i], deleted);
+ files[i].delete();
+ deleted++;
+ } else {
+ files[i].delete();
+ deleted++;
+ }
+ }
+ }
+ } catch (Exception ex) {
+ s_logger.info("Failed to clean up template downloads directory " + ex.toString());
+ }
+ return deleted;
+ }
+
+ public static class ZfsPathParser extends OutputInterpreter {
+ String _parent;
+ List<String> paths = new ArrayList<String>();
+
+ public ZfsPathParser(String parent) {
+ _parent = parent;
+ }
+
+ @Override
+ public String interpret(BufferedReader reader) throws IOException {
+ String line = null;
+ while ((line = reader.readLine()) != null) {
+ paths.add(line);
+ }
+ return null;
+ }
+
+ public List<String> getPaths() {
+ return paths;
+ }
+
+ @Override
+ public boolean drain() {
+ return true;
+ }
+ }
+
+ public DownloadManagerImpl() {
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+ _name = name;
+
+ String value = null;
+
+ _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey);
+ if (_storage == null) {
+ value = (String) params.get(StorageLayer.ClassConfigKey);
+ if (value == null) {
+ throw new ConfigurationException("Unable to find the storage layer");
+ }
+
+ Class<StorageLayer> clazz;
+ try {
+ clazz = (Class<StorageLayer>) Class.forName(value);
+ _storage = clazz.newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Unable to instantiate " + value);
+ } catch (InstantiationException e) {
+ throw new ConfigurationException("Unable to instantiate " + value);
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException("Unable to instantiate " + value);
+ }
+ }
+ String useSsl = (String)params.get("sslcopy");
+ if (useSsl != null) {
+ _sslCopy = Boolean.parseBoolean(useSsl);
+
+ }
+ String inSystemVM = (String)params.get("secondary.storage.vm");
+ if (inSystemVM != null && "true".equalsIgnoreCase(inSystemVM)) {
+ s_logger.info("DownloadManager: starting additional services since we are inside system vm");
+ startAdditionalServices();
+ blockOutgoingOnPrivate();
+ }
+
+ value = (String) params.get("install.timeout.pergig");
+ this.installTimeoutPerGig = NumbersUtil.parseInt(value, 15 * 60) * 1000;
+
+ value = (String) params.get("install.numthreads");
+ final int numInstallThreads = NumbersUtil.parseInt(value, 10);
+
+ String scriptsDir = (String) params.get("template.scripts.dir");
+ if (scriptsDir == null) {
+ scriptsDir = "scripts/storage/secondary";
+ }
+
+ listTmpltScr = Script.findScript(scriptsDir, "listvmtmplt.sh");
+ if (listTmpltScr == null) {
+ throw new ConfigurationException("Unable to find the listvmtmplt.sh");
+ }
+ s_logger.info("listvmtmplt.sh found in " + listTmpltScr);
+
+ createTmpltScr = Script.findScript(scriptsDir, "createtmplt.sh");
+ if (createTmpltScr == null) {
+ throw new ConfigurationException("Unable to find createtmplt.sh");
+ }
+ s_logger.info("createtmplt.sh found in " + createTmpltScr);
+
+ listVolScr = Script.findScript(scriptsDir, "listvolume.sh");
+ if (listVolScr == null) {
+ throw new ConfigurationException("Unable to find the listvolume.sh");
+ }
+ s_logger.info("listvolume.sh found in " + listVolScr);
+
+ createVolScr = Script.findScript(scriptsDir, "createvolume.sh");
+ if (createVolScr == null) {
+ throw new ConfigurationException("Unable to find createvolume.sh");
+ }
+ s_logger.info("createvolume.sh found in " + createVolScr);
+
+ _processors = new HashMap<String, Processor>();
+
+ Processor processor = new VhdProcessor();
+ processor.configure("VHD Processor", params);
+ _processors.put("VHD Processor", processor);
+
+ processor = new IsoProcessor();
+ processor.configure("ISO Processor", params);
+ _processors.put("ISO Processor", processor);
+
+ processor = new QCOW2Processor();
+ processor.configure("QCOW2 Processor", params);
+ _processors.put("QCOW2 Processor", processor);
+
+ processor = new VmdkProcessor();
+ processor.configure("VMDK Processor", params);
+ _processors.put("VMDK Processor", processor);
+
+ processor = new RawImageProcessor();
+ processor.configure("Raw Image Processor", params);
+ _processors.put("Raw Image Processor", processor);
+
+ _templateDir = (String) params.get("public.templates.root.dir");
+ if (_templateDir == null) {
+ _templateDir = TemplateConstants.DEFAULT_TMPLT_ROOT_DIR;
+ }
+ _templateDir += File.separator + TemplateConstants.DEFAULT_TMPLT_FIRST_LEVEL_DIR;
+ _volumeDir = TemplateConstants.DEFAULT_VOLUME_ROOT_DIR + File.separator;
+ // Add more processors here.
+ threadPool = Executors.newFixedThreadPool(numInstallThreads);
+ return true;
+ }
+
+ private void blockOutgoingOnPrivate() {
+ Script command = new Script("/bin/bash", s_logger);
+ String intf = "eth1";
+ command.add("-c");
+ command.add("iptables -A OUTPUT -o " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "80" + " -j REJECT;" +
+ "iptables -A OUTPUT -o " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j REJECT;");
+
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in blocking outgoing to port 80/443 err=" + result );
+ return;
+ }
+ }
+
+ @Override
+ public String getName() {
+ return _name;
+ }
+
+ @Override
+ public boolean start() {
+ return true;
+ }
+
+ @Override
+ public boolean stop() {
+ return true;
+ }
+
+ private void startAdditionalServices() {
+
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("if [ -d /etc/apache2 ] ; then service apache2 stop; else service httpd stop; fi ");
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in stopping httpd service err=" + result );
+ }
+ String port = Integer.toString(TemplateConstants.DEFAULT_TMPLT_COPY_PORT);
+ String intf = TemplateConstants.DEFAULT_TMPLT_COPY_INTF;
+
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + port + " -j ACCEPT;" +
+ "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j ACCEPT;");
+
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in opening up httpd port err=" + result );
+ return;
+ }
+
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("if [ -d /etc/apache2 ] ; then service apache2 start; else service httpd start; fi ");
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in starting httpd service err=" + result );
+ return;
+ }
+ command = new Script("mkdir", s_logger);
+ command.add("-p");
+ command.add("/var/www/html/copy/template");
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in creating directory =" + result );
+ return;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManager.java
----------------------------------------------------------------------
diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManager.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManager.java
new file mode 100755
index 0000000..14de150
--- /dev/null
+++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManager.java
@@ -0,0 +1,87 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.storage.template;
+
+import org.apache.cloudstack.storage.resource.SecondaryStorageResource;
+
+import com.cloud.agent.api.storage.CreateEntityDownloadURLAnswer;
+import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.DeleteEntityDownloadURLAnswer;
+import com.cloud.agent.api.storage.DeleteEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.UploadAnswer;
+import com.cloud.agent.api.storage.UploadCommand;
+import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.Upload.Status;
+import com.cloud.storage.template.TemplateUploader;
+import com.cloud.utils.component.Manager;
+
+public interface UploadManager extends Manager {
+
+
+ /**
+ * @param jobId job Id
+ * @return status of the upload job
+ */
+ public TemplateUploader.Status getUploadStatus(String jobId);
+
+ /**
+ * @param jobId job Id
+ * @return status of the upload job
+ */
+ public Status getUploadStatus2(String jobId);
+
+ /**
+ * Get the upload percent of a upload job
+ * @param jobId job Id
+ * @return
+ */
+ public int getUploadPct(String jobId);
+
+ /**
+ * Get the upload error if any
+ * @param jobId job Id
+ * @return
+ */
+ public String getUploadError(String jobId);
+
+ /**
+ * Get the local path for the upload
+ * @param jobId job Id
+ * @return
+ public String getUploadLocalPath(String jobId);
+ */
+
+ /** Handle upload commands from the management server
+ * @param cmd cmd from server
+ * @return answer representing status of upload.
+ */
+ public UploadAnswer handleUploadCommand(SecondaryStorageResource resource, UploadCommand cmd);
+
+ public String getPublicTemplateRepo();
+
+
+ String uploadPublicTemplate(long id, String url, String name,
+ ImageFormat format, Long accountId, String descr,
+ String cksum, String installPathPrefix, String user,
+ String password, long maxTemplateSizeInBytes);
+
+
+ CreateEntityDownloadURLAnswer handleCreateEntityURLCommand(CreateEntityDownloadURLCommand cmd);
+
+ DeleteEntityDownloadURLAnswer handleDeleteEntityDownloadURLCommand(DeleteEntityDownloadURLCommand cmd);
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManagerImpl.java
----------------------------------------------------------------------
diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManagerImpl.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManagerImpl.java
new file mode 100755
index 0000000..88623a9
--- /dev/null
+++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/UploadManagerImpl.java
@@ -0,0 +1,648 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.storage.template;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import javax.naming.ConfigurationException;
+
+import org.apache.cloudstack.storage.resource.SecondaryStorageResource;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.storage.CreateEntityDownloadURLAnswer;
+import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.DeleteEntityDownloadURLAnswer;
+import com.cloud.agent.api.storage.DeleteEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.UploadAnswer;
+import com.cloud.agent.api.storage.UploadCommand;
+import com.cloud.agent.api.storage.UploadProgressCommand;
+import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.StorageLayer;
+import com.cloud.storage.Upload;
+import com.cloud.storage.UploadVO;
+import com.cloud.storage.template.FtpTemplateUploader;
+import com.cloud.storage.template.Processor;
+import com.cloud.storage.template.TemplateUploader;
+import com.cloud.storage.template.TemplateUploader.Status;
+import com.cloud.storage.template.TemplateUploader.UploadCompleteCallback;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+
+public class UploadManagerImpl extends ManagerBase implements UploadManager {
+
+
+ public class Completion implements UploadCompleteCallback {
+ private final String jobId;
+
+ public Completion(String jobId) {
+ this.jobId = jobId;
+ }
+
+ @Override
+ public void uploadComplete(Status status) {
+ setUploadStatus(jobId, status);
+ }
+ }
+
+ private static class UploadJob {
+ private final TemplateUploader tu;
+ private final String jobId;
+ private final String name;
+ private final ImageFormat format;
+ private String tmpltPath;
+ private String description;
+ private String checksum;
+ private Long accountId;
+ private String installPathPrefix;
+ private long templatesize;
+ private long id;
+
+ public UploadJob(TemplateUploader tu, String jobId, long id, String name, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix) {
+ super();
+ this.tu = tu;
+ this.jobId = jobId;
+ this.name = name;
+ this.format = format;
+ this.accountId = accountId;
+ this.description = descr;
+ this.checksum = cksum;
+ this.installPathPrefix = installPathPrefix;
+ this.templatesize = 0;
+ this.id = id;
+ }
+
+ public TemplateUploader getTd() {
+ return tu;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getChecksum() {
+ return checksum;
+ }
+
+ public UploadJob(TemplateUploader td, String jobId, UploadCommand cmd) {
+ this.tu = td;
+ this.jobId = jobId;
+ this.name = cmd.getName();
+ this.format = cmd.getFormat();
+ }
+
+ public TemplateUploader getTemplateUploader() {
+ return tu;
+ }
+
+ public String getJobId() {
+ return jobId;
+ }
+
+ public String getTmpltName() {
+ return name;
+ }
+
+ public ImageFormat getFormat() {
+ return format;
+ }
+
+ public Long getAccountId() {
+ return accountId;
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public void setTmpltPath(String tmpltPath) {
+ this.tmpltPath = tmpltPath;
+ }
+
+ public String getTmpltPath() {
+ return tmpltPath;
+ }
+
+ public String getInstallPathPrefix() {
+ return installPathPrefix;
+ }
+
+ public void cleanup() {
+ if (tu != null) {
+ String upldPath = tu.getUploadLocalPath();
+ if (upldPath != null) {
+ File f = new File(upldPath);
+ f.delete();
+ }
+ }
+ }
+
+ public void setTemplatesize(long templatesize) {
+ this.templatesize = templatesize;
+ }
+
+ public long getTemplatesize() {
+ return templatesize;
+ }
+ }
+ public static final Logger s_logger = Logger.getLogger(UploadManagerImpl.class);
+ private ExecutorService threadPool;
+ private final Map<String, UploadJob> jobs = new ConcurrentHashMap<String, UploadJob>();
+ private String parentDir;
+ private List<Processor> _processors;
+ private String publicTemplateRepo;
+ private final String extractMountPoint = "/mnt/SecStorage/extractmnt";
+ private StorageLayer _storage;
+ private int installTimeoutPerGig;
+ private boolean _sslCopy;
+ private boolean hvm;
+
+
+ @Override
+ public String uploadPublicTemplate(long id, String url, String name,
+ ImageFormat format, Long accountId, String descr,
+ String cksum, String installPathPrefix, String userName,
+ String passwd, long templateSizeInBytes) {
+
+ UUID uuid = UUID.randomUUID();
+ String jobId = uuid.toString();
+
+ String completePath = parentDir + File.separator + installPathPrefix;
+ s_logger.debug("Starting upload from " + completePath);
+
+ URI uri;
+ try {
+ uri = new URI(url);
+ } catch (URISyntaxException e) {
+ s_logger.error("URI is incorrect: " + url);
+ throw new CloudRuntimeException("URI is incorrect: " + url);
+ }
+ TemplateUploader tu;
+ if ((uri != null) && (uri.getScheme() != null)) {
+ if (uri.getScheme().equalsIgnoreCase("ftp")) {
+ tu = new FtpTemplateUploader(completePath, url, new Completion(jobId), templateSizeInBytes);
+ } else {
+ s_logger.error("Scheme is not supported " + url);
+ throw new CloudRuntimeException("Scheme is not supported " + url);
+ }
+ } else {
+ s_logger.error("Unable to download from URL: " + url);
+ throw new CloudRuntimeException("Unable to download from URL: " + url);
+ }
+ UploadJob uj = new UploadJob(tu, jobId, id, name, format, hvm, accountId, descr, cksum, installPathPrefix);
+ jobs.put(jobId, uj);
+ threadPool.execute(tu);
+
+ return jobId;
+
+ }
+
+ @Override
+ public String getUploadError(String jobId) {
+ UploadJob uj = jobs.get(jobId);
+ if (uj != null) {
+ return uj.getTemplateUploader().getUploadError();
+ }
+ return null;
+ }
+
+ @Override
+ public int getUploadPct(String jobId) {
+ UploadJob uj = jobs.get(jobId);
+ if (uj != null) {
+ return uj.getTemplateUploader().getUploadPercent();
+ }
+ return 0;
+ }
+
+ @Override
+ public Status getUploadStatus(String jobId) {
+ UploadJob job = jobs.get(jobId);
+ if (job != null) {
+ TemplateUploader tu = job.getTemplateUploader();
+ if (tu != null) {
+ return tu.getStatus();
+ }
+ }
+ return Status.UNKNOWN;
+ }
+
+ public static UploadVO.Status convertStatus(Status tds) {
+ switch (tds) {
+ case ABORTED:
+ return UploadVO.Status.NOT_UPLOADED;
+ case UPLOAD_FINISHED:
+ return UploadVO.Status.UPLOAD_IN_PROGRESS;
+ case IN_PROGRESS:
+ return UploadVO.Status.UPLOAD_IN_PROGRESS;
+ case NOT_STARTED:
+ return UploadVO.Status.NOT_UPLOADED;
+ case RECOVERABLE_ERROR:
+ return UploadVO.Status.NOT_UPLOADED;
+ case UNKNOWN:
+ return UploadVO.Status.UNKNOWN;
+ case UNRECOVERABLE_ERROR:
+ return UploadVO.Status.UPLOAD_ERROR;
+ case POST_UPLOAD_FINISHED:
+ return UploadVO.Status.UPLOADED;
+ default:
+ return UploadVO.Status.UNKNOWN;
+ }
+ }
+
+ @Override
+ public com.cloud.storage.UploadVO.Status getUploadStatus2(String jobId) {
+ return convertStatus(getUploadStatus(jobId));
+ }
+ @Override
+ public String getPublicTemplateRepo() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private UploadAnswer handleUploadProgressCmd(UploadProgressCommand cmd) {
+ String jobId = cmd.getJobId();
+ UploadAnswer answer;
+ UploadJob uj = null;
+ if (jobId != null)
+ uj = jobs.get(jobId);
+ if (uj == null) {
+ return new UploadAnswer(null, 0, "Cannot find job", com.cloud.storage.UploadVO.Status.UNKNOWN, "", "", 0);
+ }
+ TemplateUploader td = uj.getTemplateUploader();
+ switch (cmd.getRequest()) {
+ case GET_STATUS:
+ break;
+ case ABORT:
+ td.stopUpload();
+ sleep();
+ break;
+ /*case RESTART:
+ td.stopUpload();
+ sleep();
+ threadPool.execute(td);
+ break;*/
+ case PURGE:
+ td.stopUpload();
+ answer = new UploadAnswer(jobId, getUploadPct(jobId), getUploadError(jobId), getUploadStatus2(jobId), getUploadLocalPath(jobId), getInstallPath(jobId), getUploadTemplateSize(jobId));
+ jobs.remove(jobId);
+ return answer;
+ default:
+ break; // TODO
+ }
+ return new UploadAnswer(jobId, getUploadPct(jobId), getUploadError(jobId), getUploadStatus2(jobId), getUploadLocalPath(jobId), getInstallPath(jobId),
+ getUploadTemplateSize(jobId));
+ }
+
+ @Override
+ public UploadAnswer handleUploadCommand(SecondaryStorageResource resource, UploadCommand cmd) {
+ s_logger.warn("Handling the upload " +cmd.getInstallPath() + " " + cmd.getId());
+ if (cmd instanceof UploadProgressCommand) {
+ return handleUploadProgressCmd((UploadProgressCommand) cmd);
+ }
+
+ String user = null;
+ String password = null;
+ String jobId = uploadPublicTemplate(cmd.getId(), cmd.getUrl(), cmd.getName(),
+ cmd.getFormat(), cmd.getAccountId(), cmd.getDescription(),
+ cmd.getChecksum(), cmd.getInstallPath(), user, password,
+ cmd.getTemplateSizeInBytes());
+ sleep();
+ if (jobId == null) {
+ return new UploadAnswer(null, 0, "Internal Error", com.cloud.storage.UploadVO.Status.UPLOAD_ERROR, "", "", 0);
+ }
+ return new UploadAnswer(jobId, getUploadPct(jobId), getUploadError(jobId), getUploadStatus2(jobId), getUploadLocalPath(jobId), getInstallPath(jobId),
+ getUploadTemplateSize(jobId));
+ }
+
+ @Override
+ public CreateEntityDownloadURLAnswer handleCreateEntityURLCommand(CreateEntityDownloadURLCommand cmd){
+
+ boolean isApacheUp = checkAndStartApache();
+ if (!isApacheUp){
+ String errorString = "Error in starting Apache server ";
+ s_logger.error(errorString);
+ return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE);
+ }
+ // Create the directory structure so that its visible under apache server root
+ String extractDir = "/var/www/html/userdata/";
+ Script command = new Script("mkdir", s_logger);
+ command.add("-p");
+ command.add(extractDir);
+ String result = command.execute();
+ if (result != null) {
+ String errorString = "Error in creating directory =" + result;
+ s_logger.error(errorString);
+ return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE);
+ }
+
+ // Create a random file under the directory for security reasons.
+ String uuid = cmd.getExtractLinkUUID();
+ command = new Script("touch", s_logger);
+ command.add(extractDir + uuid);
+ result = command.execute();
+ if (result != null) {
+ String errorString = "Error in creating file " +uuid+ " ,error: " + result;
+ s_logger.warn(errorString);
+ return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE);
+ }
+
+
+ // Create a symbolic link from the actual directory to the template location. The entity would be directly visible under /var/www/html/userdata/cmd.getInstallPath();
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("ln -sf /mnt/SecStorage/" + cmd.getParent() + File.separator + cmd.getInstallPath() + " " + extractDir + uuid);
+ result = command.execute();
+ if (result != null) {
+ String errorString = "Error in linking err=" + result;
+ s_logger.error(errorString);
+ return new CreateEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE);
+ }
+
+ return new CreateEntityDownloadURLAnswer("", CreateEntityDownloadURLAnswer.RESULT_SUCCESS);
+
+ }
+
+ @Override
+ public DeleteEntityDownloadURLAnswer handleDeleteEntityDownloadURLCommand(DeleteEntityDownloadURLCommand cmd){
+
+ //Delete the soft link. Example path = volumes/8/74eeb2c6-8ab1-4357-841f-2e9d06d1f360.vhd
+ s_logger.warn("handleDeleteEntityDownloadURLCommand Path:"+cmd.getPath() + " Type:" +cmd.getType().toString());
+ String path = cmd.getPath();
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+
+ //We just need to remove the UUID.vhd
+ String extractUrl = cmd.getExtractUrl();
+ command.add("unlink /var/www/html/userdata/" +extractUrl.substring(extractUrl.lastIndexOf(File.separator) + 1));
+ String result = command.execute();
+ if (result != null) {
+ String errorString = "Error in deleting =" + result;
+ s_logger.warn(errorString);
+ return new DeleteEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE);
+ }
+
+ // If its a volume also delete the Hard link since it was created only for the purpose of download.
+ if(cmd.getType() == Upload.Type.VOLUME){
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("rm -f /mnt/SecStorage/" + cmd.getParentPath() +File.separator+ path);
+ s_logger.warn(" " +parentDir +File.separator+ path);
+ result = command.execute();
+ if (result != null) {
+ String errorString = "Error in linking err=" + result;
+ s_logger.warn(errorString);
+ return new DeleteEntityDownloadURLAnswer(errorString, CreateEntityDownloadURLAnswer.RESULT_FAILURE);
+ }
+ }
+
+ return new DeleteEntityDownloadURLAnswer("", CreateEntityDownloadURLAnswer.RESULT_SUCCESS);
+ }
+
+ private String getInstallPath(String jobId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private String getUploadLocalPath(String jobId) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ private long getUploadTemplateSize(String jobId){
+ UploadJob uj = jobs.get(jobId);
+ if (uj != null) {
+ return uj.getTemplatesize();
+ }
+ return 0;
+ }
+
+ @Override
+ public boolean configure(String name, Map<String, Object> params)
+ throws ConfigurationException {
+
+ String value = null;
+
+ _storage = (StorageLayer) params.get(StorageLayer.InstanceConfigKey);
+ if (_storage == null) {
+ value = (String) params.get(StorageLayer.ClassConfigKey);
+ if (value == null) {
+ throw new ConfigurationException("Unable to find the storage layer");
+ }
+
+ Class<StorageLayer> clazz;
+ try {
+ clazz = (Class<StorageLayer>) Class.forName(value);
+ _storage = clazz.newInstance();
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Unable to instantiate " + value);
+ } catch (InstantiationException e) {
+ throw new ConfigurationException("Unable to instantiate " + value);
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException("Unable to instantiate " + value);
+ }
+ }
+ String useSsl = (String)params.get("sslcopy");
+ if (useSsl != null) {
+ _sslCopy = Boolean.parseBoolean(useSsl);
+
+ }
+ String inSystemVM = (String)params.get("secondary.storage.vm");
+ if (inSystemVM != null && "true".equalsIgnoreCase(inSystemVM)) {
+ s_logger.info("UploadManager: starting additional services since we are inside system vm");
+ startAdditionalServices();
+ //blockOutgoingOnPrivate();
+ }
+
+ value = (String) params.get("install.timeout.pergig");
+ this.installTimeoutPerGig = NumbersUtil.parseInt(value, 15 * 60) * 1000;
+
+ value = (String) params.get("install.numthreads");
+ final int numInstallThreads = NumbersUtil.parseInt(value, 10);
+
+ String scriptsDir = (String) params.get("template.scripts.dir");
+ if (scriptsDir == null) {
+ scriptsDir = "scripts/storage/secondary";
+ }
+
+ // Add more processors here.
+ threadPool = Executors.newFixedThreadPool(numInstallThreads);
+
+ return true;
+ }
+
+ private void startAdditionalServices() {
+
+
+ Script command = new Script("rm", s_logger);
+ command.add("-rf");
+ command.add(extractMountPoint);
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in creating file " +extractMountPoint+ " ,error: " + result );
+ return;
+ }
+
+ command = new Script("touch", s_logger);
+ command.add(extractMountPoint);
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in creating file " +extractMountPoint+ " ,error: " + result );
+ return;
+ }
+
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("ln -sf " + parentDir + " " +extractMountPoint);
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in linking err=" + result );
+ return;
+ }
+
+ }
+
+ /**
+ * Get notified of change of job status. Executed in context of uploader thread
+ *
+ * @param jobId
+ * the id of the job
+ * @param status
+ * the status of the job
+ */
+ public void setUploadStatus(String jobId, Status status) {
+ UploadJob uj = jobs.get(jobId);
+ if (uj == null) {
+ s_logger.warn("setUploadStatus for jobId: " + jobId + ", status=" + status + " no job found");
+ return;
+ }
+ TemplateUploader tu = uj.getTemplateUploader();
+ s_logger.warn("Upload Completion for jobId: " + jobId + ", status=" + status);
+ s_logger.warn("UploadedBytes=" + tu.getUploadedBytes() + ", error=" + tu.getUploadError() + ", pct=" + tu.getUploadPercent());
+
+ switch (status) {
+ case ABORTED:
+ case NOT_STARTED:
+ case UNRECOVERABLE_ERROR:
+ // Delete the entity only if its a volume. TO DO - find a better way of finding it a volume.
+ if(uj.getTemplateUploader().getUploadLocalPath().indexOf("volume") > -1){
+ uj.cleanup();
+ }
+ break;
+ case UNKNOWN:
+ return;
+ case IN_PROGRESS:
+ s_logger.info("Resuming jobId: " + jobId + ", status=" + status);
+ tu.setResume(true);
+ threadPool.execute(tu);
+ break;
+ case RECOVERABLE_ERROR:
+ threadPool.execute(tu);
+ break;
+ case UPLOAD_FINISHED:
+ tu.setUploadError("Upload success, starting install ");
+ String result = postUpload(jobId);
+ if (result != null) {
+ s_logger.error("Failed post upload script: " + result);
+ tu.setStatus(Status.UNRECOVERABLE_ERROR);
+ tu.setUploadError("Failed post upload script: " + result);
+ } else {
+ s_logger.warn("Upload completed successfully at " + new SimpleDateFormat().format(new Date()));
+ tu.setStatus(Status.POST_UPLOAD_FINISHED);
+ tu.setUploadError("Upload completed successfully at " + new SimpleDateFormat().format(new Date()));
+ }
+ // Delete the entity only if its a volume. TO DO - find a better way of finding it a volume.
+ if(uj.getTemplateUploader().getUploadLocalPath().indexOf("volume") > -1){
+ uj.cleanup();
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private String postUpload(String jobId) {
+ return null;
+ }
+
+ private void sleep() {
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ private boolean checkAndStartApache() {
+
+ //Check whether the Apache server is running
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("if [ -d /etc/apache2 ] ; then service apache2 status | grep pid; else service httpd status | grep pid; fi ");
+ String result = command.execute();
+
+ //Apache Server is not running. Try to start it.
+ if (result != null) {
+
+ /*s_logger.warn("Apache server not running, trying to start it");
+ String port = Integer.toString(TemplateConstants.DEFAULT_TMPLT_COPY_PORT);
+ String intf = TemplateConstants.DEFAULT_TMPLT_COPY_INTF;
+
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("iptables -D INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + port + " -j DROP;" +
+ "iptables -D INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + port + " -j HTTP;" +
+ "iptables -D INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j DROP;" +
+ "iptables -D INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j HTTP;" +
+ "iptables -F HTTP;" +
+ "iptables -X HTTP;" +
+ "iptables -N HTTP;" +
+ "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + port + " -j DROP;" +
+ "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j DROP;" +
+ "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + port + " -j HTTP;" +
+ "iptables -I INPUT -i " + intf + " -p tcp -m state --state NEW -m tcp --dport " + "443" + " -j HTTP;");
+
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in opening up httpd port err=" + result );
+ return false;
+ }*/
+
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("if [ -d /etc/apache2 ] ; then service apache2 start; else service httpd start; fi ");
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in starting httpd service err=" + result );
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/tools/devcloud/pom.xml
----------------------------------------------------------------------
diff --git a/tools/devcloud/pom.xml b/tools/devcloud/pom.xml
index d7b82c9..d32d84b 100644
--- a/tools/devcloud/pom.xml
+++ b/tools/devcloud/pom.xml
@@ -142,5 +142,38 @@
</plugins>
</build>
</profile>
+ <profile>
+ <id>quicksvr</id>
+ <activation>
+ <property>
+ <name>quicksvr</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>1.2.1</version>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <executable>python</executable>
+ <arguments>
+ <argument>../marvin/marvin/deployDataCenter.py</argument>
+ <argument>-i</argument>
+ <argument>quickcloud.cfg</argument>
+ </arguments>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
</profiles>
</project>
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/tools/devcloud/quickcloud.cfg
----------------------------------------------------------------------
diff --git a/tools/devcloud/quickcloud.cfg b/tools/devcloud/quickcloud.cfg
new file mode 100644
index 0000000..0e1fb4f
--- /dev/null
+++ b/tools/devcloud/quickcloud.cfg
@@ -0,0 +1,120 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+{
+ "zones": [
+ {
+ "name": "QuickCloud00",
+ "enabled" : "True",
+ "details" : [
+ {"key" : "enable.secstorage.vm", "value": "False"},
+ {"key" : "enable.consoleproxy.vm", "value": "False"}
+ ],
+ "physical_networks": [
+ {
+ "broadcastdomainrange": "Zone",
+ "name": "test-network",
+ "traffictypes": [
+ {
+ "typ": "Guest"
+ },
+ {
+ "typ": "Management"
+ }
+ ],
+ "providers": [
+ {
+ "broadcastdomainrange": "ZONE",
+ "name": "VirtualRouter"
+ },
+ {
+ "broadcastdomainrange": "Pod",
+ "name": "SecurityGroupProvider"
+ }
+ ]
+ }
+ ],
+ "dns2": "4.4.4.4",
+ "dns1": "8.8.8.8",
+ "securitygroupenabled": "true",
+ "localstorageenabled": "true",
+ "networktype": "Basic",
+ "pods": [
+ {
+ "endip": "192.168.56.220",
+ "name": "test00",
+ "startip": "192.168.56.200",
+ "guestIpRanges": [
+ {
+ "startip": "192.168.56.100",
+ "endip": "192.168.56.199",
+ "netmask": "255.255.255.0",
+ "gateway": "192.168.56.1"
+ }
+ ],
+ "netmask": "255.255.255.0",
+ "clusters": [
+ {
+ "clustername": "test000",
+ "hypervisor": "XenServer",
+ "hosts": [
+ {
+ "username": "root",
+ "url": "http://192.168.56.10/",
+ "password": "password"
+ }
+ ],
+ "clustertype": "CloudManaged"
+ }
+ ],
+ "gateway": "192.168.56.1"
+ }
+ ],
+ "internaldns1": "192.168.56.1",
+ "secondaryStorages": [
+ {
+ "url": "nfs://192.168.56.10:/opt/storage/secondary"
+ }
+ ]
+ }
+ ],
+ "logger": [
+ {
+ "name": "TestClient",
+ "file": "testclient.log"
+ },
+ {
+ "name": "TestCase",
+ "file": "testcase.log"
+ }
+ ],
+ "mgtSvr": [
+ {
+ "mgtSvrIp": "127.0.0.1",
+ "port": 8096
+ }
+ ],
+ "dbSvr":
+ {
+ "dbSvr": "127.0.0.1",
+ "port": 3306,
+ "user": "cloud",
+ "passwd": "cloud",
+ "db": "cloud"
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/tools/marvin/marvin/cloudstackConnection.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/cloudstackConnection.py b/tools/marvin/marvin/cloudstackConnection.py
index e8b861e..1caeef3 100644
--- a/tools/marvin/marvin/cloudstackConnection.py
+++ b/tools/marvin/marvin/cloudstackConnection.py
@@ -162,9 +162,9 @@ class cloudConnection(object):
else:
requests.pop(param)
i = 0
- for v in value:
- for key, val in v.iteritems():
- requests["%s[%d].%s"%(param,i,key)] = val
+ for val in value:
+ for k,v in val.iteritems():
+ requests["%s[%d].%s"%(param,i,k)] = v
i = i + 1
if self.logging is not None:
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/tools/marvin/marvin/deployDataCenter.py
----------------------------------------------------------------------
diff --git a/tools/marvin/marvin/deployDataCenter.py b/tools/marvin/marvin/deployDataCenter.py
index cec920c..42bc5f9 100644
--- a/tools/marvin/marvin/deployDataCenter.py
+++ b/tools/marvin/marvin/deployDataCenter.py
@@ -270,6 +270,12 @@ class deployDataCenters():
zoneCmd.allocationstate = allocation_state
return self.apiClient.updateZone(zoneCmd)
+ def updateZoneDetails(self, zoneid, details):
+ zoneCmd = updateZone.updateZoneCmd()
+ zoneCmd.id = zoneid
+ zoneCmd.details = details
+ return self.apiClient.updateZone(zoneCmd)
+
def createZones(self, zones):
for zone in zones:
createzone = createZone.createZoneCmd()
@@ -320,7 +326,15 @@ class deployDataCenters():
zoneId)
self.createSecondaryStorages(zone.secondaryStorages, zoneId)
- self.enableZone(zoneId, "Enabled")
+
+ enabled = getattr(zone, 'enabled', 'True')
+ if enabled == 'True' or enabled == 'None':
+ self.enableZone(zoneId, "Enabled")
+ details = getattr(zone, 'details')
+ if details is not None:
+ det = [d.__dict__ for d in details]
+ self.updateZoneDetails(zoneId, det)
+
return
def isEipElbZone(self, zone):