잊지 않겠습니다.

'WMI'에 해당되는 글 2건

  1. 2009.07.29 Hyper-V Virtual Machine Performance Counter
  2. 2009.07.29 WMI Performance Monitoring
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 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 Y2K
,