bug CS-10789: Adding volume sync and delete functionality.

This commit is contained in:
Nitin Mehta 2012-04-16 10:58:02 +05:30
parent 54956280e6
commit ac2175bdbc
12 changed files with 379 additions and 26 deletions

View File

@ -0,0 +1,22 @@
package com.cloud.agent.api.storage;
public class DeleteVolumeCommand extends ssCommand {
private String volumePath;
public DeleteVolumeCommand() {
}
public DeleteVolumeCommand(String secUrl, String volumePath) {
this.setSecUrl(secUrl);
this.volumePath = volumePath;
}
@Override
public boolean executeInSequence() {
return true;
}
public String getVolumePath() {
return volumePath;
}
}

View File

@ -0,0 +1,37 @@
package com.cloud.agent.api.storage;
import java.util.Map;
import com.cloud.agent.api.Answer;
import com.cloud.storage.template.TemplateInfo;
public class ListVolumeAnswer extends Answer {
private String secUrl;
private Map<Long, TemplateInfo> templateInfos;
public ListVolumeAnswer() {
}
public ListVolumeAnswer(String secUrl, Map<Long, TemplateInfo> templateInfos) {
super(null, true, "success");
this.setSecUrl(secUrl);
this.templateInfos = templateInfos;
}
public Map<Long, TemplateInfo> getTemplateInfo() {
return templateInfos;
}
public void setTemplateInfo(Map<Long, TemplateInfo> templateInfos) {
this.templateInfos = templateInfos;
}
public void setSecUrl(String secUrl) {
this.secUrl = secUrl;
}
public String getSecUrl() {
return secUrl;
}
}

View File

@ -54,20 +54,20 @@ import com.cloud.agent.api.SecStorageSetupAnswer;
import com.cloud.agent.api.SecStorageSetupCommand;
import com.cloud.agent.api.SecStorageSetupCommand.Certificates;
import com.cloud.agent.api.StartupSecondaryStorageCommand;
import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig;
import com.cloud.agent.api.SecStorageVMSetupCommand;
import com.cloud.agent.api.StartupCommand;
import com.cloud.agent.api.StartupSecondaryStorageCommand;
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;
@ -156,7 +156,9 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
return execute((ComputeChecksumCommand)cmd);
} else if (cmd instanceof ListTemplateCommand){
return execute((ListTemplateCommand)cmd);
} else if (cmd instanceof downloadSnapshotFromSwiftCommand){
} else if (cmd instanceof ListVolumeCommand){
return execute((ListVolumeCommand)cmd);
}else if (cmd instanceof downloadSnapshotFromSwiftCommand){
return execute((downloadSnapshotFromSwiftCommand)cmd);
} else if (cmd instanceof DeleteSnapshotBackupCommand){
return execute((DeleteSnapshotBackupCommand)cmd);
@ -709,8 +711,8 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
}
String root = getRootDir(cmd.getSecUrl());
Map<String, TemplateInfo> templateInfos = _dlMgr.gatherTemplateInfo(root);
return new ListTemplateAnswer(cmd.getSecUrl(), templateInfos);
Map<Long, TemplateInfo> templateInfos = _dlMgr.gatherVolumeInfo(root);
return new ListVolumeAnswer(cmd.getSecUrl(), templateInfos);
}
@ -893,6 +895,54 @@ public class NfsSecondaryStorageResource extends ServerResourceBase implements S
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)) {

View File

@ -97,6 +97,7 @@ public interface DownloadManager extends Manager {
/**
* @return list of volume info for installed volumes
*/
public Map<String, TemplateInfo> gatherVolumeInfo(String volumeDir);
public Map<Long, TemplateInfo> gatherVolumeInfo(String volumeDir);
}

View File

@ -702,6 +702,8 @@ public class DownloadManagerImpl implements DownloadManager {
return result;
}
private List<String> listTemplates(String rootdir) {
List<String> result = new ArrayList<String>();
@ -771,6 +773,52 @@ public class DownloadManagerImpl implements DownloadManager {
return result;
}
@Override
public Map<Long, TemplateInfo> gatherVolumeInfo(String rootDir) {
Map<Long, TemplateInfo> result = new HashMap<Long, TemplateInfo>();
String volumeDir = rootDir + File.separator + _volumeDir;
if (! _storage.exists(volumeDir)) {
_storage.mkdirs(volumeDir);
}
List<String> vols = listVolumes(volumeDir);
for (String vol : vols) {
String path = vol.substring(0, vol.lastIndexOf(File.separator));
TemplateLocation loc = new TemplateLocation(_storage, path);
try {
if (!loc.load()) {
s_logger.warn("Post download installation was not completed for " + path);
//loc.purge();
_storage.cleanup(path, volumeDir);
continue;
}
} catch (IOException e) {
s_logger.warn("Unable to load volume location " + path, e);
continue;
}
TemplateInfo vInfo = loc.getTemplateInfo();
if ((vInfo.size == vInfo.physicalSize) && (vInfo.installPath.endsWith(ImageFormat.OVA.getFileExtension()))) {
try {
Processor processor = _processors.get("VMDK Processor");
VmdkProcessor vmdkProcessor = (VmdkProcessor)processor;
long vSize = vmdkProcessor.getTemplateVirtualSize(path, vInfo.installPath.substring(vInfo.installPath.lastIndexOf(File.separator) + 1));
vInfo.size = vSize;
loc.updateVirtualSize(vSize);
loc.save();
} catch (Exception e) {
s_logger.error("Unable to get the virtual size of the volume: " + vInfo.installPath + " due to " + e.getMessage());
}
}
result.put(vInfo.getId(), vInfo);
s_logger.debug("Added volume name: " + vInfo.templateName + ", path: " + vol);
}
return result;
}
private int deleteDownloadDirectories(File downloadPath, int deleted) {
try {
if (downloadPath.exists()) {
@ -879,11 +927,11 @@ public class DownloadManagerImpl implements DownloadManager {
}
s_logger.info("createtmplt.sh found in " + createTmpltScr);
/*listVolScr = Script.findScript(scriptsDir, "listvolume.sh");
listVolScr = Script.findScript(scriptsDir, "listvolume.sh");
if (listVolScr == null) {
throw new ConfigurationException("Unable to find the listvolume.sh");
}
s_logger.info("listvolume.sh found in " + listVolScr);*/
s_logger.info("listvolume.sh found in " + listVolScr);
createVolScr = Script.findScript(scriptsDir, "createvolume.sh");
if (createVolScr == null) {
@ -995,10 +1043,5 @@ public class DownloadManagerImpl implements DownloadManager {
return;
}
}
@Override
public Map<String, TemplateInfo> gatherVolumeInfo(String volumeDir) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -22,6 +22,7 @@ import java.util.Properties;
import org.apache.log4j.Logger;
import com.cloud.agent.api.storage.DownloadCommand.ResourceType;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.StorageLayer;
import com.cloud.storage.template.Processor.FormatInfo;
@ -34,6 +35,7 @@ public class TemplateLocation {
StorageLayer _storage;
String _templatePath;
boolean _isCorrupted;
ResourceType _resourceType = ResourceType.TEMPLATE;
File _file;
Properties _props;
@ -48,9 +50,10 @@ public class TemplateLocation {
}
_formats = new ArrayList<FormatInfo>(5);
_props = new Properties();
//TO DO remove this hack
//TO DO - remove this hack
if (_templatePath.matches(".*"+"volumes"+".*")){
_file = _storage.getFile(_templatePath + "volume.properties");
_resourceType = ResourceType.VOLUME;
}else {
_file = _storage.getFile(_templatePath + Filename);
}
@ -153,7 +156,11 @@ public class TemplateLocation {
TemplateInfo tmplInfo = new TemplateInfo();
tmplInfo.id = Long.parseLong(_props.getProperty("id"));
tmplInfo.installPath = _templatePath + File.separator + _props.getProperty("filename");
tmplInfo.installPath = tmplInfo.installPath.substring(tmplInfo.installPath.indexOf("template"));
if (_resourceType == ResourceType.VOLUME){
tmplInfo.installPath = tmplInfo.installPath.substring(tmplInfo.installPath.indexOf("volumes"));
}else {
tmplInfo.installPath = tmplInfo.installPath.substring(tmplInfo.installPath.indexOf("template"));
}
tmplInfo.isCorrupted = _isCorrupted;
tmplInfo.isPublic = Boolean.parseBoolean(_props.getProperty("public"));
tmplInfo.templateName = _props.getProperty("uniquename");

View File

@ -0,0 +1,66 @@
#!/usr/bin/env bash
# Copyright (C) 2011 Citrix Systems, Inc. All rights reserved
#
# This software is licensed under the GNU General Public License v3 or later.
#
# It is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# $Id: listvmvolume.sh 9132 2010-06-04 20:17:43Z manuel $ $HeadURL: svn://svn.lab.vmops.com/repos/vmdev/java/scripts/storage/secondary/listvmvolume.sh $
# listvolume.sh -- list volumes under a directory
usage() {
printf "Usage: %s: -r <root dir> \n" $(basename $0) >&2
}
#set -x
rflag=
rootdir=
while getopts 'r:' OPTION
do
case $OPTION in
r) rflag=1
rootdir="$OPTARG"
;;
?) usage
exit 2
;;
esac
done
if [ "$rflag" != "1" ]
then
usage
exit 2
fi
for i in $(find /$rootdir -name volume.properties );
do
d=$(dirname $i)
filename=$(grep "^filename" $i | awk -F"=" '{print $NF}')
# size=$(grep "virtualsize" $i | awk -F"=" '{print $NF}')
# if [ -n "$filename" ] && [ -n "$size" ]
# then
# d=$d/$filename/$size
# fi
echo ${d#/}/$filename #remove leading slash
done
exit 0

View File

@ -1,12 +1,17 @@
package com.cloud.storage.dao;
import java.util.List;
import com.cloud.host.HostVO;
import com.cloud.storage.VolumeHostVO;
import com.cloud.utils.db.GenericDao;
public interface VolumeHostDao extends GenericDao<VolumeHostVO, Long> {
VolumeHostVO findByHostVolume(long id, long id2);
VolumeHostVO findByHostVolume(long hostId, long volumeId);
VolumeHostVO findByVolumeId(long volumeId);
List<VolumeHostVO> listBySecStorage(long sserverId);
}

View File

@ -4,6 +4,8 @@ package com.cloud.storage.dao;
import java.util.List;
import javax.ejb.Local;
import com.cloud.host.HostVO;
import com.cloud.storage.VolumeHostVO;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.SearchBuilder;
@ -13,6 +15,7 @@ public class VolumeHostDaoImpl extends GenericDaoBase<VolumeHostVO, Long> implem
protected final SearchBuilder<VolumeHostVO> HostVolumeSearch;
protected final SearchBuilder<VolumeHostVO> VolumeSearch;
protected final SearchBuilder<VolumeHostVO> HostSearch;
VolumeHostDaoImpl(){
HostVolumeSearch = createSearchBuilder();
@ -21,6 +24,11 @@ public class VolumeHostDaoImpl extends GenericDaoBase<VolumeHostVO, Long> implem
HostVolumeSearch.and("destroyed", HostVolumeSearch.entity().getDestroyed(), SearchCriteria.Op.EQ);
HostVolumeSearch.done();
HostSearch = createSearchBuilder();
HostSearch.and("host_id", HostSearch.entity().getHostId(), SearchCriteria.Op.EQ);
HostSearch.and("destroyed", HostSearch.entity().getDestroyed(), SearchCriteria.Op.EQ);
HostSearch.done();
VolumeSearch = createSearchBuilder();
VolumeSearch.and("volume_id", VolumeSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
VolumeSearch.and("destroyed", VolumeSearch.entity().getDestroyed(), SearchCriteria.Op.EQ);
@ -46,4 +54,14 @@ public class VolumeHostDaoImpl extends GenericDaoBase<VolumeHostVO, Long> implem
return findOneBy(sc);
}
@Override
public List<VolumeHostVO> listBySecStorage(long ssHostId) {
SearchCriteria<VolumeHostVO> sc = HostSearch.create();
sc.setParameters("host_id", ssHostId);
sc.setParameters("destroyed", false);
return listAll();
}
}

View File

@ -338,13 +338,14 @@ public class DownloadListener implements Listener {
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
volumeHostDao.update(getVolumeHostId(), updateBuilder);
try {
_storageMgr.stateTransitTo(volume, Event.UploadSucceeded);
} catch (NoTransitionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
if (answer.getDownloadStatus() == Status.DOWNLOADED){
try {
_storageMgr.stateTransitTo(volume, Event.UploadSucceeded);
} catch (NoTransitionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*if (answer.getCheckSum() != null) {
VMTemplateVO templateDaoBuilder = _vmTemplateDao.createForUpdate();
templateDaoBuilder.setChecksum(answer.getCheckSum());
@ -379,9 +380,10 @@ public class DownloadListener implements Listener {
storage.getResourceType() == Storage.StorageResourceType.LOCAL_SECONDARY_STORAGE ) {
downloadMonitor.addSystemVMTemplatesToHost(agent, storage.getTemplateInfo());
downloadMonitor.handleTemplateSync(agent);
downloadMonitor.handleVolumeSync(agent);
}
} else if ( cmd instanceof StartupSecondaryStorageCommand ) {
downloadMonitor.handleTemplateSync(agent.getDataCenterId());
downloadMonitor.handleSync(agent.getDataCenterId());
}
}

View File

@ -39,10 +39,12 @@ public interface DownloadMonitor extends Manager{
void handleSysTemplateDownload(HostVO hostId);
void handleTemplateSync(Long dcId);
void handleSync(Long dcId);
void addSystemVMTemplatesToHost(HostVO host, Map<String, TemplateInfo> templateInfos);
boolean downloadVolumeToStorage(VolumeVO volume, Long zoneId, String url, String checkSum);
void handleVolumeSync(HostVO ssHost);
}

View File

@ -32,7 +32,10 @@ import com.cloud.agent.Listener;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.Command;
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.ListVolumeAnswer;
import com.cloud.agent.api.storage.ListVolumeCommand;
import com.cloud.agent.api.storage.DownloadCommand.Proxy;
import com.cloud.agent.api.storage.DownloadCommand.ResourceType;
import com.cloud.agent.api.storage.DownloadProgressCommand;
@ -620,11 +623,12 @@ public class DownloadMonitorImpl implements DownloadMonitor {
}
@Override
public void handleTemplateSync(Long dcId) {
public void handleSync(Long dcId) {
if (dcId != null) {
List<HostVO> ssHosts = _ssvmMgr.listSecondaryStorageHostsInOneZone(dcId);
for (HostVO ssHost : ssHosts) {
handleTemplateSync(ssHost);
handleVolumeSync(ssHost);
}
}
}
@ -644,6 +648,21 @@ public class DownloadMonitorImpl implements DownloadMonitor {
return null;
}
private Map<Long, TemplateInfo> listVolume(HostVO ssHost) {
ListVolumeCommand cmd = new ListVolumeCommand(ssHost.getStorageUrl());
Answer answer = _agentMgr.sendToSecStorage(ssHost, cmd);
if (answer != null && answer.getResult()) {
ListVolumeAnswer tanswer = (ListVolumeAnswer)answer;
return tanswer.getTemplateInfo();
} else {
if (s_logger.isDebugEnabled()) {
s_logger.debug("Can not list volumes for secondary storage host " + ssHost.getId());
}
}
return null;
}
private Map<String, TemplateInfo> listTemplate(SwiftVO swift) {
if (swift == null) {
return null;
@ -660,7 +679,88 @@ public class DownloadMonitorImpl implements DownloadMonitor {
}
return null;
}
@Override
public void handleVolumeSync(HostVO ssHost) {
if (ssHost == null) {
s_logger.warn("Huh? ssHost is null");
return;
}
long sserverId = ssHost.getId();
if (!(ssHost.getType() == Host.Type.SecondaryStorage || ssHost.getType() == Host.Type.LocalSecondaryStorage)) {
s_logger.warn("Huh? Agent id " + sserverId + " is not secondary storage host");
return;
}
Map<Long, TemplateInfo> volumeInfos = listVolume(ssHost);
if (volumeInfos == null) {
return;
}
List<VolumeHostVO> dbVolumes = _volumeHostDao.listBySecStorage(sserverId);
List<VolumeHostVO> toBeDownloaded = new ArrayList<VolumeHostVO>(dbVolumes);
for (VolumeHostVO volumeHost : dbVolumes){
VolumeVO volume = _volumeDao.findById(volumeHost.getVolumeId());
//Exists then don't download
if (volumeInfos.containsKey(volume.getId())){
TemplateInfo volInfo = volumeInfos.remove(volume.getId());
toBeDownloaded.remove(volumeHost);
s_logger.info("Volume Sync found " + volume.getUuid() + " already in the volume host table");
if (volumeHost.getDownloadState() != Status.DOWNLOADED) {
volumeHost.setErrorString("");
}
if (volInfo.isCorrupted()) {
volumeHost.setDownloadState(Status.DOWNLOAD_ERROR);
String msg = "Volume " + volume.getUuid() + " is corrupted on secondary storage ";
volumeHost.setErrorString(msg);
s_logger.info("msg");
if (volumeHost.getDownloadUrl() == null) {
msg = "Volume (" + volume.getUuid() + ") with install path " + volInfo.getInstallPath() + "is corrupted, please check in secondary storage: " + volumeHost.getHostId();
s_logger.warn(msg);
} else {
toBeDownloaded.add(volumeHost);
}
} else {
volumeHost.setDownloadPercent(100);
volumeHost.setDownloadState(Status.DOWNLOADED);
volumeHost.setInstallPath(volInfo.getInstallPath());
volumeHost.setSize(volInfo.getSize());
volumeHost.setPhysicalSize(volInfo.getPhysicalSize());
volumeHost.setLastUpdated(new Date());
}
_volumeHostDao.update(volumeHost.getId(), volumeHost);
}
}
//Download volumes which havent been downloaded yet.
if (toBeDownloaded.size() > 0) {
for (VolumeHostVO volumeHost : toBeDownloaded) {
if (volumeHost.getDownloadUrl() == null) { // If url is null we can't initiate the download
continue;
}
s_logger.debug("Volume " + volumeHost.getVolumeId() + " needs to be downloaded to " + ssHost.getName());
downloadVolumeToStorage(_volumeDao.findById(volumeHost.getVolumeId()), ssHost, volumeHost.getDownloadUrl(), volumeHost.getChecksum());
}
}
//Delete volumes which are not present on DB.
for (Long uniqueName : volumeInfos.keySet()) {
TemplateInfo vInfo = volumeInfos.get(uniqueName);
DeleteVolumeCommand dtCommand = new DeleteVolumeCommand(ssHost.getStorageUrl(), vInfo.getInstallPath());
try {
_agentMgr.sendToSecStorage(ssHost, dtCommand, null);
} catch (AgentUnavailableException e) {
String err = "Failed to delete " + vInfo.getTemplateName() + " on secondary storage " + sserverId + " which isn't in the database";
s_logger.error(err);
return;
}
String description = "Deleted volume " + vInfo.getTemplateName() + " on secondary storage " + sserverId + " since it isn't in the database";
s_logger.info(description);
}
}
@Override
public void handleTemplateSync(HostVO ssHost) {
if (ssHost == null) {