Hyper-V 에서 Network Usage와 Process Usage를 얻어내기 위해서 WMI 뒤지기 삽질. -_-
이번에 WMI를 보면서 많은 생각이 든 것이, MS가 참 대단하다는 생각이 든다. 정말 여러 데이터들을 잘 모아뒀다는 그런 느낌이라고 해야지 될까.
먼저 Performance Counter에 사용되는 WMI 들은 Win32_PerfFormattedData_, Win32_PerfRawData_ 두가지로 나뉘어진다. 이 두개의 데이터의 차이는 좀 더 알아보기 쉽게 변형된 데이터와 Raw Data의 차이를 가지고 있다. 예를 들어 Network Usage의 경우 FormattedData의 경우에는 Bytes/sec의 정보가 나온다. 그렇지만 Raw Data는 Bytes의 누적값으로 표현이 된다. 이렇게 약간약간 다른 값들이 있기 때문에, 좀 더 세밀하게 볼 필요성이 있다.
뭐.. 바로 본론으로 들어가면, 필요한 WMI 정보와 WMI를 얻어내는데 필요한 Helper Class를 먼저 제작해줬다.
public class WmiHelper
{
private readonly ConnectionOptions _connectionOptions;
public WmiHelper()
{
_connectionOptions = new ConnectionOptions()
{
Impersonation = ImpersonationLevel.Impersonate,
Username = Configure.UserName,
Password = Configure.Password,
Authentication = AuthenticationLevel.Default,
EnablePrivileges = true
};
}
protected ManagementObject GetManagementSingleObject(string wmiQuery, string wmiNamespace)
{
ManagementScope scope = new ManagementScope(wmiNamespace, _connectionOptions);
try
{
scope.Connect();
ObjectQuery query = new ObjectQuery(wmiQuery);
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
ManagementObjectCollection queryCollection = searcher.Get();
return GetFirsManagementObject(queryCollection);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
protected ManagementObjectCollection GetManagementCollection(string wmiQuery, string wmiNamespace)
{
ManagementScope scope = new ManagementScope(wmiNamespace, _connectionOptions);
try
{
scope.Connect();
ObjectQuery query = new ObjectQuery(wmiQuery);
using(ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
ManagementObjectCollection queryCollection = searcher.Get();
return queryCollection;
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
protected ManagementObject GetFirsManagementObject(ManagementObjectCollection managementObjectCollection)
{
if(managementObjectCollection.Count == 0)
{
return null;
}
else
{
var enumerator = managementObjectCollection.GetEnumerator();
enumerator.MoveNext();
return (ManagementObject)enumerator.Current;
}
}
}
WmiHelper는 Connection과 WMI Query를 Execute, 얻어진 ManagementObject와 ObjectCollection 얻어오는 일을 담당한다.
이제 Hyper-V의 Network Usage와 CPU Usage를 얻어오는 PerformanceCounter Class의 내용은 다음과 같다.
public class VirtualMachinePerformanceCounter : WmiHelper
{
//public long NetworkReadBytes { get; private set; }
//public long NetworkWriteBytes { get; private set; }
//public double CpuUsage { get; private set; }
private readonly string _vmName;
private readonly string _hostName;
public VirtualMachinePerformanceCounter(string vmName, string hostName)
{
_vmName = vmName;
_hostName = hostName;
}
public NetworkBandwidth MonitorNetworkBandwidth()
{
string wmiNamespace = string.Format("\\\\{0}\\Root\\cimv2", _hostName);
string vmComputerName = GetVirtualComputerSystemGuidName();
string wmiQuery =
string.Format(
"SELECT * FROM Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter WHERE Name like \"%_{0}--%\"",
vmComputerName);
ManagementObject managementObject = GetManagementSingleObject(wmiQuery, wmiNamespace);
if(managementObject != null)
{
string recevedBytesString = managementObject["BytesReceivedPersec"].ToString();
string sentBytesString = managementObject["BytesSentPersec"].ToString();
string totalBytesString = managementObject["BytesPersec"].ToString();
NetworkBandwidth networkBandwidth = new NetworkBandwidth(long.Parse(totalBytesString),
long.Parse(recevedBytesString), long.Parse(sentBytesString));
Console.WriteLine("{0}\t{1}\t{2}", totalBytesString, recevedBytesString, sentBytesString);
return networkBandwidth;
}
else
{
return NetworkBandwidth.Empty;
}
}
public CpuUsage MonitorCpuUsage()
{
/*
Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor
Frequency_PerfTime
PercentGuestRunTime
Timestamp_PerfTime
*/
string wmiNamespace = string.Format("\\\\{0}\\Root\\cimv2", _hostName);
string wmiQuery = string.Format("SELECT Frequency_PerfTime,PercentGuestRunTime, Timestamp_PerfTime " +
"FROM Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor " +
"WHERE Name like \"{0}:%\"", _vmName);
ManagementObjectCollection preManagementObjects = GetManagementCollection(wmiQuery, wmiNamespace);
int logicalProcessCount = preManagementObjects.Count;
if(logicalProcessCount == 0)
{
return CpuUsage.Empty;
}
else
{
ManagementObject m1 = GetFirsManagementObject(preManagementObjects);
long frequencyPerfTime = long.Parse(m1["Frequency_PerfTime"].ToString());
long percentGuestRunTime1 = long.Parse(m1["PercentGuestRunTime"].ToString());
long timestampPerfTime1 = long.Parse(m1["Timestamp_PerfTime"].ToString());
System.Threading.Thread.Sleep(1000);
ManagementObject m2 = GetManagementSingleObject(wmiQuery, wmiNamespace);
long percentGuestRunTime2 = long.Parse(m2["PercentGuestRunTime"].ToString());
long timestampPerfTime2 = long.Parse(m2["Timestamp_PerfTime"].ToString());
CpuUsage usage = new CpuUsage()
{
LogicalProcessCount = logicalProcessCount,
FrequencyPerfTime = frequencyPerfTime,
GuestRunTime1 = percentGuestRunTime1,
GuestRunTime2 = percentGuestRunTime2,
TimeStampPerTime1 = timestampPerfTime1,
TimeStampPerTime2 = timestampPerfTime2
};
return usage;
}
}
private string GetVirtualComputerSystemGuidName()
{
string wmiNamespace = string.Format("\\\\{0}\\Root\\virtualization", _hostName);
string wmiQuery = string.Format("SELECT * FROM Msvm_ComputerSystem WHERE elementname = \"{0}\"", _vmName);
ManagementObject managementObject = GetManagementSingleObject(wmiQuery, wmiNamespace);
return (string)managementObject["Name"];
}
}
간단히 코드 설명을 한다면,
Network Usage는 Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter에서 얻어지는 값을 이용하면 된다.
VM과 연관을 찾기 위해서는 root\virtualization에서 VirtualComputerSystem의 Name 값을 이용해서 like 검색을 하면 된다.
CPU Usage의 경우에는 http://blogs.msdn.com/tvoellm/archive/2008/07/14/how-to-get-processor-utilization-for-hyper-v-via-wmi.aspx를
참고.
* WmiHelper를 상속받는 것이 아닌, 아무래도 생성해서 Instance를 private로 들고 있는 것이 더 나았을 것 같다. 아무리 봐도.;



