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

Revision 1687, 7.0 kB (checked in by jalet, 20 years ago)

Introduction of the new 'onaccountererror' configuration directive.
Small fix for software accounter's return code which can't be None anymore.
Make software and hardware accounting code look similar : will be factorized
later.

  • 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 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20#
21# $Id$
22#
23# $Log$
24# Revision 1.11  2004/08/31 23:29:53  jalet
25# Introduction of the new 'onaccountererror' configuration directive.
26# Small fix for software accounter's return code which can't be None anymore.
27# Make software and hardware accounting code look similar : will be factorized
28# later.
29#
30# Revision 1.10  2004/08/27 22:49:04  jalet
31# No answer from subprocess now is really a fatal error. Waiting for some
32# time to make this configurable...
33#
34# Revision 1.9  2004/08/25 22:34:39  jalet
35# Now both software and hardware accounting raise an exception when no valid
36# result can be extracted from the subprocess' output.
37# Hardware accounting now reads subprocess' output until an integer is read
38# or data is exhausted : it now behaves just like software accounting in this
39# aspect.
40#
41# Revision 1.8  2004/07/22 22:41:48  jalet
42# Hardware accounting for LPRng should be OK now. UNTESTED.
43#
44# Revision 1.7  2004/07/16 12:22:47  jalet
45# LPRng support early version
46#
47# Revision 1.6  2004/07/01 19:56:42  jalet
48# Better dispatching of error messages
49#
50# Revision 1.5  2004/06/10 22:42:06  jalet
51# Better messages in logs
52#
53# Revision 1.4  2004/05/24 22:45:49  jalet
54# New 'enforcement' directive added
55# Polling loop improvements
56#
57# Revision 1.3  2004/05/24 14:36:40  jalet
58# Revert to old polling loop. Will need optimisations
59#
60# Revision 1.2  2004/05/18 14:49:22  jalet
61# Big code changes to completely remove the need for "requester" directives,
62# jsut use "hardware(... your previous requester directive's content ...)"
63#
64# Revision 1.1  2004/05/13 13:59:30  jalet
65# Code simplifications
66#
67#
68#
69
70import sys
71import os
72import popen2
73from pykota.accounter import AccounterBase, PyKotaAccounterError
74
75class Accounter(AccounterBase) :
76    def __init__(self, kotabackend, arguments) :
77        """Initializes querying accounter."""
78        AccounterBase.__init__(self, kotabackend, arguments)
79        self.isSoftware = 0
80       
81    def getPrinterInternalPageCounter(self) :   
82        """Returns the printer's internal page counter."""
83        self.filter.logdebug("Reading printer's internal page counter...")
84        counter = self.askPrinterPageCounter(self.filter.printerhostname)
85        self.filter.logdebug("Printer's internal page counter value is : %s" % str(counter))
86        return counter   
87       
88    def beginJob(self, printer) :   
89        """Saves printer internal page counter at start of job."""
90        # save page counter before job
91        self.LastPageCounter = self.getPrinterInternalPageCounter()
92        self.fakeBeginJob()
93       
94    def fakeBeginJob(self) :   
95        """Fakes a begining of a job."""
96        self.counterbefore = self.getLastPageCounter()
97       
98    def endJob(self, printer) :   
99        """Saves printer internal page counter at end of job."""
100        # save page counter after job
101        self.LastPageCounter = self.counterafter = self.getPrinterInternalPageCounter()
102       
103    def getJobSize(self) :   
104        """Returns the actual job size."""
105        try :
106            jobsize = (self.counterafter - self.counterbefore)   
107            if jobsize < 0 :
108                # Try to take care of HP printers
109                # Their internal page counter is saved to NVRAM
110                # only every 10 pages. If the printer was switched
111                # off then back on during the job, and that the
112                # counters difference is negative, we know
113                # the formula (we can't know if more than eleven
114                # pages were printed though) :
115                if jobsize > -10 :
116                    jobsize += 10
117                else :   
118                    # here we may have got a printer being replaced
119                    # DURING the job. This is HIGHLY improbable !
120                    jobsize = 0
121        except :   
122            # takes care of the case where one counter (or both) was never set.
123            jobsize = 0
124        return jobsize
125       
126    def askPrinterPageCounter(self, printer) :
127        """Returns the page counter from the printer via an external command.
128       
129           The external command must report the life time page number of the printer on stdout.
130        """
131        commandline = self.arguments.strip() % locals()
132        if printer is None :
133            raise PyKotaAccounterError, _("Unknown printer address in HARDWARE(%s) for printer %s") % (commandline, self.filter.printername)
134        self.filter.printInfo(_("Launching HARDWARE(%s)...") % commandline)
135        pagecounter = None
136        child = popen2.Popen4(commandline)   
137        try :
138            answer = child.fromchild.read()
139        except IOError :   
140            # we were interrupted by a signal, certainely a SIGTERM
141            # caused by the user cancelling the current job
142            try :
143                os.kill(child.pid, signal.SIGTERM)
144            except :   
145                pass # already killed ?
146            self.filter.printInfo(_("SIGTERM was sent to hardware accounter %s (pid: %s)") % (commandline, child.pid))
147        else :   
148            lines = [l.strip() for l in answer.split("\n")]
149            for i in range(len(lines)) : 
150                try :
151                    pagecounter = int(lines[i])
152                except (AttributeError, ValueError) :
153                    self.filter.printInfo(_("Line [%s] skipped in accounter's output. Trying again...") % lines[i])
154                else :   
155                    break
156        child.fromchild.close()   
157        child.tochild.close()
158        try :
159            status = child.wait()
160        except OSError, msg :   
161            self.filter.logdebug("Error while waiting for hardware accounter pid %s : %s" % (child.pid, msg))
162        else :   
163            if os.WIFEXITED(status) :
164                status = os.WEXITSTATUS(status)
165            self.filter.printInfo(_("Hardware accounter %s exit code is %s") % (self.arguments, str(status)))
166           
167        if pagecounter is None :
168            message = _("Unable to query printer %s via HARDWARE(%s)") % (printer, commandline)
169            if self.onerror == "CONTINUE" :
170                self.filter.printInfo(message, "error")
171            else :
172                raise PyKotaAccounterError, message 
173        return pagecounter       
Note: See TracBrowser for help on using the browser.