root / pykota / trunk / bin / pkhint @ 1860

Revision 1815, 13.0 kB (checked in by jalet, 20 years ago)

Doesn't suggest hardware(snmp) anymore if the command used was not snmpget.
This caused problem when snmpwalk was successful but not snmpget for example

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