root / pykota / trunk / bin / pkhint @ 2267

Revision 2267, 10.7 kB (checked in by jerome, 19 years ago)

Moved the copyright strings out of the docstrings

  • 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, 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%s (c) %s %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 
62This program is free software; you can redistribute it and/or modify
63it under the terms of the GNU General Public License as published by
64the Free Software Foundation; either version 2 of the License, or
65(at your option) any later version.
66
67This program is distributed in the hope that it will be useful,
68but WITHOUT ANY WARRANTY; without even the implied warranty of
69MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
70GNU General Public License for more details.
71
72You should have received a copy of the GNU General Public License
73along with this program; if not, write to the Free Software
74Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
75
76Please e-mail bugs to: %s""")
77       
78SNMPTESTS = [ \
79              'npadmin --pagecount %(printer)s 2>/dev/null', \
80              'snmpget -v1 -c public -Ov %(printer)s mib-2.43.10.2.1.4.1.1 2>/dev/null | cut -f 2,2 -d " "', \
81              'snmpwalk -v 1 -Cc -c public %(printer)s 2>/dev/null | grep mib-2.43.10.2.1.4.1.1 | cut -d " " -f4', \
82              'snmpwalk -v 1 -Cc -c public -Ov %(printer)s 2>/dev/null | grep Counter32 | tail -2 | head -1 | cut -d " " -f2', \
83            ]
84           
85NETPJLTESTS = [ \
86                '/usr/share/pykota/pagecount.pl %(printer)s %(port)s 2>/dev/null', \
87                'nc -w 2 %(printer)s %(port)s </usr/share/pykota/pagecount.pjl 2>/dev/null | tail -2', \
88              ]
89             
90class PKHint(Tool) :
91    """A class to autodetect the best accounting method for printers."""
92    def extractPrintersInformation(self) :   
93        """Extracts printer information from the printing system.
94         
95           Returns a mapping { queuename : device, ... }
96        """   
97        printers = {}
98        current_printer = None
99        for line in [l.strip() for l in sys.stdin.readlines()] :
100            testline = line.lower()
101            if testline.startswith("<printer ") or testline.startswith("<defaultprinter ") :
102                # beginning of a CUPS printer definition
103                current_printer = line.split()[-1][:-1]
104            elif testline.startswith("</printer ") :
105                # end of a CUPS printer definition
106                current_printer = None
107            elif testline.startswith("deviceuri ") :
108                # CUPS printer device_uri
109                device = testline.split()[-1]
110                if current_printer is not None :
111                    printers[current_printer] = device
112            else :       
113                # LPRng printcap specific code here
114                pass
115        return printers
116       
117    def extractDevices(self) :   
118        """Extracts the list of available CUPS devices.
119       
120           Returns a mapping { device : devicetype, ... }
121           
122           WARNING : CUPS ONLY FOR NOW
123        """   
124        inp = os.popen("lpinfo -v 2>/dev/null")
125        deviceslist = [l.strip() for l in inp.readlines()]
126        inp.close()
127        devicestypes = {}
128        for device in deviceslist :
129            (dtype, dname) = device.split()
130            devicestypes[dname] = dtype
131        return devicestypes
132       
133    def searchDeviceType(self, devicestypes, device) :   
134        """Returns the device type for current device."""
135        if device.startswith("cupspykota:") :
136            fulldevice = device[:]
137            device = fulldevice[len("cupspykota:"):]
138            if device.startswith("//") :
139                device = device[2:]
140        for (k, v) in devicestypes.items() :
141            if device.startswith(k) :
142                return v
143               
144    def extractDeviceFromURI(self, device) :   
145        """Cleans the device URI to remove any trace of PyKota."""
146        if device.startswith("cupspykota:") :
147            fulldevice = device[:]
148            device = fulldevice[len("cupspykota:"):]
149            if device.startswith("//") :
150                device = device[2:]
151        try :
152            (backend, destination) = device.split(":", 1) 
153        except ValueError :   
154            raise PyKotaToolError, _("Invalid DeviceURI : %s") % device
155        while destination.startswith("/") :
156            destination = destination[1:]
157        checkauth = destination.split("@", 1)   
158        if len(checkauth) == 2 :
159            destination = checkauth[1]
160        return destination.split("/")[0]
161       
162    def accepts(self, commands, printer, port=None) :   
163        """Tries to get the printer's internal page counter via SNMP."""
164        for command in commands :
165            inp = os.popen(command % locals())
166            value = inp.readline().strip()
167            inp.close()
168            try :
169                pagecounter = int(value)
170            except :   
171                pass
172            else :   
173                if port is None :
174                    return command
175                else :   
176                    return command.replace("%(port)s", str(port))
177       
178    def main(self, args, options) :
179        """Main work is done here."""
180        os.environ["PATH"] = "%s:/bin:/usr/bin:/usr/local/bin:/opt/bin:/sbin:/usr/sbin" % os.environ.get("PATH", "")
181        sys.stderr.write("BEWARE : This tool doesn't support LPRng's printcap files yet.\n")
182        print _("\nPlease wait while pkhint analyzes your printing system's configuration...")
183        printers = self.extractPrintersInformation()
184        devicestypes = self.extractDevices() # TODO : IT'S CUPS ONLY FOR NOW
185        configuration = []
186        for (printer, deviceuri) in printers.items() :
187            if self.matchString(printer, args) :
188                devicetype = self.searchDeviceType(devicestypes, deviceuri)
189                device = self.extractDeviceFromURI(deviceuri)
190                if devicetype is None :
191                    self.printInfo(_("Unknown device %s for printer %s") % (device, printer))
192                elif devicetype == "network" :
193                    try :
194                        hostname, port = device.split(':')
195                    except ValueError :   
196                        hostname = device
197                        port = 9100             # TODO : may cause problems with other protocols.
198                       
199                    snmpcommand = self.accepts(SNMPTESTS, hostname)
200                    if snmpcommand is not None :
201                        if hasSNMP and snmpcommand.startswith("snmpget ") :
202                            # don't do a more complex test, just consider it will work
203                            accounter = 'hardware(snmp)'
204                        else :   
205                            accounter = 'hardware(/usr/share/pykota/waitprinter.sh %(printer)s && ' + snmpcommand + ')'
206                        configuration.append((printer, accounter))
207                    else :   
208                        netpjlcommand = self.accepts(NETPJLTESTS, hostname, port)
209                        if netpjlcommand is not None :
210                            #accounter = 'hardware(' + netpjlcommand + ')'
211                            accounter = 'hardware(pjl)'
212                            configuration.append((printer, accounter))
213                        else :   
214                            configuration.append((printer, "software()"))
215                else :
216                    configuration.append((printer, "software()"))
217                   
218        if not configuration :           
219            print "\nSorry, pkhint can't help you for now. Please configure PyKota manually."
220        else :
221            print _("\nPut the following lines into your /etc/pykota/pykota.conf file :\n")
222            print _("# BEWARE : if software accounting is suggested, this doesn't mean")
223            print _("# that hardware accounting wouldn't work, this only means that PyKota")
224            print _("# wasn't able to autodetect which hardware accounting method to use.")
225            for (printer, accounter) in configuration :
226                print "[%s]" % printer
227                print "accounter: %s" % accounter
228                print   
229       
230if __name__ == "__main__" : 
231    retcode = 0
232    try :
233        short_options = "hv"
234        long_options = ["help", "version"]
235       
236        # Initializes the command line tool
237        manager = PKHint(doc=__doc__)
238        manager.deferredInit()
239       
240        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
241       
242        # sets long options
243        options["help"] = options["h"] or options["help"]
244        options["version"] = options["v"] or options["version"]
245       
246        if options["help"] :
247            manager.display_usage_and_quit()
248        elif options["version"] :
249            manager.display_version_and_quit()
250        else :
251            if not args :
252                args = [ "*" ]
253            retcode = manager.main(args, options)
254    except KeyboardInterrupt :       
255        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
256    except SystemExit :       
257        pass
258    except :
259        try :
260            manager.crashed("pkhint failed")
261        except :   
262            crashed("pkhint failed")
263        retcode = -1
264       
265    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.