December 12, 2008

Python - Monitor Windows Remotely With WMI

Below is a simple Python module for remotely monitoring Windows machines. It is used to retrieve performance/health metrics.

I only implemented 5 functions:

  • Uptime
  • CPU Utilization
  • Available Memory
  • Memory Used
  • Ping
You can get an idea of how to interact with Windows via WMI (Windows Management Instrumentation). I use it from a multithreaded manager so I can concurrently poll a distributed farm of servers.

To run this, you will first need to install the WMI module. Follow the instructions and get the download from here: http://timgolden.me.uk/python/wmi.html (hint: it is just a single script you can drop in the same directory your script runs from)

import re
import wmi
from subprocess import Popen, PIPE



def get_uptime(computer, user, password):
    c = wmi.WMI(computer=computer, user=user, password=password, find_classes=False)
    secs_up = int([uptime.SystemUpTime for uptime in c.Win32_PerfFormattedData_PerfOS_System()][0])
    hours_up = secs_up / 3600
    return hours_up


def get_cpu(computer, user, password):
    c = wmi.WMI(computer=computer, user=user, password=password, find_classes=False)
    utilizations = [cpu.LoadPercentage for cpu in c.Win32_Processor()]
    utilization = int(sum(utilizations) / len(utilizations))  # avg all cores/processors
    return utilization

    
def get_mem_mbytes(computer, user, password):
    c = wmi.WMI(computer=computer, user=user, password=password, find_classes=False)
    available_mbytes = int([mem.AvailableMBytes for mem in c.Win32_PerfFormattedData_PerfOS_Memory()][0])
    return available_mbytes


def get_mem_pct(computer, user, password):
    c = wmi.WMI(computer=computer, user=user, password=password, find_classes=False)
    pct_in_use = int([mem.PercentCommittedBytesInUse for mem in c.Win32_PerfFormattedData_PerfOS_Memory()][0])
    return pct_in_use
    
    
def ping(host_name):
    p = Popen('ping -n 1 ' + host_name, stdout=PIPE)
    m = re.search('Average = (.*)ms', p.stdout.read())
    if m:
        return True
    else:
        raise Exception  

I am building some home-brewed monitoring tools that use these functions as a basis for plugins.

How are other people monitoring Windows from Python?
Suggestions?
Improvements?

14 comments:

Anonymous said...

Home grown? Ouch. Just install GroundWork Monitor. http://www.groundworkopensource.com/

est said...

ActivePython could be interpreted via WScript.exe with a .pys extension filename, which could invoke WMI without install pywin32 stuff, I guess

etank said...

One of the things that we have to keep a close watch on is the amount of free disk space for all attached drives. You could add to and improve this:

import string
import wmi

class SysInfo(object):
def __init__(self, hostname=''):
if hostname == '':
self.host = 'localhost'
else:
self.host = hostname
try:
self.server = wmi.WMI(self.host)
except wmi.x_wmi:
print "%s is not available" % self.host

def GetLocalDrives(self):
driveList = []
for disk in self.server.Win32_LogicalDisk():
if disk.DriveType == 3:
driveList.append(str(disk.Name))
return driveList

def GetTotalDiskSpace(self, drive):
self.drive = drive
try:
for disk in self.server.Win32_LogicalDisk(Name=self.drive):
total = long(disk.Size) /1073741824.0
return total
except UnboundLocalError:
raise 'Drive Not Found'

def GetFreeDiskSpace(self, drive):
self.drive = drive
try:
for disk in self.server.Win32_LogicalDisk(Name=self.drive):
free = long(disk.FreeSpace) /1073741824.0
return total
except UnboundLocalError:
return 'Drive Not Found'

def GetUsedDiskSpace(self, drive):
self.drive = drive
total = self.GetTotalDiskSpace(self.drive)
free = self.GetFreeDiskSpace(self.drive)
used = (total - free)
return used

def GetPercentDiskUsage(self, drive):
self.drive = drive
total = self.GetTotalDiskSpace(self.drive)
used = self.GetUsedDiskSpace(self.drive)
per_used = (used * 100) / total
return per_used

def GetCpuList(self):
'''
Attempt to get a list containing the names of the CPUs in
the system. This should be one name per physical CPU but
it seems that hyperthreading seems to make it show 2x the
actual number.
'''
cpulist = []
cpudict = {}
for cpu in self.server.Win32_Processor():
name = string.strip(str(cpu.Name))
deviceid = str(cpu.DeviceID)
cpudict = {deviceid:name}
cpulist.append(cpudict)
return cpulist

def GetNumCpu(self):
'''
Call GetCpuList and then return the number of items in the list.
'''
cpus = self.GetCpuList()
return len(cpus)

def GetServiceStatus(self, caption):
self.caption = caption
for svc in self.server.Win32_Service(Caption=self.caption):
state = svc.State
return str(state)

def GetNodeName(self):
'''
This is for when you are looking at a clustered env and
want to know who the active node is.
'''
for os in self.server.Win32_OperatingSystem():
activeNode = os.CSName
return str(activeNode)

etank said...

man that mangled the tabs. oh well you can get the idea.

Corey Goldberg said...

> Home grown? Ouch.
> Just install GroundWork Monitor

I'm actually the author of the Web Plugin for GroundWork and Nagios, so I looked there first.

I'm building my own.. new wheel or not :)

Corey Goldberg said...

@etank

thanks for the code.

If you have any more code for metrics, paste it in!

Anonymous said...

w00t, Doug Heffernan uses python too!

Anonymous said...

Why not use one of the windows clients for nagios http://nagios.sourceforge.net/docs/3_0/monitoring-windows.html ?

Then you can still call python scripts that are called on either the local machine or remotely using any Windows box as a WMI proxy.

Corey Goldberg said...

@Anon,
I am not using Nagios. See my comment above.

Richard Cooke said...

Ah!

Excellent post! I have been trying to figure out how to use WMI to see if my Python app is taking up too much RAM.

I've been pulling out hair trying to figure out the syntax to get at the data in this class....

Thanks!

Anonymous said...

The wmi lib I believe only works via Windows right ? But there is a way to do it on Linux :-)

http://felimwhiteley.wordpress.com/2008/10/01/using-python-to-retrieve-wmi-data-using-wmic/

wmic is actually a library, I didn't realise it was Python initially.. oops. Uses Samba4 pre-release code to implement the WMI.

- FĂ©lim

web design company said...

Thanks for this post

Mike said...

Thanks Corey. I've been doing similar things with PHP and a mix of VBScript and SNMP, but want to go cross platform.

Don't suppose you'd like to share the multi-thread manager or point a Python beginner to somewhere? This is obviously key to getting some scale in any monitoring system.

http://www.wekadesign.co.nz/overview.html

siva said...

Hey, nice tutorial. Thanks!

I wonder if the 'get_cpu' function could be used to check for remote server's crashes/BSOD/freezes/hangs?
How would one check that?