root / pykota / trunk / bin / pkhint @ 1796

Revision 1796, 12.6 kB (checked in by jalet, 20 years ago)

Renders help translatable

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