Python实现Windows监控agent(上)


转载请注明出处:http://blog.csdn.net/dysj4099

   前面的文章《利用Linux守护进程机制完成一个简单系统监控demo》中提到在Linux中如何利用Python读取procfs中的信息,并构建基于守护进程的系统运行状况监控程序。但目前云平台中Windows虚拟机的数量还是占大多数,虽然可以利用libvirt提供的API获取虚拟机的运行信息,不过对于Windows虚拟机来说,获取到的大部分系统信息都不准确,甚至是错误的。因此,一种解决方法是在Windows虚拟机中定制一个监控agent,通过agent实时获取系统信息并发布。

   在Linux下,操作系统提供了procfs这样一种文件系统方便获取系统的各项运行信息。但Windows并没有提供类似procfs的机制,就必须另想办法了。好在微软提供了WMI(Windows Management Instrumentation)这样一种管理技术,它允许通过一个公共接口访问多种操作系统的构成单元,实现对操作系统的信息获取及管理操作。本文正是利用WMI进行数据获取。

要利用Python调用WMI获取信息,必须安装相关的软件包:

根据你的操作系统版本,下载相应版本安装。安装完后,就可以开始了:


CPU信息获取

   我们首先以CPU为例,演示Python调用WMI的过程。在此我们调用了Win32_Processor这个类来获取cpu的信息,可以看到这个类的对象包含了与处理器相关的大部分有用信息,对于多核的处理器而言,将输出多个对象。其中LoadPercentage这个参数是指上一秒此处理器的工作负载,这就是我们需要的cpu_usage。

详细的Win32_Processor class说明,请参阅开发文档:

import wmi
import os
import sys
import platform
import time
import win32com.client as client

class DataPollster(object):

    def get_cpu(self):
        # Initilization
        c = wmi.WMI()
        data_dict = {}
        for cpu in c.Win32_Processor():
            device = cpu.DeviceID.lower()

            # Get cpu_usage
            data_dict[device] = {'volume':float(cpu.LoadPercentage), 'unit':'%'}
        return data_dict

Memory信息获取

   通过Win32_ComputerSystem可以获取机器内存总大小TotalPhysicalMemory,通过Win32_OperatingSystem获取可用内存大小,而Win32_PageFileUsage可以帮助我们获取虚拟内存及交换内存的相关信息。

    def get_mem(self):
        c = wmi.WMI ()
        cs = c.Win32_ComputerSystem() 
        os = c.Win32_OperatingSystem() 
        pfu = c.Win32_PageFileUsage()
        
        data_dict = {}
        data_dict["MemTotal"] = {'volume':float(cs[0].TotalPhysicalMemory) / (1024*1024), 'unit':'MB'}
        data_dict["MemFree"] = {'volume':float(os[0].FreePhysicalMemory)/1024, 'unit':'MB'}
        data_dict["SwapTotal"] = {'volume':float(pfu[0].AllocatedBaseSize), 'unit':'MB'}
        data_dict["SwapFree"] = {'volume':float(pfu[0].AllocatedBaseSize - pfu[0].CurrentUsage), 'unit':'MB'}
        return {'data':data_dict, 'timestamp':time.asctime(time.localtime())}

Disk信息获取

   跟CPU和mem类似,我们可以通过Win32_LogicalDisk获取磁盘信息,包括驱动器号、总容量、空闲容量、文件系统类型等。不同的是通过以下代码我们将看到磁盘实时I/O的获取方式。在这里我们通过Win32_PerfFormattedData_PerfDisk_LogicalDisk对象获取到逻辑磁盘的性能数,这个对象不能直接获取,必须通过WbemScripting.SWbemRefresher加入,并通过Refresh方法来刷新获取数据。这也适用于网络实时流量等数据的获取。

    def get_disk(self):
        c = wmi.WMI ()

        data_dict = {}
        data_dict['total_available'] = 0
        data_dict['total_capacity'] = 0
        data_dict['total_free'] = 0

        #  DriveType=3 : "Local Disk",
        for disk in c.Win32_LogicalDisk (DriveType=3):
            data_dict['total_available'] += round(float(disk.FreeSpace) / (1024*1024*1024), 2)
            data_dict['total_capacity'] += round(float(disk.Size) / (1024*1024*1024), 2)
            data_dict['total_free'] += round(float(disk.FreeSpace) / (1024*1024*1024), 2)

            dev_tmp = {}
            dev_tmp['dev'] = disk.DeviceID
            dev_tmp['available'] = {'volume':round(float(disk.FreeSpace) / (1024*1024*1024), 2), 'unit':'GB'}
            dev_tmp['capacity'] = {'volume':round(float(disk.Size) / (1024*1024*1024), 2), 'unit':'GB'}
            dev_tmp['free'] = {'volume':round(float(disk.FreeSpace) / (1024*1024*1024), 2), 'unit':'GB'}
            dev_tmp['fstype'] = disk.FileSystem
            dev_tmp['mnt'] = ''
            dev_tmp['used'] = round(long(disk.FreeSpace) / long(disk.Size), 2)

            data_dict[disk.DeviceID] = dev_tmp

        com = client.Dispatch("WbemScripting.SWbemRefresher")
        obj = client.GetObject("winmgmts:\\root\cimv2")
        diskitems = com.AddEnum(obj, "Win32_PerfFormattedData_PerfDisk_LogicalDisk").objectSet
        
        com.Refresh()
        for item in diskitems:
            if item.Name in data_dict:
                data_dict[item.Name]['io_stat'] = {}
                data_dict[item.Name]['io_stat']['r/s'] = {'volume':float(item.DiskReadsPerSec), 'unit':''}
                data_dict[item.Name]['io_stat']['w/s'] = {'volume':float(item.DiskWritesPerSec), 'unit':''}
                data_dict[item.Name]['io_stat']['rkB/s'] = {'volume':(float(item.DiskReadBytesPerSec) / 1024), 'unit':'KB/s'}
                data_dict[item.Name]['io_stat']['wkB/s'] = {'volume':(float(item.DiskWriteBytesPerSec) / 1024), 'unit':'KB/s'}
        return {'data':data_dict, 'timestamp':time.asctime(time.localtime())}

Net信息获取

   照葫芦画瓢,Net可以通过以下方式获取:

    def get_net(self):
        c = wmi.WMI ()
        com = client.Dispatch("WbemScripting.SWbemRefresher")
        obj = client.GetObject("winmgmts:\\root\cimv2")
        items = com.AddEnum(obj, "Win32_PerfRawData_Tcpip_NetworkInterface").objectSet

        data_dict = {}
        interfaces = []
        for interface in c.Win32_NetworkAdapterConfiguration (IPEnabled=1):
            print interface.IPAddress[0]
            interfaces.append(interface.Description)

        net_bytes_in = 0
        net_bytes_out = 0
        net_pkts_in = 0
        net_pkts_out = 0
        
        com.Refresh()
        for item in items:
            if item.Name in interfaces:
                #print 'Name:%s -> B_in:%s, B_out:%s, P_in:%s, P_out:%s' %(item.Name, item.BytesReceivedPerSec, item.BytesSentPerSec, item.PacketsReceivedPerSec, item.PacketsSentPerSec)
                net_bytes_in += long(item.BytesReceivedPerSec)
                net_bytes_out += long(item.BytesSentPerSec)
                net_pkts_in += long(item.PacketsReceivedPerSec)
                net_pkts_out += long(item.PacketsSentPerSec)

        time.sleep(1)

        net_bytes_in_cur = 0
        net_bytes_out_cur = 0
        
        com.Refresh()
        for item in items:
            if item.Name in interfaces:
                net_bytes_in = long(item.BytesReceivedPerSec) - net_bytes_in
                net_bytes_in_cur += long(item.BytesReceivedPerSec)
                net_bytes_out = long(item.BytesSentPerSec) - net_bytes_out
                net_bytes_out_cur += long(item.BytesSentPerSec)
                net_pkts_in = long(item.PacketsReceivedPerSec) - net_pkts_in
                net_pkts_out = long(item.PacketsSentPerSec) - net_pkts_out

        data_dict['net_bytes_in'] = {'volume':net_bytes_in, 'unit':'B/s'}
        data_dict['net_bytes_in_sum'] = {'volume':net_bytes_in_cur, 'unit':'B'}
        data_dict['net_bytes_out'] = {'volume':net_bytes_out, 'unit':'B/s'}
        data_dict['net_bytes_out_sum'] = {'volume':net_bytes_out_cur, 'unit':'B'}
        data_dict['net_pkts_in'] = {'volume':net_pkts_in, 'unit':'p/s'}
        data_dict['net_pkts_out'] = {'volume':net_pkts_out, 'unit':'p/s'}

        return {'data':data_dict, 'timestamp':time.asctime(time.localtime())}

   需要注意的是,虽然在Win32_PerfRawData_Tcpip_NetworkInterface的文档中,BytesReceivedPerSec被解释为“Rate at which bytes are received on the interface, including framing characters.”,但是我仍然发现这类以PerSec结尾的参数都是累积值而不是真正的速率,因此我使用休眠1秒后取delta的方式求网卡的瞬时速率。


小结:

本文通过几个例子说明如何使用Python通过WMI获取Windows系统信息。其中涉及到的Win32类定义请参阅微软官方开发文档。下篇将演示如何用Windows服务框架包装监控轮询任务。

相关代码下载 Github



相关内容

    暂无相关文章