Common utilities for the virtualization samples (V2)
The following utilities are used by some of the C# virtualization samples.
namespace Microsoft.Samples.HyperV.Common
{
using System;
using System.Globalization;
using System.Management;
using System.Threading;
using System.IO;
using System.Xml;
using System.Collections.Generic;
enum JobState
{
New = 2,
Starting = 3,
Running = 4,
Suspended = 5,
ShuttingDown = 6,
Completed = 7,
Terminated = 8,
Killed = 9,
Exception = 10,
CompletedWithWarnings = 32768
}
public static class VirtualSystemTypeNames
{
public const string RealizedVM = "Microsoft:Hyper-V:System:Realized";
public const string PlannedVM = "Microsoft:Hyper-V:System:Planned";
public const string RealizedSnapshot = "Microsoft:Hyper-V:Snapshot:Realized";
public const string RecoverySnapshot = "Microsoft:Hyper-V:Snapshot:Recovery";
public const string PlannedSnapshot = "Microsoft:Hyper-V:Snapshot:Planned";
public const string MissingSnapshot = "Microsoft:Hyper-V:Snapshot:Missing";
public const string ReplicaStandardRecoverySnapshot = "Microsoft:Hyper-V:Snapshot:Replica:Standard";
public const string ReplicaApplicationConsistentRecoverySnapshot = "Microsoft:Hyper-V:Snapshot:Replica:ApplicationConsistent";
public const string ReplicaPlannedRecoverySnapshot = "Microsoft:Hyper-V:Snapshot:Replica:PlannedFailover";
public const string ReplicaSettings = "Microsoft:Hyper-V:Replica";
}
public static class WmiUtilities
{
/// <summary>
/// Validates the output parameters of a method call and prints errors, if any.
/// </summary>
/// <param name="outputParameters">The output parameters of a WMI method call.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns><c>true</c> if successful and not firing an alert; otherwise, <c>false</c>.</returns>
public static bool
ValidateOutput(
ManagementBaseObject outputParameters,
ManagementScope scope)
{
return ValidateOutput(outputParameters, scope, true, false);
}
/// <summary>
/// Validates the output parameters of a method call and prints errors, if any.
/// </summary>
/// <param name="outputParameters">The output parameters of a WMI method call.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <param name="throwIfFailed"> If true, the method throws on failure.</param>
/// <param name="printErrors">If true, Msvm_Error messages are displayed.</param>
/// <returns><c>true</c> if successful and not firing an alert; otherwise, <c>false</c>.</returns>
public static bool
ValidateOutput(
ManagementBaseObject outputParameters,
ManagementScope scope,
bool throwIfFailed,
bool printErrors)
{
bool succeeded = true;
string errorMessage = "The method call failed.";
if ((uint)outputParameters["ReturnValue"] == 4096)
{
//
// The method invoked an asynchronous operation. Get the Job object
// and wait for it to complete. Then we can check its result.
//
using (ManagementObject job = new ManagementObject((string)outputParameters["Job"]))
{
job.Scope = scope;
while (!IsJobComplete(job["JobState"]))
{
Thread.Sleep(TimeSpan.FromSeconds(1));
//
// ManagementObjects are offline objects. Call Get() on the object to have its
// current property state.
//
job.Get();
}
if (!IsJobSuccessful(job["JobState"]))
{
succeeded = false;
//
// In some cases the Job object can contain helpful information about
// why the method call failed. If it did contain such information,
// use it instead of a generic message.
//
if (!string.IsNullOrEmpty((string)job["ErrorDescription"]))
{
errorMessage = (string)job["ErrorDescription"];
}
if (printErrors)
{
PrintMsvmErrors(job);
}
if (throwIfFailed)
{
throw new ManagementException(errorMessage);
}
}
}
}
else if ((uint)outputParameters["ReturnValue"] != 0)
{
succeeded = false;
if (throwIfFailed)
{
throw new ManagementException(errorMessage);
}
}
return succeeded;
}
/// <summary>
/// Prints the relevant message from embedded instances of Msvm_Error.
/// </summary>
/// <param name="job">The job from which errors are to be printed.</param>
public static void
PrintMsvmErrors(
ManagementObject job)
{
string[] errorList;
using (ManagementBaseObject inParams = job.GetMethodParameters("GetErrorEx"))
using (ManagementBaseObject outParams = job.InvokeMethod("GetErrorEx", inParams, null))
{
if ((uint)outParams["ReturnValue"] != 0)
{
throw new ManagementException(string.Format(CultureInfo.CurrentCulture,
"GetErrorEx() call on the job failed"));
}
errorList = (string[])outParams["Errors"];
}
if (errorList == null)
{
Console.WriteLine("No errors found.");
return;
}
Console.WriteLine("Detailed errors: \n");
foreach (string error in errorList)
{
string errorSource = string.Empty;
string errorMessage = string.Empty;
int propId = 0;
XmlReader reader = XmlReader.Create(new StringReader(error));
while (reader.Read())
{
if (reader.Name.Equals("PROPERTY", StringComparison.OrdinalIgnoreCase))
{
propId = 0;
if (reader.HasAttributes)
{
string propName = reader.GetAttribute(0);
if (propName.Equals("ErrorSource", StringComparison.OrdinalIgnoreCase))
{
propId = 1;
}
else if (propName.Equals("Message", StringComparison.OrdinalIgnoreCase))
{
propId = 2;
}
}
}
else if (reader.Name.Equals("VALUE", StringComparison.OrdinalIgnoreCase))
{
if (propId == 1)
{
errorSource = reader.ReadElementContentAsString();
}
else if (propId == 2)
{
errorMessage = reader.ReadElementContentAsString();
}
propId = 0;
}
else
{
propId = 0;
}
}
Console.WriteLine("Error Message: {0}", errorMessage);
Console.WriteLine("Error Source: {0}\n", errorSource);
}
}
/// <summary>
/// Gets the Msvm_ComputerSystem instance that matches the requested virtual machine name.
/// </summary>
/// <param name="name">The name of the virtual machine to retrieve the path for.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The Msvm_ComputerSystem instance.</returns>
public static ManagementObject
GetVirtualMachine(
string name,
ManagementScope scope)
{
return GetVmObject(name, "Msvm_ComputerSystem", scope);
}
/// <summary>
/// Gets the Msvm_PlannedComputerSystem instance matching the requested virtual machine name.
/// </summary>
/// <param name="name">The name of the virtual machine to retrieve the path for.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The Msvm_PlannedComputerSystem instance.</returns>
public static ManagementObject
GetPlannedVirtualMachine(
string name,
ManagementScope scope)
{
return GetVmObject(name, "Msvm_PlannedComputerSystem", scope);
}
/// <summary>
/// Gets the first virtual machine object of the given class with the given name.
/// </summary>
/// <param name="name">The name of the virtual machine to retrieve the path for.</param>
/// <param name="className">The class of virtual machine to search for.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The instance representing the virtual machine.</returns>
private static ManagementObject
GetVmObject(
string name,
string className,
ManagementScope scope)
{
string vmQueryWql = string.Format(CultureInfo.InvariantCulture,
"SELECT * FROM {0} WHERE ElementName=\"{1}\"", className, name);
SelectQuery vmQuery = new SelectQuery(vmQueryWql);
using (ManagementObjectSearcher vmSearcher = new ManagementObjectSearcher(scope, vmQuery))
using (ManagementObjectCollection vmCollection = vmSearcher.Get())
{
if (vmCollection.Count == 0)
{
throw new ManagementException(string.Format(CultureInfo.CurrentCulture,
"No {0} could be found with name \"{1}\"",
className,
name));
}
//
// If multiple virtual machines exist with the requested name, return the first
// one.
//
ManagementObject vm = GetFirstObjectFromCollection(vmCollection);
return vm;
}
}
/// <summary>
/// Gets the virtual machine's configuration settings object.
/// </summary>
/// <param name="virtualMachine">The virtual machine.</param>
/// <returns>The virtual machine's configuration object.</returns>
public static ManagementObject
GetVirtualMachineSettings(
ManagementObject virtualMachine)
{
using (ManagementObjectCollection settingsCollection =
virtualMachine.GetRelated("Msvm_VirtualSystemSettingData", "Msvm_SettingsDefineState",
null, null, null, null, false, null))
{
ManagementObject virtualMachineSettings =
GetFirstObjectFromCollection(settingsCollection);
return virtualMachineSettings;
}
}
/// <summary>
/// Gets the Msvm_ComputerSystem instance that matches the host computer system.
/// </summary>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The Msvm_ComputerSystem instance for the host computer system.</returns>
public static ManagementObject
GetHostComputerSystem(
ManagementScope scope)
{
//
// The host computer system uses the same WMI class (Msvm_ComputerSystem) as the
// virtual machines, so we can simply reuse the GetVirtualMachine with the name
// of the host computer system.
//
return GetVirtualMachine(Environment.MachineName, scope);
}
/// <summary>
/// Gets the CIM_ResourcePool derived instance matching the specified type, subtype and
/// pool id.
/// </summary>
/// <param name="resourceType">The resource type of the resource pool.</param>
/// <param name="resourceSubtype">The resource subtype of the resource pool.</param>
/// <param name="poolId">The pool id of the resource pool.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The CIM_ResourcePool derived instance.</returns>
public static ManagementObject
GetResourcePool(
string resourceType,
string resourceSubtype,
string poolId,
ManagementScope scope)
{
string poolQueryWql;
if (resourceType == "1") // OtherResourceType
{
poolQueryWql = string.Format(CultureInfo.InvariantCulture,
"SELECT * FROM CIM_ResourcePool WHERE ResourceType=\"{0}\" AND " +
"OtherResourceType=\"{1}\" AND PoolId=\"{2}\"",
resourceType, resourceSubtype, poolId);
}
else
{
poolQueryWql = string.Format(CultureInfo.InvariantCulture,
"SELECT * FROM CIM_ResourcePool WHERE ResourceType=\"{0}\" AND " +
"ResourceSubType=\"{1}\" AND PoolId=\"{2}\"",
resourceType, resourceSubtype, poolId);
}
SelectQuery poolQuery = new SelectQuery(poolQueryWql);
using (ManagementObjectSearcher poolSearcher = new ManagementObjectSearcher(scope, poolQuery))
using (ManagementObjectCollection poolCollection = poolSearcher.Get())
{
//
// There will always only be one resource pool for a given type, subtype and pool id.
//
if (poolCollection.Count != 1)
{
throw new ManagementException(string.Format(CultureInfo.CurrentCulture,
"A single CIM_ResourcePool derived instance could not be found for " +
"ResourceType \"{0}\", ResourceSubtype \"{1}\" and PoolId \"{2}\"",
resourceType, resourceSubtype, poolId));
}
ManagementObject pool = GetFirstObjectFromCollection(poolCollection);
return pool;
}
}
/// <summary>
/// Gets the CIM_ResourcePool derived instances matching the specified type, and subtype.
/// </summary>
/// <param name="resourceType">The resource type of the resource pool.</param>
/// <param name="resourceSubtype">The resource subtype of the resource pool.</param>
/// <param name="scope">The ManagementScope to use to connect to WMI.</param>
/// <returns>The CIM_ResourcePool derived instance.</returns>
public static ManagementObjectCollection
GetResourcePools(
string resourceType,
string resourceSubtype,
ManagementScope scope)
{
string poolQueryWql;
if (resourceType == "1") // OtherResourceType
{
poolQueryWql = string.Format(CultureInfo.InvariantCulture,
"SELECT * FROM CIM_ResourcePool WHERE ResourceType=\"{0}\" AND " +
"OtherResourceType=\"{1}\"",
resourceType, resourceSubtype);
}
else
{
poolQueryWql = string.Format(CultureInfo.InvariantCulture,
"SELECT * FROM CIM_ResourcePool WHERE ResourceType=\"{0}\" AND " +
"ResourceSubType=\"{1}\"",
resourceType, resourceSubtype);
}
SelectQuery poolQuery = new SelectQuery(poolQueryWql);
using (ManagementObjectSearcher poolSearcher = new ManagementObjectSearcher(scope, poolQuery))
{
return poolSearcher.Get();
}
}
/// <summary>
/// Gets the array of Msvm_StorageAllocationSettingData of VHDs associated with the virtual machine.
/// </summary>
/// <param name="virtualMachine">The virtual machine object.</param>
/// <returns>Array of Msvm_StorageAllocationSettingData of VHDs associated with the virtual machine.</returns>
public static
ManagementObject[]
GetVhdSettings(
ManagementObject virtualMachine)
{
// Get the virtual machine settings (Msvm_VirtualSystemSettingData object).
using (ManagementObject vssd = WmiUtilities.GetVirtualMachineSettings(virtualMachine))
{
return GetVhdSettingsFromVirtualMachineSettings(vssd);
}
}
/// <summary>
/// Gets the array of Msvm_StorageAllocationSettingData of VHDs associated with the given virtual
/// machine settings.
/// </summary>
/// <param name="virtualMachineSettings">A ManagementObject representing the settings of a virtual
/// machine or snapshot.</param>
/// <returns>Array of Msvm_StorageAllocationSettingData of VHDs associated with the given settings.</returns>
public static
ManagementObject[]
GetVhdSettingsFromVirtualMachineSettings(
ManagementObject virtualMachineSettings)
{
const UInt16 SASDResourceTypeLogicalDisk = 31;
List<ManagementObject> sasdList = new List<ManagementObject>();
//
// Get all the SASDs (Msvm_StorageAllocationSettingData)
// and look for VHDs.
//
using (ManagementObjectCollection sasdCollection =
virtualMachineSettings.GetRelated("Msvm_StorageAllocationSettingData",
"Msvm_VirtualSystemSettingDataComponent",
null, null, null, null, false, null))
{
foreach (ManagementObject sasd in sasdCollection)
{
if ((UInt16)sasd["ResourceType"] == SASDResourceTypeLogicalDisk)
{
sasdList.Add(sasd);
}
else
{
sasd.Dispose();
}
}
}
if (sasdList.Count == 0)
{
return null;
}
else
{
return sasdList.ToArray();
}
}
/// <summary>
/// Gets the virtual system management service.
/// </summary>
/// <param name="scope">The scope to use when connecting to WMI.</param>
/// <returns>The virtual system management service.</returns>
public static ManagementObject
GetVirtualMachineManagementService(
ManagementScope scope)
{
using (ManagementClass managementServiceClass =
new ManagementClass("Msvm_VirtualSystemManagementService"))
{
managementServiceClass.Scope = scope;
ManagementObject managementService =
GetFirstObjectFromCollection(managementServiceClass.GetInstances());
return managementService;
}
}
/// <summary>
/// Gets the virtual system management service setting data.
/// </summary>
/// <param name="scope">The scope to use when connecting to WMI.</param>
/// <returns>The virtual system management service settings.</returns>
public static ManagementObject
GetVirtualMachineManagementServiceSettings(
ManagementScope scope)
{
using (ManagementClass serviceSettingsClass =
new ManagementClass("Msvm_VirtualSystemManagementServiceSettingData"))
{
serviceSettingsClass.Scope = scope;
ManagementObject serviceSettings =
GetFirstObjectFromCollection(serviceSettingsClass.GetInstances());
return serviceSettings;
}
}
/// <summary>
/// Gets the virtual system snapshot service.
/// </summary>
/// <param name="scope">The scope to use when connecting to WMI.</param>
/// <returns>The virtual system snapshot service.</returns>
public static ManagementObject
GetVirtualMachineSnapshotService(
ManagementScope scope)
{
using (ManagementClass snapshotServiceClass =
new ManagementClass("Msvm_VirtualSystemSnapshotService"))
{
snapshotServiceClass.Scope = scope;
ManagementObject snapshotService =
GetFirstObjectFromCollection(snapshotServiceClass.GetInstances());
return snapshotService;
}
}
/// <summary>
/// Gets the first object in a collection of ManagementObject instances.
/// </summary>
/// <param name="collection">The collection of ManagementObject instances.</param>
/// <returns>The first object in the collection</returns>
public static ManagementObject
GetFirstObjectFromCollection(
ManagementObjectCollection collection)
{
if (collection.Count == 0)
{
throw new ArgumentException("The collection contains no objects", "collection");
}
foreach (ManagementObject managementObject in collection)
{
return managementObject;
}
return null;
}
/// <summary>
/// Takes a WMI object path and escapes it so that it can be used inside a WQL query WHERE
/// clause. This effectively means replacing '\' and '"' characters so they are treated
/// like any other characters.
/// </summary>
/// <param name="objectPath">The object management path.</param>
/// <returns>The escaped object management path.</returns>
public static string
EscapeObjectPath(
string objectPath)
{
string escapedObjectPath = objectPath.Replace("\\", "\\\\");
escapedObjectPath = escapedObjectPath.Replace("\"", "\\\"");
return escapedObjectPath;
}
/// <summary>
/// Verifies whether a job is completed.
/// </summary>
/// <param name="jobStateObj">An object that represents the JobState of the job.</param>
/// <returns>True if the job is completed, False otherwise.</returns>
private static bool
IsJobComplete(
object jobStateObj)
{
JobState jobState = (JobState)((ushort)jobStateObj);
return (jobState == JobState.Completed) ||
(jobState == JobState.CompletedWithWarnings) ||(jobState == JobState.Terminated) ||
(jobState == JobState.Exception) || (jobState == JobState.Killed);
}
/// <summary>
/// Verifies whether a job succeeded.
/// </summary>
/// <param name="jobStateObj">An object representing the JobState of the job.</param>
/// <returns><c>true</c>if the job succeeded; otherwise, <c>false</c>.</returns>
private static bool
IsJobSuccessful(
object jobStateObj)
{
JobState jobState = (JobState)((ushort)jobStateObj);
return (jobState == JobState.Completed) || (jobState == JobState.CompletedWithWarnings);
}
}
}