root / pykota / trunk / bin / pkhint @ 1517

Revision 1517, 11.2 kB (checked in by jalet, 20 years ago)

Improved error logging.
crashrecipient directive added.
Now exports the job's size in bytes too.

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