You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pr...@apache.org on 2013/04/11 00:25:43 UTC
[02/54] [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/resource/NfsSecondaryStorageResource.java
----------------------------------------------------------------------
diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
new file mode 100755
index 0000000..6bcf98e
--- /dev/null
+++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/NfsSecondaryStorageResource.java
@@ -0,0 +1,1882 @@
+// 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.resource;
+
+import static com.cloud.utils.S3Utils.deleteDirectory;
+import static com.cloud.utils.S3Utils.getDirectory;
+import static com.cloud.utils.S3Utils.putDirectory;
+import static com.cloud.utils.StringUtils.join;
+import static com.cloud.utils.db.GlobalLock.executeWithNoWaitLock;
+import static java.lang.String.format;
+import static java.util.Arrays.asList;
+import static org.apache.commons.lang.StringUtils.substringAfterLast;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.net.URI;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.Callable;
+
+import javax.naming.ConfigurationException;
+
+import org.apache.cloudstack.storage.template.DownloadManager;
+import org.apache.cloudstack.storage.template.DownloadManagerImpl;
+import org.apache.cloudstack.storage.template.DownloadManagerImpl.ZfsPathParser;
+import org.apache.cloudstack.storage.template.UploadManager;
+import org.apache.cloudstack.storage.template.UploadManagerImpl;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CheckHealthAnswer;
+import com.cloud.agent.api.CheckHealthCommand;
+import com.cloud.agent.api.CleanupSnapshotBackupCommand;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.ComputeChecksumCommand;
+import com.cloud.agent.api.DeleteObjectFromSwiftCommand;
+import com.cloud.agent.api.DeleteSnapshotBackupCommand;
+import com.cloud.agent.api.DeleteSnapshotsDirCommand;
+import com.cloud.agent.api.DeleteTemplateFromS3Command;
+import com.cloud.agent.api.DownloadSnapshotFromS3Command;
+import com.cloud.agent.api.DownloadTemplateFromS3ToSecondaryStorageCommand;
+import com.cloud.agent.api.GetStorageStatsAnswer;
+import com.cloud.agent.api.GetStorageStatsCommand;
+import com.cloud.agent.api.PingCommand;
+import com.cloud.agent.api.PingStorageCommand;
+import com.cloud.agent.api.ReadyAnswer;
+import com.cloud.agent.api.ReadyCommand;
+import com.cloud.agent.api.SecStorageFirewallCfgCommand;
+import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig;
+import com.cloud.agent.api.SecStorageSetupAnswer;
+import com.cloud.agent.api.SecStorageSetupCommand;
+import com.cloud.agent.api.SecStorageSetupCommand.Certificates;
+import com.cloud.agent.api.SecStorageVMSetupCommand;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.agent.api.StartupSecondaryStorageCommand;
+import com.cloud.agent.api.UploadTemplateToS3FromSecondaryStorageCommand;
+import com.cloud.agent.api.downloadSnapshotFromSwiftCommand;
+import com.cloud.agent.api.downloadTemplateFromSwiftToSecondaryStorageCommand;
+import com.cloud.agent.api.uploadTemplateToSwiftFromSecondaryStorageCommand;
+import com.cloud.agent.api.storage.CreateEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.DeleteEntityDownloadURLCommand;
+import com.cloud.agent.api.storage.DeleteTemplateCommand;
+import com.cloud.agent.api.storage.DeleteVolumeCommand;
+import com.cloud.agent.api.storage.DownloadCommand;
+import com.cloud.agent.api.storage.DownloadProgressCommand;
+import com.cloud.agent.api.storage.ListTemplateAnswer;
+import com.cloud.agent.api.storage.ListTemplateCommand;
+import com.cloud.agent.api.storage.ListVolumeAnswer;
+import com.cloud.agent.api.storage.ListVolumeCommand;
+import com.cloud.agent.api.storage.UploadCommand;
+import com.cloud.agent.api.storage.ssCommand;
+import com.cloud.agent.api.to.S3TO;
+import com.cloud.agent.api.to.SwiftTO;
+import com.cloud.exception.InternalErrorException;
+import com.cloud.host.Host;
+import com.cloud.host.Host.Type;
+import com.cloud.resource.ServerResourceBase;
+import com.cloud.storage.StorageLayer;
+import com.cloud.storage.template.TemplateInfo;
+import com.cloud.storage.template.TemplateLocation;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.S3Utils;
+import com.cloud.utils.S3Utils.FileNamingStrategy;
+import com.cloud.utils.S3Utils.ObjectNamingStrategy;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
+import com.cloud.utils.script.OutputInterpreter;
+import com.cloud.utils.script.Script;
+import com.cloud.vm.SecondaryStorageVm;
+
+public class NfsSecondaryStorageResource extends ServerResourceBase implements
+SecondaryStorageResource {
+
+ private static final Logger s_logger = Logger
+ .getLogger(NfsSecondaryStorageResource.class);
+
+ private static final String TEMPLATE_ROOT_DIR = "template/tmpl";
+ private static final String SNAPSHOT_ROOT_DIR = "snapshots";
+
+ int _timeout;
+
+ String _instance;
+ String _dc;
+ String _pod;
+ String _guid;
+ String _role;
+ Map<String, Object> _params;
+ StorageLayer _storage;
+ protected boolean _inSystemVM = false;
+ boolean _sslCopy = false;
+
+ DownloadManager _dlMgr;
+ UploadManager _upldMgr;
+ private String _configSslScr;
+ private String _configAuthScr;
+ private String _configIpFirewallScr;
+ private String _publicIp;
+ private String _hostname;
+ private String _localgw;
+ private String _eth1mask;
+ private String _eth1ip;
+ private String _storageIp;
+ private String _storageNetmask;
+ private String _storageGateway;
+ private final List<String> nfsIps = new ArrayList<String>();
+ private String _parent = "/mnt/SecStorage";
+ final private String _tmpltDir = "/var/cloudstack/template";
+ final private String _tmpltpp = "template.properties";
+ @Override
+ public void disconnected() {
+ }
+
+ @Override
+ public Answer executeRequest(Command cmd) {
+ if (cmd instanceof DownloadProgressCommand) {
+ return _dlMgr.handleDownloadCommand(this, (DownloadProgressCommand)cmd);
+ } else if (cmd instanceof DownloadCommand) {
+ return _dlMgr.handleDownloadCommand(this, (DownloadCommand)cmd);
+ } else if (cmd instanceof UploadCommand) {
+ return _upldMgr.handleUploadCommand(this, (UploadCommand)cmd);
+ } else if (cmd instanceof CreateEntityDownloadURLCommand){
+ return _upldMgr.handleCreateEntityURLCommand((CreateEntityDownloadURLCommand)cmd);
+ } else if(cmd instanceof DeleteEntityDownloadURLCommand){
+ return _upldMgr.handleDeleteEntityDownloadURLCommand((DeleteEntityDownloadURLCommand)cmd);
+ } else if (cmd instanceof GetStorageStatsCommand) {
+ return execute((GetStorageStatsCommand)cmd);
+ } else if (cmd instanceof CheckHealthCommand) {
+ return new CheckHealthAnswer((CheckHealthCommand)cmd, true);
+ } else if (cmd instanceof DeleteTemplateCommand) {
+ return execute((DeleteTemplateCommand) cmd);
+ } else if (cmd instanceof DeleteVolumeCommand) {
+ return execute((DeleteVolumeCommand) cmd);
+ }else if (cmd instanceof ReadyCommand) {
+ return new ReadyAnswer((ReadyCommand)cmd);
+ } else if (cmd instanceof SecStorageFirewallCfgCommand){
+ return execute((SecStorageFirewallCfgCommand)cmd);
+ } else if (cmd instanceof SecStorageVMSetupCommand){
+ return execute((SecStorageVMSetupCommand)cmd);
+ } else if (cmd instanceof SecStorageSetupCommand){
+ return execute((SecStorageSetupCommand)cmd);
+ } else if (cmd instanceof ComputeChecksumCommand){
+ return execute((ComputeChecksumCommand)cmd);
+ } else if (cmd instanceof ListTemplateCommand){
+ return execute((ListTemplateCommand)cmd);
+ } else if (cmd instanceof ListVolumeCommand){
+ return execute((ListVolumeCommand)cmd);
+ }else if (cmd instanceof downloadSnapshotFromSwiftCommand){
+ return execute((downloadSnapshotFromSwiftCommand)cmd);
+ } else if (cmd instanceof DownloadSnapshotFromS3Command) {
+ return execute((DownloadSnapshotFromS3Command) cmd);
+ } else if (cmd instanceof DeleteSnapshotBackupCommand){
+ return execute((DeleteSnapshotBackupCommand)cmd);
+ } else if (cmd instanceof DeleteSnapshotsDirCommand){
+ return execute((DeleteSnapshotsDirCommand)cmd);
+ } else if (cmd instanceof downloadTemplateFromSwiftToSecondaryStorageCommand) {
+ return execute((downloadTemplateFromSwiftToSecondaryStorageCommand) cmd);
+ } else if (cmd instanceof DownloadTemplateFromS3ToSecondaryStorageCommand) {
+ return execute((DownloadTemplateFromS3ToSecondaryStorageCommand) cmd);
+ } else if (cmd instanceof uploadTemplateToSwiftFromSecondaryStorageCommand) {
+ return execute((uploadTemplateToSwiftFromSecondaryStorageCommand) cmd);
+ } else if (cmd instanceof UploadTemplateToS3FromSecondaryStorageCommand) {
+ return execute((UploadTemplateToS3FromSecondaryStorageCommand) cmd);
+ } else if (cmd instanceof DeleteObjectFromSwiftCommand) {
+ return execute((DeleteObjectFromSwiftCommand) cmd);
+ } else if (cmd instanceof DeleteTemplateFromS3Command) {
+ return execute((DeleteTemplateFromS3Command) cmd);
+ } else if (cmd instanceof CleanupSnapshotBackupCommand){
+ return execute((CleanupSnapshotBackupCommand)cmd);
+ } else {
+ return Answer.createUnsupportedCommandAnswer(cmd);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private String determineS3TemplateDirectory(final Long accountId,
+ final Long templateId) {
+ return join(asList(TEMPLATE_ROOT_DIR, accountId, templateId),
+ S3Utils.SEPARATOR);
+ }
+
+ @SuppressWarnings("unchecked")
+ private String determineStorageTemplatePath(final String storagePath,
+ final Long accountId, final Long templateId) {
+ return join(
+ asList(getRootDir(storagePath), TEMPLATE_ROOT_DIR, accountId,
+ templateId), File.separator);
+ }
+
+ private Answer execute(
+ final DownloadTemplateFromS3ToSecondaryStorageCommand cmd) {
+
+ final S3TO s3 = cmd.getS3();
+ final String storagePath = cmd.getStoragePath();
+ final Long accountId = cmd.getAccountId();
+ final Long templateId = cmd.getTemplateId();
+
+ try {
+
+ final File downloadDirectory = _storage
+ .getFile(determineStorageTemplatePath(storagePath,
+ accountId, templateId));
+ downloadDirectory.mkdirs();
+
+ if (!downloadDirectory.exists()) {
+ final String errMsg = format(
+ "Unable to create directory "
+ + "download directory %1$s for download of template id "
+ + "%2$s from S3.", downloadDirectory.getName(),
+ templateId);
+ s_logger.error(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+
+ getDirectory(s3, s3.getBucketName(),
+ determineS3TemplateDirectory(accountId, templateId),
+ downloadDirectory, new FileNamingStrategy() {
+ @Override
+ public String determineFileName(final String key) {
+ return substringAfterLast(key, S3Utils.SEPARATOR);
+ }
+ });
+
+ return new Answer(cmd, true, format("Successfully downloaded "
+ + "template id %1$s from S3 to directory %2$s", templateId,
+ downloadDirectory.getName()));
+
+ } catch (Exception e) {
+
+ final String errMsg = format("Failed to upload template id %1$s "
+ + "due to $2%s", templateId, e.getMessage());
+ s_logger.error(errMsg, e);
+ return new Answer(cmd, false, errMsg);
+
+ }
+
+ }
+
+ private Answer execute(downloadTemplateFromSwiftToSecondaryStorageCommand cmd) {
+ SwiftTO swift = cmd.getSwift();
+ String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
+ Long accountId = cmd.getAccountId();
+ Long templateId = cmd.getTemplateId();
+ String path = cmd.getPath();
+ String errMsg;
+ String lDir = null;
+ try {
+ String parent = getRootDir(secondaryStorageUrl);
+ lDir = parent + "/template/tmpl/" + accountId.toString() + "/" + templateId.toString();
+ String result = createLocalDir(lDir);
+ if (result != null) {
+ errMsg = "downloadTemplateFromSwiftToSecondaryStorageCommand failed due to Create local directory failed";
+ s_logger.warn(errMsg);
+ throw new InternalErrorException(errMsg);
+ }
+ String lPath = lDir + "/" + path;
+ result = swiftDownload(swift, "T-" + templateId.toString(), path, lPath);
+ if (result != null) {
+ errMsg = "failed to download template " + path + " from Swift to secondary storage " + lPath + " , err=" + result;
+ s_logger.warn(errMsg);
+ throw new CloudRuntimeException(errMsg);
+ }
+ path = "template.properties";
+ lPath = lDir + "/" + path;
+ result = swiftDownload(swift, "T-" + templateId.toString(), path, lPath);
+ if (result != null) {
+ errMsg = "failed to download template " + path + " from Swift to secondary storage " + lPath + " , err=" + result;
+ s_logger.warn(errMsg);
+ throw new CloudRuntimeException(errMsg);
+ }
+ return new Answer(cmd, true, "success");
+ } catch (Exception e) {
+ if (lDir != null) {
+ deleteLocalDir(lDir);
+ }
+ errMsg = cmd + " Command failed due to " + e.toString();
+ s_logger.warn(errMsg, e);
+ return new Answer(cmd, false, errMsg);
+ }
+ }
+
+ private Answer execute(uploadTemplateToSwiftFromSecondaryStorageCommand cmd) {
+ SwiftTO swift = cmd.getSwift();
+ String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
+ Long accountId = cmd.getAccountId();
+ Long templateId = cmd.getTemplateId();
+ try {
+ String parent = getRootDir(secondaryStorageUrl);
+ String lPath = parent + "/template/tmpl/" + accountId.toString() + "/" + templateId.toString();
+ if (!_storage.isFile(lPath + "/template.properties")) {
+ String errMsg = cmd + " Command failed due to template doesn't exist ";
+ s_logger.debug(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+ String result = swiftUpload(swift, "T-" + templateId.toString(), lPath, "*");
+ if (result != null) {
+ String errMsg = "failed to upload template from secondary storage " + lPath + " to swift , err=" + result;
+ s_logger.debug(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+ return new Answer(cmd, true, "success");
+ } catch (Exception e) {
+ String errMsg = cmd + " Command failed due to " + e.toString();
+ s_logger.warn(errMsg, e);
+ return new Answer(cmd, false, errMsg);
+ }
+ }
+
+ private Answer execute(UploadTemplateToS3FromSecondaryStorageCommand cmd) {
+
+ final S3TO s3 = cmd.getS3();
+ final Long accountId = cmd.getAccountId();
+ final Long templateId = cmd.getTemplateId();
+
+ try {
+
+ final String templatePath = determineStorageTemplatePath(
+ cmd.getStoragePath(), accountId, templateId);
+
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Found template id " + templateId
+ + " account id " + accountId + " from directory "
+ + templatePath + " to upload to S3.");
+ }
+
+ if (!_storage.isDirectory(templatePath)) {
+ final String errMsg = format("S3 Sync Failure: Directory %1$s"
+ + "for template id %2$s does not exist.", templatePath,
+ templateId);
+ s_logger.error(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+
+ if (!_storage.isFile(templatePath + "/template.properties")) {
+ final String errMsg = format("S3 Sync Failure: Template id "
+ + "%1$s does not exist on the file system.",
+ templatePath);
+ s_logger.error(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug(format(
+ "Pushing template id %1$s from %2$s to S3...",
+ templateId, templatePath));
+ }
+
+ final String bucket = s3.getBucketName();
+ putDirectory(s3, bucket, _storage.getFile(templatePath),
+ new FilenameFilter() {
+ @Override
+ public boolean accept(final File directory,
+ final String fileName) {
+ File fileToUpload = new File(directory.getAbsolutePath() + "/" + fileName);
+ return !fileName.startsWith(".") && !fileToUpload.isDirectory();
+ }
+ }, new ObjectNamingStrategy() {
+ @Override
+ public String determineKey(final File file) {
+ s_logger.debug(String
+ .format("Determining key using account id %1$s and template id %2$s",
+ accountId, templateId));
+ return join(
+ asList(determineS3TemplateDirectory(
+ accountId, templateId), file
+ .getName()), S3Utils.SEPARATOR);
+ }
+ });
+
+ return new Answer(
+ cmd,
+ true,
+ format("Uploaded the contents of directory %1$s for template id %2$s to S3 bucket %3$s",
+ templatePath, templateId, bucket));
+
+ } catch (Exception e) {
+
+ final String errMsg = format("Failed to upload template id %1$s",
+ templateId);
+ s_logger.error(errMsg, e);
+ return new Answer(cmd, false, errMsg);
+
+ }
+
+ }
+
+ private Answer execute(DeleteObjectFromSwiftCommand cmd) {
+ SwiftTO swift = cmd.getSwift();
+ String container = cmd.getContainer();
+ String object = cmd.getObject();
+ if (object == null) {
+ object = "";
+ }
+ try {
+ String result = swiftDelete(swift, container, object);
+ if (result != null) {
+ String errMsg = "failed to delete object " + container + "/" + object + " , err=" + result;
+ s_logger.warn(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+ return new Answer(cmd, true, "success");
+ } catch (Exception e) {
+ String errMsg = cmd + " Command failed due to " + e.toString();
+ s_logger.warn(errMsg, e);
+ return new Answer(cmd, false, errMsg);
+ }
+
+ }
+
+ private Answer execute(final DeleteTemplateFromS3Command cmd) {
+
+ final S3TO s3 = cmd.getS3();
+ final Long accountId = cmd.getAccountId();
+ final Long templateId = cmd.getTemplateId();
+
+ if (accountId == null || (accountId != null && accountId <= 0)) {
+ final String errorMessage = "No account id specified for S3 template deletion.";
+ s_logger.error(errorMessage);
+ return new Answer(cmd, false, errorMessage);
+ }
+
+ if (templateId == null || (templateId != null && templateId <= 0)) {
+ final String errorMessage = "No template id specified for S3 template deletion.";
+ s_logger.error(errorMessage);
+ return new Answer(cmd, false, errorMessage);
+ }
+
+ if (s3 == null) {
+ final String errorMessge = "No S3 client options provided";
+ s_logger.error(errorMessge);
+ return new Answer(cmd, false, errorMessge);
+ }
+
+ final String bucket = s3.getBucketName();
+ try {
+ deleteDirectory(s3, bucket,
+ determineS3TemplateDirectory(templateId, accountId));
+ return new Answer(cmd, true, String.format(
+ "Deleted template %1%s from bucket %2$s.", templateId,
+ bucket));
+ } catch (Exception e) {
+ final String errorMessage = String
+ .format("Failed to delete templaet id %1$s from bucket %2$s due to the following error: %3$s",
+ templateId, bucket, e.getMessage());
+ s_logger.error(errorMessage, e);
+ return new Answer(cmd, false, errorMessage);
+ }
+
+ }
+
+ String swiftDownload(SwiftTO swift, String container, String rfilename, String lFullPath) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A "
+ + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() + " -K " + swift.getKey()
+ + " download " + container + " " + rfilename + " -o " + lFullPath);
+ OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
+ String result = command.execute(parser);
+ if (result != null) {
+ String errMsg = "swiftDownload failed err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ if (parser.getLines() != null) {
+ String[] lines = parser.getLines().split("\\n");
+ for (String line : lines) {
+ if (line.contains("Errno") || line.contains("failed")) {
+ String errMsg = "swiftDownload failed , err=" + lines.toString();
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ }
+ }
+ return null;
+
+ }
+
+ String swiftDownloadContainer(SwiftTO swift, String container, String ldir) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("cd " + ldir + ";/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() + " -K "
+ + swift.getKey() + " download " + container);
+ OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
+ String result = command.execute(parser);
+ if (result != null) {
+ String errMsg = "swiftDownloadContainer failed err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ if (parser.getLines() != null) {
+ String[] lines = parser.getLines().split("\\n");
+ for (String line : lines) {
+ if (line.contains("Errno") || line.contains("failed")) {
+ String errMsg = "swiftDownloadContainer failed , err=" + lines.toString();
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ }
+ }
+ return null;
+
+ }
+
+ String swiftUpload(SwiftTO swift, String container, String lDir, String lFilename) {
+ long SWIFT_MAX_SIZE = 5L * 1024L * 1024L * 1024L;
+ List<String> files = new ArrayList<String>();
+ if (lFilename.equals("*")) {
+ File dir = new File(lDir);
+ for (String file : dir.list()) {
+ if (file.startsWith(".")) {
+ continue;
+ }
+ files.add(file);
+ }
+ } else {
+ files.add(lFilename);
+ }
+
+ for (String file : files) {
+ File f = new File(lDir + "/" + file);
+ long size = f.length();
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ if (size <= SWIFT_MAX_SIZE) {
+ command.add("cd " + lDir + ";/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName()
+ + " -K " + swift.getKey() + " upload " + container + " " + file);
+ } else {
+ command.add("cd " + lDir + ";/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName()
+ + " -K " + swift.getKey() + " upload -S " + SWIFT_MAX_SIZE + " " + container + " " + file);
+ }
+ OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
+ String result = command.execute(parser);
+ if (result != null) {
+ String errMsg = "swiftUpload failed , err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ if (parser.getLines() != null) {
+ String[] lines = parser.getLines().split("\\n");
+ for (String line : lines) {
+ if (line.contains("Errno") || line.contains("failed")) {
+ String errMsg = "swiftUpload failed , err=" + lines.toString();
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ String[] swiftList(SwiftTO swift, String container, String rFilename) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A " + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() + " -K "
+ + swift.getKey() + " list " + container + " " + rFilename);
+ OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
+ String result = command.execute(parser);
+ if (result == null && parser.getLines() != null) {
+ String[] lines = parser.getLines().split("\\n");
+ return lines;
+ } else {
+ if (result != null) {
+ String errMsg = "swiftList failed , err=" + result;
+ s_logger.warn(errMsg);
+ } else {
+ String errMsg = "swiftList failed, no lines returns";
+ s_logger.warn(errMsg);
+ }
+ }
+ return null;
+ }
+
+ String swiftDelete(SwiftTO swift, String container, String object) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("/usr/bin/python /usr/local/cloud/systemvm/scripts/storage/secondary/swift -A "
+ + swift.getUrl() + " -U " + swift.getAccount() + ":" + swift.getUserName() + " -K " + swift.getKey()
+ + " delete " + container + " " + object);
+ OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
+ String result = command.execute(parser);
+ if (result != null) {
+ String errMsg = "swiftDelete failed , err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ if (parser.getLines() != null) {
+ String[] lines = parser.getLines().split("\\n");
+ for (String line : lines) {
+ if (line.contains("Errno") || line.contains("failed")) {
+ String errMsg = "swiftDelete failed , err=" + lines.toString();
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ }
+ }
+ return null;
+ }
+
+
+ public Answer execute(DeleteSnapshotsDirCommand cmd){
+ String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
+ Long accountId = cmd.getAccountId();
+ Long volumeId = cmd.getVolumeId();
+ try {
+ String parent = getRootDir(secondaryStorageUrl);
+ String lPath = parent + "/snapshots/" + String.valueOf(accountId) + "/" + String.valueOf(volumeId) + "/*";
+ String result = deleteLocalFile(lPath);
+ if (result != null) {
+ String errMsg = "failed to delete all snapshots " + lPath + " , err=" + result;
+ s_logger.warn(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+ return new Answer(cmd, true, "success");
+ } catch (Exception e) {
+ String errMsg = cmd + " Command failed due to " + e.toString();
+ s_logger.warn(errMsg, e);
+ return new Answer(cmd, false, errMsg);
+ }
+ }
+
+ public Answer execute(final DownloadSnapshotFromS3Command cmd) {
+
+ final S3TO s3 = cmd.getS3();
+ final String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
+ final Long accountId = cmd.getAccountId();
+ final Long volumeId = cmd.getVolumeId();
+
+ try {
+
+ executeWithNoWaitLock(determineSnapshotLockId(accountId, volumeId),
+ new Callable<Void>() {
+
+ @Override
+ public Void call() throws Exception {
+
+ final String directoryName = determineSnapshotLocalDirectory(
+ secondaryStorageUrl, accountId, volumeId);
+
+ String result = createLocalDir(directoryName);
+ if (result != null) {
+ throw new InternalErrorException(
+ format("Failed to create directory %1$s during S3 snapshot download.",
+ directoryName));
+ }
+
+ final String snapshotFileName = determineSnapshotBackupFilename(cmd
+ .getSnapshotUuid());
+ final String key = determineSnapshotS3Key(
+ accountId, volumeId, snapshotFileName);
+ final File targetFile = S3Utils.getFile(s3,
+ s3.getBucketName(), key,
+ _storage.getFile(directoryName),
+ new FileNamingStrategy() {
+
+ @Override
+ public String determineFileName(
+ String key) {
+ return snapshotFileName;
+ }
+
+ });
+
+ if (cmd.getParent() != null) {
+
+ final String parentPath = join(
+ File.pathSeparator, directoryName,
+ determineSnapshotBackupFilename(cmd
+ .getParent()));
+ result = setVhdParent(
+ targetFile.getAbsolutePath(),
+ parentPath);
+ if (result != null) {
+ throw new InternalErrorException(
+ format("Failed to set the parent for backup %1$s to %2$s due to %3$s.",
+ targetFile
+ .getAbsolutePath(),
+ parentPath, result));
+ }
+
+ }
+
+ return null;
+
+ }
+
+ });
+
+ return new Answer(
+ cmd,
+ true,
+ format("Succesfully retrieved volume id %1$s for account id %2$s to %3$s from S3.",
+ volumeId, accountId, secondaryStorageUrl));
+
+ } catch (Exception e) {
+ final String errMsg = format(
+ "Failed to retrieve volume id %1$s for account id %2$s to %3$s from S3 due to exception %4$s",
+ volumeId, accountId, secondaryStorageUrl, e.getMessage());
+ s_logger.error(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+
+ }
+
+ private String determineSnapshotS3Directory(final Long accountId,
+ final Long volumeId) {
+ return join(S3Utils.SEPARATOR, SNAPSHOT_ROOT_DIR, accountId, volumeId);
+ }
+
+ private String determineSnapshotS3Key(final Long accountId,
+ final Long volumeId, final String snapshotFileName) {
+
+ final String directoryName = determineSnapshotS3Directory(accountId,
+ volumeId);
+ return join(S3Utils.SEPARATOR, directoryName, snapshotFileName);
+
+ }
+
+ private String determineSnapshotLocalDirectory(
+ final String secondaryStorageUrl, final Long accountId,
+ final Long volumeId) {
+ return join(File.pathSeparator, getRootDir(secondaryStorageUrl),
+ SNAPSHOT_ROOT_DIR, accountId, volumeId);
+ }
+
+ public Answer execute(downloadSnapshotFromSwiftCommand cmd){
+ SwiftTO swift = cmd.getSwift();
+ String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
+ Long accountId = cmd.getAccountId();
+ Long volumeId = cmd.getVolumeId();
+ String rFilename = cmd.getSnapshotUuid();
+ String sParent = cmd.getParent();
+ String errMsg = "";
+ try {
+ String parent = getRootDir(secondaryStorageUrl);
+ String lPath = parent + "/snapshots/" + String.valueOf(accountId) + "/" + String.valueOf(volumeId);
+
+ String result = createLocalDir(lPath);
+ if ( result != null ) {
+ errMsg = "downloadSnapshotFromSwiftCommand failed due to Create local path failed";
+ s_logger.warn(errMsg);
+ throw new InternalErrorException(errMsg);
+ }
+ String lFilename = rFilename;
+ if ( rFilename.startsWith("VHD-") ) {
+ lFilename = rFilename.replace("VHD-", "") + ".vhd";
+ }
+ String lFullPath = lPath + "/" + lFilename;
+ result = swiftDownload(swift, "S-" + volumeId.toString(), rFilename, lFullPath);
+ if (result != null) {
+ return new Answer(cmd, false, result);
+ }
+ if (sParent != null) {
+ if (sParent.startsWith("VHD-") || sParent.endsWith(".vhd")) {
+ String pFilename = sParent;
+ if (sParent.startsWith("VHD-")) {
+ pFilename = pFilename.replace("VHD-", "") + ".vhd";
+ }
+ String pFullPath = lPath + "/" + pFilename;
+ result = setVhdParent(lFullPath, pFullPath);
+ if (result != null) {
+ return new Answer(cmd, false, result);
+ }
+ }
+ }
+
+ return new Answer(cmd, true, "success");
+ } catch (Exception e) {
+ String msg = cmd + " Command failed due to " + e.toString();
+ s_logger.warn(msg, e);
+ throw new CloudRuntimeException(msg);
+ }
+ }
+
+ private Answer execute(ComputeChecksumCommand cmd) {
+
+ String relativeTemplatePath = cmd.getTemplatePath();
+ String parent = getRootDir(cmd);
+
+ if (relativeTemplatePath.startsWith(File.separator)) {
+ relativeTemplatePath = relativeTemplatePath.substring(1);
+ }
+
+ if (!parent.endsWith(File.separator)) {
+ parent += File.separator;
+ }
+ String absoluteTemplatePath = parent + relativeTemplatePath;
+ MessageDigest digest;
+ String checksum = null;
+ File f = new File(absoluteTemplatePath);
+ InputStream is = null;
+ byte[] buffer = new byte[8192];
+ int read = 0;
+ if(s_logger.isDebugEnabled()){
+ s_logger.debug("parent path " +parent+ " relative template path " +relativeTemplatePath );
+ }
+
+
+ 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 = bigInt.toString(16);
+ if(s_logger.isDebugEnabled()){
+ s_logger.debug("Successfully calculated checksum for file " +absoluteTemplatePath+ " - " +checksum );
+ }
+
+ }catch(IOException e) {
+ String logMsg = "Unable to process file for MD5 - " + absoluteTemplatePath;
+ s_logger.error(logMsg);
+ return new Answer(cmd, false, checksum);
+ }catch (NoSuchAlgorithmException e) {
+ return new Answer(cmd, false, checksum);
+ }
+ finally {
+ try {
+ if(is != null)
+ is.close();
+ } catch (IOException e) {
+ if(s_logger.isDebugEnabled()){
+ s_logger.debug("Could not close the file " +absoluteTemplatePath);
+ }
+ return new Answer(cmd, false, checksum);
+ }
+ }
+
+ return new Answer(cmd, true, checksum);
+ }
+
+ private void configCerts(Certificates certs) {
+ if (certs == null) {
+ configureSSL();
+ } else {
+ String prvKey = certs.getPrivKey();
+ String pubCert = certs.getPrivCert();
+ String certChain = certs.getCertChain();
+
+ try {
+ File prvKeyFile = File.createTempFile("prvkey", null);
+ String prvkeyPath = prvKeyFile.getAbsolutePath();
+ BufferedWriter out = new BufferedWriter(new FileWriter(prvKeyFile));
+ out.write(prvKey);
+ out.close();
+
+ File pubCertFile = File.createTempFile("pubcert", null);
+ String pubCertFilePath = pubCertFile.getAbsolutePath();
+
+ out = new BufferedWriter(new FileWriter(pubCertFile));
+ out.write(pubCert);
+ out.close();
+
+ configureSSL(prvkeyPath, pubCertFilePath, null);
+
+ prvKeyFile.delete();
+ pubCertFile.delete();
+
+ } catch (IOException e) {
+ s_logger.debug("Failed to config ssl: " + e.toString());
+ }
+ }
+ }
+
+ private Answer execute(SecStorageSetupCommand cmd) {
+ if (!_inSystemVM){
+ return new Answer(cmd, true, null);
+ }
+ String secUrl = cmd.getSecUrl();
+ try {
+ URI uri = new URI(secUrl);
+ String nfsHost = uri.getHost();
+
+ InetAddress nfsHostAddr = InetAddress.getByName(nfsHost);
+ String nfsHostIp = nfsHostAddr.getHostAddress();
+
+ addRouteToInternalIpOrCidr(_storageGateway, _storageIp, _storageNetmask, nfsHostIp);
+ String nfsPath = nfsHostIp + ":" + uri.getPath();
+ String dir = UUID.nameUUIDFromBytes(nfsPath.getBytes()).toString();
+ String root = _parent + "/" + dir;
+ mount(root, nfsPath);
+
+ configCerts(cmd.getCerts());
+
+ nfsIps.add(nfsHostIp);
+ return new SecStorageSetupAnswer(dir);
+ } catch (Exception e) {
+ String msg = "GetRootDir for " + secUrl + " failed due to " + e.toString();
+ s_logger.error(msg);
+ return new Answer(cmd, false, msg);
+
+ }
+ }
+
+ private String deleteSnapshotBackupFromLocalFileSystem(
+ final String secondaryStorageUrl, final Long accountId,
+ final Long volumeId, final String name, final Boolean deleteAllFlag) {
+
+ final String lPath = determineSnapshotLocalDirectory(
+ secondaryStorageUrl, accountId, volumeId)
+ + File.pathSeparator
+ + (deleteAllFlag ? "*" : "*" + name + "*");
+
+ final String result = deleteLocalFile(lPath);
+
+ if (result != null) {
+ return "failed to delete snapshot " + lPath + " , err=" + result;
+ }
+
+ return null;
+
+ }
+
+ private String deleteSnapshotBackupfromS3(final S3TO s3,
+ final String secondaryStorageUrl, final Long accountId,
+ final Long volumeId, final String name, final Boolean deleteAllFlag) {
+
+ try {
+
+ final String bucket = s3.getBucketName();
+
+ final String result = executeWithNoWaitLock(
+ determineSnapshotLockId(accountId, volumeId),
+ new Callable<String>() {
+
+ @Override
+ public String call() throws Exception {
+
+ final String innerResult = deleteSnapshotBackupFromLocalFileSystem(
+ secondaryStorageUrl, accountId, volumeId,
+ name, deleteAllFlag);
+ if (innerResult != null) {
+ return innerResult;
+ }
+
+ if (deleteAllFlag) {
+ S3Utils.deleteDirectory(
+ s3,
+ bucket,
+ determineSnapshotS3Directory(accountId,
+ volumeId));
+ } else {
+ S3Utils.deleteObject(
+ s3,
+ bucket,
+ determineSnapshotS3Key(
+ accountId,
+ volumeId,
+ determineSnapshotBackupFilename(name)));
+ }
+
+ return null;
+
+ }
+
+ });
+
+ return result;
+
+ } catch (Exception e) {
+
+ s_logger.error(
+ String.format(
+ "Failed to delete snapshot backup for account id %1$s volume id %2$sfrom S3.",
+ accountId, volumeId), e);
+ return e.getMessage();
+
+ }
+
+ }
+
+ private String determineSnapshotBackupFilename(final String snapshotUuid) {
+ return snapshotUuid + ".vhd";
+ }
+
+ private String determineSnapshotLockId(final Long accountId,
+ final Long volumeId) {
+ return join("_", "SNAPSHOT", accountId, volumeId);
+ }
+
+ protected Answer execute(final DeleteSnapshotBackupCommand cmd) {
+ String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
+ Long accountId = cmd.getAccountId();
+ Long volumeId = cmd.getVolumeId();
+ String name = cmd.getSnapshotUuid();
+ try {
+ SwiftTO swift = cmd.getSwift();
+ S3TO s3 = cmd.getS3();
+ if (swift == null) {
+ final String result = deleteSnapshotBackupFromLocalFileSystem(
+ secondaryStorageUrl, accountId, volumeId, name,
+ cmd.isAll());
+ if (result != null) {
+ s_logger.warn(result);
+ return new Answer(cmd, false, result);
+ }
+ } else if (s3 != null) {
+ final String result = deleteSnapshotBackupfromS3(s3,
+ secondaryStorageUrl, accountId, volumeId, name,
+ cmd.isAll());
+ if (result != null) {
+ s_logger.warn(result);
+ return new Answer(cmd, false, result);
+ }
+ } else {
+ String filename;
+ if (cmd.isAll()) {
+ filename = "";
+ } else {
+ filename = name;
+ }
+ String result = swiftDelete(swift, "V-" + volumeId.toString(), filename);
+ if (result != null) {
+ String errMsg = "failed to delete snapshot " + filename + " , err=" + result;
+ s_logger.warn(errMsg);
+ return new Answer(cmd, false, errMsg);
+ }
+ }
+ return new Answer(cmd, true, "success");
+ } catch (Exception e) {
+ String errMsg = cmd + " Command failed due to " + e.toString();
+ s_logger.warn(errMsg, e);
+ return new Answer(cmd, false, errMsg);
+ }
+ }
+
+ Map<String, TemplateInfo> swiftListTemplate(SwiftTO swift) {
+ String[] containers = swiftList(swift, "", "");
+ if (containers == null) {
+ return null;
+ }
+ Map<String, TemplateInfo> tmpltInfos = new HashMap<String, TemplateInfo>();
+ for( String container : containers) {
+ if ( container.startsWith("T-")) {
+ String ldir = _tmpltDir + "/" + UUID.randomUUID().toString();
+ createLocalDir(ldir);
+ String lFullPath = ldir + "/" + _tmpltpp;
+ swiftDownload(swift, container, _tmpltpp, lFullPath);
+ TemplateLocation loc = new TemplateLocation(_storage, ldir);
+ try {
+ if (!loc.load()) {
+ s_logger.warn("Can not parse template.properties file for template " + container);
+ continue;
+ }
+ } catch (IOException e) {
+ s_logger.warn("Unable to load template location " + ldir + " due to " + e.toString(), e);
+ continue;
+ }
+ TemplateInfo tInfo = loc.getTemplateInfo();
+ tInfo.setInstallPath(container);
+ tmpltInfos.put(tInfo.getTemplateName(), tInfo);
+ loc.purge();
+ deleteLocalDir(ldir);
+ }
+ }
+ return tmpltInfos;
+
+ }
+
+ private Answer execute(ListTemplateCommand cmd) {
+
+ if (cmd.getSwift() != null) {
+ Map<String, TemplateInfo> templateInfos = swiftListTemplate(cmd.getSwift());
+ return new ListTemplateAnswer(cmd.getSwift().toString(), templateInfos);
+ } else {
+ String root = getRootDir(cmd.getSecUrl());
+ Map<String, TemplateInfo> templateInfos = _dlMgr.gatherTemplateInfo(root);
+ return new ListTemplateAnswer(cmd.getSecUrl(), templateInfos);
+ }
+ }
+
+ private Answer execute(ListVolumeCommand cmd) {
+
+ String root = getRootDir(cmd.getSecUrl());
+ Map<Long, TemplateInfo> templateInfos = _dlMgr.gatherVolumeInfo(root);
+ return new ListVolumeAnswer(cmd.getSecUrl(), templateInfos);
+
+ }
+
+ private Answer execute(SecStorageVMSetupCommand cmd) {
+ if (!_inSystemVM){
+ return new Answer(cmd, true, null);
+ }
+ boolean success = true;
+ StringBuilder result = new StringBuilder();
+ for (String cidr: cmd.getAllowedInternalSites()) {
+ if (nfsIps.contains(cidr)) {
+ /*
+ * if the internal download ip is the same with secondary storage ip, adding internal sites will flush
+ * ip route to nfs through storage ip.
+ */
+ continue;
+ }
+ String tmpresult = allowOutgoingOnPrivate(cidr);
+ if (tmpresult != null) {
+ result.append(", ").append(tmpresult);
+ success = false;
+ }
+ }
+ if (success) {
+ if (cmd.getCopyPassword() != null && cmd.getCopyUserName() != null) {
+ String tmpresult = configureAuth(cmd.getCopyUserName(), cmd.getCopyPassword());
+ if (tmpresult != null) {
+ result.append("Failed to configure auth for copy ").append(tmpresult);
+ success = false;
+ }
+ }
+ }
+ return new Answer(cmd, success, result.toString());
+
+ }
+
+ private String setVhdParent(String lFullPath, String pFullPath) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("/bin/vhd-util modify -n " + lFullPath + " -p " + pFullPath);
+ String result = command.execute();
+ if (result != null) {
+ String errMsg = "failed to set vhd parent, child " + lFullPath + " parent " + pFullPath + ", err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ return null;
+ }
+
+ private String createLocalDir(String folder) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("mkdir -p " + folder);
+ String result = command.execute();
+ if (result != null) {
+ String errMsg = "Create local path " + folder + " failed , err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ return null;
+ }
+
+ private String deleteLocalDir(String folder) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("rmdir " + folder);
+ String result = command.execute();
+ if (result != null) {
+ String errMsg = "Delete local path " + folder + " failed , err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ return null;
+ }
+
+ private String deleteLocalFile(String fullPath) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("rm -f " + fullPath);
+ String result = command.execute();
+ if (result != null) {
+ String errMsg = "Failed to delete file " + fullPath + ", err=" + result;
+ s_logger.warn(errMsg);
+ return errMsg;
+ }
+ return null;
+ }
+
+ public String allowOutgoingOnPrivate(String destCidr) {
+ if (!_inSystemVM) {
+ return null;
+ }
+ Script command = new Script("/bin/bash", s_logger);
+ String intf = "eth1";
+ command.add("-c");
+ command.add("iptables -I OUTPUT -o " + intf + " -d " + destCidr + " -p tcp -m state --state NEW -m tcp -j ACCEPT");
+
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in allowing outgoing to " + destCidr + ", err=" + result );
+ return "Error in allowing outgoing to " + destCidr + ", err=" + result;
+ }
+
+ addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, destCidr);
+
+ return null;
+ }
+
+ private Answer execute(SecStorageFirewallCfgCommand cmd) {
+ if (!_inSystemVM){
+ return new Answer(cmd, true, null);
+ }
+
+ List<String> ipList = new ArrayList<String>();
+
+ for (PortConfig pCfg:cmd.getPortConfigs()){
+ if (pCfg.isAdd()) {
+ ipList.add(pCfg.getSourceIp());
+ }
+ }
+ boolean success = true;
+ String result;
+ result = configureIpFirewall(ipList, cmd.getIsAppendAIp());
+ if (result !=null)
+ success = false;
+
+ return new Answer(cmd, success, result);
+ }
+
+ protected GetStorageStatsAnswer execute(final GetStorageStatsCommand cmd) {
+ String rootDir = getRootDir(cmd.getSecUrl());
+ final long usedSize = getUsedSize(rootDir);
+ final long totalSize = getTotalSize(rootDir);
+ if (usedSize == -1 || totalSize == -1) {
+ return new GetStorageStatsAnswer(cmd, "Unable to get storage stats");
+ } else {
+ return new GetStorageStatsAnswer(cmd, totalSize, usedSize) ;
+ }
+ }
+
+ protected Answer execute(final DeleteTemplateCommand cmd) {
+ String relativeTemplatePath = cmd.getTemplatePath();
+ String parent = getRootDir(cmd);
+
+ if (relativeTemplatePath.startsWith(File.separator)) {
+ relativeTemplatePath = relativeTemplatePath.substring(1);
+ }
+
+ if (!parent.endsWith(File.separator)) {
+ parent += File.separator;
+ }
+ String absoluteTemplatePath = parent + relativeTemplatePath;
+ File tmpltParent = new File(absoluteTemplatePath).getParentFile();
+ String details = null;
+ if (!tmpltParent.exists()) {
+ details = "template parent directory " + tmpltParent.getName() + " doesn't exist";
+ s_logger.debug(details);
+ return new Answer(cmd, true, details);
+ }
+ File[] tmpltFiles = tmpltParent.listFiles();
+ if (tmpltFiles == null || tmpltFiles.length == 0) {
+ details = "No files under template parent directory " + tmpltParent.getName();
+ s_logger.debug(details);
+ } else {
+ boolean found = false;
+ for (File f : tmpltFiles) {
+ if (!found && f.getName().equals("template.properties")) {
+ found = true;
+ }
+ if (!f.delete()) {
+ return new Answer(cmd, false, "Unable to delete file " + f.getName() + " under Template path "
+ + relativeTemplatePath);
+ }
+ }
+ if (!found) {
+ details = "Can not find template.properties under " + tmpltParent.getName();
+ s_logger.debug(details);
+ }
+ }
+ if (!tmpltParent.delete()) {
+ details = "Unable to delete directory " + tmpltParent.getName() + " under Template path "
+ + relativeTemplatePath;
+ s_logger.debug(details);
+ return new Answer(cmd, false, details);
+ }
+ return new Answer(cmd, true, null);
+ }
+
+ protected Answer execute(final DeleteVolumeCommand cmd) {
+ String relativeVolumePath = cmd.getVolumePath();
+ String parent = getRootDir(cmd);
+
+ if (relativeVolumePath.startsWith(File.separator)) {
+ relativeVolumePath = relativeVolumePath.substring(1);
+ }
+
+ if (!parent.endsWith(File.separator)) {
+ parent += File.separator;
+ }
+ String absoluteVolumePath = parent + relativeVolumePath;
+ File tmpltParent = new File(absoluteVolumePath).getParentFile();
+ String details = null;
+ if (!tmpltParent.exists()) {
+ details = "volume parent directory " + tmpltParent.getName() + " doesn't exist";
+ s_logger.debug(details);
+ return new Answer(cmd, true, details);
+ }
+ File[] tmpltFiles = tmpltParent.listFiles();
+ if (tmpltFiles == null || tmpltFiles.length == 0) {
+ details = "No files under volume parent directory " + tmpltParent.getName();
+ s_logger.debug(details);
+ } else {
+ boolean found = false;
+ for (File f : tmpltFiles) {
+ if (!found && f.getName().equals("volume.properties")) {
+ found = true;
+ }
+ if (!f.delete()) {
+ return new Answer(cmd, false, "Unable to delete file " + f.getName() + " under Volume path "
+ + relativeVolumePath);
+ }
+ }
+ if (!found) {
+ details = "Can not find volume.properties under " + tmpltParent.getName();
+ s_logger.debug(details);
+ }
+ }
+ if (!tmpltParent.delete()) {
+ details = "Unable to delete directory " + tmpltParent.getName() + " under Volume path "
+ + relativeVolumePath;
+ s_logger.debug(details);
+ return new Answer(cmd, false, details);
+ }
+ return new Answer(cmd, true, null);
+ }
+
+ Answer execute(CleanupSnapshotBackupCommand cmd) {
+ String parent = getRootDir(cmd.getSecondaryStoragePoolURL());
+ if (!parent.endsWith(File.separator)) {
+ parent += File.separator;
+ }
+ String absoluteSnapsthotDir = parent + File.separator + "snapshots" + File.separator + cmd.getAccountId() + File.separator + cmd.getVolumeId();
+ File ssParent = new File(absoluteSnapsthotDir);
+ if (ssParent.exists() && ssParent.isDirectory()) {
+ File[] files = ssParent.listFiles();
+ for (File file : files) {
+ boolean found = false;
+ String filename = file.getName();
+ for (String uuid : cmd.getValidBackupUUIDs()) {
+ if (filename.startsWith(uuid)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ file.delete();
+ String msg = "snapshot " + filename + " is not recorded in DB, remove it";
+ s_logger.warn(msg);
+ }
+ }
+ }
+ return new Answer(cmd, true, null);
+ }
+
+
+ synchronized public String getRootDir(String secUrl) {
+ if (!_inSystemVM) {
+ return _parent;
+ }
+ try {
+ URI uri = new URI(secUrl);
+ String nfsHost = uri.getHost();
+
+ InetAddress nfsHostAddr = InetAddress.getByName(nfsHost);
+ String nfsHostIp = nfsHostAddr.getHostAddress();
+ String nfsPath = nfsHostIp + ":" + uri.getPath();
+ String dir = UUID.nameUUIDFromBytes(nfsPath.getBytes()).toString();
+ String root = _parent + "/" + dir;
+ mount(root, nfsPath);
+ return root;
+ } catch (Exception e) {
+ String msg = "GetRootDir for " + secUrl + " failed due to " + e.toString();
+ s_logger.error(msg, e);
+ throw new CloudRuntimeException(msg);
+ }
+ }
+
+
+ @Override
+ public String getRootDir(ssCommand cmd){
+ return getRootDir(cmd.getSecUrl());
+
+ }
+
+ protected long getUsedSize(String rootDir) {
+ return _storage.getUsedSpace(rootDir);
+ }
+
+ protected long getTotalSize(String rootDir) {
+ return _storage.getTotalSpace(rootDir);
+ }
+
+ protected long convertFilesystemSize(final String size) {
+ if (size == null || size.isEmpty()) {
+ return -1;
+ }
+
+ long multiplier = 1;
+ if (size.endsWith("T")) {
+ multiplier = 1024l * 1024l * 1024l * 1024l;
+ } else if (size.endsWith("G")) {
+ multiplier = 1024l * 1024l * 1024l;
+ } else if (size.endsWith("M")) {
+ multiplier = 1024l * 1024l;
+ } else {
+ assert (false) : "Well, I have no idea what this is: " + size;
+ }
+
+ return (long)(Double.parseDouble(size.substring(0, size.length() - 1)) * multiplier);
+ }
+
+
+ @Override
+ public Type getType() {
+ if(SecondaryStorageVm.Role.templateProcessor.toString().equals(_role))
+ return Host.Type.SecondaryStorage;
+
+ return Host.Type.SecondaryStorageCmdExecutor;
+ }
+
+ @Override
+ public PingCommand getCurrentStatus(final long id) {
+ return new PingStorageCommand(Host.Type.Storage, id, new HashMap<String, Boolean>());
+ }
+
+ @Override
+ public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+ _eth1ip = (String)params.get("eth1ip");
+ _eth1mask = (String)params.get("eth1mask");
+ if (_eth1ip != null) { //can only happen inside service vm
+ params.put("private.network.device", "eth1");
+ } else {
+ s_logger.warn("Wait, what's going on? eth1ip is null!!");
+ }
+ String eth2ip = (String) params.get("eth2ip");
+ if (eth2ip != null) {
+ params.put("public.network.device", "eth2");
+ }
+ _publicIp = (String) params.get("eth2ip");
+ _hostname = (String) params.get("name");
+
+ String inSystemVM = (String) params.get("secondary.storage.vm");
+ if (inSystemVM == null || "true".equalsIgnoreCase(inSystemVM)) {
+ _inSystemVM = true;
+ }
+
+ _storageIp = (String) params.get("storageip");
+ if (_storageIp == null) {
+ s_logger.warn("Wait, there is no storageip in /proc/cmdline, something wrong!");
+ }
+ _storageNetmask = (String) params.get("storagenetmask");
+ _storageGateway = (String) params.get("storagegateway");
+ super.configure(name, params);
+
+ _params = params;
+ String value = (String)params.get("scripts.timeout");
+ _timeout = NumbersUtil.parseInt(value, 1440) * 1000;
+
+ _storage = (StorageLayer)params.get(StorageLayer.InstanceConfigKey);
+ if (_storage == null) {
+ value = (String)params.get(StorageLayer.ClassConfigKey);
+ if (value == null) {
+ value = "com.cloud.storage.JavaStorageLayer";
+ }
+
+ try {
+ Class<?> clazz = Class.forName(value);
+ _storage = (StorageLayer)clazz.newInstance();
+ _storage.configure("StorageLayer", params);
+ } catch (ClassNotFoundException e) {
+ throw new ConfigurationException("Unable to find class " + value);
+ } catch (InstantiationException e) {
+ throw new ConfigurationException("Unable to find class " + value);
+ } catch (IllegalAccessException e) {
+ throw new ConfigurationException("Unable to find class " + value);
+ }
+ }
+
+ if (_inSystemVM)
+ _storage.mkdirs(_parent);
+
+ _configSslScr = Script.findScript(getDefaultScriptsDir(), "config_ssl.sh");
+ if (_configSslScr != null) {
+ s_logger.info("config_ssl.sh found in " + _configSslScr);
+ }
+
+ _configAuthScr = Script.findScript(getDefaultScriptsDir(), "config_auth.sh");
+ if (_configSslScr != null) {
+ s_logger.info("config_auth.sh found in " + _configAuthScr);
+ }
+
+ _configIpFirewallScr = Script.findScript(getDefaultScriptsDir(), "ipfirewall.sh");
+ if (_configIpFirewallScr != null) {
+ s_logger.info("_configIpFirewallScr found in " + _configIpFirewallScr);
+ }
+
+ _role = (String)params.get("role");
+ if(_role == null)
+ _role = SecondaryStorageVm.Role.templateProcessor.toString();
+ s_logger.info("Secondary storage runs in role " + _role);
+
+ _guid = (String)params.get("guid");
+ if (_guid == null) {
+ throw new ConfigurationException("Unable to find the guid");
+ }
+
+ _dc = (String)params.get("zone");
+ if (_dc == null) {
+ throw new ConfigurationException("Unable to find the zone");
+ }
+ _pod = (String)params.get("pod");
+
+ _instance = (String)params.get("instance");
+
+ if (!_inSystemVM) {
+ _parent = (String) params.get("mount.path");
+ }
+
+
+ if (_inSystemVM) {
+ _localgw = (String)params.get("localgw");
+ if (_localgw != null) { // can only happen inside service vm
+ String mgmtHost = (String) params.get("host");
+ addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, mgmtHost);
+
+ String internalDns1 = (String) params.get("internaldns1");
+ if (internalDns1 == null) {
+ s_logger.warn("No DNS entry found during configuration of NfsSecondaryStorage");
+ } else {
+ addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns1);
+ }
+
+ String internalDns2 = (String) params.get("internaldns2");
+ if (internalDns2 != null) {
+ addRouteToInternalIpOrCidr(_localgw, _eth1ip, _eth1mask, internalDns2);
+ }
+
+ }
+
+ startAdditionalServices();
+ _params.put("install.numthreads", "50");
+ _params.put("secondary.storage.vm", "true");
+ }
+
+ try {
+ _params.put(StorageLayer.InstanceConfigKey, _storage);
+ _dlMgr = new DownloadManagerImpl();
+ _dlMgr.configure("DownloadManager", _params);
+ _upldMgr = new UploadManagerImpl();
+ _upldMgr.configure("UploadManager", params);
+ } catch (ConfigurationException e) {
+ s_logger.warn("Caught problem while configuring DownloadManager", e);
+ return false;
+ }
+ return true;
+ }
+
+ private void startAdditionalServices() {
+ if (!_inSystemVM) {
+ return;
+ }
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("if [ -f /etc/init.d/ssh ]; then service ssh restart; else service sshd restart; fi ");
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in starting sshd service err=" + result );
+ }
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("iptables -I INPUT -i eth1 -p tcp -m state --state NEW -m tcp --dport 3922 -j ACCEPT");
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in opening up ssh port err=" + result );
+ }
+ }
+
+ private void addRouteToInternalIpOrCidr(String localgw, String eth1ip, String eth1mask, String destIpOrCidr) {
+ if (!_inSystemVM) {
+ return;
+ }
+ s_logger.debug("addRouteToInternalIp: localgw=" + localgw + ", eth1ip=" + eth1ip + ", eth1mask=" + eth1mask + ",destIp=" + destIpOrCidr);
+ if (destIpOrCidr == null) {
+ s_logger.debug("addRouteToInternalIp: destIp is null");
+ return;
+ }
+ if (!NetUtils.isValidIp(destIpOrCidr) && !NetUtils.isValidCIDR(destIpOrCidr)){
+ s_logger.warn(" destIp is not a valid ip address or cidr destIp=" + destIpOrCidr);
+ return;
+ }
+ boolean inSameSubnet = false;
+ if (NetUtils.isValidIp(destIpOrCidr)) {
+ if (eth1ip != null && eth1mask != null) {
+ inSameSubnet = NetUtils.sameSubnet(eth1ip, destIpOrCidr, eth1mask);
+ } else {
+ s_logger.warn("addRouteToInternalIp: unable to determine same subnet: _eth1ip=" + eth1ip + ", dest ip=" + destIpOrCidr + ", _eth1mask=" + eth1mask);
+ }
+ } else {
+ inSameSubnet = NetUtils.isNetworkAWithinNetworkB(destIpOrCidr, NetUtils.ipAndNetMaskToCidr(eth1ip, eth1mask));
+ }
+ if (inSameSubnet) {
+ s_logger.debug("addRouteToInternalIp: dest ip " + destIpOrCidr + " is in the same subnet as eth1 ip " + eth1ip);
+ return;
+ }
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("ip route delete " + destIpOrCidr);
+ command.execute();
+ command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("ip route add " + destIpOrCidr + " via " + localgw);
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in configuring route to internal ip err=" + result );
+ } else {
+ s_logger.debug("addRouteToInternalIp: added route to internal ip=" + destIpOrCidr + " via " + localgw);
+ }
+ }
+
+ private void configureSSL() {
+ if (!_inSystemVM) {
+ return;
+ }
+ Script command = new Script(_configSslScr);
+ command.add("-i", _publicIp);
+ command.add("-h", _hostname);
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Unable to configure httpd to use ssl");
+ }
+ }
+
+ private void configureSSL(String prvkeyPath, String prvCertPath, String certChainPath) {
+ if (!_inSystemVM) {
+ return;
+ }
+ Script command = new Script(_configSslScr);
+ command.add("-i", _publicIp);
+ command.add("-h", _hostname);
+ command.add("-k", prvkeyPath);
+ command.add("-p", prvCertPath);
+ if (certChainPath != null) {
+ command.add("-t", certChainPath);
+ }
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Unable to configure httpd to use ssl");
+ }
+ }
+
+ private String configureAuth(String user, String passwd) {
+ Script command = new Script(_configAuthScr);
+ command.add(user);
+ command.add(passwd);
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Unable to configure httpd to use auth");
+ }
+ return result;
+ }
+
+ private String configureIpFirewall(List<String> ipList, boolean isAppend){
+ Script command = new Script(_configIpFirewallScr);
+ command.add(String.valueOf(isAppend));
+ for (String ip : ipList){
+ command.add(ip);
+ }
+
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Unable to configure firewall for command : " +command);
+ }
+ return result;
+ }
+
+ protected String mount(String root, String nfsPath) {
+ File file = new File(root);
+ if (!file.exists()) {
+ if (_storage.mkdir(root)) {
+ s_logger.debug("create mount point: " + root);
+ } else {
+ s_logger.debug("Unable to create mount point: " + root);
+ return null;
+ }
+ }
+
+ Script script = null;
+ String result = null;
+ script = new Script(!_inSystemVM, "mount", _timeout, s_logger);
+ List<String> res = new ArrayList<String>();
+ ZfsPathParser parser = new ZfsPathParser(root);
+ script.execute(parser);
+ res.addAll(parser.getPaths());
+ for( String s : res ) {
+ if ( s.contains(root)) {
+ return root;
+ }
+ }
+
+ Script command = new Script(!_inSystemVM, "mount", _timeout, s_logger);
+ command.add("-t", "nfs");
+ if (_inSystemVM) {
+ //Fedora Core 12 errors out with any -o option executed from java
+ command.add("-o", "soft,timeo=133,retrans=2147483647,tcp,acdirmax=0,acdirmin=0");
+ }
+ command.add(nfsPath);
+ command.add(root);
+ result = command.execute();
+ if (result != null) {
+ s_logger.warn("Unable to mount " + nfsPath + " due to " + result);
+ file = new File(root);
+ if (file.exists())
+ file.delete();
+ return null;
+ }
+
+ // XXX: Adding the check for creation of snapshots dir here. Might have to move it somewhere more logical later.
+ if (!checkForSnapshotsDir(root)) {
+ return null;
+ }
+
+ // Create the volumes dir
+ if (!checkForVolumesDir(root)) {
+ return null;
+ }
+
+ return root;
+ }
+
+ @Override
+ public boolean start() {
+ return true;
+ }
+
+ @Override
+ public boolean stop() {
+ return true;
+ }
+
+ @Override
+ public StartupCommand[] initialize() {
+
+ final StartupSecondaryStorageCommand cmd = new StartupSecondaryStorageCommand();
+ fillNetworkInformation(cmd);
+ if(_publicIp != null)
+ cmd.setPublicIpAddress(_publicIp);
+
+ if (_inSystemVM) {
+ Script command = new Script("/bin/bash", s_logger);
+ command.add("-c");
+ command.add("ln -sf " + _parent + " /var/www/html/copy");
+ String result = command.execute();
+ if (result != null) {
+ s_logger.warn("Error in linking err=" + result);
+ return null;
+ }
+ }
+ return new StartupCommand[] {cmd};
+ }
+
+ protected boolean checkForSnapshotsDir(String mountPoint) {
+ String snapshotsDirLocation = mountPoint + File.separator + "snapshots";
+ return createDir("snapshots", snapshotsDirLocation, mountPoint);
+ }
+
+ protected boolean checkForVolumesDir(String mountPoint) {
+ String volumesDirLocation = mountPoint + "/" + "volumes";
+ return createDir("volumes", volumesDirLocation, mountPoint);
+ }
+
+ protected boolean createDir(String dirName, String dirLocation, String mountPoint) {
+ boolean dirExists = false;
+
+ File dir = new File(dirLocation);
+ if (dir.exists()) {
+ if (dir.isDirectory()) {
+ s_logger.debug(dirName + " already exists on secondary storage, and is mounted at " + mountPoint);
+ dirExists = true;
+ } else {
+ if (dir.delete() && _storage.mkdir(dirLocation)) {
+ dirExists = true;
+ }
+ }
+ } else if (_storage.mkdir(dirLocation)) {
+ dirExists = true;
+ }
+
+ if (dirExists) {
+ s_logger.info(dirName + " directory created/exists on Secondary Storage.");
+ } else {
+ s_logger.info(dirName + " directory does not exist on Secondary Storage.");
+ }
+
+ return dirExists;
+ }
+
+ @Override
+ protected String getDefaultScriptsDir() {
+ return "./scripts/storage/secondary";
+ }
+
+ @Override
+ public void setName(String name) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void setConfigParams(Map<String, Object> params) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public Map<String, Object> getConfigParams() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public int getRunLevel() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void setRunLevel(int level) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void fillNetworkInformation(final StartupCommand cmd) {
+ final String dummyMac = "00:06:0A:0B:0C:0D";
+ final String dummyNetmask = "255.255.255.0";
+ if (!_inSystemVM) {
+ cmd.setPrivateIpAddress(_eth1ip);
+ cmd.setPrivateMacAddress(dummyMac);
+ cmd.setPrivateNetmask(dummyNetmask);
+ cmd.setPublicIpAddress(_publicIp);
+ cmd.setPublicMacAddress(dummyMac);
+ cmd.setPublicNetmask(dummyNetmask);
+ cmd.setName(_hostname);
+ } else {
+ super.fillNetworkInformation(cmd);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResource.java
----------------------------------------------------------------------
diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResource.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResource.java
new file mode 100755
index 0000000..5c87b0d
--- /dev/null
+++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResource.java
@@ -0,0 +1,28 @@
+// 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.resource;
+import com.cloud.agent.api.storage.ssCommand;
+import com.cloud.resource.ServerResource;
+/**
+ *
+ * SecondaryStorageServerResource is a generic container to execute commands sent
+ */
+public interface SecondaryStorageResource extends ServerResource {
+
+ public String getRootDir(ssCommand cmd);
+
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResourceHandler.java
----------------------------------------------------------------------
diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResourceHandler.java b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResourceHandler.java
new file mode 100644
index 0000000..d03d983
--- /dev/null
+++ b/services/secondary-storage/src/org/apache/cloudstack/storage/resource/SecondaryStorageResourceHandler.java
@@ -0,0 +1,24 @@
+// 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.resource;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+
+public interface SecondaryStorageResourceHandler {
+ Answer executeRequest(Command cmd);
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/e7983b25/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManager.java
----------------------------------------------------------------------
diff --git a/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManager.java b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManager.java
new file mode 100644
index 0000000..3e5072a
--- /dev/null
+++ b/services/secondary-storage/src/org/apache/cloudstack/storage/template/DownloadManager.java
@@ -0,0 +1,105 @@
+// 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.util.Map;
+
+import org.apache.cloudstack.storage.resource.SecondaryStorageResource;
+
+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.storage.Storage.ImageFormat;
+import com.cloud.storage.VMTemplateHostVO;
+import com.cloud.storage.template.TemplateDownloader;
+import com.cloud.storage.template.TemplateInfo;
+import com.cloud.utils.component.Manager;
+
+public interface DownloadManager extends Manager {
+
+ /**
+ * Initiate download of a public template
+ * @param id unique id.
+ * @param url the url from where to download from
+ * @param hvm whether the template is a hardware virtual machine
+ * @param accountId the accountId of the iso owner (null if public iso)
+ * @param descr description of the template
+ * @param user username used for authentication to the server
+ * @param password password used for authentication to the server
+ * @param maxDownloadSizeInBytes (optional) max download size for the template, in bytes.
+ * @param resourceType signifying the type of resource like template, volume etc.
+ * @return job-id that can be used to interrogate the status of the download.
+ */
+ public String downloadPublicTemplate(long id, String url, String name, ImageFormat format, boolean hvm, Long accountId, String descr, String cksum, String installPathPrefix, String userName, String passwd, long maxDownloadSizeInBytes, Proxy proxy, ResourceType resourceType);
+
+
+ /**
+ * Get the status of a download job
+ * @param jobId job Id
+ * @return status of the download job
+ */
+ public TemplateDownloader.Status getDownloadStatus(String jobId);
+
+ /**
+ * Get the status of a download job
+ * @param jobId job Id
+ * @return status of the download job
+ */
+ public VMTemplateHostVO.Status getDownloadStatus2(String jobId);
+
+ /**
+ * Get the download percent of a download job
+ * @param jobId job Id
+ * @return
+ */
+ public int getDownloadPct(String jobId);
+
+ /**
+ * Get the download error if any
+ * @param jobId job Id
+ * @return
+ */
+ public String getDownloadError(String jobId);
+
+ /**
+ * Get the local path for the download
+ * @param jobId job Id
+ * @return
+ public String getDownloadLocalPath(String jobId);
+ */
+
+ /** Handle download commands from the management server
+ * @param cmd cmd from server
+ * @return answer representing status of download.
+ */
+ public DownloadAnswer handleDownloadCommand(SecondaryStorageResource resource, DownloadCommand cmd);
+
+ /**
+ /**
+ * @return list of template info for installed templates
+ */
+ public Map<String, TemplateInfo> gatherTemplateInfo(String templateDir);
+
+ /**
+ /**
+ * @return list of volume info for installed volumes
+ */
+ public Map<Long, TemplateInfo> gatherVolumeInfo(String volumeDir);
+
+
+}
\ No newline at end of file