root / pykota / trunk / bin / pkhint @ 2090

Revision 2090, 13.3 kB (checked in by jalet, 20 years ago)

Simplified pkhint's output to take care of the software() accounter
optimization and of the internal PJL accounter.

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