2025-11-28 00:35:46 +09:00

328 lines
14 KiB
C#

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Samples.HyperV.StorageQoS
{
using System;
using System.IO;
using System.Globalization;
using System.Management;
using Microsoft.Samples.HyperV.Common;
using System.Collections.Generic;
class SQoS
{
// Constants required for enumerating SASDs
private const string VHDResourceTypeDisplayName = "VHD";
private const string VHDResourceType = "31";
private const string VHDResourceSubType = "Microsoft:Hyper-V:Virtual Hard Disk";
// constants required to check operation status of a disk
private const UInt16 OperationalStatusOK = 2;
private const UInt16 OperationalStatusInsufficientThroughput = 32788;
//constant required to enumerate QoS events
private const int OutOfPolicyMsgId = 32930;
private const int InPolicyMsgId = 32931;
public enum QoSEvent
{
InPolicy = 0,
OutOfPolicy
};
private void
ApplySASD(
string hostMachine,
string vmName,
string[] sasd)
{
ManagementScope scope = new ManagementScope(
@"\\" + hostMachine + @"\root\virtualization\v2", null);
// Get the vm information information.
using (ManagementObject service = WmiUtilities.GetVirtualMachineManagementService(scope))
using (ManagementObject vm = WmiUtilities.GetVirtualMachine(vmName, scope))
using (ManagementBaseObject inParams = service.GetMethodParameters("ModifyResourceSettings"))
{
inParams["Resourcesettings"] = sasd;
Console.WriteLine(" Applying new QoS settings...");
ManagementBaseObject outParams = service.InvokeMethod("ModifyResourceSettings", inParams, null);
if (WmiUtilities.ValidateOutput(outParams, scope, false, true))
{
Console.WriteLine(" Succeeded");
}
}
}
/// <summary>
/// Gets the VHDss SQoS Attributes
/// </summary>
/// <param name="hostMachine">The host name of the computer on which
/// the VM is running.</param>
/// <param name="vmName">The VM name.</param>
/// <param name="vhdName">The vhd name.</param>
public void
GetSQoSAttributes(
string hostMachine,
string vmName,
string vhdName)
{
ManagementScope scope = new ManagementScope(
@"\\" + hostMachine + @"\root\virtualization\v2", null);
// Get the vm information information.
using (ManagementObject service = WmiUtilities.GetVirtualMachineManagementService(scope))
using (ManagementObject vm = WmiUtilities.GetVirtualMachine(vmName, scope))
using (ManagementObject vmSettings = WmiUtilities.GetVirtualMachineSettings(vm))
{
// Get the VHD SASDs for the VM. This will not include the snapshot VHDs.
ManagementObject[] hardDiskSettings = WmiUtilities.GetVhdSettingsFromVirtualMachineSettings(vmSettings);
if (hardDiskSettings != null)
{
foreach (ManagementObject hardDiskSettingData in hardDiskSettings)
using (hardDiskSettingData)
{
string vmVhdPath = ((string[])hardDiskSettingData["HostResource"])[0];
string vmVhdName = Path.GetFileName(vmVhdPath);
// If the passed in vhdName is empty print all VHDs QoS attributes.
if (vhdName == null || String.Compare(vmVhdName, vhdName, StringComparison.OrdinalIgnoreCase) == 0)
{
Console.WriteLine("VHD: {0}", vmVhdName);
//Print out the SQoS setting on this SASD and exit
UInt64 min = 0;
UInt64 max = 0;
try
{
max = (UInt64)hardDiskSettingData["IOPSLimit"];
min = (UInt64)hardDiskSettingData["IOPSReservation"];
Console.WriteLine(" Maximum: {0}", max);
Console.WriteLine(" Minimum: {0}", min);
}
catch (System.Management.ManagementException)
{
// This property is not available on Windows 8 and below
Console.WriteLine("Storage QoS Not supported on this host ({0})", hostMachine);
}
}
}
}
else
{
Console.WriteLine("No VHDs found associated with the VM.");
}
}
}
/// <summary>
/// Sets the Minimum and Maximum normalized IOPS associated with a VMs VHD
/// </summary>
/// <param name="hostMachine">The host name of the computer on which
/// the VM is running.</param>
/// <param name="vmName">The VM name.</param>
/// <param name="vhdName">The vhd path.</param>
/// <param name="Option">"-Max" or "-Min"</param>
/// <param name="IOPS" >Normalized IOPS</param>
public void
SetSQoSAttributes(
string hostMachine,
string vmName,
string vhdName,
string option,
UInt64 iops)
{
ManagementScope scope = new ManagementScope(
@"\\" + hostMachine + @"\root\virtualization\v2", null);
// Get the vm information information.
using (ManagementObject service = WmiUtilities.GetVirtualMachineManagementService(scope))
using (ManagementObject vm = WmiUtilities.GetVirtualMachine(vmName, scope))
using (ManagementObject vmSettings = WmiUtilities.GetVirtualMachineSettings(vm))
{
// Get the VHD SASDs for the VM. This will not include the snapshot VHDs.
ManagementObject[] hardDiskSettings = WmiUtilities.GetVhdSettingsFromVirtualMachineSettings(vmSettings);
if (hardDiskSettings != null)
{
foreach (ManagementObject hardDiskSettingData in hardDiskSettings)
using (hardDiskSettingData)
{
string[] sasd = new string[1];
string vmVhdPath = ((string[])hardDiskSettingData["HostResource"])[0];
string vmVhdName = Path.GetFileName(vmVhdPath);
if (String.Compare(vmVhdName, vhdName, StringComparison.OrdinalIgnoreCase) == 0)
{
Console.WriteLine("VHD: {0}", vmVhdName);
//Set the SASD with the limit/Reservation and Apply
try
{
if (option.Equals("-Max", StringComparison.OrdinalIgnoreCase))
{
hardDiskSettingData["IOPSLimit"] = iops;
}
else
{
hardDiskSettingData["IOPSReservation"] = iops;
}
sasd[0] = hardDiskSettingData.GetText(TextFormat.CimDtd20);
ApplySASD(hostMachine, vmName, sasd);
}
catch (MissingMemberException)
{
// This property is not available on Windows 8 and below
Console.WriteLine("Storage QoS Not supported on this host({0})", hostMachine);
}
}
}
}
else
{
Console.WriteLine("No VHDs found associated with the VM. Skipping VHDs...");
}
}
}
private String
GetEventIDString(
int messageID)
{
String result = "";
switch (messageID)
{
case OutOfPolicyMsgId:
result = QoSEvent.OutOfPolicy.ToString();
break;
case InPolicyMsgId:
result = QoSEvent.InPolicy.ToString();
break;
default:
break;
}
return result;
}
private void
PrintOutOfPolicyVHDsInPool(
ManagementObject resourcePool)
{
//Get Logical disks in the pool
ManagementObjectCollection logicalDisks = resourcePool.GetRelated(
"Msvm_LogicalDisk",
"Msvm_ElementAllocatedFromPool",
null, null, null, null, false, null);
foreach (ManagementObject logicalDisk in logicalDisks)
using (logicalDisk)
{
//Check the operational status on the logical disk
UInt16[] opStatus = (UInt16[])logicalDisk["OperationalStatus"];
if (opStatus[0] != OperationalStatusOK)
{
foreach (UInt16 opState in opStatus)
{
//Check for the specific QOS related opcode
if (opState == OperationalStatusInsufficientThroughput)
{
// Get the SASD associated with the logical disk to get the vhd name.
using (ManagementObjectCollection sasds = logicalDisk.GetRelated(
"Msvm_StorageAllocationSettingData",
"Msvm_SettingsDefineState",
null, null, null, null, false, null))
// There is only one sasd associated with a logical disk.
foreach (ManagementObject sasd in sasds)
using (sasd)
{
string vhdPath = ((string[])sasd["HostResource"])[0];
string vhdName = Path.GetFileName(vhdPath);
Console.WriteLine(" VHD Not in Policy: {0}", vhdName);
}
}
}
}
}
}
private void
QoSEventHandler(
object sender,
EventArrivedEventArgs eventArgs)
{
// Get the event ID from the event.
String eventIDStr = GetEventIDString(Int32.Parse(
eventArgs.NewEvent.Properties["MessageID"].Value.ToString(),
CultureInfo.CurrentCulture));
try
{
// Get the resource pool specified in the event.
ManagementObject resourcePool = new ManagementObject();
resourcePool.Path.Path = (String)eventArgs.NewEvent.Properties["AlertingManagedElement"].Value.ToString();
resourcePool.Get();
if (resourcePool.Properties["PoolID"].Value.ToString() == "")
{
Console.WriteLine("Processing event {0} for Primordial pool", eventIDStr);
}
else
{
Console.WriteLine("Processing event {0} for Resource pool '{1}'", eventIDStr, resourcePool.Properties["PoolID"].Value.ToString());
}
if (eventIDStr.Equals(QoSEvent.OutOfPolicy.ToString()))
{
PrintOutOfPolicyVHDsInPool(resourcePool);
}
}
catch (System.Management.ManagementException e)
{
Console.WriteLine("exception: {0}", e.Message);
}
}
public void
MonitorSQoSEvents(
string hostMachine)
{
try
{
string wmiQuery;
ManagementEventWatcher watcher;
ManagementScope scope = new ManagementScope(
@"\\" + hostMachine + @"\root\virtualization\v2", null);
ManagementObject service = WmiUtilities.GetVirtualMachineManagementService(scope);
//build the query to monitor the QoS event
wmiQuery = "Select * From Msvm_StorageAlert";
watcher = new ManagementEventWatcher(scope, new EventQuery(wmiQuery));
watcher.EventArrived += new EventArrivedEventHandler(QoSEventHandler);
watcher.Start();
Console.WriteLine("Please press ENTER to exit monitoring...\n");
Console.ReadLine();
watcher.Stop();
}
catch (Exception e)
{
Console.WriteLine("Exception {0} Trace {1}", e.Message, e.StackTrace);
}
}
}
}