root / pykota / trunk / bin / pkprinters @ 3051

Revision 3051, 16.4 kB (checked in by jerome, 18 years ago)

Now pkprinters --list indicates if a particular printer is routed through PyKota or not.

  • 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 Printers Manager
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003, 2004, 2005, 2006 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 os
28import sys
29import pwd
30
31from pykota.tool import Percent, PyKotaTool, PyKotaCommandLineError, crashed, N_
32from pykota.storage import StoragePrinter
33
34from pkipplib import pkipplib
35
36__doc__ = N_("""pkprinters v%(__version__)s (c) %(__years__)s %(__author__)s
37
38A Printers Manager for PyKota.
39
40command line usage :
41
42  pkprinters [options] printer1 printer2 printer3 ... printerN
43
44options :
45
46  -v | --version       Prints pkprinters's version number then exits.
47  -h | --help          Prints this message then exits.
48 
49  -a | --add           Adds printers if they don't exist on the Quota
50                       Storage Server. If they exist, they are modified
51                       unless -s|--skipexisting is also used.
52                       
53  -d | --delete        Deletes printers from the quota storage.
54 
55  -D | --description d Adds a textual description to printers.
56                       
57  -c | --charge p[,j]  Sets the price per page and per job to charge.
58                       Job price is optional.
59                       If both are to be set, separate them with a comma.
60                       Floating point and negative values are allowed.
61 
62  -g | --groups pg1[,pg2...] Adds or Remove the printer(s) to the printer
63                       groups pg1, pg2, etc... which must already exist.
64                       A printer group is just like a normal printer,
65                       only that it is usually unknown from the printing
66                       system. Create printer groups exactly the same
67                       way that you create printers, then add other
68                       printers to them with this option.
69                       Accounting is done on a printer and on all
70                       the printer groups it belongs to, quota checking
71                       is done on a printer and on all the printer groups
72                       it belongs to.
73                       If the --remove option below is not used, the
74                       default action is to add printers to the specified
75                       printer groups.
76                       
77  -l | --list          List informations about the printer(s) and the
78                       printers groups it is a member of.
79                       
80  -r | --remove        In combination with the --groups option above,                       
81                       remove printers from the specified printers groups.
82                       
83  -s | --skipexisting  In combination with the --add option above, tells
84                       pkprinters to not modify existing printers.
85                       
86  -m | --maxjobsize s  Sets the maximum job size allowed on the printer
87                       to s pages.
88                       
89  -p | --passthrough   Activate passthrough mode for the printer. In this
90                       mode, users are allowed to print without any impact
91                       on their quota or account balance.
92                       
93  -n | --nopassthrough Deactivate passthrough mode for the printer.
94                       Without -p or -n, printers are created in
95                       normal mode, i.e. no passthrough.
96 
97  printer1 through printerN can contain wildcards if the --add option
98  is not set.
99 
100examples :                             
101
102  $ pkprinters --add -D "HP Printer" --charge 0.05,0.1 hp2100 hp2200 hp8000
103 
104  Will create three printers named hp2100, hp2200 and hp8000.
105  Their price per page will be set at 0.05 unit, and their price
106  per job will be set at 0.1 unit. Units are in your own currency,
107  or whatever you want them to mean.
108  All of their descriptions will be set to the string "HP Printer".
109  If any of these printers already exists, it will also be modified
110  unless the -s|--skipexisting command line option is also used.
111           
112  $ pkprinters --delete "*"
113 
114  This will completely delete all printers and associated quota information,
115  as well as their job history. USE WITH CARE !
116 
117  $ pkprinters --groups Laser,HP "hp*"
118 
119  This will put all printers which name matches "hp*" into printers groups
120  Laser and HP, which MUST already exist.
121 
122  $ pkprinters --groups LexMark --remove hp2200
123 
124  This will remove the hp2200 printer from the LexMark printer group.
125""")
126       
127class PKPrinters(PyKotaTool) :       
128    """A class for a printers manager."""
129    def modifyPrinter(self, printer, charges, perpage, perjob, description, passthrough, nopassthrough, maxjobsize) :
130        if charges :
131            printer.setPrices(perpage, perjob)   
132        if description is not None :        # NB : "" is allowed !
133            printer.setDescription(description)
134        if nopassthrough :   
135            printer.setPassThrough(False)
136        if passthrough :   
137            printer.setPassThrough(True)
138        if maxjobsize is not None :
139            printer.setMaxJobSize(maxjobsize)
140           
141    def managePrintersGroups(self, pgroups, printer, remove) :       
142        """Manage printer group membership."""
143        for pgroup in pgroups :
144            if remove :
145                pgroup.delPrinterFromGroup(printer)
146            else :
147                pgroup.addPrinterToGroup(printer)   
148               
149    def getPrinterDeviceURI(self, printername) :           
150        """Returns the Device URI attribute for a particular printer."""
151        cups = pkipplib.CUPS()
152        req = cups.newRequest(pkipplib.IPP_GET_PRINTER_ATTRIBUTES)
153        req.operation["printer-uri"] = ("uri", cups.identifierToURI("printers", printername))
154        try :
155            return cups.doRequest(req).printer["device-uri"][0][1]
156        except :   
157            return ""
158       
159    def isPrinterCaptured(self, printername) :           
160        """Returns True if the printer is already redirected through PyKota's backend, else False."""
161        if self.getPrinterDeviceURI(printername).find("cupspykota:") != -1 :
162            return True
163        else :   
164            return False
165       
166    def main(self, names, options) :
167        """Manage printers."""
168        if (not self.config.isAdmin) and (not options["list"]) :
169            raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command."))
170           
171        if not options["list"] :   
172            percent = Percent(self)
173           
174        if not options["add"] :
175            if not options["list"] :
176                percent.display("%s..." % _("Extracting datas"))
177            if not names :      # NB : can't happen for --delete because it's catched earlier
178                names = ["*"]
179            printers = self.storage.getMatchingPrinters(",".join(names))
180            if not printers :
181                raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(names)
182            if not options["list"] :   
183                percent.setSize(len(printers))
184               
185        if options["list"] :
186            for printer in printers :
187                parents = ", ".join([p.Name for p in self.storage.getParentPrinters(printer)])
188                print "%s [%s] (%s + #*%s)" % \
189                      (printer.Name, printer.Description, printer.PricePerJob, \
190                       printer.PricePerPage)
191                print "    %s" % (_("Passthrough mode : %s") % ((printer.PassThrough and _("ON")) or _("OFF")))
192                print "    %s" % (_("Maximum job size : %s") % ((printer.MaxJobSize and (_("%s pages") % printer.MaxJobSize)) or _("Unlimited")))
193                print "    %s" % (_("Routed through PyKota : %s") % ((self.isPrinterCaptured(printer.Name) and _("YES")) or _("NO")))
194                if parents : 
195                    print "    %s %s" % (_("in"), parents)
196                print   
197        elif options["delete"] :   
198            percent.display("\n%s..." % _("Deletion"))
199            self.storage.deleteManyPrinters(printers)
200            percent.display("\n")
201        else :
202            if options["groups"] :       
203                printersgroups = self.storage.getMatchingPrinters(options["groups"])
204                if not printersgroups :
205                    raise PyKotaCommandLineError, _("There's no printer matching %s") % " ".join(options["groups"].split(','))
206            else :         
207                printersgroups = []
208                   
209            if options["charge"] :
210                try :
211                    charges = [float(part) for part in options["charge"].split(',', 1)]
212                except ValueError :   
213                    raise PyKotaCommandLineError, _("Invalid charge amount value %s") % options["charge"]
214                else :   
215                    if len(charges) > 2 :
216                        charges = charges[:2]
217                    if len(charges) != 2 :
218                        charges = [charges[0], None]
219                    (perpage, perjob) = charges
220            else :       
221                charges = perpage = perjob = None
222                   
223            if options["maxjobsize"] :       
224                try :
225                    maxjobsize = int(options["maxjobsize"])
226                    if maxjobsize < 0 :
227                        raise ValueError
228                except ValueError :   
229                    raise PyKotaCommandLineError, _("Invalid maximum job size value %s") % options["maxjobsize"]
230            else :       
231                maxjobsize = None
232                   
233            description = options["description"]
234            if description :
235                description = description.strip()
236               
237            nopassthrough = options["nopassthrough"]   
238            passthrough = options["passthrough"]
239            remove = options["remove"]
240            skipexisting = options["skipexisting"]
241            self.storage.beginTransaction()
242            try :
243                if options["add"] :   
244                    percent.display("%s...\n" % _("Creation"))
245                    percent.setSize(len(names))
246                    for pname in names :
247                        if self.isValidName(pname) :
248                            printer = StoragePrinter(self.storage, pname)
249                            self.modifyPrinter(printer, charges, perpage, perjob, \
250                                           description, passthrough, \
251                                           nopassthrough, maxjobsize)
252                            oldprinter = self.storage.addPrinter(printer)               
253                            if oldprinter is not None :
254                                if skipexisting :
255                                    self.logdebug(_("Printer %s already exists, skipping.") % pname)
256                                else :   
257                                    self.logdebug(_("Printer %s already exists, will be modified.") % pname)
258                                    self.modifyPrinter(oldprinter, charges, \
259                                               perpage, perjob, description, \
260                                               passthrough, nopassthrough, \
261                                               maxjobsize)
262                                    oldprinter.save()           
263                                    self.managePrintersGroups(printersgroups, oldprinter, remove)
264                            elif printersgroups :       
265                                self.managePrintersGroups(printersgroups, \
266                                                          self.storage.getPrinter(pname), \
267                                                          remove)
268                        else :   
269                            raise PyKotaCommandLineError, _("Invalid printer name %s") % pname
270                        percent.oneMore()
271                else :       
272                    percent.display("\n%s...\n" % _("Modification"))
273                    for printer in printers :       
274                        self.modifyPrinter(printer, charges, perpage, perjob, \
275                                           description, passthrough, \
276                                           nopassthrough, maxjobsize)
277                        printer.save()   
278                        self.managePrintersGroups(printersgroups, printer, remove)
279                        percent.oneMore()
280            except :                   
281                self.storage.rollbackTransaction()
282                raise
283            else :   
284                self.storage.commitTransaction()
285               
286        if not options["list"] :
287            percent.done()
288                     
289if __name__ == "__main__" : 
290    retcode = 0
291    try :
292        short_options = "hvac:D:dg:lrsnpm:"
293        long_options = ["help", "version", "add", "charge=", "description=", \
294                        "delete", "groups=", "list", "remove", \
295                        "skipexisting", "passthrough", "nopassthrough", \
296                        "maxjobsize="]
297       
298        # Initializes the command line tool
299        manager = PKPrinters(doc=__doc__)
300        manager.deferredInit()
301       
302        # parse and checks the command line
303        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
304       
305        # sets long options
306        options["help"] = options["h"] or options["help"]
307        options["version"] = options["v"] or options["version"]
308        options["add"] = options["a"] or options["add"]
309        options["charge"] = options["c"] or options["charge"]
310        options["description"] = options["D"] or options["description"]
311        options["delete"] = options["d"] or options["delete"] 
312        options["groups"] = options["g"] or options["groups"]
313        options["list"] = options["l"] or options["list"]
314        options["remove"] = options["r"] or options["remove"]
315        options["skipexisting"] = options["s"] or options["skipexisting"]
316        options["maxjobsize"] = options["m"] or options["maxjobsize"]
317        options["passthrough"] = options["p"] or options["passthrough"]
318        options["nopassthrough"] = options["n"] or options["nopassthrough"]
319       
320        if options["help"] :
321            manager.display_usage_and_quit()
322        elif options["version"] :
323            manager.display_version_and_quit()
324        elif (options["delete"] and (options["add"] or options["groups"] or options["charge"] or options["remove"] or options["description"])) \
325           or (options["skipexisting"] and not options["add"]) \
326           or (options["list"] and (options["add"] or options["delete"] or options["groups"] or options["charge"] or options["remove"] or options["description"])) \
327           or (options["passthrough"] and options["nopassthrough"]) \
328           or (options["remove"] and options["add"]) :
329            raise PyKotaCommandLineError, _("incompatible options, see help.")
330        elif options["remove"] and not options["groups"] :
331            raise PyKotaCommandLineError, _("You have to pass printer groups names on the command line")
332        elif (not args) and (options["add"] or options["delete"]) :
333            raise PyKotaCommandLineError, _("You have to pass printer names on the command line")
334        else :
335            retcode = manager.main(args, options)
336    except KeyboardInterrupt :       
337        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
338        retcode = -3
339    except PyKotaCommandLineError, msg :   
340        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
341        retcode = -2
342    except SystemExit :       
343        pass
344    except :
345        try :
346            manager.crashed("pkprinters failed")
347        except :   
348            crashed("pkprinters failed")
349        retcode = -1
350
351    try :
352        manager.storage.close()
353    except (TypeError, NameError, AttributeError) :   
354        pass
355       
356    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.