diff --git a/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java b/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java
index aa854013c59..9ee90b72ddd 100644
--- a/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java
+++ b/core/src/org/apache/cloudstack/storage/to/TemplateObjectTO.java
@@ -135,6 +135,10 @@ public class TemplateObjectTO implements DataTO {
return this.imageDataStore;
}
+ public void setHypervisorType(Hypervisor.HypervisorType hypervisorType) {
+ this.hypervisorType = hypervisorType;
+ }
+
@Override
public Hypervisor.HypervisorType getHypervisorType() {
return this.hypervisorType;
diff --git a/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml b/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
index ade22dbc25b..fb69da57a6d 100644
--- a/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
+++ b/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
@@ -32,4 +32,6 @@
class="org.apache.cloudstack.storage.motion.XenServerStorageMotionStrategy" />
+
diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
index d9150634d95..cf9e9dc11a9 100644
--- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java
@@ -18,8 +18,12 @@
*/
package org.apache.cloudstack.storage.motion;
+import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import javax.inject.Inject;
+
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
@@ -27,20 +31,68 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
+import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.storage.command.CopyCommand;
+import org.apache.cloudstack.storage.command.CopyCmdAnswer;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.configuration.Config;
import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.org.Cluster;
+import com.cloud.org.Grouping.AllocationState;
+import com.cloud.resource.ResourceState;
+import com.cloud.server.ManagementService;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.dao.SnapshotDetailsDao;
+import com.cloud.storage.dao.SnapshotDetailsVO;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VirtualMachineManager;
@Component
public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
private static final Logger s_logger = Logger.getLogger(StorageSystemDataMotionStrategy.class);
+ @Inject private AgentManager _agentMgr;
+ @Inject private ConfigurationDao _configDao;
+ @Inject private HostDao _hostDao;
+ @Inject private ManagementService _mgr;
+ @Inject private PrimaryDataStoreDao _storagePoolDao;
+ @Inject private SnapshotDetailsDao _snapshotDetailsDao;
+ @Inject private VolumeService _volService;
+
@Override
public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
- return StrategyPriority.DEFAULT;
+ if (srcData instanceof SnapshotInfo && destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache) {
+ DataStore dataStore = srcData.getDataStore();
+ Map mapCapabilities = dataStore.getDriver().getCapabilities();
+
+ if (mapCapabilities != null) {
+ String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
+ Boolean supportsStorageSystemSnapshots = new Boolean(value);
+
+ if (supportsStorageSystemSnapshots) {
+ s_logger.info("Using 'StorageSystemDataMotionStrategy'");
+
+ return StrategyPriority.HIGHEST;
+ }
+ }
+ }
+
+ return StrategyPriority.CANT_HANDLE;
}
@Override
@@ -50,10 +102,114 @@ public class StorageSystemDataMotionStrategy implements DataMotionStrategy {
@Override
public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback callback) {
+ if (srcData instanceof SnapshotInfo && destData.getDataStore().getRole() == DataStoreRole.Image || destData.getDataStore().getRole() == DataStoreRole.ImageCache) {
+ SnapshotInfo snapshotInfo = (SnapshotInfo)srcData;
+ HostVO hostVO = getHost(srcData);
+ DataStore srcDataStore = srcData.getDataStore();
+
+ String value = _configDao.getValue(Config.PrimaryStorageDownloadWait.toString());
+ int primaryStorageDownloadWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.PrimaryStorageDownloadWait.getDefaultValue()));
+ CopyCommand copyCommand = new CopyCommand(srcData.getTO(), destData.getTO(), primaryStorageDownloadWait, VirtualMachineManager.ExecuteInSequence.value());
+
+ CopyCmdAnswer copyCmdAnswer = null;
+
+ try {
+ _volService.grantAccess(snapshotInfo, hostVO, srcDataStore);
+
+ Map srcDetails = getSourceDetails(_storagePoolDao.findById(srcDataStore.getId()), snapshotInfo);
+
+ copyCommand.setOptions(srcDetails);
+
+ copyCmdAnswer = (CopyCmdAnswer)_agentMgr.send(hostVO.getId(), copyCommand);
+ }
+ catch (Exception ex) {
+ throw new CloudRuntimeException(ex.getMessage());
+ }
+ finally {
+ try {
+ _volService.revokeAccess(snapshotInfo, hostVO, srcDataStore);
+ }
+ catch (Exception ex) {
+ s_logger.debug(ex.getMessage(), ex);
+ }
+ }
+
+ String errMsg = null;
+
+ if (copyCmdAnswer == null || !copyCmdAnswer.getResult()) {
+ if (copyCmdAnswer != null && copyCmdAnswer.getDetails() != null && !copyCmdAnswer.getDetails().isEmpty()) {
+ errMsg = copyCmdAnswer.getDetails();
+ }
+ else {
+ errMsg = "Unable to perform host-side operation";
+ }
+ }
+
+ CopyCommandResult result = new CopyCommandResult(null, copyCmdAnswer);
+
+ result.setResult(errMsg);
+
+ callback.complete(result);
+ }
return null;
}
+ private Map getSourceDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) {
+ Map srcDetails = new HashMap();
+
+ srcDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
+ srcDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
+
+ long snapshotId = snapshotInfo.getId();
+
+ srcDetails.put(DiskTO.IQN, getProperty(snapshotId, DiskTO.IQN));
+
+ srcDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_USERNAME));
+ srcDetails.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET));
+ srcDetails.put(DiskTO.CHAP_TARGET_USERNAME, getProperty(snapshotId, DiskTO.CHAP_TARGET_USERNAME));
+ srcDetails.put(DiskTO.CHAP_TARGET_SECRET, getProperty(snapshotId, DiskTO.CHAP_TARGET_SECRET));
+
+ return srcDetails;
+ }
+
+ private String getProperty(long snapshotId, String property) {
+ SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, property);
+
+ if (snapshotDetails != null) {
+ return snapshotDetails.getValue();
+ }
+
+ return null;
+ }
+
+ public HostVO getHost(DataObject srcData) {
+ long dataStoreId = srcData.getDataStore().getId();
+ StoragePoolVO storagePoolVO = _storagePoolDao.findById(dataStoreId);
+
+ List extends Cluster> clusters = _mgr.searchForClusters(storagePoolVO.getDataCenterId(), new Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString());
+
+ if (clusters == null) {
+ throw new CloudRuntimeException("Unable to locate an applicable cluster");
+ }
+
+ for (Cluster cluster : clusters) {
+ if (cluster.getAllocationState() == AllocationState.Enabled) {
+ List hosts = _hostDao.findByClusterId(cluster.getId());
+
+ if (hosts != null) {
+ for (HostVO host : hosts) {
+ if (host.getResourceState() == ResourceState.Enabled) {
+ return host;
+ }
+ }
+ }
+ }
+ }
+
+ throw new CloudRuntimeException("Unable to locate an applicable cluster");
+ }
+
@Override
public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) {
return copyAsync(srcData, destData, null, callback);
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
index 7288d454c30..e4b04de9e9b 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
@@ -217,6 +217,9 @@ public class TemplateObject implements TemplateInfo {
// For template created from snapshot, template name is determine by resource code.
templateVO.setUniqueName(newTemplate.getName());
}
+ if (newTemplate.getHypervisorType() != null) {
+ templateVO.setHypervisorType(newTemplate.getHypervisorType());
+ }
templateVO.setSize(newTemplate.getSize());
imageDao.update(templateVO.getId(), templateVO);
}
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
index 7427f37c4cb..9a6c51b709e 100644
--- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
+++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
@@ -226,7 +226,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
sourceDetails = getSourceDetails(volumeInfo);
}
- HostVO hostVO = getHostId(hostId, volumeVO);
+ HostVO hostVO = getHost(hostId, volumeVO);
long storagePoolId = volumeVO.getPoolId();
StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
@@ -340,7 +340,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
return null;
}
- private HostVO getHostId(Long hostId, VolumeVO volumeVO) {
+ private HostVO getHost(Long hostId, VolumeVO volumeVO) {
HostVO hostVO = _hostDao.findById(hostId);
if (hostVO != null) {
@@ -404,7 +404,8 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
Map mapCapabilities = dataStore.getDriver().getCapabilities();
- if(mapCapabilities != null) {
+
+ if (mapCapabilities != null) {
String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString());
Boolean supportsStorageSystemSnapshots = new Boolean(value);
@@ -412,6 +413,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
return StrategyPriority.HIGHEST;
}
}
+
return StrategyPriority.CANT_HANDLE;
}
}
diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
index 813bf2bc424..f261410b1ee 100644
--- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
+++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
@@ -64,8 +64,10 @@ import com.cloud.agent.api.to.S3TO;
import com.cloud.agent.api.to.StorageFilerTO;
import com.cloud.agent.api.to.SwiftTO;
import com.cloud.exception.InternalErrorException;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase.SRType;
import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.resource.StorageProcessor;
import com.cloud.utils.S3Utils;
@@ -1457,7 +1459,126 @@ public class XenServerStorageProcessor implements StorageProcessor {
@Override
public Answer createTemplateFromSnapshot(CopyCommand cmd) {
- return null; //To change body of implemented methods use File | Settings | File Templates.
+ Connection conn = hypervisorResource.getConnection();
+
+ SnapshotObjectTO snapshotObjTO = (SnapshotObjectTO)cmd.getSrcTO();
+ TemplateObjectTO templateObjTO = (TemplateObjectTO)cmd.getDestTO();
+
+ if (!(snapshotObjTO.getDataStore() instanceof PrimaryDataStoreTO) || !(templateObjTO.getDataStore() instanceof NfsTO)) {
+ return null;
+ }
+
+ String userSpecifiedTemplateName = templateObjTO.getName();
+
+ NfsTO destStore = null;
+ URI destUri = null;
+
+ try {
+ destStore = (NfsTO)templateObjTO.getDataStore();
+
+ destUri = new URI(destStore.getUrl());
+ } catch (Exception ex) {
+ s_logger.debug("Invalid URI", ex);
+
+ return new CopyCmdAnswer("Invalid URI: " + ex.toString());
+ }
+
+ SR srcSr = null;
+ SR destSr = null;
+
+ String destDir = templateObjTO.getPath();
+ VDI destVdi = null;
+
+ boolean result = false;
+
+ try {
+ Map srcDetails = cmd.getOptions();
+
+ String iScsiName = srcDetails.get(DiskTO.IQN);
+ String storageHost = srcDetails.get(DiskTO.STORAGE_HOST);
+ String chapInitiatorUsername = srcDetails.get(DiskTO.CHAP_INITIATOR_USERNAME);
+ String chapInitiatorSecret = srcDetails.get(DiskTO.CHAP_INITIATOR_SECRET);
+
+ srcSr = hypervisorResource.getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, true);
+
+ String destNfsPath = destUri.getHost() + ":" + destUri.getPath();
+
+ if (!hypervisorResource.createSecondaryStorageFolder(conn, destNfsPath, destDir)) {
+ String details = " Failed to create folder " + destDir + " in secondary storage";
+
+ s_logger.warn(details);
+
+ return new CopyCmdAnswer(details);
+ }
+
+ URI templateUri = new URI(destStore.getUrl() + "/" + destDir);
+
+ destSr = hypervisorResource.createNfsSRbyURI(conn, templateUri, false);
+
+ // there should only be one VDI in this SR
+ VDI srcVdi = srcSr.getVDIs(conn).iterator().next();
+
+ destVdi = srcVdi.copy(conn, destSr);
+
+ // scan makes XenServer pick up VDI physicalSize
+ destSr.scan(conn);
+
+ if (userSpecifiedTemplateName != null) {
+ destVdi.setNameLabel(conn, userSpecifiedTemplateName);
+ }
+
+ String templateUuid = destVdi.getUuid(conn);
+ String templateFilename = templateUuid + ".vhd";
+ long virtualSize = destVdi.getVirtualSize(conn);
+ long physicalSize = destVdi.getPhysicalUtilisation(conn);
+
+ // create the template.properties file
+ String templatePath = destNfsPath + "/" + destDir;
+
+ templatePath = templatePath.replaceAll("//", "/");
+
+ result = hypervisorResource.postCreatePrivateTemplate(conn, templatePath, templateFilename, templateUuid, userSpecifiedTemplateName, null,
+ physicalSize, virtualSize, templateObjTO.getId());
+
+ if (!result) {
+ throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + templateUri);
+ }
+
+ TemplateObjectTO newTemplate = new TemplateObjectTO();
+
+ newTemplate.setPath(destDir + "/" + templateFilename);
+ newTemplate.setFormat(Storage.ImageFormat.VHD);
+ newTemplate.setHypervisorType(HypervisorType.XenServer);
+ newTemplate.setSize(virtualSize);
+ newTemplate.setPhysicalSize(physicalSize);
+ newTemplate.setName(templateUuid);
+
+ result = true;
+
+ return new CopyCmdAnswer(newTemplate);
+ } catch (Exception ex) {
+ s_logger.error("Failed to create a template from a snapshot", ex);
+
+ return new CopyCmdAnswer("Failed to create a template from a snapshot: " + ex.toString());
+ } finally {
+ if (!result) {
+ if (destVdi != null) {
+ try {
+ destVdi.destroy(conn);
+ } catch (Exception e) {
+ s_logger.debug("Cleaned up leftover VDI on destination storage due to failure: ", e);
+ }
+ }
+ }
+
+ if (srcSr != null) {
+ hypervisorResource.removeSR(conn, srcSr);
+ }
+
+ if (destSr != null) {
+ hypervisorResource.removeSR(conn, destSr);
+ }
+ }
}
@Override
diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
index 20baffd6bf4..7e2a097dd7d 100644
--- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
+++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/Xenserver625StorageProcessor.java
@@ -61,69 +61,94 @@ public class Xenserver625StorageProcessor extends XenServerStorageProcessor {
public Xenserver625StorageProcessor(CitrixResourceBase resource) {
super(resource);
}
+
protected boolean mountNfs(Connection conn, String remoteDir, String localDir) {
if (localDir == null) {
localDir = "/var/cloud_mount/" + UUID.nameUUIDFromBytes(remoteDir.getBytes());
}
+
String results = hypervisorResource.callHostPluginAsync(conn, "cloud-plugin-storage", "mountNfsSecondaryStorage", 100 * 1000,
"localDir", localDir, "remoteDir", remoteDir);
+
if (results == null || results.isEmpty()) {
String errMsg = "Could not mount secondary storage " + remoteDir + " on host " + localDir;
+
s_logger.warn(errMsg);
+
throw new CloudRuntimeException(errMsg);
}
+
return true;
}
protected boolean makeDirectory(Connection conn, String path) {
String result = hypervisorResource.callHostPlugin(conn, "cloud-plugin-storage", "makeDirectory", "path", path);
+
if (result == null || result.isEmpty()) {
return false;
}
+
return true;
}
protected SR createFileSR(Connection conn, String path) {
SR sr = null;
PBD pbd = null;
+
try {
String srname = hypervisorResource.getHost().uuid + path.trim();
+
Set srs = SR.getByNameLabel(conn, srname);
- if ( srs != null && !srs.isEmpty()) {
+
+ if (srs != null && !srs.isEmpty()) {
return srs.iterator().next();
}
+
Map smConfig = new HashMap();
+
Host host = Host.getByUuid(conn, hypervisorResource.getHost().uuid);
String uuid = UUID.randomUUID().toString();
sr = SR.introduce(conn, uuid, srname, srname, "file", "file", false, smConfig);
+
PBD.Record record = new PBD.Record();
+
record.host = host;
record.SR = sr;
+
smConfig.put("location", path);
+
record.deviceConfig = smConfig;
+
pbd = PBD.create(conn, record);
+
pbd.plug(conn);
+
sr.scan(conn);
+
return sr;
- } catch (Exception e) {
+ } catch (Exception ex) {
try {
if (pbd != null) {
pbd.destroy(conn);
}
} catch (Exception e1) {
- s_logger.debug("Failed to destroy pbd", e);
+ s_logger.debug("Failed to destroy PBD", ex);
}
+
try {
if (sr != null) {
sr.forget(conn);
}
} catch (Exception e2) {
- s_logger.error("Failed to forget sr", e);
+ s_logger.error("Failed to forget SR", ex);
}
- String msg = "createFileSR failed! due to " + e.toString();
- s_logger.warn(msg, e);
- throw new CloudRuntimeException(msg, e);
+
+ String msg = "createFileSR failed! due to the following: " + ex.toString();
+
+ s_logger.warn(msg, ex);
+
+ throw new CloudRuntimeException(msg, ex);
}
}