root / pykota / trunk / bin / pkhint @ 1597

Revision 1584, 11.7 kB (checked in by jalet, 20 years ago)

Better dispatching of error messages

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