root / pykota / trunk / bin / pkhint @ 1579

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

Doesn't output the warning message when --help or --version is asked

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