diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs index 96379944d9f..889ed29ce74 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/CloudStackTypes.cs @@ -900,5 +900,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 a1326064694..5db58e698f9 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -2042,6 +2042,40 @@ namespace HypervResource } } + // POST api/HypervResource/HostVmStateReportCommand + [HttpPost] + [ActionName(CloudStackTypes.HostVmStateReportCommand)] + public JContainer HostVmStateReportCommand([FromBody]dynamic cmd) + { + using (log4net.NDC.Push(Guid.NewGuid().ToString())) + { + logger.Info(CloudStackTypes.HostVmStateReportCommand + cmd.ToString()); + + string details = null; + Dictionary[] hostVmStateReport = null; + + try + { + 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.HostVmStateReportCommand + " failed due to " + sysEx.Message; + logger.Error(details, sysEx); + } + + return JArray.FromObject(hostVmStateReport); + } + } + public static System.Net.NetworkInformation.NetworkInterface GetNicInfoFromIpAddress(string ipAddress, out string subnet) { System.Net.NetworkInformation.NetworkInterface[] nics = System.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces(); diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/IWmiCallsV2.cs index 5f814c59535..0fe6a754139 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 f5a04dce847..991ef558272 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -1812,6 +1812,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(); @@ -2412,5 +2418,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 a4dca6a3fa7..0904d5c5bda 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; @@ -132,6 +133,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; /** * Implementation of dummy resource to be returned from discoverer. @@ -139,6 +141,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(); @@ -206,6 +209,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 + "\""); @@ -328,8 +332,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 + ")"); @@ -345,6 +348,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