root / pykota / trunk / bin / pkhint @ 1483

Revision 1483, 11.1 kB (checked in by jalet, 20 years ago)

Big code changes to completely remove the need for "requester" directives,
jsut use "hardware(... your previous requester directive's content ...)"

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