root / pykota / trunk / bin / pkhint @ 2086

Revision 2028, 13.1 kB (checked in by jalet, 20 years ago)

Modified copyright years

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