0

I'm attempting to create a small service for my home network that reads SNMP values from my switch for the port that the router is on, does a few calculations and returns the current average bandwidth utilization.

I got pysnmp working well enough reading the snmp values every two seconds... then I decided to integrate web.py to create a very uber simple web service that returns JSON of the upload and download average in kilobits.

To do this I have to use threading to keep the SNMP polling going in the background. I am a NOOOB though and it's not working right -- when I load the value in my browser it seems to create a second thread of the SNMP process for some reason, I can't really figure out why.

Here is the code:

from pysnmp.entity.rfc3413.oneliner import cmdgen
from time import sleep
import threading
import json
import web

LOCK = threading.Lock()

class getBW(threading.Thread):

    def __init__(self):
        threading.Thread.__init__(self)
        self.data = dict(upload="0",download="0",)
        self.lock = LOCK
        self.start()

    def run(self):

        router_ip = "192.168.2.2"
        snmp_community = "public"
        upload_mib = "1.3.6.1.2.1.2.2.1.16.1"
        download_mib = "1.3.6.1.2.1.2.2.1.10.1"
        read_interval = 2
        up_octets = 0
        dn_octets = 0
        last_up_octets = 0
        last_dn_octets = 0
        up_change = 0
        dn_change = 0
        avg_up =0
        avg_dn =0
        up_hist = [0,0,0,0,0] 
        dn_hist = [0,0,0,0,0]
        i = 0
        cmdGen = cmdgen.CommandGenerator()

        while (1):
            errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd(
                cmdgen.CommunityData(snmp_community),
                cmdgen.UdpTransportTarget((router_ip, 161)),
                upload_mib,
                download_mib
            )

            # Check for errors and print out results
            if errorIndication:
                print(errorIndication)
            else:
                if errorStatus:
                    print('%s at %s' % (
                        errorStatus.prettyPrint(),
                        errorIndex and varBinds[int(errorIndex)-1] or '?'
                        )
                    )
                else:
                    for name, val in varBinds:
                        if str(name) == upload_mib:
                            if up_octets > 0: 
                                last_up_octets = up_octets
                                up_change = val-last_up_octets
                                # convert to kilobits/s
                                up_change = (((up_change*8)/1000)/read_interval)
                                up_hist[i] = up_change
                                avg_up=(sum(up_hist)/len(up_hist))
                            up_octets = val
                            #print ('%d kilobit per second upload, %d average' % (up_change,avg_up))

                        elif str(name) == download_mib:
                            if dn_octets > 0:
                                last_dn_octets = dn_octets
                                dn_change = val-last_dn_octets
                                # convert to kilobits/s
                                dn_change = (((dn_change*8)/1000)/read_interval)
                                dn_hist[i] = dn_change
                                avg_dn=(sum(dn_hist)/len(dn_hist))
                                i += 1
                                i = i % 5 # if I is 5 reset to 0
                            dn_octets = val
                            #print ('%d kilobit per second download, %d average' % (dn_change,avg_dn))

            #output values to dict
            self.lock.acquire()
            self.data = dict(upload=str(avg_up),download=str(avg_dn),)
            self.lock.release()
            print("UPLOAD: %d change, %d average; DOWNLOAD: %d change, %d average" % (up_change,avg_up,dn_change,avg_dn))
            print("sleeping for %i secs...\r\n" % read_interval)
            sleep(read_interval)

try:
    BW = getBW()
except:
    print "Error: unable to start thread"

urls = ("/.*", "hello")
app = web.application(urls, globals())

class hello:
    def __init__(self):
        self.lock = LOCK
    def GET(self):
        self.lock.acquire()
        thedata = BW.data
        self.lock.release()
        return json.dumps(thedata)

if __name__ == "__main__":
    #start web service
    app.run()

I would REALLY appreciate your help. Please remember I'm a total noob to threads and just learning Python. Thanks!

Blark
  • 307
  • 1
  • 3
  • 15

1 Answers1

1

Your are using web.py's builtin webserver. By default it enables "module reloading" and the reloader loads the main module twice. Since you are starting your thread at import time you get two threads running.

An easy fix will be to move the thread creation to the __main__ block so it's only created once:

if __name__ == "__main__":
    BW = getBW()
    #start web service
    app.run()
barracel
  • 1,831
  • 13
  • 24