gt:             sourceDetails = getSourceDetails(volumeInfo);
context: // 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.snapshot;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Random;
  
  import javax.inject.Inject;
  
  import org.apache.log4j.Logger;
  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;
  import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
  import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
  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.StoragePoolVO;
  
  import org.springframework.stereotype.Component;
  
  import com.google.common.base.Optional;
  
  import com.cloud.agent.AgentManager;
  import com.cloud.agent.api.to.DiskTO;
  import com.cloud.dc.dao.ClusterDao;
  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.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.utils.db.DB;
  import com.cloud.utils.exception.CloudRuntimeException;
  import com.cloud.utils.fsm.NoTransitionException;
  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 AgentManager agentMgr;
      @Inject private ClusterDao clusterDao;
      @Inject private DataStoreManager dataStoreMgr;
      @Inject private HostDao hostDao;
      @Inject private ManagementService mgr;
      @Inject private PrimaryDataStoreDao storagePoolDao;
      @Inject private SnapshotDao snapshotDao;
      @Inject private SnapshotDataFactory snapshotDataFactory;
      @Inject private SnapshotDetailsDao snapshotDetailsDao;
      @Inject private VMInstanceDao vmInstanceDao;
      @Inject private VolumeDao volumeDao;
      @Inject private VolumeService volService;
  
      @Override
      public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) {
          return snapshotInfo;
      }
  
      @Override
      public boolean deleteSnapshot(Long snapshotId) {
          SnapshotVO snapshotVO = snapshotDao.findById(snapshotId);
  
          if (Snapshot.State.Destroyed.equals(snapshotVO.getState())) {
              return true;
          }
  
          if (Snapshot.State.Error.equals(snapshotVO.getState())) {
              snapshotDao.remove(snapshotId);
  
              return true;
          }
  
          if (!Snapshot.State.BackedUp.equals(snapshotVO.getState())) {
              throw new InvalidParameterValueException("Unable to delete snapshotshot " + snapshotId + " because it is in the following state: " + snapshotVO.getState());
          }
  
          SnapshotObject snapshotObj = (SnapshotObject)snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary);
  
          if (snapshotObj == null) {
              s_logger.debug("Can't find snapshot; deleting it in DB");
  
              snapshotDao.remove(snapshotId);
  
              return true;
          }
  
          if (ObjectInDataStoreStateMachine.State.Copying.equals(snapshotObj.getStatus())) {
              throw new InvalidParameterValueException("Unable to delete snapshotshot " + snapshotId + " because it is in the copying state.");
          }
  
          try {
              snapshotObj.processEvent(Snapshot.Event.DestroyRequested);
          }
          catch (NoTransitionException e) {
              s_logger.debug("Failed to set the state to destroying: ", e);
  
              return false;
          }
  
          try {
              snapshotSvr.deleteSnapshot(snapshotObj);
  
              snapshotObj.processEvent(Snapshot.Event.OperationSucceeded);
          }
          catch (Exception e) {
              s_logger.debug("Failed to delete snapshot: ", e);
  
              try {
                  snapshotObj.processEvent(Snapshot.Event.OperationFailed);
              }
              catch (NoTransitionException e1) {
                  s_logger.debug("Failed to change snapshot state: " + e.toString());
              }
  
              return false;
          }
  
          return true;
      }
  
      @Override
      public boolean revertSnapshot(SnapshotInfo snapshot) {
          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) {
              throw new CloudRuntimeException("Failed to acquire lock on the following snapshot: " + snapshotInfo.getId());
          }
  
          SnapshotResult result = null;
  
          try {
              volumeInfo.stateTransit(Volume.Event.SnapshotRequested);
  
              // only XenServer is currently supported
              HostVO hostVO = getHost(volumeInfo.getId());
  
              boolean canStorageSystemCreateVolumeFromSnapshot = canStorageSystemCreateVolumeFromSnapshot(volumeInfo.getPoolId());
              boolean computeClusterSupportsResign = clusterDao.getSupportsResigning(hostVO.getClusterId());
  
              // if canStorageSystemCreateVolumeFromSnapshot && computeClusterSupportsResign, then take a back-end snapshot or create a back-end clone;
              // else, just create a new back-end volume (eventually used to create a new SR on and to copy a VDI to)
  
              if (canStorageSystemCreateVolumeFromSnapshot && computeClusterSupportsResign) {
                  SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(snapshotInfo.getId(),
                      "takeSnapshot",
                      Boolean.TRUE.toString(),
                      false);
  
                  snapshotDetailsDao.persist(snapshotDetail);
              }
  
              result = snapshotSvr.takeSnapshot(snapshotInfo);
  
              if (result.isFailed()) {
                  s_logger.debug("Failed to take a snapshot: " + result.getResult());
  
                  throw new CloudRuntimeException(result.getResult());
              }
  
              if (!canStorageSystemCreateVolumeFromSnapshot || !computeClusterSupportsResign) {
                  performSnapshotAndCopyOnHostSide(volumeInfo, snapshotInfo);
              }
  
              markAsBackedUp((SnapshotObject)result.getSnashot());
          }
          finally {
              if (result != null && result.isSuccess()) {
                  volumeInfo.stateTransit(Volume.Event.OperationSucceeded);
              }
              else {
                  volumeInfo.stateTransit(Volume.Event.OperationFailed);
              }
  
              snapshotDao.releaseFromLockTable(snapshotInfo.getId());
          }
  
          return snapshotInfo;
      }
  
      private boolean canStorageSystemCreateVolumeFromSnapshot(long storagePoolId) {
          boolean supportsCloningVolumeFromSnapshot = false;
  
          DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
  
          Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities();
  
          if (mapCapabilities != null) {
              String value = mapCapabilities.get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString());
  
              supportsCloningVolumeFromSnapshot = Boolean.valueOf(value);
          }
  
          return supportsCloningVolumeFromSnapshot;
      }
  
      private void performSnapshotAndCopyOnHostSide(VolumeInfo volumeInfo, SnapshotInfo snapshotInfo) {
          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) {
                  hostId = vmInstanceVO.getLastHostId();
  
                  if (hostId == null) {
                      sourceDetails = getSourceDetails(volumeInfo);
                  }
              }
          }
          // volume to snapshot is not associated with a VM (could be a data disk in the detached state)
          else {
  