root / pykota / trunk / bin / pkhint @ 2258

Revision 2216, 10.7 kB (checked in by jerome, 20 years ago)

Now exits with no traceback in case of Ctrl+C

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