Add ability to pass kvp data via the key cloudstack-vm-userdata
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/53fd4e8c Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/53fd4e8c Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/53fd4e8c Branch: refs/heads/hyperv Commit: 53fd4e8c7244d8db148fd248613b748e422b8639 Parents: 1d0a931 Author: Devdeep Singh <devd...@gmail.com> Authored: Thu Oct 10 18:11:58 2013 +0530 Committer: Devdeep Singh <devd...@gmail.com> Committed: Thu Oct 10 18:11:58 2013 +0530 ---------------------------------------------------------------------- .../AgentShell/AgentSettings.Designer.cs | 12 + .../AgentShell/AgentSettings.settings | 11 +- .../HypervResource/HypervResource.csproj | 3 +- .../HypervResource/HypervResourceController.cs | 4 +- .../ServerResource/HypervResource/WmiCalls.cs | 219 -- .../ServerResource/HypervResource/WmiCallsV2.cs | 501 +++++ .../HypervResourceControllerTest.cs | 84 + ...OOT.virtualization.v2.Msvm_ComputerSystem.cs | 1800 ++++++++++++++++ .../ROOT.virtualization.v2.Msvm_ConcreteJob.cs | 1826 +++++++++++++++++ ...rtualization.v2.Msvm_KvpExchangeComponent.cs | 1597 +++++++++++++++ ...n.v2.Msvm_KvpExchangeComponentSettingData.cs | 1037 ++++++++++ ...irtualization.v2.Msvm_KvpExchangeDataItem.cs | 653 ++++++ ...on.v2.Msvm_VirtualSystemManagementService.cs | 1916 ++++++++++++++++++ ...lization.v2.Msvm_VirtualSystemSettingData.cs | 1673 +++++++++++++++ .../WmiWrappers/WmiWrappers.csproj | 21 + 15 files changed, 11134 insertions(+), 223 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/53fd4e8c/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs index 44df706..a73e6bb 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.Designer.cs @@ -367,5 +367,17 @@ namespace CloudStack.Plugin.AgentShell { this["testCifsPath"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("CentOS64")] + public string testKvpVmName { + get { + return ((string)(this["testKvpVmName"])); + } + set { + this["testKvpVmName"] = value; + } + } } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/53fd4e8c/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings index 0d54f44..435b8e0 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/AgentShell/AgentSettings.settings @@ -98,5 +98,14 @@ <Setting Name="dom0MinMemory" Type="System.UInt64" Scope="Application"> <Value Profile="(Default)">2048</Value> </Setting> + <Setting Name="testCifsUrl" Type="System.String" Scope="User"> + <Value Profile="(Default)">cifs://10.1.1.1/secondary?user\u003dadministrator\u0026password\u003d1pass%40word1</Value> + </Setting> + <Setting Name="testCifsPath" Type="System.String" Scope="User"> + <Value Profile="(Default)">template/tmpl/2/201/61d5316a-3cc4-30cf-a557-78691ff5c143.vhd</Value> + </Setting> + <Setting Name="testKvpVmName" Type="System.String" Scope="User"> + <Value Profile="(Default)">CentOS64</Value> + </Setting> </Settings> -</SettingsFile> \ No newline at end of file +</SettingsFile> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/53fd4e8c/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj index 48cf5a5..dbd7b15 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResource.csproj @@ -76,6 +76,7 @@ </ItemGroup> <ItemGroup> <Compile Include="CloudStackTypes.cs" /> + <Compile Include="WmiCallsV2.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="HypervResourceController.cs" /> <Compile Include="Utils.cs" /> @@ -99,4 +100,4 @@ <Target Name="AfterBuild"> </Target> --> -</Project> \ No newline at end of file +</Project> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/53fd4e8c/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 afceced..cce40e1 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/HypervResourceController.cs @@ -754,7 +754,7 @@ namespace HypervResource } } - // POST api/HypervResource/StartCommand + // POST api/HypervResource/StopCommand [HttpPost] [ActionName(CloudStackTypes.StopCommand)] public JContainer StopCommand([FromBody]dynamic cmd) @@ -1396,4 +1396,4 @@ namespace HypervResource } } } -} \ No newline at end of file +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/53fd4e8c/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCalls.cs ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCalls.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCalls.cs index 0442be3..ad43d1f 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCalls.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCalls.cs @@ -1247,223 +1247,4 @@ namespace HypervResource throw ex; } } - - public class WmiException : Exception - { - public WmiException() - { - } - - public WmiException(string message) - : base(message) - { - } - - public WmiException(string message, Exception inner) - : base(message, inner) - { - } - } - - /// <summary> - /// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx - /// </summary> - public static class ReturnCode - { - public const UInt32 Completed = 0; - public const UInt32 Started = 4096; - public const UInt32 Failed = 32768; - public const UInt32 AccessDenied = 32769; - public const UInt32 NotSupported = 32770; - public const UInt32 Unknown = 32771; - public const UInt32 Timeout = 32772; - public const UInt32 InvalidParameter = 32773; - public const UInt32 SystemInUse = 32774; - public const UInt32 InvalidState = 32775; - public const UInt32 IncorrectDataType = 32776; - public const UInt32 SystemNotAvailable = 32777; - public const UInt32 OutofMemory = 32778; - public static string ToString(UInt32 value) - { - string result = "Unknown return code"; - switch (value) - { - case Completed: result = "Completed"; break; - case Started: result = "Started"; break; - case Failed: result = "Failed"; break; - case AccessDenied: result = "AccessDenied"; break; - case NotSupported: result = "NotSupported"; break; - case Unknown: result = "Unknown"; break; - case Timeout: result = "Timeout"; break; - case InvalidParameter: result = "InvalidParameter"; break; - case SystemInUse: result = "SystemInUse"; break; - case InvalidState: result = "InvalidState"; break; - case IncorrectDataType: result = "IncorrectDataType"; break; - case SystemNotAvailable: result = "SystemNotAvailable"; break; - case OutofMemory: result = "OutofMemory"; break; - } - return result; - } - } - - /// <summary> - /// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx - /// </summary> - public static class JobState - { - public const UInt16 New = 2; - public const UInt16 Starting = 3; - public const UInt16 Running = 4; - public const UInt16 Suspended = 5; - public const UInt16 ShuttingDown = 6; - public const UInt16 Completed = 7; - public const UInt16 Terminated = 8; - public const UInt16 Killed = 9; - public const UInt16 Exception = 10; - public const UInt16 Service = 11; - public static string ToString(UInt16 value) - { - string result = "Unknown JobState code"; - switch (value) - { - case New: result = "New"; break; - case Starting: result = "Starting"; break; - case Running: result = "Running"; break; - case Suspended: result = "Suspended"; break; - case ShuttingDown: result = "ShuttingDown"; break; - case Completed: result = "Completed"; break; - case Terminated: result = "Terminated"; break; - case Killed: result = "Killed"; break; - case Exception: result = "Exception"; break; - case Service: result = "Service"; break; - } - return result; - } - } - - /// <summary> - /// http://msdn.microsoft.com/en-us/library/cc723874%28v=vs.85%29.aspx - /// </summary> - public class RequiredState - { - public const UInt16 Enabled = 2; // Turns the VM on. - public const UInt16 Disabled = 3; // Turns the VM off. - public const UInt16 Reboot = 10; // A hard reset of the VM. - public const UInt16 Reset = 11; // For future use. - public const UInt16 Paused = 32768; // Pauses the VM. - public const UInt16 Suspended = 32769; // Saves the state of the VM. - public static string ToString(UInt16 value) - { - string result = "Unknown RequiredState code"; - switch (value) - { - case Enabled: result = "Enabled"; break; - case Disabled: result = "Disabled"; break; - case Reboot: result = "Reboot"; break; - case Reset: result = "Reset"; break; - case Paused: result = "Paused"; break; - case Suspended: result = "Suspended"; break; - } - return result; - } - } - - /// <summary> - /// http://msdn.microsoft.com/en-us/library/cc136822%28v=vs.85%29.aspx - /// </summary> - public class EnabledState - { - /// <summary> - /// The state of the VM could not be determined. - /// </summary> - public const UInt16 Unknown = 0; - /// <summary> - /// The VM is running. - /// </summary> - public const UInt16 Enabled = 2; - /// <summary> - /// The VM is turned off. - /// </summary> - public const UInt16 Disabled = 3; - /// <summary> - /// The VM is paused. - /// </summary> - public const UInt16 Paused = 32768; - /// <summary> - /// The VM is in a saved state. - /// </summary> - public const UInt16 Suspended = 32769; - /// <summary> - /// The VM is starting. This is a transitional state between 3 (Disabled) - /// or 32769 (Suspended) and 2 (Enabled) initiated by a call to the - /// RequestStateChange method with a RequestedState parameter of 2 (Enabled). - /// </summary> - public const UInt16 Starting = 32770; - /// <summary> - /// Starting with Windows Server 2008 R2 this value is not supported. - /// If the VM is performing a snapshot operation, the element at index 1 - /// of the OperationalStatus property array will contain 32768 (Creating Snapshot), - /// 32769 (Applying Snapshot), or 32770 (Deleting Snapshot). - /// </summary> - public const UInt16 Snapshotting = 32771; - /// <summary> - /// The VM is saving its state. This is a transitional state between 2 (Enabled) - /// and 32769 (Suspended) initiated by a call to the RequestStateChange method - /// with a RequestedState parameter of 32769 (Suspended). - /// </summary> - public const UInt16 Saving = 32773; - /// <summary> - /// The VM is turning off. This is a transitional state between 2 (Enabled) - /// and 3 (Disabled) initiated by a call to the RequestStateChange method - /// with a RequestedState parameter of 3 (Disabled) or a guest operating system - /// initiated power off. - /// </summary> - public const UInt16 Stopping = 32774; - /// <summary> - /// The VM is pausing. This is a transitional state between 2 (Enabled) and 32768 (Paused) initiated by a call to the RequestStateChange method with a RequestedState parameter of 32768 (Paused). - /// </summary> - public const UInt16 Pausing = 32776; - /// <summary> - /// The VM is resuming from a paused state. This is a transitional state between 32768 (Paused) and 2 (Enabled). - /// </summary> - public const UInt16 Resuming = 32777; - - public static string ToString(UInt16 value) - { - string result = "Unknown"; - switch (value) - { - case Enabled: result = "Enabled"; break; - case Disabled: result = "Disabled"; break; - case Paused: result = "Paused"; break; - case Suspended: result = "Suspended"; break; - case Starting: result = "Starting"; break; - case Snapshotting: result = "Snapshotting"; break; // NOT used - case Saving: result = "Saving"; break; - case Stopping: result = "Stopping"; break; - case Pausing: result = "Pausing"; break; - case Resuming: result = "Resuming"; break; - } - return result; - } - - public static string ToCloudStackState(UInt16 value) - { - string result = "Unknown"; - switch (value) - { - case Enabled: result = "Running"; break; - case Disabled: result = "Stopped"; break; - case Paused: result = "Unknown"; break; - case Suspended: result = "Unknown"; break; - case Starting: result = "Starting"; break; - case Snapshotting: result = "Unknown"; break; // NOT used - case Saving: result = "Saving"; break; - case Stopping: result = "Stopping"; break; - case Pausing: result = "Unknown"; break; - case Resuming: result = "Starting"; break; - } - return result; - } - } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/53fd4e8c/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 new file mode 100755 index 0000000..3f2761f --- /dev/null +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/HypervResource/WmiCallsV2.cs @@ -0,0 +1,501 @@ +// 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. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using CloudStack.Plugin.WmiWrappers.ROOT.VIRTUALIZATION.V2; +using log4net; +using System.Globalization; +using System.Management; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using CloudStack.Plugin.WmiWrappers.ROOT.CIMV2; +using System.IO; + +namespace HypervResource +{ + public class WmiCallsV2 + { + public static String CloudStackUserDataKey = "cloudstack-vm-userdata"; + + public static void Initialize() + { + // Trigger assembly load into curren appdomain + } + + private static ILog logger = LogManager.GetLogger(typeof(WmiCallsV2)); + + /// <summary> + /// Returns ComputerSystem lacking any NICs and VOLUMEs + /// </summary> + public static ComputerSystem AddUserData(ComputerSystem vm, string userData) + { + // Obtain controller for Hyper-V virtualisation subsystem + VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService(); + + // Create object to hold the data. + KvpExchangeDataItem kvpItem = KvpExchangeDataItem.CreateInstance(); + kvpItem.LateBoundObject["Name"] = WmiCallsV2.CloudStackUserDataKey; + kvpItem.LateBoundObject["Data"] = userData; + kvpItem.LateBoundObject["Source"] = 0; + logger.Debug("VM " + vm.Name + " gets userdata " + userData); + + String kvpStr = kvpItem.LateBoundObject.GetText(TextFormat.CimDtd20); + + // Update the resource settings for the VM. + ManagementPath jobPath; + + uint ret_val = vmMgmtSvc.AddKvpItems(new String[] { kvpStr }, vm.Path, out jobPath); + + // If the Job is done asynchronously + if (ret_val == ReturnCode.Started) + { + JobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted", + vm.ElementName, + vm.Name, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + return vm; + } + + /// <summary> + /// Returns ComputerSystem lacking any NICs and VOLUMEs + /// </summary> + public static void DeleteHostKvpItem(ComputerSystem vm, string key) + { + // Obtain controller for Hyper-V virtualisation subsystem + VirtualSystemManagementService vmMgmtSvc = GetVirtualisationSystemManagementService(); + + // Create object to hold the data. + KvpExchangeDataItem kvpItem = KvpExchangeDataItem.CreateInstance(); + kvpItem.LateBoundObject["Name"] = WmiCallsV2.CloudStackUserDataKey; + kvpItem.LateBoundObject["Data"] = "dummy"; + kvpItem.LateBoundObject["Source"] = 0; + logger.Debug("VM " + vm.Name + " will have KVP key " + key + " removed."); + + String kvpStr = kvpItem.LateBoundObject.GetText(TextFormat.CimDtd20); + + // Update the resource settings for the VM. + ManagementPath jobPath; + + uint ret_val = vmMgmtSvc.RemoveKvpItems(new String[] { kvpStr }, vm.Path, out jobPath); + + // If the Job is done asynchronously + if (ret_val == ReturnCode.Started) + { + JobCompleted(jobPath); + } + else if (ret_val != ReturnCode.Completed) + { + var errMsg = string.Format( + "Failed to update VM {0} (GUID {1}) due to {2} (ModifyVirtualSystem call), existing VM not deleted", + vm.ElementName, + vm.Name, + ReturnCode.ToString(ret_val)); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + public static VirtualSystemManagementService GetVirtualisationSystemManagementService() + { + // VirtualSystemManagementService is a singleton, most anonymous way of lookup is by asking for the set + // of local instances, which should be size 1. + + var virtSysMgmtSvcCollection = VirtualSystemManagementService.GetInstances(); + foreach (VirtualSystemManagementService item in virtSysMgmtSvcCollection) + { + return item; + } + + var errMsg = string.Format("No Hyper-V subsystem on server"); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + /// <summary> + /// Similar to http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx + /// </summary> + /// <param name="jobPath"></param> + /// <returns></returns> + private static void JobCompleted(ManagementPath jobPath) + { + ConcreteJob jobObj = null; + for(;;) + { + jobObj = new ConcreteJob(jobPath); + if (jobObj.JobState != JobState.Starting && jobObj.JobState != JobState.Running) + { + break; + } + logger.InfoFormat("In progress... {0}% completed.", jobObj.PercentComplete); + System.Threading.Thread.Sleep(1000); + } + + if (jobObj.JobState != JobState.Completed) + { + var errMsg = string.Format( + "Hyper-V Job failed, Error Code:{0}, Description: {1}", + jobObj.ErrorCode, + jobObj.ErrorDescription); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + logger.DebugFormat("WMI job succeeded: {0}, Elapsed={1}", jobObj.Description, jobObj.ElapsedTime); + } + + public static ComputerSystem GetComputerSystem(string displayName) + { + var wmiQuery = String.Format("ElementName=\"{0}\"", displayName); + ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances(wmiQuery); + + // Return the first one + foreach (ComputerSystem vm in vmCollection) + { + return vm; + } + return null; + } + + public static List<string> GetVmElementNames() + { + List<string> result = new List<string>(); + ComputerSystem.ComputerSystemCollection vmCollection = ComputerSystem.GetInstances(); + + // Return the first one + foreach (ComputerSystem vm in vmCollection) + { + if (vm.Caption.StartsWith("Hosting Computer System") ) + { + continue; + } + result.Add(vm.ElementName); + } + return result; + } + + public static VirtualSystemSettingData GetVmSettings(ComputerSystem vm) + { + // An ASSOCIATOR object provides the cross reference from the ComputerSettings and the + // VirtualSystemSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method. + // Instead, we use the System.Management to code the equivalant of + // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vm.path, resultclassName); + // + var wmiObjQuery = new RelatedObjectQuery(vm.Path.Path, VirtualSystemSettingData.CreatedClassName); + + // NB: default scope of ManagementObjectSearcher is '\\.\root\cimv2', which does not contain + // the virtualisation objects. + var wmiObjectSearch = new ManagementObjectSearcher(vm.Scope, wmiObjQuery); + var wmiObjCollection = new VirtualSystemSettingData.VirtualSystemSettingDataCollection(wmiObjectSearch.Get()); + + // When snapshots are taken into account, there can be multiple settings objects + // take the first one that isn't a snapshot + foreach (VirtualSystemSettingData wmiObj in wmiObjCollection) + { + if (wmiObj.VirtualSystemType == "Microsoft:Hyper-V:System:Realized" || + wmiObj.VirtualSystemType == "Microsoft:Hyper-V:System:Planned") + { + return wmiObj; + } + } + + var errMsg = string.Format("No VirtualSystemSettingData for VM {0}, path {1}", vm.ElementName, vm.Path.Path); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + + public static KvpExchangeComponentSettingData GetKvpSettings(VirtualSystemSettingData vmSettings) + { + // An ASSOCIATOR object provides the cross reference from the VirtualSystemSettingData and the + // KvpExchangeComponentSettingData, but generated wrappers do not expose a ASSOCIATOR OF query as a method. + // Instead, we use the System.Management to code the equivalant of + // string query = string.Format( "ASSOCIATORS OF {{{0}}} WHERE ResultClass = {1}", vmSettings.path, resultclassName); + // + var wmiObjQuery = new RelatedObjectQuery(vmSettings.Path.Path, KvpExchangeComponentSettingData.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 KvpExchangeComponentSettingData.KvpExchangeComponentSettingDataCollection(wmiObjectSearch.Get()); + + foreach (KvpExchangeComponentSettingData wmiObj in wmiObjCollection) + { + return wmiObj; + } + + var errMsg = string.Format("No KvpExchangeComponentSettingData in VirtualSystemSettingData {0}", vmSettings.Path.Path); + var ex = new WmiException(errMsg); + logger.Error(errMsg, ex); + throw ex; + } + } + + public class WmiException : Exception + { + public WmiException() + { + } + + public WmiException(string message) + : base(message) + { + } + + public WmiException(string message, Exception inner) + : base(message, inner) + { + } + } + + /// <summary> + /// Covers V2 API, see + /// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx + /// </summary> + public static class ReturnCode + { + public const UInt32 Completed = 0; + public const UInt32 Started = 4096; + public const UInt32 Failed = 32768; + public const UInt32 AccessDenied = 32769; + public const UInt32 NotSupported = 32770; + public const UInt32 Unknown = 32771; + public const UInt32 Timeout = 32772; + public const UInt32 InvalidParameter = 32773; + public const UInt32 SystemInUse = 32774; + public const UInt32 InvalidState = 32775; + public const UInt32 IncorrectDataType = 32776; + public const UInt32 SystemNotAvailable = 32777; + public const UInt32 OutofMemory = 32778; + public static string ToString(UInt32 value) + { + string result = "Unknown return code"; + switch (value) + { + case Completed: result = "Completed"; break; + case Started: result = "Started"; break; + case Failed: result = "Failed"; break; + case AccessDenied: result = "AccessDenied"; break; + case NotSupported: result = "NotSupported"; break; + case Unknown: result = "Unknown"; break; + case Timeout: result = "Timeout"; break; + case InvalidParameter: result = "InvalidParameter"; break; + case SystemInUse: result = "SystemInUse"; break; + case InvalidState: result = "InvalidState"; break; + case IncorrectDataType: result = "IncorrectDataType"; break; + case SystemNotAvailable: result = "SystemNotAvailable"; break; + case OutofMemory: result = "OutofMemory"; break; + } + return result; + } + } + + /// <summary> + /// Covers V2 API, see + /// http://msdn.microsoft.com/en-us/library/hh850031%28v=vs.85%29.aspx + /// </summary> + public static class JobState + { + public const UInt16 New = 2; + public const UInt16 Starting = 3; + public const UInt16 Running = 4; + public const UInt16 Suspended = 5; + public const UInt16 ShuttingDown = 6; + public const UInt16 Completed = 7; + public const UInt16 Terminated = 8; + public const UInt16 Killed = 9; + public const UInt16 Exception = 10; + public const UInt16 Service = 11; + public static string ToString(UInt16 value) + { + string result = "Unknown JobState code"; + switch (value) + { + case New: result = "New"; break; + case Starting: result = "Starting"; break; + case Running: result = "Running"; break; + case Suspended: result = "Suspended"; break; + case ShuttingDown: result = "ShuttingDown"; break; + case Completed: result = "Completed"; break; + case Terminated: result = "Terminated"; break; + case Killed: result = "Killed"; break; + case Exception: result = "Exception"; break; + case Service: result = "Service"; break; + } + return result; + } + } + + /// <summary> + /// V2 API (see http://msdn.microsoft.com/en-us/library/hh850279(v=vs.85).aspx) + /// has removed 'Paused' and 'Suspended' as compared to the + /// V1 API (see http://msdn.microsoft.com/en-us/library/cc723874%28v=vs.85%29.aspx) + /// However, Paused and Suspended appear on the VM state transition table + /// (see http://msdn.microsoft.com/en-us/library/hh850116(v=vs.85).aspx#methods) + /// </summary> + public class RequiredState + { + public const UInt16 Enabled = 2; // Turns the VM on. + public const UInt16 Disabled = 3; // Turns the VM off. + public const UInt16 ShutDown = 4; + public const UInt16 Offline = 6; + public const UInt16 Test = 7; + public const UInt16 Defer = 8; + public const UInt16 Quiesce = 9; + public const UInt16 Reboot = 10; // A hard reset of the VM. + public const UInt16 Reset = 11; // For future use. + public const UInt16 Paused = 32768; // Pauses the VM. + public const UInt16 Suspended = 32769; // Saves the state of the VM. + + public static string ToString(UInt16 value) + { + string result = "Unknown RequiredState code"; + switch (value) + { + case Enabled: result = "Enabled"; break; + case Disabled: result = "Disabled"; break; + case ShutDown: result = "ShutDown"; break; + case Offline: result = "Offline"; break; + case Test: result = "Test"; break; + case Defer: result = "Defer"; break; + case Quiesce: result = "Quiesce"; break; + case Reboot: result = "Reboot"; break; + case Reset: result = "Reset"; break; + } + return result; + } + } + + /// <summary> + /// V2 API specifies the states below in its state transition graph at + /// http://msdn.microsoft.com/en-us/library/hh850116(v=vs.85).aspx#methods + /// However, the CIM standard has additional possibilities based on the description + /// of EnabledState. + /// The previous V1 API is described by + /// http://msdn.microsoft.com/en-us/library/cc136822%28v=vs.85%29.aspx + /// </summary> + public class EnabledState + { + /// <summary> + /// The state of the VM could not be determined. + /// </summary> + public const UInt16 Unknown = 0; + /// <summary> + /// The VM is running. + /// </summary> + public const UInt16 Enabled = 2; + /// <summary> + /// The VM is turned off. + /// </summary> + public const UInt16 Disabled = 3; + /// <summary> + /// The VM is paused. + /// </summary> + public const UInt16 Paused = 32768; + /// <summary> + /// The VM is in a saved state. + /// </summary> + public const UInt16 Suspended = 32769; + /// <summary> + /// The VM is starting. This is a transitional state between 3 (Disabled) + /// or 32769 (Suspended) and 2 (Enabled) initiated by a call to the + /// RequestStateChange method with a RequestedState parameter of 2 (Enabled). + /// </summary> + public const UInt16 Starting = 32770; + /// <summary> + /// Starting with Windows Server 2008 R2 this value is not supported. + /// If the VM is performing a snapshot operation, the element at index 1 + /// of the OperationalStatus property array will contain 32768 (Creating Snapshot), + /// 32769 (Applying Snapshot), or 32770 (Deleting Snapshot). + /// </summary> + public const UInt16 Snapshotting = 32771; + /// <summary> + /// The VM is saving its state. This is a transitional state between 2 (Enabled) + /// and 32769 (Suspended) initiated by a call to the RequestStateChange method + /// with a RequestedState parameter of 32769 (Suspended). + /// </summary> + public const UInt16 Saving = 32773; + /// <summary> + /// The VM is turning off. This is a transitional state between 2 (Enabled) + /// and 3 (Disabled) initiated by a call to the RequestStateChange method + /// with a RequestedState parameter of 3 (Disabled) or a guest operating system + /// initiated power off. + /// </summary> + public const UInt16 Stopping = 32774; + /// <summary> + /// The VM is pausing. This is a transitional state between 2 (Enabled) and 32768 (Paused) initiated by a call to the RequestStateChange method with a RequestedState parameter of 32768 (Paused). + /// </summary> + public const UInt16 Pausing = 32776; + /// <summary> + /// The VM is resuming from a paused state. This is a transitional state between 32768 (Paused) and 2 (Enabled). + /// </summary> + public const UInt16 Resuming = 32777; + + public static string ToString(UInt16 value) + { + string result = "Unknown"; + switch (value) + { + case Enabled: result = "Enabled"; break; + case Disabled: result = "Disabled"; break; + case Paused: result = "Paused"; break; + case Suspended: result = "Suspended"; break; + case Starting: result = "Starting"; break; + case Snapshotting: result = "Snapshotting"; break; // NOT used + case Saving: result = "Saving"; break; + case Stopping: result = "Stopping"; break; + case Pausing: result = "Pausing"; break; + case Resuming: result = "Resuming"; break; + } + return result; + } + + public static string ToCloudStackState(UInt16 value) + { + string result = "Unknown"; + switch (value) + { + case Enabled: result = "Running"; break; + case Disabled: result = "Stopped"; break; + case Paused: result = "Unknown"; break; + case Suspended: result = "Unknown"; break; + case Starting: result = "Starting"; break; + case Snapshotting: result = "Unknown"; break; // NOT used + case Saving: result = "Saving"; break; + case Stopping: result = "Stopping"; break; + case Pausing: result = "Unknown"; break; + case Resuming: result = "Starting"; break; + } + return result; + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/53fd4e8c/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceControllerTest.cs ---------------------------------------------------------------------- diff --git a/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceControllerTest.cs b/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceControllerTest.cs index 7513311..7d7f627 100644 --- a/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceControllerTest.cs +++ b/plugins/hypervisors/hyperv/DotNet/ServerResource/ServerResource.Tests/HypervResourceControllerTest.cs @@ -25,6 +25,7 @@ using log4net; using HypervResource; using CloudStack.Plugin.AgentShell; using System.Collections.Generic; +using System.Xml; namespace ServerResource.Tests { @@ -761,6 +762,89 @@ namespace ServerResource.Tests } [TestMethod] + public void TestPassingUserdataToVm() + { + // Sample data + String key = "cloudstack-vm-userdata"; + String value = "username=root;password=1pass@word1"; + + // Find the VM + List<String> vmNames = WmiCallsV2.GetVmElementNames(); + + // Get associated WMI object + var vm = WmiCallsV2.GetComputerSystem(AgentSettings.Default.testKvpVmName); + + // Get existing KVP + var vmSettings = WmiCallsV2.GetVmSettings(vm); + var kvpInfo = WmiCallsV2.GetKvpSettings(vmSettings); + + // HostExchangesItems are embedded objects in the sense that the object value is stored and not a reference to the object. + string[] kvpProps = kvpInfo.HostExchangeItems; + + // If the value already exists, delete it + foreach (var item in kvpProps) + { + String wmiObjectXml = item; + String existingKey; + String existingValue; + ParseKVP(wmiObjectXml, out existingKey, out existingValue); + + if (existingKey == key) + { + WmiCallsV2.DeleteHostKvpItem(vm, existingKey); + break; + } + } + + // Add new user data + WmiCallsV2.AddUserData(vm, value); + + // Verify key added to subsystem + kvpInfo = WmiCallsV2.GetKvpSettings(vmSettings); + + // HostExchangesItems are embedded objects in the sense that the object value is stored and not a reference to the object. + kvpProps = kvpInfo.HostExchangeItems; + + // If the value already exists, delete it + bool userDataInPlace = false; + foreach (var item in kvpProps) + { + String wmiObjectXml = item; + String existingKey; + String existingValue; + ParseKVP(wmiObjectXml, out existingKey, out existingValue); + + if (existingKey == key && existingValue == value) + { + WmiCallsV2.DeleteHostKvpItem(vm, existingKey); + userDataInPlace = true; + break; + } + } + + Assert.IsTrue(userDataInPlace, "User data key / value did no save properly"); + } + + private static void ParseKVP(String wmiObjectXml, out String existingKey, out String existingValue) + { + // Reference: http://blogs.msdn.com/b/virtual_pc_guy/archive/2008/12/05/enumerating-parent-kvp-data.aspx + + // Create XML parser + var xmlDoc = new XmlDocument(); + + // Load WMI object + xmlDoc.LoadXml(wmiObjectXml); + + // Use xpath to get name and value + var namePropXpath = "/INSTANCE/PROPERTY[@NAME='Name']/VALUE"; + var nameNode = xmlDoc.SelectSingleNode(namePropXpath); + existingKey = nameNode.InnerText; + var dataPropXpath = "/INSTANCE/PROPERTY[@NAME='Data']/VALUE"; + var dataNode = xmlDoc.SelectSingleNode(dataPropXpath); + existingValue = dataNode.InnerText; + } + + [TestMethod] public void GetVmStatsCommandFail() { // Use WMI to find existing VMs