cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mtutkow...@apache.org
Subject [04/12] git commit: updated refs/heads/master to 97aa02c
Date Wed, 22 Oct 2014 18:45:00 GMT
Support for back-end snapshots on primary storage


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/0cea0346
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/0cea0346
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/0cea0346

Branch: refs/heads/master
Commit: 0cea0346aed5590950712210be92f54b698057c7
Parents: 6beeeff
Author: Mike Tutkowski <mike.tutkowski@solidfire.com>
Authored: Sun Oct 19 18:34:36 2014 -0600
Committer: Mike Tutkowski <mike.tutkowski@solidfire.com>
Committed: Tue Oct 21 16:01:14 2014 -0600

----------------------------------------------------------------------
 api/src/com/cloud/agent/api/to/DiskTO.java      |   1 +
 api/src/com/cloud/storage/Volume.java           |   5 -
 .../storage/resource/StorageProcessor.java      |   7 +-
 .../storage/command/SnapshotAndCopyAnswer.java  |  41 +++
 .../storage/command/SnapshotAndCopyCommand.java |  60 +++++
 .../api/storage/DataMotionStrategy.java         |   3 -
 .../snapshot/StorageSystemSnapshotStrategy.java | 247 +++++++++++++------
 .../kvm/storage/KVMStorageProcessor.java        |   9 +
 .../resource/SimulatorStorageProcessor.java     |   9 +
 .../resource/VmwareStorageProcessor.java        |   9 +
 .../xenserver/resource/CitrixResourceBase.java  |   5 +-
 .../resource/XenServerStorageProcessor.java     |  63 +++++
 .../driver/SolidFirePrimaryDataStoreDriver.java |  24 +-
 .../storage/datastore/util/SolidFireUtil.java   |   2 +-
 14 files changed, 379 insertions(+), 106 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/api/src/com/cloud/agent/api/to/DiskTO.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/agent/api/to/DiskTO.java b/api/src/com/cloud/agent/api/to/DiskTO.java
index afab856..9e4dfd9 100644
--- a/api/src/com/cloud/agent/api/to/DiskTO.java
+++ b/api/src/com/cloud/agent/api/to/DiskTO.java
@@ -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;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/api/src/com/cloud/storage/Volume.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java
index 48145a7..b67a395 100755
--- a/api/src/com/cloud/storage/Volume.java
+++ b/api/src/com/cloud/storage/Volume.java
@@ -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;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/core/src/com/cloud/storage/resource/StorageProcessor.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/storage/resource/StorageProcessor.java b/core/src/com/cloud/storage/resource/StorageProcessor.java
index b673d70..e2bf1b7 100644
--- a/core/src/com/cloud/storage/resource/StorageProcessor.java
+++ b/core/src/com/cloud/storage/resource/StorageProcessor.java
@@ -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);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyAnswer.java
----------------------------------------------------------------------
diff --git a/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyAnswer.java b/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyAnswer.java
new file mode 100644
index 0000000..b99d182
--- /dev/null
+++ b/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyAnswer.java
@@ -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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyCommand.java
----------------------------------------------------------------------
diff --git a/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyCommand.java b/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyCommand.java
new file mode 100644
index 0000000..c599916
--- /dev/null
+++ b/core/src/org/apache/cloudstack/storage/command/SnapshotAndCopyCommand.java
@@ -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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/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 17856bb..b5601e9 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,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);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java
----------------------------------------------------------------------
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 dc43d7d..dd6c654 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
@@ -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,103 +146,91 @@ 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
+        throw new UnsupportedOperationException("Reverting not supported. Create a template
or volume based on the snapshot instead.");
+    }
 
-        SnapshotInfo snapshotInfo = _snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
+    @Override
+    @DB
+    public SnapshotInfo takeSnapshot(SnapshotInfo snapshotInfo) {
         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");
+        if (volumeInfo.getFormat() != ImageFormat.VHD) {
+            throw new CloudRuntimeException("Only the " + ImageFormat.VHD.toString() + "
image type is currently supported.");
         }
 
-        SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotId);
+        SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotInfo.getId());
 
         if (snapshotVO == null) {
-            throw new CloudRuntimeException("Failed to acquire lock on the following snapshot:
" + snapshotId);
+            throw new CloudRuntimeException("Failed to acquire lock on the following snapshot:
" + snapshotInfo.getId());
         }
 
         try {
-            volumeInfo.stateTransit(Volume.Event.RevertRequested);
+            volumeInfo.stateTransit(Volume.Event.SnapshotRequested);
 
-            boolean result = false;
+            SnapshotResult result = null;
 
             try {
-                result = snapshotSvr.revertSnapshot(snapshotId);
-            }
-            finally {
-                if (result) {
-                    volumeInfo.stateTransit(Volume.Event.OperationSucceeded);
+                // 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 a snapshot: " + result.getResult());
+
+                    throw new CloudRuntimeException(result.getResult());
                 }
-                else {
-                    String msg = "Failed to revert the volume to a snapshot";
 
-                    s_logger.debug(msg);
+                // send a command to XenServer to create a VM snapshot on the applicable
SR (get back the VDI UUID of the VM snapshot)
 
-                    volumeInfo.stateTransit(Volume.Event.OperationFailed);
+                Map<String, String> sourceDetails = null;
 
-                    throw new CloudRuntimeException("Failed to revert the volume to a snapshot");
-                }
-            }
-        }
-        finally {
-            if (snapshotVO != null) {
-                _snapshotDao.releaseFromLockTable(snapshotId);
-            }
-        }
+                VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId());
 
-        return true;
-    }
+                Long vmInstanceId = volumeVO.getInstanceId();
+                VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(vmInstanceId);
 
-    @Override
-    @DB
-    public SnapshotInfo takeSnapshot(SnapshotInfo snapshotInfo) {
-        SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotInfo.getId());
+                Long hostId = null;
 
-        if (snapshotVO == null) {
-            throw new CloudRuntimeException("Failed to acquire lock on the following snapshot:
" + snapshotInfo.getId());
-        }
+                // if the volume to snapshot is associated with a VM
+                if (vmInstanceVO != null) {
+                    hostId = vmInstanceVO.getHostId();
 
-        try {
-            VolumeInfo volumeInfo = snapshotInfo.getBaseVolume();
+                    // if the VM is not associated with a host
+                    if (hostId == null) {
+                        sourceDetails = getSourceDetails(volumeInfo);
 
-            volumeInfo.stateTransit(Volume.Event.SnapshotRequested);
+                        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);
+                }
 
-            SnapshotResult result = null;
+                long storagePoolId = volumeVO.getPoolId();
+                StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId);
 
-            try {
-                result = snapshotSvr.takeSnapshot(snapshotInfo);
+                Map<String, String> destDetails = getDestDetails(storagePoolVO, snapshotInfo);
 
-                if (result.isFailed()) {
-                    s_logger.debug("Failed to take the following snapshot: " + result.getResult());
+                SnapshotAndCopyCommand snapshotAndCopyCommand = new SnapshotAndCopyCommand(volumeInfo.getPath(),
sourceDetails, destDetails);
 
-                    throw new CloudRuntimeException(result.getResult());
+                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);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
index e7ed264..21f5919 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java
@@ -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;
@@ -142,6 +144,13 @@ public class KVMStorageProcessor implements StorageProcessor {
     }
 
     @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();
         DataTO destData = cmd.getDestTO();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorStorageProcessor.java
b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorStorageProcessor.java
index bdb3277..418dd0c 100644
--- a/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorStorageProcessor.java
+++ b/plugins/hypervisors/simulator/src/com/cloud/resource/SimulatorStorageProcessor.java
@@ -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;
@@ -58,6 +60,13 @@ public class SimulatorStorageProcessor implements StorageProcessor {
     }
 
     @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();
         template.setPath(UUID.randomUUID().toString());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
index 27f6160..1a39410 100644
--- a/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/storage/resource/VmwareStorageProcessor.java
@@ -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);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
index 1412b17..a1067f4 100644
--- a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
@@ -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);
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/XenServerStorageProcessor.java
----------------------------------------------------------------------
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 2a1ee1b..933fd20 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
@@ -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();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/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 1a3085a..17fc4ba 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,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

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/0cea0346/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 5ea28cb..174dc18 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
@@ -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";


Mime
View raw message