cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From devd...@apache.org
Subject git commit: updated refs/heads/master to 2aff39f
Date Fri, 14 Mar 2014 11:13:40 GMT
Repository: cloudstack
Updated Branches:
  refs/heads/master f293c94bc -> 2aff39f8c


CLOUDSTACK-6143: Storage motion support for hyper-v. With these changes a volume on a shared
storage pool (SMB) and attached to a running vm can be live migrated to another shared storage
pool. Also a vm and its volumes can be live migrated to another host and storage pool respectively.


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

Branch: refs/heads/master
Commit: 2aff39f8c75d20ea086dbb873030f3e513fc6719
Parents: f293c94
Author: Devdeep Singh <devdeep@gmail.com>
Authored: Wed Mar 12 02:22:22 2014 +0530
Committer: Devdeep Singh <devdeep@gmail.com>
Committed: Fri Mar 14 16:27:58 2014 +0530

----------------------------------------------------------------------
 .../agent/api/MigrateWithStorageCommand.java    |  16 ++
 .../subsystem/api/storage/StorageAction.java    |   3 +-
 ...ngine-storage-datamotion-storage-context.xml |   3 +-
 .../motion/AncientDataMotionStrategy.java       |   2 +-
 .../endpoint/DefaultEndPointSelector.java       |   9 +
 .../storage/volume/VolumeServiceImpl.java       |  10 +-
 .../HypervResource/CloudStackTypes.cs           |  51 ++++--
 .../HypervResource/HypervResourceController.cs  | 139 +++++++++++++-
 .../HypervResource/IWmiCallsV2.cs               |   2 +
 .../ServerResource/HypervResource/WmiCallsV2.cs | 139 ++++++++++++++
 ...tion.v2.Msvm_StorageAllocationSettingData.cs |   2 +-
 .../motion/HypervStorageMotionStrategy.java     | 179 +++++++++++++++++++
 server/src/com/cloud/vm/UserVmManagerImpl.java  |   3 +-
 setup/db/db/schema-430to440.sql                 |   1 +
 14 files changed, 530 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java b/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java
index a94697a..43ae854 100644
--- a/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java
+++ b/core/src/com/cloud/agent/api/MigrateWithStorageCommand.java
@@ -16,26 +16,38 @@
 // under the License.
 package com.cloud.agent.api;
 
+import java.util.List;
 import java.util.Map;
 
 import com.cloud.agent.api.to.StorageFilerTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.utils.Pair;
 
 public class MigrateWithStorageCommand extends Command {
     VirtualMachineTO vm;
     Map<VolumeTO, StorageFilerTO> volumeToFiler;
+    List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerAsList;
     String tgtHost;
 
     public MigrateWithStorageCommand(VirtualMachineTO vm, Map<VolumeTO, StorageFilerTO>
volumeToFiler) {
         this.vm = vm;
         this.volumeToFiler = volumeToFiler;
+        this.volumeToFilerAsList = null;
         this.tgtHost = null;
     }
 
     public MigrateWithStorageCommand(VirtualMachineTO vm, Map<VolumeTO, StorageFilerTO>
volumeToFiler, String tgtHost) {
         this.vm = vm;
         this.volumeToFiler = volumeToFiler;
+        this.volumeToFilerAsList = null;
+        this.tgtHost = tgtHost;
+    }
+
+    public MigrateWithStorageCommand(VirtualMachineTO vm, List<Pair<VolumeTO, StorageFilerTO>>
volumeToFilerAsList, String tgtHost) {
+        this.vm = vm;
+        this.volumeToFiler = null;
+        this.volumeToFilerAsList = volumeToFilerAsList;
         this.tgtHost = tgtHost;
     }
 
@@ -47,6 +59,10 @@ public class MigrateWithStorageCommand extends Command {
         return volumeToFiler;
     }
 
+    public List<Pair<VolumeTO, StorageFilerTO>> getVolumeToFilerAsList() {
+        return volumeToFilerAsList;
+    }
+
     public String getTargetHost() {
         return tgtHost;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java
----------------------------------------------------------------------
diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java
b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java
index 4fbb20e..965df84 100644
--- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java
+++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageAction.java
@@ -21,5 +21,6 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
 public enum StorageAction {
     TAKESNAPSHOT,
     BACKUPSNAPSHOT,
-    DELETESNAPSHOT
+    DELETESNAPSHOT,
+    MIGRATEVOLUME
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
----------------------------------------------------------------------
diff --git a/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
b/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
index 725f7d3..ade22db 100644
--- a/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
+++ b/engine/storage/datamotion/resources/META-INF/cloudstack/storage/spring-engine-storage-datamotion-storage-context.xml
@@ -30,5 +30,6 @@
         class="org.apache.cloudstack.storage.motion.AncientDataMotionStrategy" />
     <bean id="xenserverStorageMotionStrategy"
         class="org.apache.cloudstack.storage.motion.XenServerStorageMotionStrategy" />
-
+    <bean id="hypervStorageMotionStrategy"
+        class="org.apache.cloudstack.storage.motion.HypervStorageMotionStrategy" />
 </beans>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/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 df81199..fbdacfa 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
@@ -372,7 +372,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy {
         VolumeInfo volume = (VolumeInfo)srcData;
         StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destData.getDataStore().getId(),
DataStoreRole.Primary);
         MigrateVolumeCommand command = new MigrateVolumeCommand(volume.getId(), volume.getPath(),
destPool, volume.getAttachedVmName());
-        EndPoint ep = selector.select(volume.getDataStore());
+        EndPoint ep = selector.select(srcData, StorageAction.MIGRATEVOLUME);
         Answer answer = null;
         if (ep == null) {
             String errMsg = "No remote endpoint to send command, check if host or ssvm is
down?";

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
----------------------------------------------------------------------
diff --git a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
index d64f755..304f959 100644
--- a/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
+++ b/engine/storage/src/org/apache/cloudstack/storage/endpoint/DefaultEndPointSelector.java
@@ -305,6 +305,15 @@ public class DefaultEndPointSelector implements EndPointSelector {
                     return getEndPointFromHostId(hostId);
                 }
             }
+        } else if (action == StorageAction.MIGRATEVOLUME) {
+            VolumeInfo volume = (VolumeInfo)object;
+            if (volume.getHypervisorType() == Hypervisor.HypervisorType.Hyperv) {
+                VirtualMachine vm = volume.getAttachedVM();
+                if ((vm != null) && (vm.getState() == VirtualMachine.State.Running))
{
+                    Long hostId = vm.getHostId();
+                    return getEndPointFromHostId(hostId);
+                }
+            }
         }
         return select(object);
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
----------------------------------------------------------------------
diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
index 1c01401..68e5a56 100644
--- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
+++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java
@@ -77,6 +77,7 @@ import com.cloud.host.Host;
 import com.cloud.host.dao.HostDao;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.ScopeType;
+import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.VMTemplateStoragePoolVO;
 import com.cloud.storage.VMTemplateStorageResourceAssoc;
@@ -814,9 +815,16 @@ public class VolumeServiceImpl implements VolumeService {
 
     protected VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePool pool) {
         Long lastPoolId = volume.getPoolId();
+        String folder = pool.getPath();
+        // For SMB, pool credentials are also stored in the uri query string.  We trim the
query string
+        // part  here to make sure the credentials do not get stored in the db unencrypted.
+        if (pool.getPoolType() == StoragePoolType.SMB && folder != null &&
folder.contains("?")) {
+            folder = folder.substring(0, folder.indexOf("?"));
+        }
+
         VolumeVO newVol = new VolumeVO(volume);
         newVol.setPoolId(pool.getId());
-        newVol.setFolder(pool.getPath());
+        newVol.setFolder(folder);
         newVol.setPodId(pool.getPodId());
         newVol.setPoolId(pool.getId());
         newVol.setLastPoolId(lastPoolId);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
index 869f108..c222102 100644
--- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
@@ -173,33 +173,35 @@ namespace HypervResource
                     PrimaryDataStoreTO store = this.primaryDataStore;
                     if (store.isLocal)
                     {
-                        fileName = Path.Combine(store.Path, this.uuid);
+                        String volume = this.path;
+                        if (String.IsNullOrEmpty(volume))
+                        {
+                            volume = this.uuid;
+                        }
+                        fileName = Path.Combine(store.Path, volume);
                     }
                     else
                     {
-                        fileName = @"\\" + store.uri.Host + store.uri.LocalPath + @"\" +
this.uuid;
+                        String volume = this.path;
+                        if (String.IsNullOrEmpty(volume))
+                        {
+                            volume = this.uuid;
+                        }
+                        fileName = @"\\" + store.uri.Host + store.uri.LocalPath + @"\" +
volume;
                         fileName = Utils.NormalizePath(fileName);
                     }
                 }
                 else if (this.nfsDataStore != null)
                 {
-                    if (this.path != null && File.Exists(this.path))
+                    fileName = this.nfsDataStore.UncPath;
+                    if (this.path != null)
                     {
-                        fileName = this.path;
+                        fileName = Utils.NormalizePath(fileName + @"\" + this.path);
                     }
-                    else
-                    {
-                        fileName = this.nfsDataStore.UncPath;
-                        if (this.path != null)
-                        {
-                            fileName += @"\" + this.path;
-                        }
 
-                        fileName = Utils.NormalizePath(fileName);
-                        if (Directory.Exists(fileName))
-                        {
-                            fileName = Utils.NormalizePath(fileName + @"\" + this.uuid);
-                        }
+                    if (fileName != null && !File.Exists(fileName))
+                    {
+                        fileName = Utils.NormalizePath(fileName + @"\" + this.uuid);
                     }
                 }
                 else
@@ -344,8 +346,17 @@ namespace HypervResource
                 }
                 else if (this.nfsDataStoreTO != null)
                 {
-                    NFSTO store = this.nfsDataStoreTO;
-                    fileName = store.UncPath + @"\" + this.path + @"\" + this.uuid;
+                    fileName = this.nfsDataStoreTO.UncPath;
+                    if (this.path != null)
+                    {
+                        fileName = Utils.NormalizePath(fileName + @"\" + this.path);
+                    }
+
+                    if (fileName != null && !File.Exists(fileName))
+                    {
+                        fileName = Utils.NormalizePath(fileName + @"\" + this.uuid);
+                    }
+
                     if (!this.format.Equals("RAW"))
                     {
                         fileName = fileName + '.' + this.format.ToLowerInvariant();
@@ -769,6 +780,8 @@ namespace HypervResource
         public const string ManageSnapshotCommand = "com.cloud.agent.api.ManageSnapshotCommand";
         public const string MigrateAnswer = "com.cloud.agent.api.MigrateAnswer";
         public const string MigrateCommand = "com.cloud.agent.api.MigrateCommand";
+        public const string MigrateWithStorageAnswer = "com.cloud.agent.api.MigrateWithStorageAnswer";
+        public const string MigrateWithStorageCommand = "com.cloud.agent.api.MigrateWithStorageCommand";
         public const string ModifySshKeysCommand = "com.cloud.agent.api.ModifySshKeysCommand";
         public const string ModifyStoragePoolAnswer = "com.cloud.agent.api.ModifyStoragePoolAnswer";
         public const string ModifyStoragePoolCommand = "com.cloud.agent.api.ModifyStoragePoolCommand";
@@ -853,6 +866,8 @@ namespace HypervResource
         public const string CreateCommand = "com.cloud.agent.api.storage.CreateCommand";
         public const string CreatePrivateTemplateAnswer = "com.cloud.agent.api.storage.CreatePrivateTemplateAnswer";
         public const string DestroyCommand = "com.cloud.agent.api.storage.DestroyCommand";
+        public const string MigrateVolumeAnswer = "com.cloud.agent.api.storage.MigrateVolumeAnswer";
+        public const string MigrateVolumeCommand = "com.cloud.agent.api.storage.MigrateVolumeCommand";
         public const string PrimaryStorageDownloadAnswer = "com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer";
         public const string PrimaryStorageDownloadCommand = "com.cloud.agent.api.storage.PrimaryStorageDownloadCommand";
         public const string ResizeVolumeAnswer = "com.cloud.agent.api.storage.ResizeVolumeAnswer";

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
index e233d03..2d44753 100644
--- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
@@ -1187,7 +1187,7 @@ namespace HypervResource
                         volumePath = Utils.NormalizePath(volumePath);
                         Utils.ConnectToRemote(primary.UncPath, primary.Domain, primary.User,
primary.Password);
                     }
-                    volume.path = volumePath;
+                    volume.path = volume.uuid;
                     wmiCallsV2.CreateDynamicVirtualHardDisk(volumeSize, volumePath);
                     if (File.Exists(volumePath))
                     {
@@ -1578,7 +1578,7 @@ namespace HypervResource
                             {
                                 // TODO: thin provision instead of copying the full file.
                                 File.Copy(srcFile, destFile);
-                                destVolumeObjectTO.path = destFile;
+                                destVolumeObjectTO.path = destVolumeObjectTO.uuid;
                                 JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO,
destVolumeObjectTO);
                                 newData = ansObj;
                                 result = true;
@@ -1612,8 +1612,25 @@ namespace HypervResource
                                 // doesn't do anything if the directory is already present.
                                 Directory.CreateDirectory(Path.GetDirectoryName(destFile));
                                 File.Copy(srcFile, destFile);
+
+                                if (srcVolumeObjectTO.nfsDataStore != null && srcVolumeObjectTO.primaryDataStore
== null)
+                                {
+                                    logger.Info("Copied volume from secondary data store
to primary. Path: " + destVolumeObjectTO.path);
+                                }
+                                else if (srcVolumeObjectTO.primaryDataStore != null &&
srcVolumeObjectTO.nfsDataStore == null)
+                                {
+                                    destVolumeObjectTO.path = destVolumeObjectTO.path + "/"
+ destVolumeObjectTO.uuid;
+                                    if (destVolumeObjectTO.format != null)
+                                    {
+                                        destVolumeObjectTO.path += "." + destVolumeObjectTO.format.ToLower();
+                                    }
+                                }
+                                else
+                                {
+                                    logger.Error("Destination volume path wasn't set. Unsupported
source volume data store.");
+                                }
+
                                 // Create volumeto object deserialize and send it
-                                destVolumeObjectTO.path = destFile;
                                 JObject ansObj = Utils.CreateCloudStackObject(CloudStackTypes.VolumeObjectTO,
destVolumeObjectTO);
                                 newData = ansObj;
                                 result = true;
@@ -1656,7 +1673,11 @@ namespace HypervResource
                                 TemplateObjectTO destTemplateObject = new TemplateObjectTO();
                                 destTemplateObject.size = srcVolumeObjectTO.size.ToString();
                                 destTemplateObject.format = srcVolumeObjectTO.format;
-                                destTemplateObject.path = destFile;
+                                destTemplateObject.path = destTemplateObjectTO.path + "/"
+ destTemplateObjectTO.uuid;
+                                if (destTemplateObject.format != null)
+                                {
+                                    destTemplateObject.path += "." + destTemplateObject.format.ToLower();
+                                }
                                 destTemplateObject.nfsDataStoreTO = destTemplateObjectTO.nfsDataStoreTO;
                                 destTemplateObject.checksum = destTemplateObjectTO.checksum;
                                 newData = destTemplateObject;
@@ -1990,6 +2011,113 @@ namespace HypervResource
             }
         }
 
+        // POST api/HypervResource/MigrateVolumeCommand
+        [HttpPost]
+        [ActionName(CloudStackTypes.MigrateVolumeCommand)]
+        public JContainer MigrateVolumeCommand([FromBody]dynamic cmd)
+        {
+            using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+            {
+                logger.Info(CloudStackTypes.MigrateVolumeCommand + cmd.ToString());
+
+                string details = null;
+                bool result = false;
+
+                try
+                {
+                    string vm = (string)cmd.attachedVmName;
+                    string volume = (string)cmd.volumePath;
+                    wmiCallsV2.MigrateVolume(vm, volume, GetStoragePoolPath(cmd.pool));
+                    result = true;
+                }
+                catch (Exception sysEx)
+                {
+                    details = CloudStackTypes.MigrateVolumeCommand + " failed due to " +
sysEx.Message;
+                    logger.Error(details, sysEx);
+                }
+
+                object ansContent = new
+                {
+                    result = result,
+                    volumePath = (string)cmd.volumePath,
+                    details = details,
+                    contextMap = contextMap
+                };
+
+                return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateVolumeAnswer);
+            }
+        }
+
+        // POST api/HypervResource/MigrateWithStorageCommand
+        [HttpPost]
+        [ActionName(CloudStackTypes.MigrateWithStorageCommand)]
+        public JContainer MigrateWithStorageCommand([FromBody]dynamic cmd)
+        {
+            using (log4net.NDC.Push(Guid.NewGuid().ToString()))
+            {
+                logger.Info(CloudStackTypes.MigrateWithStorageCommand + cmd.ToString());
+
+                string details = null;
+                bool result = false;
+                List<dynamic> volumeTos = new List<dynamic>();
+
+                try
+                {
+                    string vm = (string)cmd.vm.name;
+                    string destination = (string)cmd.tgtHost;
+                    var volumeToPoolList = cmd.volumeToFilerAsList;
+                    var volumeToPool = new Dictionary<string, string>();
+                    foreach (var item in volumeToPoolList)
+                    {
+                        volumeTos.Add(item.t);
+                        string poolPath = GetStoragePoolPath(item.u);
+                        volumeToPool.Add((string)item.t.path, poolPath);
+                    }
+
+                    wmiCallsV2.MigrateVmWithVolume(vm, destination, volumeToPool);
+                    result = true;
+                }
+                catch (Exception sysEx)
+                {
+                    details = CloudStackTypes.MigrateWithStorageCommand + " failed due to
" + sysEx.Message;
+                    logger.Error(details, sysEx);
+                }
+
+                object ansContent = new
+                {
+                    result = result,
+                    volumeTos = JArray.FromObject(volumeTos),
+                    details = details,
+                    contextMap = contextMap
+                };
+
+                return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.MigrateWithStorageAnswer);
+            }
+        }
+
+        private string GetStoragePoolPath(dynamic pool)
+        {
+            string poolTypeStr = pool.type;
+            StoragePoolType poolType;
+            if (!Enum.TryParse<StoragePoolType>(poolTypeStr, out poolType))
+            {
+                throw new ArgumentException("Invalid pool type " + poolTypeStr);
+            }
+            else if (poolType == StoragePoolType.SMB)
+            {
+                NFSTO share = new NFSTO();
+                String uriStr = "cifs://" + (string)pool.host + (string)pool.path;
+                share.uri = new Uri(uriStr);
+                return Utils.NormalizePath(share.UncPath);
+            }
+            else if (poolType == StoragePoolType.Filesystem)
+            {
+                return pool.path;
+            }
+
+            throw new ArgumentException("Couldn't parse path for pool type " + poolTypeStr);
+        }
+
         // POST api/HypervResource/StartupCommand
         [HttpPost]
         [ActionName(CloudStackTypes.StartupCommand)]
@@ -2016,7 +2144,8 @@ namespace HypervResource
                 strtRouteCmd.storageNetmask = subnet;
                 strtRouteCmd.storageMacAddress = storageNic.GetPhysicalAddress().ToString();
                 strtRouteCmd.gatewayIpAddress = storageNic.GetPhysicalAddress().ToString();
-
+                strtRouteCmd.hypervisorVersion = System.Environment.OSVersion.Version.Major.ToString()
+ "." +
+                        System.Environment.OSVersion.Version.Minor.ToString();
                 strtRouteCmd.caps = "hvm";
 
                 dynamic details = strtRouteCmd.hostDetails;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs
b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs
index da6ad83..6018896 100644
--- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs
@@ -40,6 +40,8 @@ namespace HypervResource
         void DestroyVm(dynamic jsonObj);
         void DestroyVm(string displayName);
         void MigrateVm(string vmName, string destination);
+        void MigrateVolume(string vmName, string volume, string destination);
+        void MigrateVmWithVolume(string vmName, string destination, Dictionary<string,
string> volumeToPool);
         void DetachDisk(string displayName, string diskFileName);
         ComputerSystem GetComputerSystem(string displayName);
         ComputerSystem.ComputerSystemCollection GetComputerSystemCollection();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs
b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs
index d2b9ce1..73156c5 100644
--- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs
@@ -1059,6 +1059,125 @@ namespace HypervResource
         }
 
         /// <summary>
+        /// Migrates the volume of a vm to a given destination storage
+        /// </summary>
+        /// <param name="displayName"></param>
+        /// <param name="volume"></param>
+        /// <param name="destination storage pool"></param>
+        public void MigrateVolume(string vmName, string volume, string destination)
+        {
+            ComputerSystem vm = GetComputerSystem(vmName);
+            VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+            VirtualSystemMigrationSettingData migrationSettingData = VirtualSystemMigrationSettingData.CreateInstance();
+            VirtualSystemMigrationService service = GetVirtualisationSystemMigrationService();
+            StorageAllocationSettingData[] sasd = GetStorageSettings(vm);
+
+            string[] rasds = null;
+            if (sasd != null)
+            {
+                rasds = new string[sasd.Length];
+                uint index = 0;
+                foreach (StorageAllocationSettingData item in sasd)
+                {
+                    string vhdFileName = Path.GetFileNameWithoutExtension(item.HostResource[0]);
+                    if (!String.IsNullOrEmpty(vhdFileName) && vhdFileName.Equals(volume))
+                    {
+                        string newVhdPath = Path.Combine(destination, Path.GetFileName(item.HostResource[0]));
+                        item.LateBoundObject["HostResource"] = new string[] { newVhdPath
};
+                        item.LateBoundObject["PoolId"] = "";
+                    }
+
+                    rasds[index++] = item.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20);
+                }
+            }
+
+            migrationSettingData.LateBoundObject["MigrationType"] = MigrationType.Storage;
+            migrationSettingData.LateBoundObject["TransportType"] = TransportType.TCP;
+            string migrationSettings = migrationSettingData.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20);
+
+            ManagementPath jobPath;
+            var ret_val = service.MigrateVirtualSystemToHost(vm.Path, null, migrationSettings,
rasds, null, out jobPath);
+            if (ret_val == ReturnCode.Started)
+            {
+                MigrationJobCompleted(jobPath);
+            }
+            else if (ret_val != ReturnCode.Completed)
+            {
+                var errMsg = string.Format(
+                    "Failed migrating volume {0} of VM {1} (GUID {2}) due to {3}",
+                    volume,
+                    vm.ElementName,
+                    vm.Name,
+                    ReturnCode.ToString(ret_val));
+                var ex = new WmiException(errMsg);
+                logger.Error(errMsg, ex);
+                throw ex;
+            }
+        }
+
+        /// <summary>
+        /// Migrates the volume of a vm to a given destination storage
+        /// </summary>
+        /// <param name="displayName"></param>
+        /// <param name="destination host"></param>
+        /// <param name="volumeToPool"> volume to me migrated to which pool</param>
+        public void MigrateVmWithVolume(string vmName, string destination, Dictionary<string,
string> volumeToPool)
+        {
+            ComputerSystem vm = GetComputerSystem(vmName);
+            VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+            VirtualSystemMigrationSettingData migrationSettingData = VirtualSystemMigrationSettingData.CreateInstance();
+            VirtualSystemMigrationService service = GetVirtualisationSystemMigrationService();
+            StorageAllocationSettingData[] sasd = GetStorageSettings(vm);
+
+            string[] rasds = null;
+            if (sasd != null)
+            {
+                rasds = new string[sasd.Length];
+                uint index = 0;
+                foreach (StorageAllocationSettingData item in sasd)
+                {
+                    string vhdFileName = Path.GetFileNameWithoutExtension(item.HostResource[0]);
+                    if (!String.IsNullOrEmpty(vhdFileName) && volumeToPool.ContainsKey(vhdFileName))
+                    {
+                        string newVhdPath = Path.Combine(volumeToPool[vhdFileName], Path.GetFileName(item.HostResource[0]));
+                        item.LateBoundObject["HostResource"] = new string[] { newVhdPath
};
+                        item.LateBoundObject["PoolId"] = "";
+                    }
+
+                    rasds[index++] = item.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20);
+                }
+            }
+
+            IPAddress addr = IPAddress.Parse(destination);
+            IPHostEntry entry = Dns.GetHostEntry(addr);
+            string[] destinationHost = new string[] { destination };
+
+            migrationSettingData.LateBoundObject["MigrationType"] = MigrationType.VirtualSystemAndStorage;
+            migrationSettingData.LateBoundObject["TransportType"] = TransportType.TCP;
+            migrationSettingData.LateBoundObject["DestinationIPAddressList"] = destinationHost;
+            string migrationSettings = migrationSettingData.LateBoundObject.GetText(System.Management.TextFormat.CimDtd20);
+
+            ManagementPath jobPath;
+            var ret_val = service.MigrateVirtualSystemToHost(vm.Path, entry.HostName, migrationSettings,
rasds, null, out jobPath);
+            if (ret_val == ReturnCode.Started)
+            {
+                MigrationJobCompleted(jobPath);
+            }
+            else if (ret_val != ReturnCode.Completed)
+            {
+                var errMsg = string.Format(
+                    "Failed migrating VM {0} and its volumes to destination {1} (GUID {2})
due to {3}",
+                    vm.ElementName,
+                    destination,
+                    vm.Name,
+                    ReturnCode.ToString(ret_val));
+                var ex = new WmiException(errMsg);
+                logger.Error(errMsg, ex);
+                throw ex;
+            }
+        }
+
+        /// <summary>
         /// Create new storage media resources, e.g. hard disk images and ISO disk images
         /// see http://msdn.microsoft.com/en-us/library/hh859775(v=vs.85).aspx
         /// </summary>
@@ -2081,6 +2200,26 @@ namespace HypervResource
             return result.ToArray();
         }
 
+        public StorageAllocationSettingData[] GetStorageSettings(ComputerSystem vm)
+        {
+            // ComputerSystem -> VirtualSystemSettingData -> EthernetPortAllocationSettingData
+            VirtualSystemSettingData vmSettings = GetVmSettings(vm);
+
+            var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, StorageAllocationSettingData.CreatedClassName);
+
+            // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does
not contain
+            // the virtualisation objects.
+            var wmiObjectSearch = new ManagementObjectSearcher(vmSettings.Scope, wmiObjQuery);
+            var wmiObjCollection = new StorageAllocationSettingData.StorageAllocationSettingDataCollection(wmiObjectSearch.Get());
+
+            var result = new List<StorageAllocationSettingData>(wmiObjCollection.Count);
+            foreach (StorageAllocationSettingData item in wmiObjCollection)
+            {
+                result.Add(item);
+            }
+            return result.ToArray();
+        }
+
 
         public EthernetSwitchPortVlanSettingData GetVlanSettings(EthernetPortAllocationSettingData
ethernetConnection)
         {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs
b/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs
index 8553fed..602347f 100644
--- a/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/WmiWrappers/ROOT.virtualization.v2.Msvm_StorageAllocationSettingData.cs
@@ -36,7 +36,7 @@ namespace CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2 {
         private static string CreatedWmiNamespace = "ROOT\\virtualization\\v2";
         
         // Private property to hold the name of WMI class which created this class.
-        private static string CreatedClassName = "Msvm_StorageAllocationSettingData";
+        public static string CreatedClassName = "Msvm_StorageAllocationSettingData";
         
         // Private member variable to hold the ManagementScope which is used by the various
methods.
         private static System.Management.ManagementScope statMgmtScope = null;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java
b/plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java
new file mode 100644
index 0000000..011bd12
--- /dev/null
+++ b/plugins/hypervisors/hyperv/src/org/apache/cloudstack/storage/motion/HypervStorageMotionStrategy.java
@@ -0,0 +1,179 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cloudstack.storage.motion;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.inject.Inject;
+
+import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.to.VolumeObjectTO;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.MigrateWithStorageAnswer;
+import com.cloud.agent.api.MigrateWithStorageCommand;
+import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.host.Host;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.utils.Pair;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.dao.VMInstanceDao;
+
+@Component
+public class HypervStorageMotionStrategy implements DataMotionStrategy {
+    private static final Logger s_logger = Logger.getLogger(HypervStorageMotionStrategy.class);
+    @Inject AgentManager agentMgr;
+    @Inject VolumeDao volDao;
+    @Inject VolumeDataFactory volFactory;
+    @Inject PrimaryDataStoreDao storagePoolDao;
+    @Inject VMInstanceDao instanceDao;
+
+    @Override
+    public StrategyPriority canHandle(DataObject srcData, DataObject destData) {
+        return StrategyPriority.CANT_HANDLE;
+    }
+
+    @Override
+    public StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost,
Host destHost) {
+        if (srcHost.getHypervisorType() == HypervisorType.Hyperv &&
+                destHost.getHypervisorType() == HypervisorType.Hyperv) {
+            return StrategyPriority.HYPERVISOR;
+        }
+
+        return StrategyPriority.CANT_HANDLE;
+    }
+
+    @Override
+    public Void copyAsync(DataObject srcData, DataObject destData, Host destHost, AsyncCompletionCallback<CopyCommandResult>
callback) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback<CopyCommandResult>
callback) {
+        CopyCommandResult result = new CopyCommandResult(null, null);
+        result.setResult("Unsupported operation requested for copying data.");
+        callback.complete(result);
+
+        return null;
+    }
+
+    @Override
+    public Void copyAsync(Map<VolumeInfo, DataStore> volumeMap, VirtualMachineTO vmTo,
Host srcHost, Host destHost,
+            AsyncCompletionCallback<CopyCommandResult> callback) {
+        Answer answer = null;
+        String errMsg = null;
+        try {
+            VMInstanceVO instance = instanceDao.findById(vmTo.getId());
+            if (instance != null) {
+                answer = migrateVmWithVolumes(instance, vmTo, srcHost, destHost, volumeMap);
+            } else {
+                throw new CloudRuntimeException("Unsupported operation requested for moving
data.");
+            }
+        } catch (Exception e) {
+            s_logger.error("copy failed", e);
+            errMsg = e.toString();
+        }
+
+        CopyCommandResult result = new CopyCommandResult(null, answer);
+        result.setResult(errMsg);
+        callback.complete(result);
+        return null;
+    }
+
+    private Answer migrateVmWithVolumes(VMInstanceVO vm, VirtualMachineTO to, Host srcHost,
+            Host destHost, Map<VolumeInfo, DataStore> volumeToPool) throws AgentUnavailableException
{
+
+        // Initiate migration of a virtual machine with it's volumes.
+        try {
+            List<Pair<VolumeTO, StorageFilerTO>> volumeToFilerto = new ArrayList<Pair<VolumeTO,
StorageFilerTO>>();
+            for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet())
{
+                VolumeInfo volume = entry.getKey();
+                VolumeTO volumeTo = new VolumeTO(volume, storagePoolDao.findById(volume.getPoolId()));
+                StorageFilerTO filerTo = new StorageFilerTO((StoragePool)entry.getValue());
+                volumeToFilerto.add(new Pair<VolumeTO, StorageFilerTO>(volumeTo, filerTo));
+            }
+
+            MigrateWithStorageCommand command = new MigrateWithStorageCommand(to, volumeToFilerto,
destHost.getPrivateIpAddress());
+            MigrateWithStorageAnswer answer = (MigrateWithStorageAnswer) agentMgr.send(srcHost.getId(),
command);
+            if (answer == null) {
+                s_logger.error("Migration with storage of vm " + vm + " failed.");
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + "
to host " + destHost);
+            } else if (!answer.getResult()) {
+                s_logger.error("Migration with storage of vm " + vm+ " failed. Details: "
+ answer.getDetails());
+                throw new CloudRuntimeException("Error while migrating the vm " + vm + "
to host " + destHost +
+                        ". " + answer.getDetails());
+            } else {
+                // Update the volume details after migration.
+                updateVolumePathsAfterMigration(volumeToPool, answer.getVolumeTos());
+            }
+
+            return answer;
+        } catch (OperationTimedoutException e) {
+            s_logger.error("Error while migrating vm " + vm + " to host " + destHost, e);
+            throw new AgentUnavailableException("Operation timed out on storage motion for
" + vm, destHost.getId());
+        }
+    }
+
+    private void updateVolumePathsAfterMigration(Map<VolumeInfo, DataStore> volumeToPool,
List<VolumeObjectTO> volumeTos) {
+        for (Map.Entry<VolumeInfo, DataStore> entry : volumeToPool.entrySet()) {
+            boolean updated = false;
+            VolumeInfo volume = entry.getKey();
+            StoragePool pool = (StoragePool)entry.getValue();
+            for (VolumeObjectTO volumeTo : volumeTos) {
+                if (volume.getId() == volumeTo.getId()) {
+                    VolumeVO volumeVO = volDao.findById(volume.getId());
+                    Long oldPoolId = volumeVO.getPoolId();
+                    volumeVO.setPath(volumeTo.getPath());
+                    volumeVO.setFolder(pool.getPath());
+                    volumeVO.setPodId(pool.getPodId());
+                    volumeVO.setPoolId(pool.getId());
+                    volumeVO.setLastPoolId(oldPoolId);
+                    volDao.update(volume.getId(), volumeVO);
+                    updated = true;
+                    break;
+                }
+            }
+
+            if (!updated) {
+                s_logger.error("Volume path wasn't updated for volume " + volume + " after
it was migrated.");
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 0e4fb5e..393613a 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -3996,7 +3996,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager,
Vir
         }
 
         if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware)
&& !vm.getHypervisorType().equals(HypervisorType.KVM)
-                && !vm.getHypervisorType().equals(HypervisorType.Ovm) &&
!vm.getHypervisorType().equals(HypervisorType.Simulator)) {
+                && !vm.getHypervisorType().equals(HypervisorType.Ovm) &&
!vm.getHypervisorType().equals(HypervisorType.Hyperv)
+                && !vm.getHypervisorType().equals(HypervisorType.Simulator)) {
             throw new InvalidParameterValueException("Unsupported hypervisor type for vm
migration, we support" + " XenServer/VMware/KVM only");
         }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2aff39f8/setup/db/db/schema-430to440.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-430to440.sql b/setup/db/db/schema-430to440.sql
index 9d41fe9..51bce2d 100644
--- a/setup/db/db/schema-430to440.sql
+++ b/setup/db/db/schema-430to440.sql
@@ -26,6 +26,7 @@ ALTER TABLE `cloud`.`disk_offering` ADD `cache_mode` VARCHAR( 16 ) DEFAULT
'none
 
 UPDATE `cloud`.`hypervisor_capabilities` set max_guests_limit='150' WHERE hypervisor_version='6.1.0';
 UPDATE `cloud`.`hypervisor_capabilities` set max_guests_limit='500' WHERE hypervisor_version='6.2.0';
+UPDATE `cloud`.`hypervisor_capabilities` set storage_motion_supported='1' WHERE hypervisor_version='6.2'
AND hypervisor_type="Hyperv";
 
 DROP VIEW IF EXISTS `cloud`.`disk_offering_view`;
 CREATE VIEW `cloud`.`disk_offering_view` AS


Mime
View raw message