root / pykota / trunk / bin / pkhint @ 1546

Revision 1546, 11.4 kB (checked in by jalet, 20 years ago)

Now all tracebacks include PyKota's version number

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