You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by de...@apache.org on 2014/03/11 21:53:41 UTC
git commit: updated refs/heads/hyperv-storagemotion to 5b4fa7d
Repository: cloudstack
Updated Branches:
refs/heads/hyperv-storagemotion [created] 5b4fa7dc9
CLOUDSTACK-6143: Storage live migration support for hyper-v.
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/5b4fa7dc
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/5b4fa7dc
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/5b4fa7dc
Branch: refs/heads/hyperv-storagemotion
Commit: 5b4fa7dc9a141ed77c47097d7de64f3c1c4acffa
Parents: 36558e4
Author: Devdeep Singh <de...@gmail.com>
Authored: Wed Mar 12 02:22:22 2014 +0530
Committer: Devdeep Singh <de...@gmail.com>
Committed: Wed Mar 12 02:22:22 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 +
.../HypervResource/CloudStackTypes.cs | 4 +
.../HypervResource/HypervResourceController.cs | 107 +++++++++++
.../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 +-
12 files changed, 464 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5b4fa7dc/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/5b4fa7dc/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/5b4fa7dc/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/5b4fa7dc/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/5b4fa7dc/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/5b4fa7dc/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..caa3a0f 100644
--- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs
@@ -769,6 +769,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 +855,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/5b4fa7dc/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..c275913 100644
--- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
+++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs
@@ -1990,6 +1990,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)]
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/5b4fa7dc/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/5b4fa7dc/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/5b4fa7dc/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/5b4fa7dc/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..8544dd3
--- /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();
+ 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/5b4fa7dc/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 be00aa8..edc33f7 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -4175,7 +4175,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");
}