From b86d45b0036482cfc9c9fc55f62954a1b9599188 Mon Sep 17 00:00:00 2001 From: Anshul Gangwar Date: Mon, 10 Feb 2014 09:41:41 -0800 Subject: [PATCH] CLOUDSTACK-6054: Changes for making vmsync work for hyper-v. Made changes to PingCommand and StartupCommand to return the state of all vms on the host. --- .../HypervResource/CloudStackTypes.cs | 1 + .../HypervResourceController.cs | 76 ++++++++++++++----- .../HypervResource/IWmiCallsV2.cs | 1 + .../HypervResource/WmiCallsV2.cs | 25 ++++++ .../resource/HypervDirectConnectResource.java | 51 ++++++++++++- 5 files changed, 131 insertions(+), 23 deletions(-) diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index c2421efb7e6..2f5be7f04a6 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -918,5 +918,6 @@ namespace HypervResource public const string DeleteCommand = "org.apache.cloudstack.storage.command.DeleteCommand"; public const string DettachAnswer = "org.apache.cloudstack.storage.command.DettachAnswer"; public const string DettachCommand = "org.apache.cloudstack.storage.command.DettachCommand"; + public const string HostVmStateReportCommand = "org.apache.cloudstack.HostVmStateReportCommand"; } } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs index 718ef0504eb..5ba78fe5a49 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -2103,42 +2103,76 @@ namespace HypervResource } } - // POST api/HypervResource/GetVncPortCommand + // POST api/HypervResource/GetVncPortCommand + [HttpPost] + [ActionName(CloudStackTypes.GetVncPortCommand)] + public JContainer GetVncPortCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.GetVncPortCommand + cmd.ToString()); + + string details = null; + bool result = false; + string address = null; + int port = -9; + + try + { + string vmName = (string)cmd.name; + var sys = wmiCallsV2.GetComputerSystem(vmName); + address = "instanceId=" + sys.Name ; + result = true; + } + catch (Exception sysEx) + { + details = CloudStackTypes.GetVncPortAnswer + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + object ansContent = new + { + result = result, + details = details, + address = address, + port = port + }; + + return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVncPortAnswer); + } + } + + // POST api/HypervResource/HostVmStateReportCommand [HttpPost] - [ActionName(CloudStackTypes.GetVncPortCommand)] - public JContainer GetVncPortCommand([FromBody]dynamic cmd) + [ActionName(CloudStackTypes.HostVmStateReportCommand)] + public JContainer HostVmStateReportCommand([FromBody]dynamic cmd) { using (log4net.NDC.Push(Guid.NewGuid().ToString())) { - logger.Info(CloudStackTypes.GetVncPortCommand + cmd.ToString()); + logger.Info(CloudStackTypes.HostVmStateReportCommand + cmd.ToString()); string details = null; - bool result = false; - string address = null; - int port = -9; + Dictionary[] hostVmStateReport = null; try { - string vmName = (string)cmd.name; - var sys = wmiCallsV2.GetComputerSystem(vmName); - address = "instanceId=" + sys.Name ; - result = true; + var vmCollection = wmiCallsV2.GetComputerSystemCollection(); + hostVmStateReport = new Dictionary[vmCollection.Count]; + int i = 0; + foreach (ComputerSystem vm in vmCollection) + { + var dict = new Dictionary(); + dict.Add(vm.ElementName, EnabledState.ToCloudStackPowerState(vm.EnabledState)); + hostVmStateReport[i++] = dict; + } } catch (Exception sysEx) { - details = CloudStackTypes.GetVncPortAnswer + " failed due to " + sysEx.Message; + details = CloudStackTypes.HostVmStateReportCommand + " failed due to " + sysEx.Message; logger.Error(details, sysEx); } - object ansContent = new - { - result = result, - details = details, - address = address, - port = port - }; - - return ReturnCloudStackTypedJArray(ansContent, CloudStackTypes.GetVncPortAnswer); + return JArray.FromObject(hostVmStateReport); } } diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs index 9042d7c5e12..da6ad838757 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs @@ -42,6 +42,7 @@ namespace HypervResource void MigrateVm(string vmName, string destination); void DetachDisk(string displayName, string diskFileName); ComputerSystem GetComputerSystem(string displayName); + ComputerSystem.ComputerSystemCollection GetComputerSystemCollection(); string GetDefaultDataRoot(); string GetDefaultVirtualDiskFolder(); ResourceAllocationSettingData GetDvdDriveSettings(VirtualSystemSettingData vmSettings); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs index dde6e214f09..d2b9ce19760 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -1874,6 +1874,12 @@ namespace HypervResource return null; } + public ComputerSystem.ComputerSystemCollection GetComputerSystemCollection() + { + var wmiQuery = String.Format("Caption=\"Virtual Machine\""); + return ComputerSystem.GetInstances(wmiQuery); + } + public Dictionary GetVmSync(String privateIpAddress) { List vms = GetVmElementNames(); @@ -2474,5 +2480,24 @@ namespace HypervResource } return result; } + + public static string ToCloudStackPowerState(UInt16 value) + { + string result = "Unknown"; + switch (value) + { + case Enabled: result = "PowerOn"; break; + case Disabled: result = "PowerOff"; break; + case Paused: result = "PowerUnknown"; break; + case Suspended: result = "PowerUnknown"; break; + case Starting: result = "PowerOn"; break; + case Snapshotting: result = "PowerUnknown"; break; // NOT used + case Saving: result = "PowerOn"; break; + case Stopping: result = "PowerOff"; break; + case Pausing: result = "PowerUnknown"; break; + case Resuming: result = "PowerOn"; break; + } + return result; + } } } diff --git a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java index 2f645906317..c79373a0eee 100644 --- a/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java +++ b/plugins/hypervisors/hyperv/src/com/cloud/hypervisor/hyperv/resource/HypervDirectConnectResource.java @@ -62,6 +62,7 @@ import org.apache.http.util.EntityUtils; import org.apache.log4j.Logger; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import com.cloud.agent.api.Answer; import com.cloud.agent.api.CheckRouterAnswer; @@ -139,6 +140,7 @@ import com.cloud.utils.StringUtils; import com.cloud.utils.net.NetUtils; import com.cloud.utils.ssh.SshHelper; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.PowerState; import com.cloud.vm.VirtualMachineName; @@ -148,6 +150,7 @@ import com.cloud.vm.VirtualMachineName; @Local(value = ServerResource.class) public class HypervDirectConnectResource extends ServerResourceBase implements ServerResource { public static final int DEFAULT_AGENT_PORT = 8250; + public static final String HOST_VM_STATE_REPORT_COMMAND = "org.apache.cloudstack.HostVmStateReportCommand"; private static final Logger s_logger = Logger.getLogger(HypervDirectConnectResource.class.getName()); private static final Gson s_gson = GsonHelper.getGson(); @@ -209,6 +212,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S defaultStartRoutCmd.setPrivateIpAddress(_agentIp); defaultStartRoutCmd.setStorageIpAddress(_agentIp); defaultStartRoutCmd.setPool(_clusterGuid); + defaultStartRoutCmd.setHostVmStateReport(getHostVmStateReport()); s_logger.debug("Generated StartupRoutingCommand for _agentIp \"" + _agentIp + "\""); @@ -300,8 +304,7 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S @Override public final PingCommand getCurrentStatus(final long id) { - // TODO, need to report VM states on host - PingCommand pingCmd = new PingRoutingCommand(getType(), id, null, null); + PingCommand pingCmd = new PingRoutingCommand(getType(), id, null, getHostVmStateReport()); if (s_logger.isDebugEnabled()) { s_logger.debug("Ping host " + _name + " (IP " + _agentIp + ")"); @@ -316,6 +319,50 @@ public class HypervDirectConnectResource extends ServerResourceBase implements S return pingCmd; } + public final ArrayList> requestHostVmStateReport() { + URI agentUri = null; + try { + agentUri = new URI("https", null, _agentIp, _port, "/api/HypervResource/" + HOST_VM_STATE_REPORT_COMMAND, null, null); + } catch (URISyntaxException e) { + String errMsg = "Could not generate URI for Hyper-V agent"; + s_logger.error(errMsg, e); + return null; + } + String incomingCmd = postHttpRequest("{}", agentUri); + + if (incomingCmd == null) { + return null; + } + ArrayList> result = null; + try { + result = s_gson.fromJson(incomingCmd, new TypeToken>>() { + }.getType()); + } catch (Exception ex) { + String errMsg = "Failed to deserialize Command[] " + incomingCmd; + s_logger.error(errMsg, ex); + } + s_logger.debug("HostVmStateReportCommand received response " + + s_gson.toJson(result)); + if (!result.isEmpty()) { + return result; + } + return null; + } + + protected HashMap getHostVmStateReport() { + final HashMap vmStates = new HashMap(); + ArrayList> vmList = requestHostVmStateReport(); + if (vmList == null || vmList.isEmpty()) { + return null; + } + + for (Map vmMap : vmList) { + String name = (String)vmMap.keySet().toArray()[0]; + vmStates.put(name, new HostVmStateReportEntry(PowerState.valueOf(vmMap.get(name)), _guid, null)); + } + return vmStates; + } + // TODO: Is it valid to return NULL, or should we throw on error? // Returns StartupCommand with fields revised with values known only to the // host