mirror of https://github.com/apache/cloudstack.git
Support for back-end snapshots on primary storage
This commit is contained in:
parent
6beeeff7d4
commit
0cea0346ae
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
Loading…
Reference in New Issue