| 93 | try : |
| 94 | from pysnmp.mapping.udp.role import Manager |
| 95 | from pysnmp.proto.api import alpha |
| 96 | except ImportError : |
| 97 | hasSNMP = 0 |
| 98 | else : |
| 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 | |
| 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 |