Support for back-end snapshots on primary storage

This commit is contained in:
Mike Tutkowski 2014-10-19 18:34:36 -06:00
parent 6beeeff7d4
commit 0cea0346ae
14 changed files with 383 additions and 110 deletions

View File

@ -34,6 +34,7 @@ public class DiskTO {
public static final String VOLUME_SIZE = "volumeSize";
public static final String MOUNT_POINT = "mountpoint";
public static final String PROTOCOL_TYPE = "protocoltype";
public static final String PATH = "path";
private DataTO data;
private Long diskSeq;

View File

@ -38,7 +38,6 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
Ready("The volume is ready to be used."),
Migrating("The volume is migrating to other storage pool"),
Snapshotting("There is a snapshot created on this volume, not backed up to secondary storage yet"),
Reverting("Replace the existing volume on a storage system with a snapshot of it"),
Resizing("The volume is being resized"),
Expunging("The volume is being expunging"),
Expunged("The volume is being expunging"),
@ -86,11 +85,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
s_fsm.addTransition(Expunging, Event.OperationSucceeded, Expunged);
s_fsm.addTransition(Expunging, Event.OperationFailed, Destroy);
s_fsm.addTransition(Ready, Event.SnapshotRequested, Snapshotting);
s_fsm.addTransition(Ready, Event.RevertRequested, Reverting);
s_fsm.addTransition(Snapshotting, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Snapshotting, Event.OperationFailed, Ready);
s_fsm.addTransition(Reverting, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Reverting, Event.OperationFailed, Ready);
s_fsm.addTransition(Ready, Event.MigrationRequested, Migrating);
s_fsm.addTransition(Migrating, Event.OperationSucceeded, Ready);
s_fsm.addTransition(Migrating, Event.OperationFailed, Ready);
@ -115,7 +111,6 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
UploadRequested,
MigrationRequested,
SnapshotRequested,
RevertRequested,
DestroyRequested,
ExpungingRequested,
ResizeRequested;

View File

@ -26,6 +26,7 @@ import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DettachCommand;
import org.apache.cloudstack.storage.command.ForgetObjectCmd;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
import com.cloud.agent.api.Answer;
@ -62,7 +63,9 @@ public interface StorageProcessor {
public Answer deleteSnapshot(DeleteCommand cmd);
Answer introduceObject(IntroduceObjectCmd cmd);
public Answer introduceObject(IntroduceObjectCmd cmd);
Answer forgetObject(ForgetObjectCmd cmd);
public Answer forgetObject(ForgetObjectCmd cmd);
public Answer snapshotAndCopy(SnapshotAndCopyCommand cmd);
}

View File

@ -0,0 +1,41 @@
//
// 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.command;
import com.cloud.agent.api.Answer;
public class SnapshotAndCopyAnswer extends Answer {
private String _path;
public SnapshotAndCopyAnswer() {
}
public SnapshotAndCopyAnswer(String errMsg) {
super(null, false, errMsg);
}
public void setPath(String path) {
_path = path;
}
public String getPath() {
return _path;
}
}

View File

@ -0,0 +1,60 @@
//
// 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.command;
import com.cloud.agent.api.Command;
import java.util.Map;
public final class SnapshotAndCopyCommand extends Command implements StorageSubSystemCommand {
private String _uuidOfSourceVdi;
private Map<String, String> _sourceDetails;
private Map<String, String> _destDetails;
private boolean _executeInSequence = true;
public SnapshotAndCopyCommand(String uuidOfSourceVdi, Map<String, String> sourceDetails, Map<String, String> destDetails) {
_uuidOfSourceVdi = uuidOfSourceVdi;
_sourceDetails = sourceDetails;
_destDetails = destDetails;
}
public String getUuidOfSourceVdi() {
return _uuidOfSourceVdi;
}
public Map<String, String> getSourceDetails() {
return _sourceDetails;
}
public Map<String, String> getDestDetails() {
return _destDetails;
}
@Override
public void setExecuteInSequence(boolean executeInSequence) {
_executeInSequence = executeInSequence;
}
@Override
public boolean executeInSequence() {
return _executeInSequence;
}
}

View File

@ -26,9 +26,6 @@ import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.host.Host;
public interface DataMotionStrategy {
// IQN is used by the StorageSystemDataMotionStrategy to create a template from a snapshot or clone that resides on a storage system
public static final String IQN = "iqn";
StrategyPriority canHandle(DataObject srcData, DataObject destData);
StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost);

View File

@ -16,12 +16,15 @@
// under the License.
package org.apache.cloudstack.storage.snapshot;
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;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
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.DataStoreManager;
@ -30,43 +33,53 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotResult;
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.storage.command.SnapshotAndCopyAnswer;
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.exception.InvalidParameterValueException;
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.Snapshot;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage.ImageFormat;
import com.cloud.storage.Volume;
import com.cloud.storage.Volume.Type;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.dao.SnapshotDetailsDao;
import com.cloud.storage.dao.SnapshotDetailsVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.uservm.UserVm;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.EntityManager;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
@Component
public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
private static final Logger s_logger = Logger.getLogger(StorageSystemSnapshotStrategy.class);
@Inject private ClusterDao _clusterDao;
@Inject private AgentManager _agentMgr;
@Inject private DataStoreManager _dataStoreMgr;
@Inject private EntityManager _entityMgr;
@Inject private HostDao _hostDao;
@Inject private ManagementService _mgr;
@Inject private PrimaryDataStoreDao _storagePoolDao;
@Inject private SnapshotDao _snapshotDao;
@Inject private SnapshotDataFactory _snapshotDataFactory;
@Inject private SnapshotDataStoreDao _snapshotStoreDao;
@Inject private PrimaryDataStoreDao _storagePoolDao;
@Inject private SnapshotDetailsDao _snapshotDetailsDao;
@Inject private VMInstanceDao _vmInstanceDao;
@Inject private VolumeDao _volumeDao;
@Inject private VolumeService _volService;
@Override
public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) {
@ -133,81 +146,18 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
@Override
public boolean revertSnapshot(Long snapshotId) {
// verify the following:
// if root disk, the VM cannot be running (don't allow this at all for ESX)
// if data disk, the disk cannot be in the attached state
SnapshotInfo snapshotInfo = _snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
VolumeVO volume = _volumeDao.findById(volumeInfo.getId());
if (volume.getVolumeType() == Type.DATADISK) {
if (volume.getAttached() != null) {
throw new CloudRuntimeException("A data disk must be in the detached state in order to perform a revert.");
}
}
else if (volume.getVolumeType() == Type.ROOT) {
Long instanceId = volume.getInstanceId();
UserVm vm = _entityMgr.findById(UserVm.class, instanceId);
Long hostId = vm.getHostId();
HostVO hostVO = _hostDao.findById(hostId);
Long clusterId = hostVO.getClusterId();
ClusterVO clusterVO = _clusterDao.findById(clusterId);
if (clusterVO.getHypervisorType() != HypervisorType.XenServer && clusterVO.getHypervisorType() != HypervisorType.KVM) {
throw new CloudRuntimeException("Unsupported hypervisor type for root disk revert. Create a template from this disk and use it instead.");
}
if (vm.getState() != State.Stopped) {
throw new CloudRuntimeException("A root disk cannot be reverted unless the VM it's attached to is in the stopped state.");
}
}
else {
throw new CloudRuntimeException("Unsupported volume type");
}
SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotId);
if (snapshotVO == null) {
throw new CloudRuntimeException("Failed to acquire lock on the following snapshot: " + snapshotId);
}
try {
volumeInfo.stateTransit(Volume.Event.RevertRequested);
boolean result = false;
try {
result = snapshotSvr.revertSnapshot(snapshotId);
}
finally {
if (result) {
volumeInfo.stateTransit(Volume.Event.OperationSucceeded);
}
else {
String msg = "Failed to revert the volume to a snapshot";
s_logger.debug(msg);
volumeInfo.stateTransit(Volume.Event.OperationFailed);
throw new CloudRuntimeException("Failed to revert the volume to a snapshot");
}
}
}
finally {
if (snapshotVO != null) {
_snapshotDao.releaseFromLockTable(snapshotId);
}
}
return true;
throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead.");
}
@Override
@DB
public SnapshotInfo takeSnapshot(SnapshotInfo snapshotInfo) {
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
if (volumeInfo.getFormat() != ImageFormat.VHD) {
throw new CloudRuntimeException("Only the " + ImageFormat.VHD.toString() + " image type is currently supported.");
}
SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotInfo.getId());
if (snapshotVO == null) {
@ -215,21 +165,72 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
}
try {
VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
volumeInfo.stateTransit(Volume.Event.SnapshotRequested);
SnapshotResult result = null;
try {
// tell the storage driver to create a back-end volume (eventually used to create a new SR on and to copy the VM snapshot VDI to)
result = snapshotSvr.takeSnapshot(snapshotInfo);
if (result.isFailed()) {
s_logger.debug("Failed to take the following snapshot: " + result.getResult());
s_logger.debug("Failed to take a snapshot: " + result.getResult());
throw new CloudRuntimeException(result.getResult());
}
// send a command to XenServer to create a VM snapshot on the applicable SR (get back the VDI UUID of the VM snapshot)
Map<String, String> sourceDetails = null;
VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
Long vmInstanceId = volumeVO.getInstanceId();
VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(vmInstanceId);
Long hostId = null;
// if the volume to snapshot is associated with a VM
if (vmInstanceVO != null) {
hostId = vmInstanceVO.getHostId();
// if the VM is not associated with a host
if (hostId == null) {
sourceDetails = getSourceDetails(volumeInfo);
hostId = vmInstanceVO.getLastHostId();
}
}
// volume to snapshot is not associated with a VM (could be a data disk in the detached state)
else {
sourceDetails = getSourceDetails(volumeInfo);
}
long storagePoolId = volumeVO.getPoolId();
StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
Map<String, String> destDetails = getDestDetails(storagePoolVO, snapshotInfo);
SnapshotAndCopyCommand snapshotAndCopyCommand = new SnapshotAndCopyCommand(volumeInfo.getPath(), sourceDetails, destDetails);
SnapshotAndCopyAnswer snapshotAndCopyAnswer = null;
try {
snapshotAndCopyAnswer = (SnapshotAndCopyAnswer)_agentMgr.send(getHostId(hostId, volumeVO), snapshotAndCopyCommand);
}
catch (Exception e) {
throw new CloudRuntimeException(e.getMessage());
}
String path = snapshotAndCopyAnswer.getPath(); // for XenServer, this is the VDI's UUID
SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(snapshotInfo.getId(),
DiskTO.PATH,
path,
false);
_snapshotDetailsDao.persist(snapshotDetail);
markAsBackedUp((SnapshotObject)result.getSnashot());
}
finally {
@ -250,6 +251,92 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase {
return snapshotInfo;
}
private Map<String, String> getSourceDetails(VolumeInfo volumeInfo) {
Map<String, String> sourceDetails = new HashMap<String, String>();
VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
long storagePoolId = volumeVO.getPoolId();
StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
sourceDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
sourceDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
sourceDetails.put(DiskTO.IQN, volumeVO.get_iScsiName());
ChapInfo chapInfo = _volService.getChapInfo(volumeInfo, volumeInfo.getDataStore());
if (chapInfo != null) {
sourceDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername());
sourceDetails.put(DiskTO.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret());
sourceDetails.put(DiskTO.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername());
sourceDetails.put(DiskTO.CHAP_TARGET_SECRET, chapInfo.getTargetSecret());
}
return sourceDetails;
}
private Map<String, String> getDestDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) {
Map<String, String> destDetails = new HashMap<String, String>();
destDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress());
destDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort()));
long snapshotId = snapshotInfo.getId();
destDetails.put(DiskTO.IQN, getProperty(snapshotId, DiskTO.IQN));
destDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_USERNAME));
destDetails.put(DiskTO.CHAP_INITIATOR_SECRET, getProperty(snapshotId, DiskTO.CHAP_INITIATOR_SECRET));
destDetails.put(DiskTO.CHAP_TARGET_USERNAME, getProperty(snapshotId, DiskTO.CHAP_TARGET_USERNAME));
destDetails.put(DiskTO.CHAP_TARGET_SECRET, getProperty(snapshotId, DiskTO.CHAP_TARGET_SECRET));
return destDetails;
}
private String getProperty(long snapshotId, String property) {
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, property);
if (snapshotDetails != null) {
return snapshotDetails.getValue();
}
return null;
}
private Long getHostId(Long hostId, VolumeVO volumeVO) {
HostVO hostVO = _hostDao.findById(hostId);
if (hostVO != null) {
return hostId;
}
// pick a host in any XenServer cluster that's in the applicable zone
long zoneId = volumeVO.getDataCenterId();
List<? extends Cluster> clusters = _mgr.searchForClusters(zoneId, 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<HostVO> hosts = _hostDao.findByClusterId(cluster.getId());
if (hosts != null) {
for (HostVO host : hosts) {
if (host.getResourceState() == ResourceState.Enabled) {
return host.getId();
}
}
}
}
}
throw new CloudRuntimeException("Unable to locate an applicable cluster");
}
private void markAsBackedUp(SnapshotObject snapshotObj) {
try {
snapshotObj.processEvent(Snapshot.Event.BackupToSecondary);

View File

@ -62,6 +62,8 @@ import org.apache.cloudstack.storage.command.DettachAnswer;
import org.apache.cloudstack.storage.command.DettachCommand;
import org.apache.cloudstack.storage.command.ForgetObjectCmd;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer;
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
@ -141,6 +143,13 @@ public class KVMStorageProcessor implements StorageProcessor {
return true;
}
@Override
public SnapshotAndCopyAnswer snapshotAndCopy(SnapshotAndCopyCommand cmd) {
s_logger.info("'SnapshotAndCopyAnswer snapshotAndCopy(SnapshotAndCopyCommand)' not currently used for KVMStorageProcessor");
return new SnapshotAndCopyAnswer();
}
@Override
public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) {
DataTO srcData = cmd.getSrcTO();

View File

@ -35,6 +35,8 @@ import org.apache.cloudstack.storage.command.DettachAnswer;
import org.apache.cloudstack.storage.command.DettachCommand;
import org.apache.cloudstack.storage.command.ForgetObjectCmd;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer;
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
@ -57,6 +59,13 @@ public class SimulatorStorageProcessor implements StorageProcessor {
this.hypervisorResource = resource;
}
@Override
public SnapshotAndCopyAnswer snapshotAndCopy(SnapshotAndCopyCommand cmd) {
s_logger.info("'SnapshotAndCopyAnswer snapshotAndCopy(SnapshotAndCopyCommand)' not currently used for SimulatorStorageProcessor");
return new SnapshotAndCopyAnswer();
}
@Override
public Answer copyTemplateToPrimaryStorage(CopyCommand cmd) {
TemplateObjectTO template = new TemplateObjectTO();

View File

@ -59,6 +59,8 @@ import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.command.DettachCommand;
import org.apache.cloudstack.storage.command.ForgetObjectCmd;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer;
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
@ -124,6 +126,13 @@ public class VmwareStorageProcessor implements StorageProcessor {
_gson = GsonHelper.getGsonLogger();
}
@Override
public SnapshotAndCopyAnswer snapshotAndCopy(SnapshotAndCopyCommand cmd) {
s_logger.info("'SnapshotAndCopyAnswer snapshotAndCopy(SnapshotAndCopyCommand)' not currently used for VmwareStorageProcessor");
return new SnapshotAndCopyAnswer();
}
private String getOVFFilePath(String srcOVAFileName) {
File file = new File(srcOVAFileName);
assert (_storage != null);

View File

@ -1911,7 +1911,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
String chapInitiatorUsername = details.get(DiskTO.CHAP_INITIATOR_USERNAME);
String chapInitiatorSecret = details.get(DiskTO.CHAP_INITIATOR_SECRET);
String mountpoint = details.get(DiskTO.MOUNT_POINT);
String protocoltype=details.get(DiskTO.PROTOCOL_TYPE);
String protocoltype = details.get(DiskTO.PROTOCOL_TYPE);
if (StoragePoolType.NetworkFilesystem.toString().equalsIgnoreCase(protocoltype)) {
String poolid = storageHost + ":" + mountpoint;
@ -6124,8 +6124,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
VDI vdi = null;
if (cmd.getAttach() && cmd.isManaged()) {
SR sr =
getIscsiSR(conn, cmd.get_iScsiName(), cmd.getStorageHost(), cmd.get_iScsiName(), cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword(), true);
SR sr = getIscsiSR(conn, cmd.get_iScsiName(), cmd.getStorageHost(), cmd.get_iScsiName(), cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword(), true);
vdi = getVDIbyUuid(conn, cmd.getVolumePath(), false);

View File

@ -44,6 +44,8 @@ import org.apache.cloudstack.storage.command.DettachCommand;
import org.apache.cloudstack.storage.command.ForgetObjectCmd;
import org.apache.cloudstack.storage.command.IntroduceObjectAnswer;
import org.apache.cloudstack.storage.command.IntroduceObjectCmd;
import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer;
import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand;
import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol;
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
import org.apache.cloudstack.storage.to.SnapshotObjectTO;
@ -93,6 +95,67 @@ public class XenServerStorageProcessor implements StorageProcessor {
hypervisorResource = resource;
}
// if the source SR needs to be attached to, do so
// take a snapshot of the source VDI (on the source SR)
// create an iSCSI SR based on the new back-end volume
// copy the snapshot to the new SR
// delete the snapshot
// detach the new SR
// if we needed to perform an attach to the source SR, detach from it
@Override
public SnapshotAndCopyAnswer snapshotAndCopy(SnapshotAndCopyCommand cmd) {
Connection conn = hypervisorResource.getConnection();
try {
SR sourceSr = null;
Map<String, String> sourceDetails = cmd.getSourceDetails();
if (sourceDetails != null && sourceDetails.keySet().size() > 0) {
String iScsiName = sourceDetails.get(DiskTO.IQN);
String storageHost = sourceDetails.get(DiskTO.STORAGE_HOST);
String chapInitiatorUsername = sourceDetails.get(DiskTO.CHAP_INITIATOR_USERNAME);
String chapInitiatorSecret = sourceDetails.get(DiskTO.CHAP_INITIATOR_SECRET);
sourceSr = hypervisorResource.getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, false);
}
VDI vdiToSnapshot = VDI.getByUuid(conn, cmd.getUuidOfSourceVdi());
VDI vdiSnapshot = vdiToSnapshot.snapshot(conn, new HashMap<String, String>());
Map<String, String> destDetails = cmd.getDestDetails();
String iScsiName = destDetails.get(DiskTO.IQN);
String storageHost = destDetails.get(DiskTO.STORAGE_HOST);
String chapInitiatorUsername = destDetails.get(DiskTO.CHAP_INITIATOR_USERNAME);
String chapInitiatorSecret = destDetails.get(DiskTO.CHAP_INITIATOR_SECRET);
SR newSr = hypervisorResource.getIscsiSR(conn, iScsiName, storageHost, iScsiName, chapInitiatorUsername, chapInitiatorSecret, false);
VDI vdiCopy = vdiSnapshot.copy(conn, newSr);
vdiSnapshot.destroy(conn);
if (sourceSr != null) {
hypervisorResource.removeSR(conn, sourceSr);
}
hypervisorResource.removeSR(conn, newSr);
SnapshotAndCopyAnswer snapshotAndCopyAnswer = new SnapshotAndCopyAnswer();
snapshotAndCopyAnswer.setPath(vdiCopy.getUuid(conn));
return snapshotAndCopyAnswer;
}
catch (Exception ex) {
s_logger.warn("Failed to take and copy snapshot: " + ex.toString(), ex);
return new SnapshotAndCopyAnswer(ex.getMessage());
}
}
@Override
public AttachAnswer attachIso(AttachCommand cmd) {
DiskTO disk = cmd.getDisk();

View File

@ -26,7 +26,6 @@ import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@ -48,6 +47,7 @@ import com.cloud.agent.api.Answer;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.capacity.CapacityManager;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.ClusterDetailsVO;
@ -269,7 +269,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
if (lstSnapshots != null) {
for (SnapshotVO snapshot : lstSnapshots) {
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.SNAPSHOT_STORAGE_POOL_ID);
SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.STORAGE_POOL_ID);
// if this snapshot belongs to the storagePool that was passed in
if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePool.getId()) {
@ -554,33 +554,33 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
}
private void updateSnapshotDetails(long csSnapshotId, long sfNewVolumeId, long storagePoolId, long sfNewVolumeSize, String sfNewVolumeIqn) {
SnapshotDetailsVO accountDetail = new SnapshotDetailsVO(csSnapshotId,
SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.VOLUME_ID,
String.valueOf(sfNewVolumeId),
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(snapshotDetail);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.SNAPSHOT_STORAGE_POOL_ID,
snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.STORAGE_POOL_ID,
String.valueOf(storagePoolId),
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(snapshotDetail);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
SolidFireUtil.VOLUME_SIZE,
String.valueOf(sfNewVolumeSize),
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(snapshotDetail);
accountDetail = new SnapshotDetailsVO(csSnapshotId,
DataMotionStrategy.IQN,
snapshotDetail = new SnapshotDetailsVO(csSnapshotId,
DiskTO.IQN,
sfNewVolumeIqn,
false);
_snapshotDetailsDao.persist(accountDetail);
_snapshotDetailsDao.persist(snapshotDetail);
}
// return null for no error message

View File

@ -95,7 +95,7 @@ public class SolidFireUtil {
public static final String VOLUME_SIZE = "sfVolumeSize";
public static final String SNAPSHOT_STORAGE_POOL_ID = "sfSnapshotStoragePoolId";
public static final String STORAGE_POOL_ID = "sfStoragePoolId";
public static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername";
public static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret";