You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mc...@apache.org on 2013/10/29 05:21:53 UTC

[2/2] git commit: updated refs/heads/master to 6be228a

CLOUDSTACK-4024:Provide a way to upgrade from existing NFS secondary
storage to S3.

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

Branch: refs/heads/master
Commit: 6be228a438e5ce3fba831be8130c73603cb0457b
Parents: 271a7df
Author: Min Chen <mi...@citrix.com>
Authored: Mon Oct 28 21:01:31 2013 -0700
Committer: Min Chen <mi...@citrix.com>
Committed: Mon Oct 28 21:01:31 2013 -0700

----------------------------------------------------------------------
 api/src/com/cloud/event/EventTypes.java         |   3 +
 api/src/com/cloud/storage/StorageService.java   |  16 +-
 .../cloudstack/api/ApiCommandJobType.java       |   1 +
 .../PrepareSecondaryStorageForMigrationCmd.java | 109 ++++++++
 client/tomcatconf/commands.properties.in        |   1 +
 .../api/storage/DataStoreLifeCycle.java         |   2 +
 .../subsystem/api/storage/DataStoreManager.java |   3 +
 .../api/storage/SnapshotDataFactory.java        |   4 +
 .../api/storage/TemplateDataFactory.java        |   6 +
 .../subsystem/api/storage/TemplateService.java  |   6 +-
 .../api/storage/VolumeDataFactory.java          |   4 +
 .../storage/datastore/db/ImageStoreDao.java     |   2 +
 .../datastore/db/SnapshotDataStoreDao.java      |   9 +
 .../datastore/db/SnapshotDataStoreVO.java       |  34 ++-
 .../datastore/db/TemplateDataStoreDao.java      |  10 +-
 .../datastore/db/TemplateDataStoreVO.java       |  51 ++--
 .../datastore/db/VolumeDataStoreDao.java        |   2 +
 .../storage/datastore/db/VolumeDataStoreVO.java |  47 ++--
 .../motion/AncientDataMotionStrategy.java       |  37 ++-
 .../storage/image/TemplateDataFactoryImpl.java  |  38 ++-
 .../storage/image/TemplateServiceImpl.java      |  95 ++++++-
 .../manager/ImageStoreProviderManagerImpl.java  |  15 +-
 .../storage/image/store/TemplateObject.java     |   8 +
 .../snapshot/SnapshotDataFactoryImpl.java       |  22 +-
 .../snapshot/XenserverSnapshotStrategy.java     |  57 +++--
 .../storage/datastore/DataStoreManagerImpl.java |   5 +
 .../image/datastore/ImageStoreHelper.java       |  21 +-
 .../datastore/ImageStoreProviderManager.java    |   2 +
 .../storage/image/db/ImageStoreDaoImpl.java     |  10 +-
 .../image/db/SnapshotDataStoreDaoImpl.java      |  82 +++++-
 .../image/db/TemplateDataStoreDaoImpl.java      | 104 ++++++++
 .../image/db/VolumeDataStoreDaoImpl.java        |  55 +++-
 .../storage/volume/VolumeDataFactoryImpl.java   |  33 ++-
 .../SimulatorImageStoreLifeCycleImpl.java       |  36 ++-
 .../CloudStackImageStoreLifeCycleImpl.java      |   9 +
 .../lifecycle/S3ImageStoreLifeCycleImpl.java    |  12 +-
 .../SampleImageStoreLifeCycleImpl.java          |   9 +
 .../lifecycle/SwiftImageStoreLifeCycleImpl.java |  30 ++-
 ...CloudStackPrimaryDataStoreLifeCycleImpl.java |  17 +-
 .../SamplePrimaryDataStoreLifeCycleImpl.java    |  34 ++-
 .../SolidFirePrimaryDataStoreLifeCycle.java     | 254 ++++++++++---------
 .../com/cloud/server/ManagementServerImpl.java  |  71 +++---
 .../com/cloud/storage/StorageManagerImpl.java   |  74 ++++--
 .../com/cloud/storage/VolumeApiServiceImpl.java | 125 ++++-----
 .../template/HypervisorTemplateAdapter.java     |  29 ++-
 .../com/cloud/template/TemplateManagerImpl.java |  91 ++++---
 tools/apidoc/gen_toc.py                         |   1 +
 47 files changed, 1238 insertions(+), 448 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/api/src/com/cloud/event/EventTypes.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java
index c7e7a45..d9f80eb 100755
--- a/api/src/com/cloud/event/EventTypes.java
+++ b/api/src/com/cloud/event/EventTypes.java
@@ -449,6 +449,9 @@ public class EventTypes {
 
     public static final String EVENT_UCS_ASSOCIATED_PROFILE = "UCS.ASSOCIATEPROFILE";
 
+    // Object store migration
+    public static final String EVENT_MIGRATE_PREPARE_SECONDARY_STORAGE = "MIGRATE.PREPARE.SS";
+
     static {
 
         // TODO: need a way to force author adding event types to declare the entity details as well, with out braking

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/api/src/com/cloud/storage/StorageService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/storage/StorageService.java b/api/src/com/cloud/storage/StorageService.java
index 1ae1d3a..cbbc1f3 100644
--- a/api/src/com/cloud/storage/StorageService.java
+++ b/api/src/com/cloud/storage/StorageService.java
@@ -22,9 +22,9 @@ import org.apache.cloudstack.api.command.admin.storage.AddImageStoreCmd;
 import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd;
 import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd;
 import org.apache.cloudstack.api.command.admin.storage.CreateStoragePoolCmd;
-import org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd;
 import org.apache.cloudstack.api.command.admin.storage.DeleteImageStoreCmd;
 import org.apache.cloudstack.api.command.admin.storage.DeletePoolCmd;
+import org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd;
 import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd;
 
 import com.cloud.exception.DiscoveryException;
@@ -97,4 +97,18 @@ public interface StorageService{
 
     ImageStore discoverImageStore(AddImageStoreCmd cmd) throws IllegalArgumentException, DiscoveryException, InvalidParameterValueException;
 
+    /**
+     * Prepare NFS secondary storage for object store migration
+     *
+     * @param cmd
+     *            - the command specifying secondaryStorageId
+     * @return the storage pool
+     * @throws ResourceUnavailableException
+     *             TODO
+     * @throws InsufficientCapacityException
+     *             TODO
+     */
+    public ImageStore prepareSecondaryStorageForObjectStoreMigration(Long storeId) throws ResourceUnavailableException,
+            InsufficientCapacityException;
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/api/src/org/apache/cloudstack/api/ApiCommandJobType.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/ApiCommandJobType.java b/api/src/org/apache/cloudstack/api/ApiCommandJobType.java
index 204fe3e..3067af8 100644
--- a/api/src/org/apache/cloudstack/api/ApiCommandJobType.java
+++ b/api/src/org/apache/cloudstack/api/ApiCommandJobType.java
@@ -28,6 +28,7 @@ public enum ApiCommandJobType {
     SystemVm,
     Host,
     StoragePool,
+    ImageStore,
     IpAddress,
     PortableIpAddress,
     SecurityGroup,

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/api/src/org/apache/cloudstack/api/command/admin/storage/PrepareSecondaryStorageForMigrationCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/storage/PrepareSecondaryStorageForMigrationCmd.java b/api/src/org/apache/cloudstack/api/command/admin/storage/PrepareSecondaryStorageForMigrationCmd.java
new file mode 100644
index 0000000..d0c995a
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/command/admin/storage/PrepareSecondaryStorageForMigrationCmd.java
@@ -0,0 +1,109 @@
+// 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.api.command.admin.storage;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.APICommand;
+import org.apache.cloudstack.api.ApiCommandJobType;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.ApiErrorCode;
+import org.apache.cloudstack.api.BaseAsyncCmd;
+import org.apache.cloudstack.api.Parameter;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.response.ImageStoreResponse;
+import org.apache.cloudstack.context.CallContext;
+
+import com.cloud.event.EventTypes;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.storage.ImageStore;
+import com.cloud.user.Account;
+
+@APICommand(name = "prepareSecondaryStorageForMigration", description = "Prepare a NFS secondary storage to migrate to use object store like S3", responseObject = ImageStoreResponse.class)
+public class PrepareSecondaryStorageForMigrationCmd extends BaseAsyncCmd {
+    public static final Logger s_logger = Logger.getLogger(PrepareSecondaryStorageForMigrationCmd.class.getName());
+    private static final String s_name = "preparesecondarystorageformigrationresponse";
+
+    /////////////////////////////////////////////////////
+    //////////////// API parameters /////////////////////
+    /////////////////////////////////////////////////////
+
+    @Parameter(name = ApiConstants.ID, type = CommandType.UUID, entityType = ImageStoreResponse.class,
+            required = true, description = "Secondary image store ID")
+    private Long id;
+
+    /////////////////////////////////////////////////////
+    /////////////////// Accessors ///////////////////////
+    /////////////////////////////////////////////////////
+
+    public Long getId() {
+        return id;
+    }
+
+    /////////////////////////////////////////////////////
+    /////////////// API Implementation///////////////////
+    /////////////////////////////////////////////////////
+
+    @Override
+    public String getCommandName() {
+        return s_name;
+    }
+
+    @Override
+    public ApiCommandJobType getInstanceType() {
+        return ApiCommandJobType.ImageStore;
+    }
+
+    @Override
+    public Long getInstanceId() {
+        return getId();
+    }
+
+    @Override
+    public long getEntityOwnerId() {
+        Account account = CallContext.current().getCallingAccount();
+        if (account != null) {
+            return account.getId();
+        }
+
+        return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
+    }
+
+    @Override
+    public String getEventType() {
+        return EventTypes.EVENT_MIGRATE_PREPARE_SECONDARY_STORAGE;
+    }
+
+    @Override
+    public String getEventDescription() {
+        return "preparing secondary storage: " + getId() + " for object store migration";
+    }
+
+    @Override
+    public void execute() throws ResourceUnavailableException, InsufficientCapacityException{
+        ImageStore result = _storageService.prepareSecondaryStorageForObjectStoreMigration(getId());
+        if (result != null){
+            ImageStoreResponse response = _responseGenerator.createImageStoreResponse(result);
+            response.setResponseName(getCommandName());
+            response.setResponseName("secondarystorage");
+            setResponseObject(response);
+        } else {
+            throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to prepare secondary storage for object store migration");
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index 3fe0463..1503ab8 100644
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -256,6 +256,7 @@ deleteImageStore=1
 createSecondaryStagingStore=1
 listSecondaryStagingStores=1
 deleteSecondaryStagingStore=1
+prepareSecondaryStorageForMigration=1
 
 #### host commands
 addHost=3

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreLifeCycle.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreLifeCycle.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreLifeCycle.java
index 1e893db..c881570 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreLifeCycle.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreLifeCycle.java
@@ -37,4 +37,6 @@ public interface DataStoreLifeCycle {
     boolean cancelMaintain(DataStore store);
 
     boolean deleteDataStore(DataStore store);
+
+    boolean migrateToObjectStore(DataStore store);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java
index 0884453..7fbec0a 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreManager.java
@@ -19,6 +19,7 @@
 package org.apache.cloudstack.engine.subsystem.api.storage;
 
 import java.util.List;
+
 import com.cloud.storage.DataStoreRole;
 
 public interface DataStoreManager {
@@ -37,4 +38,6 @@ public interface DataStoreManager {
     DataStore getImageCacheStore(long zoneId);
 
     List<DataStore> listImageStores();
+
+    List<DataStore> listImageCacheStores();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java
index 0b8d1f1..d5255f4 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotDataFactory.java
@@ -18,6 +18,8 @@
  */
 package org.apache.cloudstack.engine.subsystem.api.storage;
 
+import java.util.List;
+
 import com.cloud.storage.DataStoreRole;
 
 public interface SnapshotDataFactory {
@@ -26,4 +28,6 @@ public interface SnapshotDataFactory {
     SnapshotInfo getSnapshot(DataObject obj, DataStore store);
 
     SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role);
+
+    List<SnapshotInfo> listSnapshotOnCache(long snapshotId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateDataFactory.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateDataFactory.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateDataFactory.java
index 0b78da0..801c442 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateDataFactory.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateDataFactory.java
@@ -18,6 +18,8 @@
  */
 package org.apache.cloudstack.engine.subsystem.api.storage;
 
+import java.util.List;
+
 import com.cloud.storage.DataStoreRole;
 
 public interface TemplateDataFactory {
@@ -28,4 +30,8 @@ public interface TemplateDataFactory {
     TemplateInfo getTemplate(long templateId, DataStoreRole storeRole);
 
     TemplateInfo getTemplate(long templateId, DataStoreRole storeRole, Long zoneId);
+
+    TemplateInfo getReadyTemplateOnCache(long templateId);
+
+    List<TemplateInfo> listTemplateOnCache(long templateId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
index 4950597..185d254 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java
@@ -36,7 +36,7 @@ public interface TemplateService {
         }
 
         public TemplateInfo getTemplate() {
-            return this.template;
+            return template;
         }
     }
 
@@ -54,6 +54,8 @@ public interface TemplateService {
 
     AsyncCallFuture<TemplateApiResult> prepareTemplateOnPrimary(TemplateInfo srcTemplate, StoragePool pool);
 
+    void syncTemplateToRegionStore(long templateId, DataStore store);
+
     void handleSysTemplateDownload(HypervisorType hostHyper, Long dcId);
 
     void handleTemplateSync(DataStore store);
@@ -62,5 +64,7 @@ public interface TemplateService {
 
     void addSystemVMTemplatesToSecondary(DataStore store);
     
+    void associateTemplateToZone(long templateId, Long zoneId);
+
     void associateCrosszoneTemplatesToZone(long dcId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeDataFactory.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeDataFactory.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeDataFactory.java
index 99e3b59..3de0b5b 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeDataFactory.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeDataFactory.java
@@ -18,6 +18,8 @@
  */
 package org.apache.cloudstack.engine.subsystem.api.storage;
 
+import java.util.List;
+
 import com.cloud.storage.DataStoreRole;
 
 public interface VolumeDataFactory {
@@ -28,4 +30,6 @@ public interface VolumeDataFactory {
     VolumeInfo getVolume(long volumeId, DataStoreRole storeRole);
 
     VolumeInfo getVolume(long volumeId);
+
+    List<VolumeInfo> listVolumeOnCache(long volumeId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/schema/src/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java
index f95e66c..d0f8fe8 100644
--- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java
+++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/ImageStoreDao.java
@@ -36,4 +36,6 @@ public interface ImageStoreDao extends GenericDao<ImageStoreVO, Long> {
     List<ImageStoreVO> findImageCacheByScope(ZoneScope scope);
 
     List<ImageStoreVO> listImageStores();
+
+    List<ImageStoreVO> listImageCacheStores();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java
index e350a76..dfa03ad 100644
--- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java
+++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreDao.java
@@ -41,4 +41,13 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
 
     List<SnapshotDataStoreVO> listDestroyed(long storeId);
     List<SnapshotDataStoreVO> findBySnapshotId(long snapshotId);
+
+    void duplicateCacheRecordsOnRegionStore(long storeId);
+
+    // delete the snapshot entry on primary data store to make sure that next snapshot will be full snapshot
+    void deleteSnapshotRecordsOnPrimary();
+
+    List<SnapshotDataStoreVO> listOnCache(long snapshotId);
+
+    void updateStoreRoleToCache(long storeId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java
index 0fe5e08..db86c3f 100644
--- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java
+++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java
@@ -34,7 +34,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreState
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
 
 import com.cloud.storage.DataStoreRole;
-import com.cloud.utils.db.GenericDao;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.fsm.StateObject;
 
@@ -98,6 +97,7 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     @Column(name = "volume_id")
     Long volumeId;
 
+    @Override
     public String getInstallPath() {
         return installPath;
     }
@@ -108,7 +108,7 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     }
 
     public void setDataStoreId(long storeId) {
-        this.dataStoreId = storeId;
+        dataStoreId = storeId;
     }
 
     public long getSnapshotId() {
@@ -135,15 +135,16 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
         lastUpdated = date;
     }
 
+    @Override
     public void setInstallPath(String installPath) {
         this.installPath = installPath;
     }
 
     public SnapshotDataStoreVO(long hostId, long snapshotId) {
         super();
-        this.dataStoreId = hostId;
+        dataStoreId = hostId;
         this.snapshotId = snapshotId;
-        this.state = ObjectInDataStoreStateMachine.State.Allocated;
+        state = ObjectInDataStoreStateMachine.State.Allocated;
     }
 
     public SnapshotDataStoreVO() {
@@ -158,14 +159,16 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
         return jobId;
     }
 
+    @Override
     public boolean equals(Object obj) {
         if (obj instanceof SnapshotDataStoreVO) {
             SnapshotDataStoreVO other = (SnapshotDataStoreVO) obj;
-            return (this.snapshotId == other.getSnapshotId() && this.dataStoreId == other.getDataStoreId());
+            return (snapshotId == other.getSnapshotId() && dataStoreId == other.getDataStoreId());
         }
         return false;
     }
 
+    @Override
     public int hashCode() {
         Long tid = new Long(snapshotId);
         Long hid = new Long(dataStoreId);
@@ -192,21 +195,22 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
         return -1;
     }
 
+    @Override
     public String toString() {
         return new StringBuilder("SnapshotDataStore[").append(id).append("-").append(snapshotId).append("-")
                 .append(dataStoreId).append(installPath).append("]").toString();
     }
 
     public long getUpdatedCount() {
-        return this.updatedCount;
+        return updatedCount;
     }
 
     public void incrUpdatedCount() {
-        this.updatedCount++;
+        updatedCount++;
     }
 
     public void decrUpdatedCount() {
-        this.updatedCount--;
+        updatedCount--;
     }
 
     public Date getUpdated() {
@@ -216,7 +220,7 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     @Override
     public ObjectInDataStoreStateMachine.State getState() {
         // TODO Auto-generated method stub
-        return this.state;
+        return state;
     }
 
     public void setState(ObjectInDataStoreStateMachine.State state) {
@@ -225,7 +229,7 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
 
     @Override
     public long getObjectId() {
-        return this.getSnapshotId();
+        return getSnapshotId();
     }
 
     public DataStoreRole getRole() {
@@ -238,7 +242,7 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
 
     @Override
     public State getObjectInStoreState() {
-        return this.state;
+        return state;
     }
 
     public long getParentSnapshotId() {
@@ -253,12 +257,16 @@ public class SnapshotDataStoreVO implements StateObject<ObjectInDataStoreStateMa
         return refCnt;
     }
 
+    public void setRefCnt(Long refCnt) {
+        this.refCnt = refCnt;
+    }
+
     public void incrRefCnt() {
-        this.refCnt++;
+        refCnt++;
     }
 
     public void decrRefCnt() {
-        this.refCnt--;
+        refCnt--;
     }
 
     public Long getVolumeId() {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java
index 9350751..93adaaf 100644
--- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java
+++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreDao.java
@@ -18,9 +18,9 @@ package org.apache.cloudstack.storage.datastore.db;
 
 import java.util.List;
 
-import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
+import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
 
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.VMTemplateStorageResourceAssoc;
@@ -62,4 +62,12 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
     TemplateDataStoreVO findByTemplateZone(long templateId, Long zoneId, DataStoreRole role);
 
     List<TemplateDataStoreVO> listByTemplate(long templateId);
+
+    void duplicateCacheRecordsOnRegionStore(long storeId);
+
+    TemplateDataStoreVO findReadyOnCache(long templateId);
+
+    List<TemplateDataStoreVO> listOnCache(long templateId);
+
+    void updateStoreRoleToCachce(long storeId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java
index a890e4b..a3696d8 100755
--- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java
+++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/TemplateDataStoreVO.java
@@ -35,7 +35,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreState
 
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
-
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.fsm.StateObject;
 
@@ -117,17 +116,17 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
 
     public TemplateDataStoreVO(Long hostId, long templateId) {
         super();
-        this.dataStoreId = hostId;
+        dataStoreId = hostId;
         this.templateId = templateId;
-        this.state = ObjectInDataStoreStateMachine.State.Allocated;
-        this.refCnt = 0L;
+        state = ObjectInDataStoreStateMachine.State.Allocated;
+        refCnt = 0L;
     }
 
     public TemplateDataStoreVO(Long hostId, long templateId, Date lastUpdated, int downloadPercent,
             Status downloadState, String localDownloadPath, String errorString, String jobId, String installPath,
             String downloadUrl) {
         super();
-        this.dataStoreId = hostId;
+        dataStoreId = hostId;
         this.templateId = templateId;
         this.lastUpdated = lastUpdated;
         this.downloadPercent = downloadPercent;
@@ -135,33 +134,33 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
         this.localDownloadPath = localDownloadPath;
         this.errorString = errorString;
         this.jobId = jobId;
-        this.refCnt = 0L;
+        refCnt = 0L;
         this.installPath = installPath;
-        this.setDownloadUrl(downloadUrl);
+        setDownloadUrl(downloadUrl);
         switch (downloadState) {
         case DOWNLOADED:
-            this.state = ObjectInDataStoreStateMachine.State.Ready;
+            state = ObjectInDataStoreStateMachine.State.Ready;
             break;
         case CREATING:
         case DOWNLOAD_IN_PROGRESS:
         case UPLOAD_IN_PROGRESS:
-            this.state = ObjectInDataStoreStateMachine.State.Creating2;
+            state = ObjectInDataStoreStateMachine.State.Creating2;
             break;
         case DOWNLOAD_ERROR:
         case UPLOAD_ERROR:
-            this.state = ObjectInDataStoreStateMachine.State.Failed;
+            state = ObjectInDataStoreStateMachine.State.Failed;
             break;
         case ABANDONED:
-            this.state = ObjectInDataStoreStateMachine.State.Destroyed;
+            state = ObjectInDataStoreStateMachine.State.Destroyed;
             break;
         default:
-            this.state = ObjectInDataStoreStateMachine.State.Allocated;
+            state = ObjectInDataStoreStateMachine.State.Allocated;
             break;
         }
     }
 
     public TemplateDataStoreVO() {
-        this.refCnt = 0L;
+        refCnt = 0L;
     }
 
     @Override
@@ -175,7 +174,7 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     }
 
     public void setDataStoreId(long storeId) {
-        this.dataStoreId = storeId;
+        dataStoreId = storeId;
     }
 
     public long getTemplateId() {
@@ -224,7 +223,7 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     }
 
     public void setLocalDownloadPath(String localPath) {
-        this.localDownloadPath = localPath;
+        localDownloadPath = localPath;
     }
 
     public String getLocalDownloadPath() {
@@ -251,7 +250,7 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     public boolean equals(Object obj) {
         if (obj instanceof TemplateDataStoreVO) {
             TemplateDataStoreVO other = (TemplateDataStoreVO) obj;
-            return (this.templateId == other.getTemplateId() && this.dataStoreId == other.getDataStoreId());
+            return (templateId == other.getTemplateId() && dataStoreId == other.getDataStoreId());
         }
         return false;
     }
@@ -316,7 +315,7 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     @Override
     public ObjectInDataStoreStateMachine.State getState() {
         // TODO Auto-generated method stub
-        return this.state;
+        return state;
     }
 
     public void setState(ObjectInDataStoreStateMachine.State state) {
@@ -324,15 +323,15 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
     }
 
     public long getUpdatedCount() {
-        return this.updatedCount;
+        return updatedCount;
     }
 
     public void incrUpdatedCount() {
-        this.updatedCount++;
+        updatedCount++;
     }
 
     public void decrUpdatedCount() {
-        this.updatedCount--;
+        updatedCount--;
     }
 
     public Date getUpdated() {
@@ -341,12 +340,12 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
 
     @Override
     public long getObjectId() {
-        return this.getTemplateId();
+        return getTemplateId();
     }
 
     @Override
     public State getObjectInStoreState() {
-        return this.state;
+        return state;
     }
 
     public DataStoreRole getDataStoreRole() {
@@ -361,12 +360,16 @@ public class TemplateDataStoreVO implements StateObject<ObjectInDataStoreStateMa
         return refCnt;
     }
 
+    public void setRefCnt(Long refCnt) {
+        this.refCnt = refCnt;
+    }
+
     public void incrRefCnt() {
-        this.refCnt++;
+        refCnt++;
     }
 
     public void decrRefCnt() {
-        this.refCnt--;
+        refCnt--;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreDao.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreDao.java
index 698465f..00a6d2a 100644
--- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreDao.java
+++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreDao.java
@@ -40,4 +40,6 @@ StateDao<ObjectInDataStoreStateMachine.State, ObjectInDataStoreStateMachine.Even
     VolumeDataStoreVO findByStoreVolume(long storeId, long volumeId, boolean lock);
 
     List<VolumeDataStoreVO> listDestroyed(long storeId);
+
+    void duplicateCacheRecordsOnRegionStore(long storeId);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java
index e11071b..e34d4a6 100755
--- a/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java
+++ b/engine/schema/src/org/apache/cloudstack/storage/datastore/db/VolumeDataStoreVO.java
@@ -33,8 +33,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
 
-import com.cloud.storage.Storage;
-import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.fsm.StateObject;
@@ -117,6 +115,7 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
     @Column(name = "ref_cnt")
     Long refCnt = 0L;
 
+    @Override
     public String getInstallPath() {
         return installPath;
     }
@@ -127,7 +126,7 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
     }
 
     public void setDataStoreId(long storeId) {
-        this.dataStoreId = storeId;
+        dataStoreId = storeId;
     }
 
     public long getVolumeId() {
@@ -174,6 +173,7 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
         lastUpdated = date;
     }
 
+    @Override
     public void setInstallPath(String installPath) {
         this.installPath = installPath;
     }
@@ -192,17 +192,17 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
 
     public VolumeDataStoreVO(long hostId, long volumeId) {
         super();
-        this.dataStoreId = hostId;
+        dataStoreId = hostId;
         this.volumeId = volumeId;
-        this.state = ObjectInDataStoreStateMachine.State.Allocated;
-        this.refCnt = 0L;
+        state = ObjectInDataStoreStateMachine.State.Allocated;
+        refCnt = 0L;
     }
 
     public VolumeDataStoreVO(long hostId, long volumeId, Date lastUpdated, int downloadPercent, Status downloadState,
             String localDownloadPath, String errorString, String jobId, String installPath, String downloadUrl,
             String checksum) {
         // super();
-        this.dataStoreId = hostId;
+        dataStoreId = hostId;
         this.volumeId = volumeId;
         // this.zoneId = zoneId;
         this.lastUpdated = lastUpdated;
@@ -212,17 +212,17 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
         this.errorString = errorString;
         this.jobId = jobId;
         this.installPath = installPath;
-        this.setDownloadUrl(downloadUrl);
+        setDownloadUrl(downloadUrl);
         this.checksum = checksum;
-        this.refCnt = 0L;
+        refCnt = 0L;
     }
 
     public VolumeDataStoreVO() {
-        this.refCnt = 0L;
+        refCnt = 0L;
     }
 
     public void setLocalDownloadPath(String localPath) {
-        this.localDownloadPath = localPath;
+        localDownloadPath = localPath;
     }
 
     public String getLocalDownloadPath() {
@@ -245,14 +245,16 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
         return jobId;
     }
 
+    @Override
     public boolean equals(Object obj) {
         if (obj instanceof VolumeDataStoreVO) {
             VolumeDataStoreVO other = (VolumeDataStoreVO) obj;
-            return (this.volumeId == other.getVolumeId() && this.dataStoreId == other.getDataStoreId());
+            return (volumeId == other.getVolumeId() && dataStoreId == other.getDataStoreId());
         }
         return false;
     }
 
+    @Override
     public int hashCode() {
         Long tid = new Long(volumeId);
         Long hid = new Long(dataStoreId);
@@ -295,21 +297,22 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
         return -1;
     }
 
+    @Override
     public String toString() {
         return new StringBuilder("VolumeDataStore[").append(id).append("-").append(volumeId).append("-").append(dataStoreId)
                 .append(installPath).append("]").toString();
     }
 
     public long getUpdatedCount() {
-        return this.updatedCount;
+        return updatedCount;
     }
 
     public void incrUpdatedCount() {
-        this.updatedCount++;
+        updatedCount++;
     }
 
     public void decrUpdatedCount() {
-        this.updatedCount--;
+        updatedCount--;
     }
 
     public Date getUpdated() {
@@ -319,7 +322,7 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
     @Override
     public ObjectInDataStoreStateMachine.State getState() {
         // TODO Auto-generated method stub
-        return this.state;
+        return state;
     }
 
     public void setState(ObjectInDataStoreStateMachine.State state) {
@@ -328,24 +331,28 @@ public class VolumeDataStoreVO implements StateObject<ObjectInDataStoreStateMach
 
     @Override
     public long getObjectId() {
-        return this.getVolumeId();
+        return getVolumeId();
     }
 
     @Override
     public State getObjectInStoreState() {
-        return this.state;
+        return state;
     }
 
     public Long getRefCnt() {
         return refCnt;
     }
 
+    public void setRefCnt(Long refCnt) {
+        this.refCnt = refCnt;
+    }
+
     public void incrRefCnt() {
-        this.refCnt++;
+        refCnt++;
     }
 
     public void decrRefCnt() {
-        this.refCnt--;
+        refCnt--;
     }
 
     public String getExtractUrl() {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
index d70c7c0..a451ca4 100644
--- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
+++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java
@@ -22,6 +22,9 @@ import java.util.Map;
 
 import javax.inject.Inject;
 
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
 import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
 import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
 import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
@@ -48,9 +51,6 @@ import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
 import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
-import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
 
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.storage.MigrateVolumeAnswer;
@@ -65,7 +65,6 @@ import com.cloud.host.Host;
 import com.cloud.host.dao.HostDao;
 import com.cloud.server.ManagementService;
 import com.cloud.storage.DataStoreRole;
-import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VolumeVO;
@@ -112,8 +111,9 @@ AncientDataMotionStrategy implements DataMotionStrategy {
         DataTO destTO = destData.getTO();
         DataStoreTO srcStoreTO = srcTO.getDataStore();
         DataStoreTO destStoreTO = destTO.getDataStore();
-        if (srcStoreTO instanceof NfsTO || srcStoreTO.getRole() == DataStoreRole.ImageCache ||
-                (srcStoreTO instanceof PrimaryDataStoreTO && ((PrimaryDataStoreTO)srcStoreTO).getPoolType() == StoragePoolType.NetworkFilesystem)) {
+        if (srcStoreTO instanceof NfsTO || srcStoreTO.getRole() == DataStoreRole.ImageCache) {
+            //||
+            //    (srcStoreTO instanceof PrimaryDataStoreTO && ((PrimaryDataStoreTO)srcStoreTO).getPoolType() == StoragePoolType.NetworkFilesystem)) {
             return false;
         }
 
@@ -185,7 +185,8 @@ AncientDataMotionStrategy implements DataMotionStrategy {
                     cacheMgr.deleteCacheObject(srcForCopy);
                 } else {
                     // for template, we want to leave it on cache for performance reason
-                    if (answer == null || !answer.getResult()) {
+                    if ((answer == null || !answer.getResult()) && srcForCopy.getRefCount() < 2) {
+                        // cache object created by this copy, not already there
                         cacheMgr.deleteCacheObject(srcForCopy);
                     } else {
                         cacheMgr.releaseCacheObject(srcForCopy);
@@ -222,6 +223,13 @@ AncientDataMotionStrategy implements DataMotionStrategy {
         }
     }
 
+    protected void releaseSnapshotCacheChain(SnapshotInfo snapshot) {
+        while (snapshot != null) {
+            cacheMgr.releaseCacheObject(snapshot);
+            snapshot = snapshot.getParent();
+        }
+    }
+
     protected Answer copyVolumeFromSnapshot(DataObject snapObj, DataObject volObj) {
         SnapshotInfo snapshot = (SnapshotInfo) snapObj;
         StoragePool pool = (StoragePool) volObj.getDataStore();
@@ -255,7 +263,8 @@ AncientDataMotionStrategy implements DataMotionStrategy {
             throw new CloudRuntimeException(basicErrMsg);
         } finally {
             if (!(storTO instanceof NfsTO)) {
-                deleteSnapshotCacheChain((SnapshotInfo) srcData);
+                // still keep snapshot on cache which may be migrated from previous secondary storage
+                releaseSnapshotCacheChain((SnapshotInfo)srcData);
             }
         }
     }
@@ -281,7 +290,7 @@ AncientDataMotionStrategy implements DataMotionStrategy {
         if (cacheStore == null) {
             // need to find a nfs or cifs image store, assuming that can't copy volume
             // directly to s3
-            ImageStoreEntity imageStore = (ImageStoreEntity) this.dataStoreMgr.getImageStore(destScope.getScopeId());
+            ImageStoreEntity imageStore = (ImageStoreEntity) dataStoreMgr.getImageStore(destScope.getScopeId());
             if (!imageStore.getProtocol().equalsIgnoreCase("nfs") && !imageStore.getProtocol().equalsIgnoreCase("cifs")) {
                 s_logger.debug("can't find a nfs (or cifs) image store to satisfy the need for a staging store");
                 return null;
@@ -290,7 +299,7 @@ AncientDataMotionStrategy implements DataMotionStrategy {
             DataObject objOnImageStore = imageStore.create(srcData);
             objOnImageStore.processEvent(Event.CreateOnlyRequested);
 
-            Answer answer = this.copyObject(srcData, objOnImageStore);
+            Answer answer = copyObject(srcData, objOnImageStore);
             if (answer == null || !answer.getResult()) {
                 if (answer != null) {
                     s_logger.debug("copy to image store failed: " + answer.getDetails());
@@ -336,7 +345,7 @@ AncientDataMotionStrategy implements DataMotionStrategy {
 
     protected Answer migrateVolumeToPool(DataObject srcData, DataObject destData) {
         VolumeInfo volume = (VolumeInfo)srcData;
-        StoragePool destPool = (StoragePool)this.dataStoreMgr.getDataStore(destData.getDataStore().getId(), DataStoreRole.Primary);
+        StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destData.getDataStore().getId(), DataStoreRole.Primary);
         MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(), destPool);
         EndPoint ep = selector.select(volume.getDataStore());
         MigrateVolumeAnswer answer = (MigrateVolumeAnswer) ep.sendMessage(command);
@@ -345,14 +354,14 @@ AncientDataMotionStrategy implements DataMotionStrategy {
             throw new CloudRuntimeException("Failed to migrate volume " + volume + " to storage pool " + destPool);
         } else {
             // Update the volume details after migration.
-            VolumeVO volumeVo = this.volDao.findById(volume.getId());
+            VolumeVO volumeVo = volDao.findById(volume.getId());
             Long oldPoolId = volume.getPoolId();
             volumeVo.setPath(answer.getVolumePath());
             volumeVo.setFolder(destPool.getPath());
             volumeVo.setPodId(destPool.getPodId());
             volumeVo.setPoolId(destPool.getId());
             volumeVo.setLastPoolId(oldPoolId);
-            this.volDao.update(volume.getId(), volumeVo);
+            volDao.update(volume.getId(), volumeVo);
         }
 
         return answer;
@@ -426,7 +435,7 @@ AncientDataMotionStrategy implements DataMotionStrategy {
 
         // clean up snapshot copied to staging
         if (needCache && srcData != null) {
-            cacheMgr.deleteCacheObject(srcData);
+            cacheMgr.releaseCacheObject(srcData);  // reduce ref count, but keep it there on cache which is converted from previous secondary storage
         }
         return answer;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
index a67f08f..6369311 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateDataFactoryImpl.java
@@ -18,8 +18,14 @@
  */
 package org.apache.cloudstack.storage.image;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.inject.Inject;
 
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
 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.DataStoreManager;
@@ -28,8 +34,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
 import org.apache.cloudstack.storage.image.store.TemplateObject;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
 
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.VMTemplateStoragePoolVO;
@@ -87,7 +91,7 @@ public class TemplateDataFactoryImpl implements TemplateDataFactory {
         TemplateDataStoreVO tmplStore = templateStoreDao.findByTemplate(templateId, storeRole);
         DataStore store = null;
         if (tmplStore != null) {
-            store = this.storeMgr.getDataStore(tmplStore.getDataStoreId(), storeRole);
+            store = storeMgr.getDataStore(tmplStore.getDataStoreId(), storeRole);
         }
         return this.getTemplate(templateId, store);
     }
@@ -97,7 +101,7 @@ public class TemplateDataFactoryImpl implements TemplateDataFactory {
         TemplateDataStoreVO tmplStore = templateStoreDao.findByTemplateZone(templateId, zoneId, storeRole);
         DataStore store = null;
         if (tmplStore != null) {
-            store = this.storeMgr.getDataStore(tmplStore.getDataStoreId(), storeRole);
+            store = storeMgr.getDataStore(tmplStore.getDataStoreId(), storeRole);
         }
         return this.getTemplate(templateId, store);
     }
@@ -113,4 +117,30 @@ public class TemplateDataFactoryImpl implements TemplateDataFactory {
         tmpObj.setUrl(origTmpl.getUrl());
         return tmpObj;
     }
+
+    @Override
+    public TemplateInfo getReadyTemplateOnCache(long templateId) {
+        TemplateDataStoreVO tmplStore = templateStoreDao.findReadyOnCache(templateId);
+        if (tmplStore != null) {
+            DataStore store = storeMgr.getDataStore(tmplStore.getDataStoreId(), DataStoreRole.ImageCache);
+            return getTemplate(templateId, store);
+        } else {
+            return null;
+        }
+
+    }
+
+    @Override
+    public List<TemplateInfo> listTemplateOnCache(long templateId) {
+        List<TemplateDataStoreVO> cacheTmpls = templateStoreDao.listOnCache(templateId);
+        List<TemplateInfo> tmplObjs = new ArrayList<TemplateInfo>();
+        for (TemplateDataStoreVO cacheTmpl : cacheTmpls) {
+            long storeId = cacheTmpl.getDataStoreId();
+            DataStore store = storeMgr.getDataStore(storeId, DataStoreRole.ImageCache);
+            TemplateInfo tmplObj = getTemplate(templateId, store);
+            tmplObjs.add(tmplObj);
+        }
+        return tmplObjs;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
index a68e40c..ce6198d 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java
@@ -28,6 +28,9 @@ import java.util.Set;
 
 import javax.inject.Inject;
 
+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.CreateCmdResult;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService;
@@ -39,6 +42,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
 import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.StorageCacheManager;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService;
@@ -59,9 +63,6 @@ import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
 import org.apache.cloudstack.storage.image.store.TemplateObject;
 import org.apache.cloudstack.storage.to.TemplateObjectTO;
 
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
-
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.storage.ListTemplateAnswer;
 import com.cloud.agent.api.storage.ListTemplateCommand;
@@ -73,8 +74,9 @@ import com.cloud.dc.dao.DataCenterDao;
 import com.cloud.exception.ResourceAllocationException;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.storage.DataStoreRole;
-import com.cloud.storage.StoragePool;
+import com.cloud.storage.ScopeType;
 import com.cloud.storage.Storage.TemplateType;
+import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.VMTemplateZoneVO;
@@ -128,6 +130,8 @@ public class TemplateServiceImpl implements TemplateService {
     TemplateManager _tmpltMgr;
     @Inject
     ConfigurationDao _configDao;
+    @Inject
+    StorageCacheManager _cacheMgr;
 
     class TemplateOpContext<T> extends AsyncRpcContext<T> {
         final TemplateObject template;
@@ -466,7 +470,8 @@ public class TemplateServiceImpl implements TemplateService {
     // persist entry in template_zone_ref table. zoneId can be empty for
     // region-wide image store, in that case,
     // we will associate the template to all the zones.
-    private void associateTemplateToZone(long templateId, Long zoneId) {
+    @Override
+    public void associateTemplateToZone(long templateId, Long zoneId) {
         List<Long> dcs = new ArrayList<Long>();
         if (zoneId != null) {
             dcs.add(zoneId);
@@ -608,6 +613,86 @@ public class TemplateServiceImpl implements TemplateService {
         return copyAsync(volume, template, store);
     }
 
+    private AsyncCallFuture<TemplateApiResult> syncToRegionStoreAsync(TemplateInfo template, DataStore store) {
+        AsyncCallFuture<TemplateApiResult> future = new AsyncCallFuture<TemplateApiResult>();
+        // no need to create entry on template_store_ref here, since entries are already created when prepareSecondaryStorageForMigration is invoked.
+        // But we need to set default install path so that sync can be done in the right s3 path
+        TemplateInfo templateOnStore = _templateFactory.getTemplate(template, store);
+        String installPath = TemplateConstants.DEFAULT_TMPLT_ROOT_DIR + "/"
+                + TemplateConstants.DEFAULT_TMPLT_FIRST_LEVEL_DIR
+                + template.getAccountId() + "/" + template.getId() + "/" + template.getUniqueName();
+        ((TemplateObject)templateOnStore).setInstallPath(installPath);
+        TemplateOpContext<TemplateApiResult> context = new TemplateOpContext<TemplateApiResult>(null,
+                (TemplateObject)templateOnStore, future);
+        AsyncCallbackDispatcher<TemplateServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this);
+        caller.setCallback(caller.getTarget().syncTemplateCallBack(null, null)).setContext(context);
+        _motionSrv.copyAsync(template, templateOnStore, caller);
+        return future;
+    }
+
+    protected Void syncTemplateCallBack(AsyncCallbackDispatcher<TemplateServiceImpl, CopyCommandResult> callback,
+            TemplateOpContext<TemplateApiResult> context) {
+        TemplateInfo destTemplate = context.getTemplate();
+        CopyCommandResult result = callback.getResult();
+        AsyncCallFuture<TemplateApiResult> future = context.getFuture();
+        TemplateApiResult res = new TemplateApiResult(destTemplate);
+        try {
+            if (result.isFailed()) {
+                res.setResult(result.getResult());
+                // no change to existing template_store_ref, will try to re-sync later if other call triggers this sync operation, like copy template
+            } else {
+                // this will update install path properly, next time it will not sync anymore.
+                destTemplate.processEvent(Event.OperationSuccessed, result.getAnswer());
+            }
+            future.complete(res);
+        } catch (Exception e) {
+            s_logger.debug("Failed to process sync template callback", e);
+            res.setResult(e.toString());
+            future.complete(res);
+        }
+
+        return null;
+    }
+
+    private boolean isRegionStore(DataStore store) {
+        if (store.getScope().getScopeType() == ScopeType.ZONE && store.getScope().getScopeId() == null)
+            return true;
+        else
+            return false;
+    }
+
+    // This routine is used to push templates currently on cache store, but not in region store to region store.
+    // used in migrating existing NFS secondary storage to S3.
+    @Override
+    public void syncTemplateToRegionStore(long templateId, DataStore store) {
+        if (isRegionStore(store)) {
+            // if template is on region wide object store, check if it is really downloaded there (by checking install_path). Sync template to region
+            // wide store if it is not there physically.
+            TemplateInfo tmplOnStore = _templateFactory.getTemplate(templateId, store);
+            if (tmplOnStore == null) {
+                throw new CloudRuntimeException("Cannot find an entry in template_store_ref for template " + templateId + " on region store: " + store.getName());
+            }
+            if (tmplOnStore.getInstallPath() == null || tmplOnStore.getInstallPath().length() == 0) {
+                // template is not on region store yet, sync to region store
+                TemplateInfo srcTemplate = _templateFactory.getReadyTemplateOnCache(templateId);
+                if (srcTemplate == null) {
+                    throw new CloudRuntimeException("Cannot find template " + templateId + "  on cache store");
+                }
+                AsyncCallFuture<TemplateApiResult> future = syncToRegionStoreAsync(srcTemplate, store);
+                try {
+                    TemplateApiResult result = future.get();
+                    if (result.isFailed()) {
+                        throw new CloudRuntimeException("sync template from cache to region wide store failed for image store " + store.getName() + ":"
+                                + result.getResult());
+                    }
+                    _cacheMgr.releaseCacheObject(srcTemplate); // reduce reference count for template on cache, so it can recycled by schedule
+                } catch (Exception ex) {
+                    throw new CloudRuntimeException("sync template from cache to region wide store failed for image store " + store.getName());
+                }
+            }
+        }
+    }
+
     @Override
     public AsyncCallFuture<TemplateApiResult> copyTemplate(TemplateInfo srcTemplate, DataStore destStore) {
         // generate a URL from source template ssvm to download to destination data store

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/image/src/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java
index 64ef78f..0991860 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/manager/ImageStoreProviderManagerImpl.java
@@ -26,6 +26,9 @@ import java.util.Map;
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
 import org.apache.cloudstack.engine.subsystem.api.storage.ImageStoreProvider;
@@ -37,8 +40,6 @@ import org.apache.cloudstack.storage.image.ImageStoreDriver;
 import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
 import org.apache.cloudstack.storage.image.datastore.ImageStoreProviderManager;
 import org.apache.cloudstack.storage.image.store.ImageStoreImpl;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
 
 import com.cloud.storage.ScopeType;
 import com.cloud.storage.dao.VMTemplateDao;
@@ -95,6 +96,16 @@ public class ImageStoreProviderManagerImpl implements ImageStoreProviderManager
     }
 
     @Override
+    public List<DataStore> listImageCacheStores() {
+        List<ImageStoreVO> stores = dataStoreDao.listImageCacheStores();
+        List<DataStore> imageStores = new ArrayList<DataStore>();
+        for (ImageStoreVO store : stores) {
+            imageStores.add(getImageStore(store.getId()));
+        }
+        return imageStores;
+    }
+
+    @Override
     public List<DataStore> listImageStoresByScope(ZoneScope scope) {
         List<ImageStoreVO> stores = dataStoreDao.findByScope(scope);
         List<DataStore> imageStores = new ArrayList<DataStore>();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
----------------------------------------------------------------------
diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
index f0675f3..0a5b608 100644
--- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
+++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java
@@ -58,6 +58,7 @@ public class TemplateObject implements TemplateInfo {
     private VMTemplateVO imageVO;
     private DataStore dataStore;
     private String url;
+    private String installPath; // temporarily set installPath before passing to resource for entries with empty installPath for object store migration case
     @Inject
     VMTemplateDao imageDao;
     @Inject
@@ -293,6 +294,9 @@ public class TemplateObject implements TemplateInfo {
 
     @Override
     public String getInstallPath() {
+        if (installPath != null)
+            return installPath;
+
         if (dataStore == null) {
             return null;
         }
@@ -300,6 +304,10 @@ public class TemplateObject implements TemplateInfo {
         return obj.getInstallPath();
     }
 
+    public void setInstallPath(String installPath) {
+        this.installPath = installPath;
+    }
+
     @Override
     public long getAccountId() {
         return imageVO.getAccountId();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java
index aafdad0..6205fe4 100644
--- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java
+++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java
@@ -18,8 +18,13 @@
  */
 package org.apache.cloudstack.storage.snapshot;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.inject.Inject;
 
+import org.springframework.stereotype.Component;
+
 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.DataStoreManager;
@@ -28,7 +33,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
 import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
-import org.springframework.stereotype.Component;
 
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.SnapshotVO;
@@ -70,8 +74,22 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory {
         if (snapshotStore == null) {
             return null;
         }
-        DataStore store = this.storeMgr.getDataStore(snapshotStore.getDataStoreId(), role);
+        DataStore store = storeMgr.getDataStore(snapshotStore.getDataStoreId(), role);
         SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store);
         return so;
     }
+
+    @Override
+    public List<SnapshotInfo> listSnapshotOnCache(long snapshotId) {
+        List<SnapshotDataStoreVO> cacheSnapshots = snapshotStoreDao.listOnCache(snapshotId);
+        List<SnapshotInfo> snapObjs = new ArrayList<SnapshotInfo>();
+        for (SnapshotDataStoreVO cacheSnap : cacheSnapshots) {
+            long storeId = cacheSnap.getDataStoreId();
+            DataStore store = storeMgr.getDataStore(storeId, DataStoreRole.ImageCache);
+            SnapshotInfo tmplObj = getSnapshot(snapshotId, store);
+            snapObjs.add(tmplObj);
+        }
+        return snapObjs;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
----------------------------------------------------------------------
diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
index 15335bf..7eec5ff 100644
--- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
+++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java
@@ -16,17 +16,14 @@
 // under the License.
 package org.apache.cloudstack.storage.snapshot;
 
-import com.cloud.exception.InvalidParameterValueException;
-import com.cloud.storage.DataStoreRole;
-import com.cloud.storage.Snapshot;
-import com.cloud.storage.SnapshotVO;
-import com.cloud.storage.Volume;
-import com.cloud.storage.dao.SnapshotDao;
-import com.cloud.storage.snapshot.SnapshotManager;
-import com.cloud.utils.NumbersUtil;
-import com.cloud.utils.db.DB;
-import com.cloud.utils.exception.CloudRuntimeException;
-import com.cloud.utils.fsm.NoTransitionException;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
@@ -42,10 +39,18 @@ import org.apache.cloudstack.storage.command.CreateObjectAnswer;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
 import org.apache.cloudstack.storage.to.SnapshotObjectTO;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
 
-import javax.inject.Inject;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.storage.DataStoreRole;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.Volume;
+import com.cloud.storage.dao.SnapshotDao;
+import com.cloud.storage.snapshot.SnapshotManager;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.db.DB;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.fsm.NoTransitionException;
 
 @Component
 public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
@@ -71,7 +76,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
         if (parentSnapshot != null && snapshot.getPath().equalsIgnoreCase(parentSnapshot.getPath())) {
             s_logger.debug("backup an empty snapshot");
             // don't need to backup this snapshot
-            SnapshotDataStoreVO parentSnapshotOnBackupStore = this.snapshotStoreDao.findBySnapshot(
+            SnapshotDataStoreVO parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(
                     parentSnapshot.getId(), DataStoreRole.Image);
             if (parentSnapshotOnBackupStore != null && parentSnapshotOnBackupStore.getState() == State.Ready) {
                 DataStore store = dataStoreMgr.getDataStore(parentSnapshotOnBackupStore.getDataStoreId(),
@@ -93,7 +98,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
                     s_logger.debug("Failed to change state: " + snapshot.getId() + ": " + e.toString());
                     throw new CloudRuntimeException(e.toString());
                 }
-                return this.snapshotDataFactory.getSnapshot(snapObj.getId(), store);
+                return snapshotDataFactory.getSnapshot(snapObj.getId(), store);
             } else {
                 s_logger.debug("parent snapshot hasn't been backed up yet");
             }
@@ -111,7 +116,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
             int i;
             SnapshotDataStoreVO parentSnapshotOnBackupStore = null;
             for (i = 1; i < deltaSnap; i++) {
-                parentSnapshotOnBackupStore = this.snapshotStoreDao.findBySnapshot(parentSnapshot.getId(),
+                parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(parentSnapshot.getId(),
                         DataStoreRole.Image);
                 if (parentSnapshotOnBackupStore == null) {
                     break;
@@ -122,7 +127,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
                     break;
                 }
 
-                parentSnapshotOnBackupStore = this.snapshotStoreDao.findBySnapshot(prevBackupId, DataStoreRole.Image);
+                parentSnapshotOnBackupStore = snapshotStoreDao.findBySnapshot(prevBackupId, DataStoreRole.Image);
             }
             if (i >= deltaSnap) {
                 fullBackup = true;
@@ -130,7 +135,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
         }
 
         snapshot.addPayload(fullBackup);
-        return this.snapshotSvr.backupSnapshot(snapshot);
+        return snapshotSvr.backupSnapshot(snapshot);
     }
 
     protected boolean deleteSnapshotChain(SnapshotInfo snapshot) {
@@ -164,7 +169,15 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
                     }
                 }
                 if (!deleted) {
-                    boolean r = this.snapshotSvr.deleteSnapshot(snapshot);
+                    boolean r = snapshotSvr.deleteSnapshot(snapshot);
+                    if (r) {
+                        // delete snapshot in cache if there is
+                        List<SnapshotInfo> cacheSnaps = snapshotDataFactory.listSnapshotOnCache(snapshot.getId());
+                        for (SnapshotInfo cacheSnap : cacheSnaps) {
+                            s_logger.debug("Delete snapshot " + snapshot.getId() + " from image cache store: " + cacheSnap.getDataStore().getName());
+                            cacheSnap.delete();
+                        }
+                    }
                     if (!resultIsSet) {
                         result = r;
                         resultIsSet = true;
@@ -200,7 +213,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
         // first mark the snapshot as destroyed, so that ui can't see it, but we
         // may not destroy the snapshot on the storage, as other snapshots may
         // depend on it.
-        SnapshotInfo snapshotOnImage = this.snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image);
+        SnapshotInfo snapshotOnImage = snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image);
         if (snapshotOnImage == null) {
             s_logger.debug("Can't find snapshot on backup storage, delete it in db");
             snapshotDao.remove(snapshotId);
@@ -273,7 +286,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase {
             snapshot = result.getSnashot();
             DataStore primaryStore = snapshot.getDataStore();
 
-            SnapshotInfo backupedSnapshot = this.backupSnapshot(snapshot);
+            SnapshotInfo backupedSnapshot = backupSnapshot(snapshot);
 
             try {
                 SnapshotInfo parent = snapshot.getParent();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java
index a9263a9..88061aa 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/DataStoreManagerImpl.java
@@ -107,6 +107,11 @@ public class DataStoreManagerImpl implements DataStoreManager {
         return imageDataStoreMgr.listImageStores();
     }
 
+    @Override
+    public List<DataStore> listImageCacheStores() {
+        return imageDataStoreMgr.listImageCacheStores();
+    }
+
     public void setPrimaryStoreMgr(PrimaryDataStoreProviderManager primaryStoreMgr) {
         this.primaryStoreMgr = primaryStoreMgr;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreHelper.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreHelper.java b/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreHelper.java
index a641146..e2c48ea 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreHelper.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreHelper.java
@@ -24,11 +24,14 @@ import java.util.UUID;
 
 import javax.inject.Inject;
 
+import org.springframework.stereotype.Component;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailVO;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreDetailsDao;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
-import org.springframework.stereotype.Component;
+import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
 
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.ScopeType;
@@ -40,6 +43,8 @@ public class ImageStoreHelper {
     ImageStoreDao imageStoreDao;
     @Inject
     ImageStoreDetailsDao imageStoreDetailsDao;
+    @Inject
+    SnapshotDataStoreDao snapshotStoreDao;
 
     public ImageStoreVO createImageStore(Map<String, Object> params) {
         ImageStoreVO store = imageStoreDao.findByName((String) params.get("name"));
@@ -115,4 +120,18 @@ public class ImageStoreHelper {
         imageStoreDao.remove(id);
         return true;
     }
+
+    /**
+     * Convert current NFS secondary storage to Staging store to be ready to migrate to S3 object store.
+     * @param store NFS image store.
+     * @return true if successful.
+     */
+    public boolean convertToStagingStore(DataStore store) {
+        ImageStoreVO nfsStore = imageStoreDao.findById(store.getId());
+        nfsStore.setRole(DataStoreRole.ImageCache);
+        imageStoreDao.update(store.getId(), nfsStore);
+        // clear snapshot entry on primary store to make next snapshot become full snapshot
+        snapshotStoreDao.deleteSnapshotRecordsOnPrimary();
+        return true;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreProviderManager.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreProviderManager.java b/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreProviderManager.java
index be66cc5..8afb3d9 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreProviderManager.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/datastore/ImageStoreProviderManager.java
@@ -32,6 +32,8 @@ public interface ImageStoreProviderManager {
 
     List<DataStore> listImageStores();
 
+    List<DataStore> listImageCacheStores();
+
     List<DataStore> listImageStoresByScope(ZoneScope scope);
 
     List<DataStore> listImageStoreByProvider(String provider);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageStoreDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageStoreDaoImpl.java
index b9ef9c3..13a7f47 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageStoreDaoImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/ImageStoreDaoImpl.java
@@ -23,10 +23,11 @@ import java.util.Map;
 
 import javax.naming.ConfigurationException;
 
+import org.springframework.stereotype.Component;
+
 import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
 import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
-import org.springframework.stereotype.Component;
 
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.ScopeType;
@@ -118,4 +119,11 @@ public class ImageStoreDaoImpl extends GenericDaoBase<ImageStoreVO, Long> implem
         return listBy(sc);
     }
 
+    @Override
+    public List<ImageStoreVO> listImageCacheStores() {
+        SearchCriteria<ImageStoreVO> sc = createSearchCriteria();
+        sc.addAnd("role", SearchCriteria.Op.EQ, DataStoreRole.ImageCache);
+        return listBy(sc);
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6be228a4/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java
index 48416a2..ee00dd5 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java
@@ -25,23 +25,23 @@ import java.util.Map;
 
 import javax.naming.ConfigurationException;
 
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
 
-import com.cloud.utils.db.DB;
 import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
 import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
 import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
-import org.apache.log4j.Logger;
-import org.springframework.stereotype.Component;
 
 import com.cloud.storage.DataStoreRole;
+import com.cloud.utils.db.DB;
 import com.cloud.utils.db.GenericDaoBase;
 import com.cloud.utils.db.SearchBuilder;
 import com.cloud.utils.db.SearchCriteria;
-import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.utils.db.SearchCriteria.Op;
+import com.cloud.utils.db.TransactionLegacy;
 import com.cloud.utils.db.UpdateBuilder;
 
 @Component
@@ -54,7 +54,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
     private SearchBuilder<SnapshotDataStoreVO> snapshotSearch;
     private SearchBuilder<SnapshotDataStoreVO> storeSnapshotSearch;
     private SearchBuilder<SnapshotDataStoreVO> snapshotIdSearch;
-    private String parentSearch = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where store_id = ? " +
+    private final String parentSearch = "select store_id, store_role, snapshot_id from cloud.snapshot_store_ref where store_id = ? " +
             " and store_role = ? and volume_id = ? and state = 'Ready'" +
             " order by created DESC " +
             " limit 1";
@@ -171,6 +171,16 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
     }
 
     @Override
+    public void deleteSnapshotRecordsOnPrimary() {
+        SearchCriteria<SnapshotDataStoreVO> sc = storeSearch.create();
+        sc.setParameters("store_role", DataStoreRole.Primary);
+        TransactionLegacy txn = TransactionLegacy.currentTxn();
+        txn.start();
+        remove(sc);
+        txn.commit();
+    }
+
+    @Override
     public SnapshotDataStoreVO findByStoreSnapshot(DataStoreRole role, long storeId, long snapshotId) {
         SearchCriteria<SnapshotDataStoreVO> sc = storeSnapshotSearch.create();
         sc.setParameters("store_id", storeId);
@@ -195,7 +205,7 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
                 long sid = rs.getLong(1);
                 String rl = rs.getString(2);
                 long snid = rs.getLong(3);
-                return this.findByStoreSnapshot(role, sid, snid);
+                return findByStoreSnapshot(role, sid, snid);
             }
         } catch (SQLException e) {
             s_logger.debug("Failed to find parent snapshot: " + e.toString());
@@ -236,4 +246,64 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase<SnapshotDataStoreVO
         sc.setParameters("ref_cnt", 0);
         return listBy(sc);
     }
+
+    @Override
+    public void duplicateCacheRecordsOnRegionStore(long storeId) {
+        // find all records on image cache
+        SearchCriteria<SnapshotDataStoreVO> sc = storeSnapshotSearch.create();
+        sc.setParameters("store_role", DataStoreRole.ImageCache);
+        sc.setParameters("destroyed", false);
+        List<SnapshotDataStoreVO> snapshots = listBy(sc);
+        // create an entry for each record, but with empty install path since the content is not yet on region-wide store yet
+        if (snapshots != null) {
+            s_logger.info("Duplicate " + snapshots.size() + " snapshot cache store records to region store");
+            for (SnapshotDataStoreVO snap : snapshots) {
+                SnapshotDataStoreVO snapStore = findByStoreSnapshot(DataStoreRole.Image, storeId, snap.getSnapshotId());
+                if (snapStore != null) {
+                    s_logger.info("There is already entry for snapshot " + snap.getSnapshotId() + " on region store " + storeId);
+                    continue;
+                }
+                s_logger.info("Persisting an entry for snapshot " + snap.getSnapshotId() + " on region store " + storeId);
+                SnapshotDataStoreVO ss = new SnapshotDataStoreVO();
+                ss.setSnapshotId(snap.getSnapshotId());
+                ss.setDataStoreId(storeId);
+                ss.setRole(DataStoreRole.Image);
+                ss.setVolumeId(snap.getVolumeId());
+                ss.setParentSnapshotId(snap.getParentSnapshotId());
+                ss.setState(snap.getState());
+                ss.setSize(snap.getSize());
+                ss.setPhysicalSize(snap.getPhysicalSize());
+                ss.setRefCnt(snap.getRefCnt());
+                persist(ss);
+                // increase ref_cnt so that this will not be recycled before the content is pushed to region-wide store
+                snap.incrRefCnt();
+                update(snap.getId(), snap);
+            }
+        }
+
+    }
+
+    @Override
+    public List<SnapshotDataStoreVO> listOnCache(long snapshotId) {
+        SearchCriteria<SnapshotDataStoreVO> sc = storeSnapshotSearch.create();
+        sc.setParameters("snapshot_id", snapshotId);
+        sc.setParameters("store_role", DataStoreRole.ImageCache);
+        return search(sc, null);
+    }
+
+    @Override
+    public void updateStoreRoleToCache(long storeId) {
+        SearchCriteria<SnapshotDataStoreVO> sc = storeSearch.create();
+        sc.setParameters("store_id", storeId);
+        sc.setParameters("destroyed", false);
+        List<SnapshotDataStoreVO> snaps = listBy(sc);
+        if (snaps != null) {
+            s_logger.info("Update to cache store role for " + snaps.size() + " entries in snapshot_store_ref");
+            for (SnapshotDataStoreVO snap : snaps) {
+                snap.setRole(DataStoreRole.ImageCache);
+                update(snap.getId(), snap);
+            }
+        }
+    }
+
 }