root / pykota / trunk / bin / pkhint @ 1803

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

Postponed string interpolation to help message's output method

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