root / pykota / trunk / bin / pkhint @ 1697

Revision 1652, 12.0 kB (checked in by jalet, 20 years ago)

Small fix for %(port)s thanks to rpinheiro

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