root / pykota / trunk / pykota / accounters / hardware.py @ 3162

Revision 3162, 8.0 kB (checked in by jerome, 17 years ago)

Added the 'skipinitialwait' directive to pykota.conf.
This halves the inter-job delay when using hardware accounting
when set.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota
2# -*- coding: ISO-8859-15 -*-
3#
4# PyKota - Print Quotas for CUPS and LPRng
5#
6# (c) 2003, 2004, 2005, 2006, 2007 Jerome Alet <alet@librelogiciel.com>
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20#
21# $Id$
22#
23#
24
25import os
26import signal
27import popen2
28
29from pykota.accounter import AccounterBase, PyKotaAccounterError
30from pykota.accounters import snmp, pjl
31
32class Accounter(AccounterBase) :
33    def __init__(self, kotabackend, arguments, ispreaccounter=0) :
34        """Initializes querying accounter."""
35        AccounterBase.__init__(self, kotabackend, arguments)
36        self.isSoftware = 0
37       
38    def getPrinterInternalPageCounter(self) :   
39        """Returns the printer's internal page counter."""
40        self.filter.logdebug("Reading printer %s's internal page counter..." % self.filter.PrinterName)
41        counter = self.askPrinterPageCounter(self.filter.PrinterHostName)
42        self.filter.logdebug("Printer %s's internal page counter value is : %s" % (self.filter.PrinterName, str(counter)))
43        return counter   
44       
45    def beginJob(self, printer) :   
46        """Saves printer internal page counter at start of job."""
47        # save page counter before job
48        self.LastPageCounter = self.getPrinterInternalPageCounter()
49        self.fakeBeginJob()
50       
51    def fakeBeginJob(self) :   
52        """Fakes a begining of a job."""
53        self.counterbefore = self.getLastPageCounter()
54       
55    def endJob(self, printer) :   
56        """Saves printer internal page counter at end of job."""
57        # save page counter after job
58        self.LastPageCounter = self.counterafter = self.getPrinterInternalPageCounter()
59       
60    def getJobSize(self, printer) :   
61        """Returns the actual job size."""
62        if (not self.counterbefore) or (not self.counterafter) :
63            # there was a problem retrieving page counter
64            self.filter.printInfo(_("A problem occured while reading printer %s's internal page counter.") % printer.Name, "warn")
65            if printer.LastJob.Exists :
66                # if there's a previous job, use the last value from database
67                self.filter.printInfo(_("Retrieving printer %s's page counter from database instead.") % printer.Name, "warn")
68                if not self.counterbefore : 
69                    self.counterbefore = printer.LastJob.PrinterPageCounter or 0
70                if not self.counterafter :
71                    self.counterafter = printer.LastJob.PrinterPageCounter or 0
72                before = min(self.counterbefore, self.counterafter)   
73                after = max(self.counterbefore, self.counterafter)   
74                self.counterbefore = before
75                self.counterafter = after
76                if (not self.counterbefore) or (not self.counterafter) or (self.counterbefore == self.counterafter) :
77                    self.filter.printInfo(_("Couldn't retrieve printer %s's internal page counter either before or after printing.") % printer.Name, "warn")
78                    self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
79                    self.counterbefore = 0
80                    self.counterafter = 1
81            else :
82                self.filter.printInfo(_("No previous job in database for printer %s.") % printer.Name, "warn")
83                self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
84                self.counterbefore = 0
85                self.counterafter = 1
86               
87        jobsize = (self.counterafter - self.counterbefore)   
88        if jobsize < 0 :
89            # Try to take care of HP printers
90            # Their internal page counter is saved to NVRAM
91            # only every 10 pages. If the printer was switched
92            # off then back on during the job, and that the
93            # counters difference is negative, we know
94            # the formula (we can't know if more than eleven
95            # pages were printed though) :
96            if jobsize > -10 :
97                jobsize += 10
98            else :   
99                # here we may have got a printer being replaced
100                # DURING the job. This is HIGHLY improbable (but already happened) !
101                self.filter.printInfo(_("Inconsistent values for printer %s's internal page counter.") % printer.Name, "warn")
102                self.filter.printInfo(_("Job's size forced to 1 page for printer %s.") % printer.Name, "warn")
103                jobsize = 1
104        return jobsize
105       
106    def askPrinterPageCounter(self, printer) :
107        """Returns the page counter from the printer via an external command.
108       
109           The external command must report the life time page number of the printer on stdout.
110        """
111        skipinitialwait = self.filter.config.getPrinterSkipInitialWait(printer)
112        commandline = self.arguments.strip() % locals()
113        cmdlower = commandline.lower()
114        if (cmdlower == "snmp") or cmdlower.startswith("snmp:") :
115            return snmp.Handler(self, printer, skipinitialwait).retrieveInternalPageCounter()
116        elif (cmdlower == "pjl") or cmdlower.startswith("pjl:") :
117            return pjl.Handler(self, printer, skipinitialwait).retrieveInternalPageCounter()
118           
119        if printer is None :
120            raise PyKotaAccounterError, _("Unknown printer address in HARDWARE(%s) for printer %s") % (commandline, self.filter.PrinterName)
121        while 1 :   
122            self.filter.printInfo(_("Launching HARDWARE(%s)...") % commandline)
123            pagecounter = None
124            child = popen2.Popen4(commandline)   
125            try :
126                answer = child.fromchild.read()
127            except IOError :   
128                # we were interrupted by a signal, certainely a SIGTERM
129                # caused by the user cancelling the current job
130                try :
131                    os.kill(child.pid, signal.SIGTERM)
132                except :   
133                    pass # already killed ?
134                self.filter.printInfo(_("SIGTERM was sent to hardware accounter %s (pid: %s)") % (commandline, child.pid))
135            else :   
136                lines = [l.strip() for l in answer.split("\n")]
137                for i in range(len(lines)) : 
138                    try :
139                        pagecounter = int(lines[i])
140                    except (AttributeError, ValueError) :
141                        self.filter.printInfo(_("Line [%s] skipped in accounter's output. Trying again...") % lines[i])
142                    else :   
143                        break
144            child.fromchild.close()   
145            child.tochild.close()
146            try :
147                status = child.wait()
148            except OSError, msg :   
149                self.filter.logdebug("Error while waiting for hardware accounter pid %s : %s" % (child.pid, msg))
150            else :   
151                if os.WIFEXITED(status) :
152                    status = os.WEXITSTATUS(status)
153                self.filter.printInfo(_("Hardware accounter %s exit code is %s") % (self.arguments, str(status)))
154               
155            if pagecounter is None :
156                message = _("Unable to query printer %s via HARDWARE(%s)") % (printer, commandline)
157                if self.onerror == "CONTINUE" :
158                    self.filter.printInfo(message, "error")
159                else :
160                    raise PyKotaAccounterError, message 
161            else :       
162                return pagecounter       
Note: See TracBrowser for help on using the browser.