Show
Ignore:
Timestamp:
09/21/04 15:30:53 (20 years ago)
Author:
jalet
Message:

First try at full SNMP handling from the Python code.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/pykota/accounters/hardware.py

    r1715 r1729  
    2222# 
    2323# $Log$ 
     24# Revision 1.16  2004/09/21 13:30:53  jalet 
     25# First try at full SNMP handling from the Python code. 
     26# 
    2427# Revision 1.15  2004/09/14 11:38:59  jalet 
    2528# Minor fix 
     
    8285import sys 
    8386import os 
     87import time 
    8488import signal 
    8589import popen2 
     90 
    8691from pykota.accounter import AccounterBase, PyKotaAccounterError 
    8792 
     93try : 
     94    from pysnmp.mapping.udp.role import Manager 
     95    from pysnmp.proto.api import alpha 
     96except ImportError : 
     97    hasSNMP = 0 
     98else :     
     99    hasSNMP = 1 
     100    SNMPDELAY = 2.0             # 2 Seconds 
     101    STABILIZATIONDELAY = 3      # We must read three times the same value to consider it to be stable 
     102    pageCounterOID = ".1.3.6.1.2.1.43.10.2.1.4.1.1" 
     103    hrPrinterStatusOID = ".1.3.6.1.2.1.25.3.5.1.1.1" 
     104    printerStatusValues = { 1 : 'other', 
     105                            2 : 'unknown', 
     106                            3 : 'idle', 
     107                            4 : 'printing', 
     108                            5 : 'warmup', 
     109                          } 
     110                           
     111    class SNMPAccounter : 
     112        """A class for SNMP print accounting.""" 
     113        def __init__(self, parent, printerhostname) : 
     114            self.parent = parent 
     115            self.printerHostname = printerhostname 
     116            self.printerInternalPageCounter = self.printerStatus = None 
     117             
     118        def retrieveSNMPValues(self) :     
     119            """Retrieves a printer's internal page counter and status via SNMP.""" 
     120            ver = alpha.protoVersions[alpha.protoVersionId1] 
     121            req = ver.Message() 
     122            req.apiAlphaSetCommunity('public') 
     123            req.apiAlphaSetPdu(ver.GetRequestPdu()) 
     124            req.apiAlphaGetPdu().apiAlphaSetVarBindList((pageCounterOID, ver.Null()), (hrPrinterStatusOID, ver.Null())) 
     125            tsp = Manager() 
     126            try : 
     127                tsp.sendAndReceive(req.berEncode(), (self.printerHostname, 161), (self.handleAnswer, req)) 
     128            except pysnmp.mapping.udp.SnmpOverUdpError, msg :     
     129                raise PyKotaAccounterError, _("Network error while doing SNMP queries : %s") % msg 
     130            tsp.close() 
     131     
     132        def handleAnswer(self, wholeMsg, transportAddr, req): 
     133            """Decodes and handles the SNMP answer.""" 
     134            ver = alpha.protoVersions[alpha.protoVersionId1] 
     135            rsp = ver.Message() 
     136            rsp.berDecode(wholeMsg) 
     137            if req.apiAlphaMatch(rsp): 
     138                errorStatus = rsp.apiAlphaGetPdu().apiAlphaGetErrorStatus() 
     139                if errorStatus: 
     140                    self.parent.filter.printInfo(_("Problem encountered while doing SNMP queries : %s") % errorStatus, "warn") 
     141                else: 
     142                    self.values = [] 
     143                    for varBind in rsp.apiAlphaGetPdu().apiAlphaGetVarBindList(): 
     144                        self.values.append(varBind.apiAlphaGetOidVal()[1].rawAsn1Value) 
     145                    try :     
     146                        # keep maximum value seen for printer's internal page counter 
     147                        self.printerInternalPageCounter = max(self.printerInternalPageCounter, self.values[0]) 
     148                        self.printerStatus = self.values[1] 
     149                    except IndexError :     
     150                        pass 
     151                    else :     
     152                        return 1 
     153                         
     154        def waitPrinting(self) : 
     155            """Waits for printer status being 'printing'.""" 
     156            while 1: 
     157                self.retrieveSNMPValues() 
     158                statusAsString = printerStatusValues.get(self.printerStatus) 
     159                if statusAsString in ('idle', 'printing') : 
     160                    break 
     161                time.sleep(SNMPDELAY) 
     162             
     163        def waitIdle(self) : 
     164            """Waits for printer status being 'idle'.""" 
     165            idle_num = idle_flag = 0 
     166            while 1 : 
     167                self.retrieveSNMPValues() 
     168                statusAsString = printerStatusValues.get(self.printerStatus) 
     169                idle_flag = 0 
     170                if statusAsString in ('idle',) : 
     171                    idle_flag = 1 
     172                if idle_flag :     
     173                    idle_num += 1 
     174                    if idle_num > STABILIZATIONDELAY : 
     175                        # printer status is stable, we can exit 
     176                        break 
     177                else :     
     178                    idle_num = 0 
     179                self.parent.filter.printInfo(_("Waiting for printer's status to stabilize..."))     
     180                time.sleep(SNMPDELAY) 
     181     
    88182class Accounter(AccounterBase) : 
    89183    def __init__(self, kotabackend, arguments) : 
     
    166260        """ 
    167261        commandline = self.arguments.strip() % locals() 
     262        if commandline.lower() == "snmp" : 
     263            if hasSNMP : 
     264                return self.askWithSNMP(printer) 
     265            else :     
     266                raise PyKotaAccounterError, _("Internal SNMP accounting asked, but Python-SNMP is not available. Please download it from http://pysnmp.sourceforge.net") 
     267             
    168268        if printer is None : 
    169269            raise PyKotaAccounterError, _("Unknown printer address in HARDWARE(%s) for printer %s") % (commandline, self.filter.printername) 
     
    208308                raise PyKotaAccounterError, message  
    209309        return pagecounter         
     310         
     311    def askWithSNMP(self, printer) : 
     312        """Returns the page counter from the printer via internal SNMP handling.""" 
     313        acc = SNMPAccounter(self, printer) 
     314        try : 
     315            if (os.environ.get("PYKOTASTATUS") != "CANCELLED") and \ 
     316               (os.environ.get("PYKOTAACTION") != "DENY") and \ 
     317               (os.environ.get("PYKOTAPHASE") == "AFTER" : 
     318                acc.waitPrinting() 
     319            acc.waitIdle()     
     320        except :     
     321            if acc.printerInternalPageCounter is None : 
     322                raise 
     323            else :     
     324                self.filter.printInfo(_("SNMP querying stage interrupted. Using latest value seen for internal page counter (%s).") % acc.printerInternalPageCounter, "warn") 
     325        return acc.printerInternalPageCounter