잊지 않겠습니다.

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로 들고 있는 것이 더 나았을 것 같다. 아무리 봐도.;
저작자 표시 비영리 변경 금지
신고
Posted by xyzlast Y2K
indows에서 System의 모든 Monitoring은 WMI를 통해서 가능하다.
WMI를 이용하는 경우, Remote에서 접근이 용의하다는 장점과 Server군의 경우 다양한 속성들을 모두 Monitoring할 수 있다는 큰 장점을 가지고 있다. 또한, 기본적인 WMI의 Query를 통해서 값을 얻는 것이 아닌, .NET에서는 WMI에 대한 Event Handling이 가능하다. 

지정될 수 있는 Event는 http://msdn.microsoft.com/en-us/library/aa394583(VS.85).aspx을 참조.

모니터링 소스는 다음과 같다. 

ConnectionOptions connectionOptions = new ConnectionOptions()
     {
            Authentication = AuthenticationLevel.Default,
            Username = @"UserName",
            Password = @"Password",
            EnablePrivileges = true,
            Impersonation = ImpersonationLevel.Impersonate
      };
            
ManagementScope scope = new ManagementScope(@"\\hyperv\root\cimv2", connectionOptions);
scope.Connect();

//__InstanceCreationEvent : Wmi instance create event
//__InstanceModificationEvent : Wmi instance value modified event
//__InstanceDeletionEvent : Wmi instance delete event
//__InstanceOperationEvent : Wmi instance method call event

WqlEventQuery wQuery = new WqlEventQuery("Select * From __InstanceModificationEvent Within 1 " + "Where TargetInstance ISA 'Win32_PerfFormattedData_Tcpip_NetworkInterface'");

ManagementEventWatcher watcher = new ManagementEventWatcher(scope, wQuery);
watcher.EventArrived += new EventArrivedEventHandler(DisplayWatcherEvent);
watcher.Start();

Console.ReadLine();
            
watcher.Stop();

일반적인 Monitoring Handling과 동일하다. 특징이 나타나는 곳은 오히려 Event 처리기쪽이 더 특징이 나타난다.

private static void DisplayWatcherEvent(object sender, EventArrivedEventArgs e)
{
    foreach(var property in e.NewEvent.Properties)
    {
        if(property.Name == "TargetInstance")
        {
            ManagementBaseObject managementObject = property.Value as ManagementBaseObject;
            if (managementObject == null)
            {
                Console.WriteLine("Data convert is null");
            }
            else
            {
                var bytesPerSec = managementObject.Properties["BytesTotalPersec"].Value;
                var bytesReceivedPersec = managementObject.Properties["BytesReceivedPersec"].Value;
                var bytesSentPersec = managementObject.Properties["BytesSentPersec"].Value;
                string name = managementObject.Properties["Name"].Value.ToString();

                Console.WriteLine("{0}\t{1}\t{2}\t{3}", 
                    name.Substring(0, 4), bytesPerSec, bytesReceivedPersec, bytesSentPersec);
            }
        }
    }
}
특징으로 보이는 것이 EventArrivedEventArgs에서 Property의 Value형태로 WMI Instance의 값이 전달된다는 점이다. 그리고, Performance Counter의 경우에는 이전값, 현재값이 Property의 Name으로 전달되기 때문에 Diff. Monitoring에도 용의하다. 


저작자 표시 비영리 변경 금지
신고
Posted by xyzlast Y2K
RSS Feed Script
([xml](new-object System.Net.WebClient).DownloadString("http://blogs.msdn.com/powershell/rss.aspx")).rss.channel.item | format-table title,link


FAST Windows Form
[void][reflection.assembly]::LoadWithPartialName("System.Windows.Forms")
$form = new-object Windows.Forms.Form
$form.Text = "My First Form"
$button = new-object Windows.Forms.Button $button.text="Push Me!"
$button.Dock="fill"
$button.add_click({$form.close()})
$form.controls.add($button)
$form.Add_Shown({$form.Activate()})
$form.ShowDialog()

:여기서 중요한 코드는 첫줄. Assembly의 동적 Loading이 저렇게 쉽게 가능하다는 것이 가장 멋진 일이다.

ERROR LOG FINDER
dir $env:windir\*.log | select-string -list error | format-table path,linenumber –autosize
저작자 표시 비영리 변경 금지
신고
Posted by xyzlast Y2K
Using Powershell, You cannot use get-credential without some type of user-prompt. 

But! 
I found the method without user-prompt window!!

Here is my code
function Get-PSCredential
{
    param($userName, $password)    
    $secured_password = ConvertTo-SecureString $password -AsPlainText -Force
    New-object Management.Automation.PSCredential($userName, $secured_password)    
}

$cred = Get-PSCredential -userName "Administrator" -password "abcdefghijk"


저작자 표시 비영리 변경 금지
신고
Posted by xyzlast Y2K


티스토리 툴바