The greatest challenge to any thinker is stating the problem in a way that will allow a solution

Bertrand Russell

By

On 21 Nov 2012

PowerShell

Tags: , , ,


A while back I discovered a SNMP .NET offering that (given that PowerShell is .NET) meant that I could use PowerShell as a SNMP client, which I’ve documented on my Wiki – http://vwiki.co.uk/SNMP_and_PowerShell.

At the time my intended aim for the ability disappeared, and so I never really got to put into use.  This post brings together a couple of techniques in order to achieve regular SNMP polling of a device.  The results of which go into an RRD database (you don’t need to do this, what you do with the results of the SNMP polls is up to you).

SNMP Polls (Gets)

To do the SNMP Gets, I’ve used Lex Li’s #SNMP library, which, whilst never intended to be used with PowerShell, it was intended to be used with .NET, which is just as good.  There is a slight gotcha in that it uses Generic Objects.  I don’t entirely their point or purpose, but haven’t always worked in PowerShell, supposedly they do now, but I’ve needed to use a workaround provided by Lee Holmes to gain any success (please let me know if there’s an easy way of doing this).

The script below re-uses two functions that I’ve already written/obtained…

Running Regular Tasks

In order to do the SNMP Gets at regular intervals. I’ve re-used a scheduling procedure that I wrote a while back.  This allows a function or sub-routine to run at specific intervals (eg every 10 mins), which is perfect for scheduling regular polling.  This provides the overall structure for the script.

For more info on just the scheduling structure see – http://vwiki.co.uk/Scheduling_Within_a_PowerShell_Script

The Script

To use this script but not insert data into an RRD, delete or comment out the lines which have # RRD only at the end.

$device = "192.168.10.212"                          # The SNMP server/device to poll/pull data back from
$OIDarray = @()                                     # Initialise an array to hold OIDs
$OIDarray += ".1.3.6.1.4.1.24681.1.2.6.0"           # Sys temp (for QNAP NAS)
$OIDarray += ".1.3.6.1.4.1.24681.1.2.11.1.3.1"      # Disk temp (for QNAP NAS, disks 1 to 4)
$OIDarray += ".1.3.6.1.4.1.24681.1.2.11.1.3.2"
$OIDarray += ".1.3.6.1.4.1.24681.1.2.11.1.3.3"
$OIDarray += ".1.3.6.1.4.1.24681.1.2.11.1.3.4"
 
$RRDtool = ".\lib\rrdtool\rrdtool.exe"              # Path to RRDtool executable (only required to store data in RRD)
$RRDfile = ".\data\nas-temp.rrd"                    # Path to RRD to store data in
 
# Scheduler stuff...
$DoSingleRun = 0            # Ignore scheduling and just run once (useful for debugging main function)
$IntervalMins = 5           # Should fit into an hour exactly
#$End = "18:50"              # Time of day that script should cease (should be just after last required run time)
$End = "Never"              # Never end script
$CheckThrottle = 250        # Idle throttle / time check interval (msec)
 
$Logfile = "SNMP-NAS.log"
 
# Functions ===========================================================================
 
# The main function that gets called at regular intervals
function Do-Stuff {
 
    $results = Invoke-SnmpGet $device $OIDarray
    if (!$results) {
        Log "SNMP error: No data returned"
        Return
    }
    $time = ((Get-Date).ToUniversalTime() - ([datetime]'1/1/1970 00:00:00')).TotalSeconds
 
    # Start building string for RRD add
    [string]$data2add = $time                                                   # RRD only
 
    Foreach ($res in $results) {
        # Extract data for SNMP Get results
        $temp = [regex]::Matches($res.Data, "^\d+")[0].Value
        Log ("Current system temp is (C): $temp [Raw: " + $res.Data + " for OID: " + $res.OID + "]")
        $data2add += "`:$temp"                                                  # RRD only
    }
 
    # Do RRD add
    $cmd = "$RRDtool update $RRDfile $data2add"                                 # RRD only
    Log $cmd                                                                    # RRD only
    $proc_res = &$executioncontext.InvokeCommand.NewScriptBlock($cmd)       # RRD only
    if ($proc_res) {                                                            # RRD only
        Log "RRDtool error: $proc_res"                                          # RRD only
    }                                                                           # RRD only
}
 
# Helper functions
 
function Log ($text) {
    $stamp = (Get-Date).ToString("HH:mm:ss.fff")
    Write-Host "$stamp | $text"
}
 
function Invoke-SNMPget ([string]$sIP, $sOIDs, [string]$Community = "public", [int]$UDPport = 161, [int]$TimeOut=3000) {
    # $OIDs can be a single OID string, or an array of OID strings
    # $TimeOut is in msec, 0 or -1 for infinite
 
    # Create OID variable list
    $vList = New-GenericObject System.Collections.Generic.List Lextm.SharpSnmpLib.Variable
    foreach ($sOID in $sOIDs) {
        $oid = New-Object Lextm.SharpSnmpLib.ObjectIdentifier ($sOID)
        $vList.Add($oid)
    }
 
    # Create endpoint for SNMP server
    $ip = [System.Net.IPAddress]::Parse($sIP)
    $svr = New-Object System.Net.IpEndPoint ($ip, 161)
 
    # Use SNMP v2
    $ver = [Lextm.SharpSnmpLib.VersionCode]::V2
 
    # Perform SNMP Get
    try {
        $msg = [Lextm.SharpSnmpLib.Messaging.Messenger]::Get($ver, $svr, $Community, $vList, $TimeOut)
    } catch [Lextm.SharpSnmpLib.Messaging.TimeoutException] {
        Log "SNMP Get on $sIP timed-out"
        Return $null
    } catch {
        Log "SNMP Get error: $_"
        Return $null
    }
 
    $res = @()
    foreach ($var in $msg) {
        $line = "" | Select OID, Data
        $line.OID = $var.Id.ToString()
        $line.Data = $var.Data.ToString()
        $res += $line
    }
 
    $res
}
 
function New-GenericObject {
    param(
        [string] $typeName = $(throw “Please specify a generic type name”),
        [string[]] $typeParameters = $(throw “Please specify the type parameters”),
        [object[]] $constructorParameters
        )
 
    ## Create the generic type name
    $genericTypeName = $typeName + ‘`’ + $typeParameters.Count
    $genericType = [Type] $genericTypeName
 
    if(-not $genericType)
        {
        throw “Could not find generic type $genericTypeName}
 
    ## Bind the type arguments to it
    [type[]] $typedParameters = $typeParameters
    $closedType = $genericType.MakeGenericType($typedParameters)
    if(-not $closedType)
        {
        throw “Could not make closed type $genericType}
 
    ## Create the closed version of the generic type
    ,[Activator]::CreateInstance($closedType, $constructorParameters)
}
 
# ==============================================================
# Pre-amble
 
Start-Transcript -Path $Logfile
Log "Started script"
 
# Load SNMP assembly
Load-SnmpAssembly
 
# MAIN SCHEDULED PART OF SCRIPT ==================================================================
 
# Initial scheduling prep ------------------------------------------------------------------------
 
# Check scheduler variables
if ((60 % $IntervalMins) -ne 0) {
    Log "Interval error - $IntervalMins mins doesn't fit into an hour!"
    Exit
}
 
if ($End -ne "Never") { 
    try {
        $EndTime = Get-Date $End
    } catch {
        Log "Invalid end time: $End hrs"
        Log $_.Exception.GetType().FullName
        Log $_.Exception.Message
        Exit
    }
} else {
    $EndTime = (Get-Date).AddYears(10)
}
 
# Set dummy last run time (aligned to nice start time), and next run time
$LastRunTime = Get-Date
$offset = ($IntervalMins + ($LastRunTime.Minute % $IntervalMins))
$LastRunTime = $LastRunTime.AddMinutes(-$offset)
$LastRunTime = $LastRunTime.AddSeconds(-$LastRunTime.Second)
$NextRunTime = $LastRunTime.AddMinutes($IntervalMins)
 
Log ("Last runtime is " + $LastRunTime)
Log ("Next runtime is " + $NextRunTime)
Log ("Script cease at " + $EndTime)
 
# Main scheduled loop ------------------------------------------------------------------------------
 
if ($DoSingleRun) {
    Log ("Doing single run...")
    Do-Stuff
} else {
    While (1) {
        if ($NextRunTime -lt (Get-Date)) {
            Log ("Starting run at " + (Get-Date))
            Do-Stuff
            $LastRunTime = $LastRunTime.AddMinutes($IntervalMins)
            $NextRunTime = $LastRunTime.AddMinutes($IntervalMins)
            Log ("Completed run at " + (Get-Date) + ", next run at $NextRunTime")
            if ($NextRunTime -lt (Get-Date)) {
                Log "WARNING: Next run is going to be late!"
            }
        }
        if ($EndTime -lt (Get-Date)) {
            Log ("Script ending at " + (Get-Date))
            Break
        }
        Start-Sleep -Milliseconds $CheckThrottle
    }
}
 
# End of  Main scheduled loop ---------------------------------------------------------------------
 
Stop-Transcript

 


Leave a Reply

XHTML: You can use these tags if you know what they are: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

ERROR: si-captcha.php plugin says GD image support not detected in PHP!

Contact your web host and ask them why GD image support is not enabled for PHP.

ERROR: si-captcha.php plugin says imagepng function not detected in PHP!

Contact your web host and ask them why imagepng function is not enabled for PHP.