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를 먼저 제작해줬다.
01.
public
class
WmiHelper
02.
{
03.
private
readonly
ConnectionOptions _connectionOptions;
04.
public
WmiHelper()
05.
{
06.
_connectionOptions =
new
ConnectionOptions()
07.
{
08.
Impersonation = ImpersonationLevel.Impersonate,
09.
Username = Configure.UserName,
10.
Password = Configure.Password,
11.
Authentication = AuthenticationLevel.Default,
12.
EnablePrivileges =
true
13.
14.
};
15.
}
16.
17.
protected
ManagementObject GetManagementSingleObject(
string
wmiQuery,
string
wmiNamespace)
18.
{
19.
ManagementScope scope =
new
ManagementScope(wmiNamespace, _connectionOptions);
20.
try
21.
{
22.
scope.Connect();
23.
ObjectQuery query =
new
ObjectQuery(wmiQuery);
24.
using
(ManagementObjectSearcher searcher =
new
ManagementObjectSearcher(scope, query))
25.
{
26.
ManagementObjectCollection queryCollection = searcher.Get();
27.
return
GetFirsManagementObject(queryCollection);
28.
}
29.
}
30.
catch
(Exception ex)
31.
{
32.
Console.WriteLine(ex.Message);
33.
return
null
;
34.
}
35.
}
36.
37.
protected
ManagementObjectCollection GetManagementCollection(
string
wmiQuery,
string
wmiNamespace)
38.
{
39.
ManagementScope scope =
new
ManagementScope(wmiNamespace, _connectionOptions);
40.
try
41.
{
42.
scope.Connect();
43.
ObjectQuery query =
new
ObjectQuery(wmiQuery);
44.
using
(ManagementObjectSearcher searcher =
new
ManagementObjectSearcher(scope, query))
45.
{
46.
ManagementObjectCollection queryCollection = searcher.Get();
47.
return
queryCollection;
48.
}
49.
}
50.
catch
(Exception ex)
51.
{
52.
Console.WriteLine(ex.Message);
53.
return
null
;
54.
}
55.
}
56.
57.
protected
ManagementObject GetFirsManagementObject(ManagementObjectCollection managementObjectCollection)
58.
{
59.
if
(managementObjectCollection.Count == 0)
60.
{
61.
return
null
;
62.
}
63.
else
64.
{
65.
var enumerator = managementObjectCollection.GetEnumerator();
66.
enumerator.MoveNext();
67.
return
(ManagementObject)enumerator.Current;
68.
}
69.
}
70.
}
WmiHelper는 Connection과 WMI Query를 Execute, 얻어진 ManagementObject와 ObjectCollection 얻어오는 일을 담당한다.
이제 Hyper-V의 Network Usage와 CPU Usage를 얻어오는 PerformanceCounter Class의 내용은 다음과 같다.
001.
public
class
VirtualMachinePerformanceCounter : WmiHelper
002.
{
003.
//public long NetworkReadBytes { get; private set; }
004.
//public long NetworkWriteBytes { get; private set; }
005.
//public double CpuUsage { get; private set; }
006.
007.
private
readonly
string
_vmName;
008.
private
readonly
string
_hostName;
009.
010.
public
VirtualMachinePerformanceCounter(
string
vmName,
string
hostName)
011.
{
012.
_vmName = vmName;
013.
_hostName = hostName;
014.
}
015.
016.
public
NetworkBandwidth MonitorNetworkBandwidth()
017.
{
018.
string
wmiNamespace =
string
.Format(
"\\\\{0}\\Root\\cimv2"
, _hostName);
019.
string
vmComputerName = GetVirtualComputerSystemGuidName();
020.
string
wmiQuery =
021.
string
.Format(
022.
"SELECT * FROM Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter WHERE Name like \"%_{0}--%\""
,
023.
vmComputerName);
024.
025.
ManagementObject managementObject = GetManagementSingleObject(wmiQuery, wmiNamespace);
026.
if
(managementObject !=
null
)
027.
{
028.
string
recevedBytesString = managementObject[
"BytesReceivedPersec"
].ToString();
029.
string
sentBytesString = managementObject[
"BytesSentPersec"
].ToString();
030.
string
totalBytesString = managementObject[
"BytesPersec"
].ToString();
031.
032.
NetworkBandwidth networkBandwidth =
new
NetworkBandwidth(
long
.Parse(totalBytesString),
033.
long
.Parse(recevedBytesString),
long
.Parse(sentBytesString));
034.
Console.WriteLine(
"{0}\t{1}\t{2}"
, totalBytesString, recevedBytesString, sentBytesString);
035.
return
networkBandwidth;
036.
}
037.
else
038.
{
039.
return
NetworkBandwidth.Empty;
040.
}
041.
}
042.
043.
044.
public
CpuUsage MonitorCpuUsage()
045.
{
046.
/*
047.
Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor
048.
Frequency_PerfTime
049.
PercentGuestRunTime
050.
Timestamp_PerfTime
051.
*/
052.
string
wmiNamespace =
string
.Format(
"\\\\{0}\\Root\\cimv2"
, _hostName);
053.
string
wmiQuery =
string
.Format(
"SELECT Frequency_PerfTime,PercentGuestRunTime, Timestamp_PerfTime "
+
054.
"FROM Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor "
+
055.
"WHERE Name like \"{0}:%\""
, _vmName);
056.
057.
ManagementObjectCollection preManagementObjects = GetManagementCollection(wmiQuery, wmiNamespace);
058.
059.
int
logicalProcessCount = preManagementObjects.Count;
060.
061.
if
(logicalProcessCount == 0)
062.
{
063.
return
CpuUsage.Empty;
064.
}
065.
else
066.
{
067.
ManagementObject m1 = GetFirsManagementObject(preManagementObjects);
068.
long
frequencyPerfTime =
long
.Parse(m1[
"Frequency_PerfTime"
].ToString());
069.
070.
long
percentGuestRunTime1 =
long
.Parse(m1[
"PercentGuestRunTime"
].ToString());
071.
long
timestampPerfTime1 =
long
.Parse(m1[
"Timestamp_PerfTime"
].ToString());
072.
073.
System.Threading.Thread.Sleep(1000);
074.
075.
ManagementObject m2 = GetManagementSingleObject(wmiQuery, wmiNamespace);
076.
long
percentGuestRunTime2 =
long
.Parse(m2[
"PercentGuestRunTime"
].ToString());
077.
long
timestampPerfTime2 =
long
.Parse(m2[
"Timestamp_PerfTime"
].ToString());
078.
079.
CpuUsage usage =
new
CpuUsage()
080.
{
081.
LogicalProcessCount = logicalProcessCount,
082.
FrequencyPerfTime = frequencyPerfTime,
083.
GuestRunTime1 = percentGuestRunTime1,
084.
GuestRunTime2 = percentGuestRunTime2,
085.
TimeStampPerTime1 = timestampPerfTime1,
086.
TimeStampPerTime2 = timestampPerfTime2
087.
};
088.
089.
return
usage;
090.
}
091.
}
092.
093.
private
string
GetVirtualComputerSystemGuidName()
094.
{
095.
string
wmiNamespace =
string
.Format(
"\\\\{0}\\Root\\virtualization"
, _hostName);
096.
string
wmiQuery =
string
.Format(
"SELECT * FROM Msvm_ComputerSystem WHERE elementname = \"{0}\""
, _vmName);
097.
098.
ManagementObject managementObject = GetManagementSingleObject(wmiQuery, wmiNamespace);
099.
return
(
string
)managementObject[
"Name"
];
100.
}
101.
}
간단히 코드 설명을 한다면,
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로 들고 있는 것이 더 나았을 것 같다. 아무리 봐도.;