root / pykota / trunk / bin / pkhint @ 1578

Revision 1578, 11.9 kB (checked in by jalet, 20 years ago)

Improved pkhint

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKota tool to hint for printer accounters
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003-2004 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22#
23# $Id$
24#
25# $Log$
26# Revision 1.12  2004/06/29 07:53:11  jalet
27# Improved pkhint
28#
29# Revision 1.11  2004/06/18 13:34:48  jalet
30# Now all tracebacks include PyKota's version number
31#
32# Revision 1.10  2004/06/07 18:43:40  jalet
33# Fixed over-verbose exits when displaying help or version number
34#
35# Revision 1.9  2004/06/03 21:50:34  jalet
36# Improved error logging.
37# crashrecipient directive added.
38# Now exports the job's size in bytes too.
39#
40# Revision 1.8  2004/05/18 14:48:47  jalet
41# Big code changes to completely remove the need for "requester" directives,
42# jsut use "hardware(... your previous requester directive's content ...)"
43#
44# Revision 1.7  2004/05/13 13:59:27  jalet
45# Code simplifications
46#
47# Revision 1.6  2004/03/30 12:59:47  jalet
48# Fixed path problem
49#
50# Revision 1.5  2004/02/09 13:07:06  jalet
51# Should now be able to handle network + pjl printers
52#
53# Revision 1.4  2004/02/09 12:35:19  jalet
54# De-uglyfication.
55# Now works with older CUPS (1.14) which don't detect the cupspykota backend but accept it anyway.
56#
57# Revision 1.3  2004/02/07 13:56:03  jalet
58# Help
59#
60# Revision 1.2  2004/02/07 13:47:55  jalet
61# More warnings
62#
63# Revision 1.1  2004/02/07 13:45:51  jalet
64# Preliminary work on the pkhint command
65#
66#
67#
68
69import sys
70import os
71
72from pykota import version
73from pykota.tool import PyKotaTool, PyKotaToolError, crashed
74from pykota.config import PyKotaConfigError
75from pykota.storage import PyKotaStorageError
76
77__doc__ = """pkhint v%s (c) 2003-2004 C@LL - Conseil Internet & Logiciels Libres
78A tool to give hints on what accounting method is best for each printer.
79
80command line usage :
81
82  pkhint [options] [printer1 printer2 printer3 ... printerN] <file.conf
83
84options :
85
86  -v | --version       Prints pkhint's version number then exits.
87  -h | --help          Prints this message then exits.
88 
89examples :                             
90
91  $ pkhint "hp*" printer103 </etc/cups/printers.conf
92 
93  Will analyze your printing system to test which accounter
94  is the best for each of the defined printer which
95  name matches one of the parameters.
96 
97  If you don't pass any argument on the command line, all
98  printers will be analyzed.
99 
100This program is free software; you can redistribute it and/or modify
101it under the terms of the GNU General Public License as published by
102the Free Software Foundation; either version 2 of the License, or
103(at your option) any later version.
104
105This program is distributed in the hope that it will be useful,
106but WITHOUT ANY WARRANTY; without even the implied warranty of
107MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
108GNU General Public License for more details.
109
110You should have received a copy of the GNU General Public License
111along with this program; if not, write to the Free Software
112Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
113
114Please e-mail bugs to: %s""" % (version.__version__, version.__author__)
115       
116SNMPTESTS = [ \
117              '/usr/bin/npadmin --pagecount %(printer)s 2>/dev/null', \
118              '/usr/bin/snmpget -v1 -c public -Ov %(printer)s mib-2.43.10.2.1.4.1.1 2>/dev/null | cut -f 2,2 -d " "', \
119              '/usr/bin/snmpwalk -v 1 -Cc -c public %(printer)s 2>/dev/null | grep mib-2.43.10.2.1.4.1.1 | cut -d " " -f4', \
120              '/usr/bin/snmpwalk -v 1 -Cc -c public -Ov %(printer)s 2>/dev/null | grep Counter32 | tail -2 | head -1 | cut -d " " -f2', \
121            ]
122           
123NETPJLTESTS = [ \
124                '/usr/share/pykota/pagecount.pl %(printer)s %(port)s 2>/dev/null', \
125                '/bin/nc -w 2 %(printer)s %(port)s </usr/share/pykota/pagecount.pjl 2>/dev/null | /usr/bin/tail -2', \
126              ]
127             
128class PKHint(PyKotaTool) :
129    """A class to autodetect the best accounting method for printers."""
130    def extractPrintersInformation(self) :   
131        """Extracts printer information from the printing system.
132         
133           Returns a mapping { queuename : device, ... }
134        """   
135        printers = {}
136        current_printer = None
137        for line in [l.strip() for l in sys.stdin.readlines()] :
138            testline = line.lower()
139            if testline.startswith("<printer ") or testline.startswith("<defaultprinter ") :
140                # beginning of a CUPS printer definition
141                current_printer = line.split()[-1][:-1]
142            elif testline.startswith("</printer ") :
143                # end of a CUPS printer definition
144                current_printer = None
145            elif testline.startswith("deviceuri ") :
146                # CUPS printer device_uri
147                device = testline.split()[-1]
148                if current_printer is not None :
149                    printers[current_printer] = device
150            else :       
151                # LPRng printcap specific code here
152                pass
153        return printers
154       
155    def extractDevices(self) :   
156        """Extracts the list of available CUPS devices.
157       
158           Returns a mapping { device : devicetype, ... }
159           
160           WARNING : CUPS ONLY FOR NOW
161        """   
162        inp = os.popen("/usr/sbin/lpinfo -v 2>/dev/null")
163        deviceslist = [l.strip() for l in inp.readlines()]
164        inp.close()
165        devicestypes = {}
166        for device in deviceslist :
167            (dtype, dname) = device.split()
168            devicestypes[dname] = dtype
169        return devicestypes
170       
171    def searchDeviceType(self, devicestypes, device) :   
172        """Returns the device type for current device."""
173        if device.startswith("cupspykota:") :
174            fulldevice = device[:]
175            device = fulldevice[len("cupspykota:"):]
176            if device.startswith("//") :
177                device = device[2:]
178        for (k, v) in devicestypes.items() :
179            if device.startswith(k) :
180                return v
181               
182    def extractDeviceFromURI(self, device) :   
183        """Cleans the device URI to remove any trace of PyKota."""
184        if device.startswith("cupspykota:") :
185            fulldevice = device[:]
186            device = fulldevice[len("cupspykota:"):]
187            if device.startswith("//") :
188                device = device[2:]
189        try :
190            (backend, destination) = device.split(":", 1) 
191        except ValueError :   
192            raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device
193        while destination.startswith("/") :
194            destination = destination[1:]
195        checkauth = destination.split("@", 1)   
196        if len(checkauth) == 2 :
197            destination = checkauth[1]
198        return destination.split("/")[0]
199       
200    def accepts(self, commands, printer, port=None) :   
201        """Tries to get the printer's internal page counter via SNMP."""
202        for command in commands :
203            inp = os.popen(command % locals())
204            value = inp.readline().strip()
205            inp.close()
206            try :
207                pagecounter = int(value)
208            except :   
209                pass
210            else :   
211                return command
212       
213    def main(self, args, options) :
214        """Main work is done here."""
215        print "\nPlease wait while pkhint analyzes your printing system's configuration..."
216        printers = self.extractPrintersInformation()
217        devicestypes = self.extractDevices() # TODO : IT'S CUPS ONLY FOR NOW
218        configuration = []
219        for (printer, deviceuri) in printers.items() :
220            if self.matchString(printer, args) :
221                devicetype = self.searchDeviceType(devicestypes, deviceuri)
222                device = self.extractDeviceFromURI(deviceuri)
223                if devicetype is None :
224                    sys.stderr.write("Unknown device %s for printer %s\n" % (device, printer))
225                elif devicetype == "network" :
226                    try :
227                        hostname, port = device.split(':')
228                    except ValueError :   
229                        hostname = device
230                        port = 9100             # TODO : may cause problems with other protocols.
231                       
232                    snmpcommand = self.accepts(SNMPTESTS, hostname)
233                    if snmpcommand is not None :
234                        accounter = 'hardware(/usr/share/pykota/waitprinter.sh %(printer)s && ' + snmpcommand + ')'
235                        configuration.append((printer, accounter))
236                    else :   
237                        netpjlcommand = self.accepts(NETPJLTESTS, hostname, port)
238                        if netpjlcommand is not None :
239                            accounter = 'hardware(' + netpjlcommand + ')'
240                            configuration.append((printer, accounter))
241                        else :   
242                            configuration.append((printer, "software(/usr/bin/pkpgcounter)"))
243                # elif devicetype == "direct" :
244                #    sys.stderr.write("Can't currently handle device %s for printer %s\n" % (device, printer))
245                #elif devicetype == "serial" :
246                #    sys.stderr.write("Can't currently handle device %s for printer %s\n" % (device, printer))
247                #else :
248                #    sys.stderr.write("Can't currently handle device %s for printer %s\n" % (device, printer))
249                else :
250                    configuration.append((printer, "software(/usr/bin/pkpgcounter)"))
251                   
252        if not configuration :           
253            print "\nSorry, pkhint can't help you for now. Please configure PyKota manually."
254        else :
255            print "\nPut the following lines into your /etc/pykota/pykota.conf file :\n"
256            print "# BEWARE : if software accounting is suggested, this doesn't mean"
257            print "# that hardware accounting wouldn't work, this only means that PyKota"
258            print "# wasn't able to autodetect which hardware accounting method to use."
259            for (printer, accounter) in configuration :
260                print "[%s]" % printer
261                print "accounter: %s" % accounter
262                print   
263       
264if __name__ == "__main__" : 
265    sys.stderr.write("BEWARE : This tool doesn't support LPRng's printcap files yet.\n")
266    retcode = 0
267    try :
268        short_options = "hv"
269        long_options = ["help", "version"]
270       
271        # Initializes the command line tool
272        manager = PKHint(doc=__doc__)
273       
274        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
275       
276        # sets long options
277        options["help"] = options["h"] or options["help"]
278        options["version"] = options["v"] or options["version"]
279       
280        if options["help"] :
281            manager.display_usage_and_quit()
282        elif options["version"] :
283            manager.display_version_and_quit()
284        else :
285            if not args :
286                args = [ "*" ]
287            retcode = manager.main(args, options)
288    except SystemExit :       
289        pass
290    except :
291        try :
292            manager.crashed("pkhint failed")
293        except :   
294            crashed("pkhint failed")
295        retcode = -1
296
297    try :
298        manager.storage.close()
299    except (TypeError, NameError, AttributeError) :   
300        pass
301       
302    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.