Return-Path: X-Original-To: apmail-cloudstack-commits-archive@www.apache.org Delivered-To: apmail-cloudstack-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id A8AEF10437 for ; Wed, 10 Apr 2013 22:25:43 +0000 (UTC) Received: (qmail 47534 invoked by uid 500); 10 Apr 2013 22:25:43 -0000 Delivered-To: apmail-cloudstack-commits-archive@cloudstack.apache.org Received: (qmail 47456 invoked by uid 500); 10 Apr 2013 22:25:43 -0000 Mailing-List: contact commits-help@cloudstack.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cloudstack.apache.org Delivered-To: mailing list commits@cloudstack.apache.org Received: (qmail 47400 invoked by uid 99); 10 Apr 2013 22:25:42 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 10 Apr 2013 22:25:42 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 95EF0880044; Wed, 10 Apr 2013 22:25:42 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: prachidamle@apache.org To: commits@cloudstack.apache.org Date: Wed, 10 Apr 2013 22:25:43 -0000 Message-Id: <78e610ae22884daeb903f44521e58d9c@git.apache.org> In-Reply-To: <4511f15a80314d38b78b57fcb124932d@git.apache.org> References: <4511f15a80314d38b78b57fcb124932d@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [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 _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 nfsIps = new ArrayList(); + 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 files = new ArrayList(); + 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() { + + @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() { + + @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 swiftListTemplate(SwiftTO swift) { + String[] containers = swiftList(swift, "", ""); + if (containers == null) { + return null; + } + Map tmpltInfos = new HashMap(); + 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 templateInfos = swiftListTemplate(cmd.getSwift()); + return new ListTemplateAnswer(cmd.getSwift().toString(), templateInfos); + } else { + String root = getRootDir(cmd.getSecUrl()); + Map templateInfos = _dlMgr.gatherTemplateInfo(root); + return new ListTemplateAnswer(cmd.getSecUrl(), templateInfos); + } + } + + private Answer execute(ListVolumeCommand cmd) { + + String root = getRootDir(cmd.getSecUrl()); + Map 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 ipList = new ArrayList(); + + 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()); + } + + @Override + public boolean configure(String name, Map 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 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 res = new ArrayList(); + 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 params) { + // TODO Auto-generated method stub + + } + + @Override + public Map 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 gatherTemplateInfo(String templateDir); + + /** + /** + * @return list of volume info for installed volumes + */ + public Map gatherVolumeInfo(String volumeDir); + + +} \ No newline at end of file