root / pykota / trunk / bin / pkhint @ 2394

Revision 2344, 10.0 kB (checked in by jerome, 19 years ago)

Moved the GPL blurb into a single location.
Now uses named parameters in commands' help.

  • 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, 2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22#
23# $Id$
24#
25#
26
27import sys
28import os
29
30from pykota.tool import Tool, PyKotaToolError, crashed, N_
31try :
32    import pysnmp
33except ImportError :   
34    hasSNMP = 0
35else :   
36    hasSNMP = 1
37
38__doc__ = N_("""pkhint v%(__version__)s (c) %(__years__)s %(__author__)s
39
40A tool to give hints on what accounting method is best for each printer.
41
42command line usage :
43
44  pkhint [options] [printer1 printer2 printer3 ... printerN] <file.conf
45
46options :
47
48  -v | --version       Prints pkhint's version number then exits.
49  -h | --help          Prints this message then exits.
50 
51examples :                             
52
53  $ pkhint "hp*" printer103 </etc/cups/printers.conf
54 
55  Will analyze your printing system to test which accounter
56  is the best for each of the defined printer which
57  name matches one of the parameters.
58 
59  If you don't pass any argument on the command line, all
60  printers will be analyzed.
61""")
62       
63SNMPTESTS = [ \
64              'npadmin --pagecount %(printer)s 2>/dev/null', \
65              'snmpget -v1 -c public -Ov %(printer)s mib-2.43.10.2.1.4.1.1 2>/dev/null | cut -f 2,2 -d " "', \
66              'snmpwalk -v 1 -Cc -c public %(printer)s 2>/dev/null | grep mib-2.43.10.2.1.4.1.1 | cut -d " " -f4', \
67              'snmpwalk -v 1 -Cc -c public -Ov %(printer)s 2>/dev/null | grep Counter32 | tail -2 | head -1 | cut -d " " -f2', \
68            ]
69           
70NETPJLTESTS = [ \
71                '/usr/share/pykota/pagecount.pl %(printer)s %(port)s 2>/dev/null', \
72                'nc -w 2 %(printer)s %(port)s </usr/share/pykota/pagecount.pjl 2>/dev/null | tail -2', \
73              ]
74             
75class PKHint(Tool) :
76    """A class to autodetect the best accounting method for printers."""
77    def extractPrintersInformation(self) :   
78        """Extracts printer information from the printing system.
79         
80           Returns a mapping { queuename : device, ... }
81        """   
82        printers = {}
83        current_printer = None
84        for line in [l.strip() for l in sys.stdin.readlines()] :
85            testline = line.lower()
86            if testline.startswith("<printer ") or testline.startswith("<defaultprinter ") :
87                # beginning of a CUPS printer definition
88                current_printer = line.split()[-1][:-1]
89            elif testline.startswith("</printer ") :
90                # end of a CUPS printer definition
91                current_printer = None
92            elif testline.startswith("deviceuri ") :
93                # CUPS printer device_uri
94                device = testline.split()[-1]
95                if current_printer is not None :
96                    printers[current_printer] = device
97            else :       
98                # LPRng printcap specific code here
99                pass
100        return printers
101       
102    def extractDevices(self) :   
103        """Extracts the list of available CUPS devices.
104       
105           Returns a mapping { device : devicetype, ... }
106           
107           WARNING : CUPS ONLY FOR NOW
108        """   
109        inp = os.popen("lpinfo -v 2>/dev/null")
110        deviceslist = [l.strip() for l in inp.readlines()]
111        inp.close()
112        devicestypes = {}
113        for device in deviceslist :
114            (dtype, dname) = device.split()
115            devicestypes[dname] = dtype
116        return devicestypes
117       
118    def searchDeviceType(self, devicestypes, device) :   
119        """Returns the device type for current device."""
120        if device.startswith("cupspykota:") :
121            fulldevice = device[:]
122            device = fulldevice[len("cupspykota:"):]
123            if device.startswith("//") :
124                device = device[2:]
125        for (k, v) in devicestypes.items() :
126            if device.startswith(k) :
127                return v
128               
129    def extractDeviceFromURI(self, device) :   
130        """Cleans the device URI to remove any trace of PyKota."""
131        if device.startswith("cupspykota:") :
132            fulldevice = device[:]
133            device = fulldevice[len("cupspykota:"):]
134            if device.startswith("//") :
135                device = device[2:]
136        try :
137            (backend, destination) = device.split(":", 1) 
138        except ValueError :   
139            raise PyKotaToolError, _("Invalid DeviceURI : %s") % device
140        while destination.startswith("/") :
141            destination = destination[1:]
142        checkauth = destination.split("@", 1)   
143        if len(checkauth) == 2 :
144            destination = checkauth[1]
145        return destination.split("/")[0]
146       
147    def accepts(self, commands, printer, port=None) :   
148        """Tries to get the printer's internal page counter via SNMP."""
149        for command in commands :
150            inp = os.popen(command % locals())
151            value = inp.readline().strip()
152            inp.close()
153            try :
154                pagecounter = int(value)
155            except :   
156                pass
157            else :   
158                if port is None :
159                    return command
160                else :   
161                    return command.replace("%(port)s", str(port))
162       
163    def main(self, args, options) :
164        """Main work is done here."""
165        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "")
166        sys.stderr.write("BEWARE : This tool doesn't support LPRng's printcap files yet.\n")
167        print _("\nPlease wait while pkhint analyzes your printing system's configuration...")
168        printers = self.extractPrintersInformation()
169        devicestypes = self.extractDevices() # TODO : IT'S CUPS ONLY FOR NOW
170        configuration = []
171        for (printer, deviceuri) in printers.items() :
172            if self.matchString(printer, args) :
173                devicetype = self.searchDeviceType(devicestypes, deviceuri)
174                device = self.extractDeviceFromURI(deviceuri)
175                if devicetype is None :
176                    self.printInfo(_("Unknown device %s for printer %s") % (device, printer))
177                elif devicetype == "network" :
178                    try :
179                        hostname, port = device.split(':')
180                    except ValueError :   
181                        hostname = device
182                        port = 9100             # TODO : may cause problems with other protocols.
183                       
184                    snmpcommand = self.accepts(SNMPTESTS, hostname)
185                    if snmpcommand is not None :
186                        if hasSNMP and snmpcommand.startswith("snmpget ") :
187                            # don't do a more complex test, just consider it will work
188                            accounter = 'hardware(snmp)'
189                        else :   
190                            accounter = 'hardware(/usr/share/pykota/waitprinter.sh %(printer)s && ' + snmpcommand + ')'
191                        configuration.append((printer, accounter))
192                    else :   
193                        netpjlcommand = self.accepts(NETPJLTESTS, hostname, port)
194                        if netpjlcommand is not None :
195                            #accounter = 'hardware(' + netpjlcommand + ')'
196                            accounter = 'hardware(pjl)'
197                            configuration.append((printer, accounter))
198                        else :   
199                            configuration.append((printer, "software()"))
200                else :
201                    configuration.append((printer, "software()"))
202                   
203        if not configuration :           
204            print "\nSorry, pkhint can't help you for now. Please configure PyKota manually."
205        else :
206            print _("\nPut the following lines into your /etc/pykota/pykota.conf file :\n")
207            print _("# BEWARE : if software accounting is suggested, this doesn't mean")
208            print _("# that hardware accounting wouldn't work, this only means that PyKota")
209            print _("# wasn't able to autodetect which hardware accounting method to use.")
210            for (printer, accounter) in configuration :
211                print "[%s]" % printer
212                print "accounter: %s" % accounter
213                print   
214       
215if __name__ == "__main__" : 
216    retcode = 0
217    try :
218        short_options = "hv"
219        long_options = ["help", "version"]
220       
221        # Initializes the command line tool
222        manager = PKHint(doc=__doc__)
223        manager.deferredInit()
224       
225        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
226       
227        # sets long options
228        options["help"] = options["h"] or options["help"]
229        options["version"] = options["v"] or options["version"]
230       
231        if options["help"] :
232            manager.display_usage_and_quit()
233        elif options["version"] :
234            manager.display_version_and_quit()
235        else :
236            if not args :
237                args = [ "*" ]
238            retcode = manager.main(args, options)
239    except KeyboardInterrupt :       
240        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
241    except SystemExit :       
242        pass
243    except :
244        try :
245            manager.crashed("pkhint failed")
246        except :   
247            crashed("pkhint failed")
248        retcode = -1
249       
250    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.