Snapshot support
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/06f6b00c Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/06f6b00c Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/06f6b00c Branch: refs/heads/hotfix/CLOUDSTACK-7776 Commit: 06f6b00cd12ef4a88b47ce51e3eb7bfbd9965163 Parents: 45611a0 Author: Mike Tutkowski <mike.tutkow...@solidfire.com> Authored: Fri Oct 17 14:42:17 2014 -0600 Committer: Mike Tutkowski <mike.tutkow...@solidfire.com> Committed: Tue Oct 21 16:01:13 2014 -0600 ---------------------------------------------------------------------- .../api/storage/DataMotionStrategy.java | 3 + .../motion/StorageSystemDataMotionStrategy.java | 72 ++++++++++++++++++++ .../driver/SolidFirePrimaryDataStoreDriver.java | 46 +++++++++++-- .../storage/datastore/util/SolidFireUtil.java | 12 ++-- .../com/cloud/template/TemplateManagerImpl.java | 36 ++++++++-- 5 files changed, 156 insertions(+), 13 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/06f6b00c/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java ---------------------------------------------------------------------- diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java index b5601e9..17856bb 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataMotionStrategy.java @@ -26,6 +26,9 @@ 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); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/06f6b00c/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java ---------------------------------------------------------------------- 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 new file mode 100644 index 0000000..d915063 --- /dev/null +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java @@ -0,0 +1,72 @@ +/* + * 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.motion; + +import java.util.Map; + +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +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.StrategyPriority; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; + +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.host.Host; + +@Component +public class StorageSystemDataMotionStrategy implements DataMotionStrategy { + private static final Logger s_logger = Logger.getLogger(StorageSystemDataMotionStrategy.class); + + @Override + public StrategyPriority canHandle(DataObject srcData, DataObject destData) { + return StrategyPriority.DEFAULT; + } + + @Override + public StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) { + return StrategyPriority.CANT_HANDLE; + } + + @Override + public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) { + + return null; + } + + @Override + public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) { + return copyAsync(srcData, destData, null, callback); + } + + @Override + public Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost, AsyncCompletionCallback<CopyCommandResult> callback) { + CopyCommandResult result = new CopyCommandResult(null, null); + + result.setResult("Unsupported operation requested for copying data."); + + callback.complete(result); + + return null; + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/06f6b00c/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index 04970b7..931069e 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -26,6 +26,7 @@ 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; @@ -523,11 +524,27 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { long sfSnapshotId = SolidFireUtil.createSolidFireSnapshot(sfConnection, sfVolumeId, snapshotInfo.getUuid()); - // Now that we have successfully taken a snapshot, update the space usage in the storage_pool table (even - // though storage_pool.used_bytes is likely no longer in use). + long sfCloneId; + String sfCloneIqn; + + try { + sfCloneId = SolidFireUtil.createSolidFireClone(sfConnection, sfVolumeId, sfSnapshotId, snapshotInfo.getUuid()); + + SolidFireUtil.SolidFireVolume sfClonedVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfCloneId); + + sfCloneIqn = sfClonedVolume.getIqn(); + } + catch (Exception ex) { + SolidFireUtil.deleteSolidFireSnapshot(sfConnection, sfSnapshotId); + + throw ex; + } + + // Now that we have successfully taken a snapshot (for the purpose of reverting) and a clone (for the purpose of creating a template + // and a volume), update the space usage in the storage_pool table (even though storage_pool.used_bytes is likely no longer in use). _storagePoolDao.update(storagePoolId, storagePool); - updateSnapshotDetails(snapshotInfo.getId(), sfSnapshotId, storagePoolId, sfVolumeSize); + updateSnapshotDetails(snapshotInfo.getId(), sfSnapshotId, storagePoolId, sfVolumeSize, sfCloneId, sfCloneIqn); SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshotInfo.getTO(); @@ -550,7 +567,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { callback.complete(result); } - private void updateSnapshotDetails(long csSnapshotId, long sfSnapshotId, long storagePoolId, long sfSnapshotSize) { + private void updateSnapshotDetails(long csSnapshotId, long sfSnapshotId, long storagePoolId, long sfSnapshotSize, long sfCloneId, String sfCloneIqn) { SnapshotDetailsVO accountDetail = new SnapshotDetailsVO(csSnapshotId, SolidFireUtil.SNAPSHOT_ID, String.valueOf(sfSnapshotId), @@ -571,6 +588,20 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { false); _snapshotDetailsDao.persist(accountDetail); + + accountDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.CLONE_ID, + String.valueOf(sfCloneId), + false); + + _snapshotDetailsDao.persist(accountDetail); + + accountDetail = new SnapshotDetailsVO(csSnapshotId, + DataMotionStrategy.IQN, + sfCloneIqn, + false); + + _snapshotDetailsDao.persist(accountDetail); } // return null for no error message @@ -581,6 +612,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); SolidFireUtil.deleteSolidFireSnapshot(sfConnection, getSolidFireSnapshotId(snapshotInfo.getId())); + SolidFireUtil.deleteSolidFireVolume(sfConnection, getSolidFireCloneId(snapshotInfo.getId())); _snapshotDetailsDao.removeDetails(snapshotInfo.getId()); @@ -608,6 +640,12 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return Long.parseLong(snapshotDetails.getValue()); } + private long getSolidFireCloneId(long csSnapshotId) { + SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.CLONE_ID); + + return Long.parseLong(snapshotDetails.getValue()); + } + @Override public void revertSnapshot(SnapshotInfo snapshotInfo, AsyncCompletionCallback<CommandResult> callback) { String errMsg = null; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/06f6b00c/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java index 786f227..6ea1f37 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/util/SolidFireUtil.java @@ -703,10 +703,10 @@ public class SolidFireUtil { verifyResult(rollbackInitiatedResult.result, strRollbackInitiatedResultJson, gson); } - public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, String cloneName) { + public static long createSolidFireClone(SolidFireConnection sfConnection, long lVolumeId, long lSnapshotId, String cloneName) { final Gson gson = new GsonBuilder().create(); - CloneToCreate cloneToCreate = new CloneToCreate(lVolumeId, cloneName); + CloneToCreate cloneToCreate = new CloneToCreate(lVolumeId, lSnapshotId, cloneName); String strCloneToCreateJson = gson.toJson(cloneToCreate); @@ -1332,16 +1332,18 @@ public class SolidFireUtil { private final String method = "CloneVolume"; private final CloneToCreateParams params; - private CloneToCreate(final long lVolumeId, final String cloneName) { - params = new CloneToCreateParams(lVolumeId, cloneName); + private CloneToCreate(final long lVolumeId, final long lSnapshotId, final String cloneName) { + params = new CloneToCreateParams(lVolumeId, lSnapshotId, cloneName); } private static final class CloneToCreateParams { private long volumeID; + private long snapshotID; private String name; - private CloneToCreateParams(final long lVolumeId, final String cloneName) { + private CloneToCreateParams(final long lVolumeId, final long lSnapshotId, final String cloneName) { volumeID = lVolumeId; + snapshotID = lSnapshotId; name = cloneName; } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/06f6b00c/server/src/com/cloud/template/TemplateManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 880c003..d3ad2db 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -54,6 +54,7 @@ import org.apache.cloudstack.api.command.user.template.UpdateTemplatePermissions import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; 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; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; @@ -1376,12 +1377,20 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, throw new CloudRuntimeException("cannot find an image store for zone " + zoneId); } AsyncCallFuture<TemplateApiResult> future = null; + if (snapshotId != null) { - SnapshotInfo snapInfo = _snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image); - DataStore snapStore = snapInfo.getDataStore(); - if (snapStore != null) { - store = snapStore; // pick snapshot image store to create template + DataStoreRole dataStoreRole = getDataStoreRole(snapshot); + + SnapshotInfo snapInfo = _snapshotFactory.getSnapshot(snapshotId, dataStoreRole); + + if (dataStoreRole == DataStoreRole.Image) { + DataStore snapStore = snapInfo.getDataStore(); + + if (snapStore != null) { + store = snapStore; // pick snapshot image store to create template + } } + future = _tmpltSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store); } else if (volumeId != null) { VolumeInfo volInfo = _volFactory.getVolume(volumeId); @@ -1465,6 +1474,25 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } } + private DataStoreRole getDataStoreRole(Snapshot snapshot) { + long volumeId = snapshot.getVolumeId(); + VolumeVO volumeVO = _volumeDao.findById(volumeId); + + long storagePoolId = volumeVO.getPoolId(); + DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + + Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities(); + + String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString()); + Boolean supportsStorageSystemSnapshots = new Boolean(value); + + if (supportsStorageSystemSnapshots) { + return DataStoreRole.Primary; + } + + return DataStoreRole.Image; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_TEMPLATE_CREATE, eventDescription = "creating template", create = true) public VMTemplateVO createPrivateTemplateRecord(CreateTemplateCmd cmd, Account templateOwner) throws ResourceAllocationException {