// 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"); } } } /// /// Gets the VHDss SQoS Attributes /// /// The host name of the computer on which /// the VM is running. /// The VM name. /// The vhd name. 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."); } } } /// /// Sets the Minimum and Maximum normalized IOPS associated with a VMs VHD /// /// The host name of the computer on which /// the VM is running. /// The VM name. /// The vhd path. /// "-Max" or "-Min" /// Normalized IOPS 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); } } } }