root / pykota / trunk / bin / pkhint @ 1740

Revision 1740, 12.5 kB (checked in by jalet, 20 years ago)

If SNMP accounting is possible, pkhint now suggests to use the internal
SNMP handling instead of the external one. No real test is done for now,
though.

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