root / pykota / trunk / bin / edpykota @ 1064

Revision 1064, 24.0 kB (checked in by jalet, 21 years ago)

More on previous fix.

  • 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
3# PyKota Print Quota Editor
4#
5# PyKota - Print Quotas for CUPS and LPRng
6#
7# (c) 2003 Jerome Alet <alet@librelogiciel.com>
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
21#
22# $Id$
23#
24# $Log$
25# Revision 1.50  2003/07/05 12:33:53  jalet
26# More on previous fix.
27#
28# Revision 1.49  2003/07/05 12:32:07  jalet
29# Ensure that the user don't pass more than two prices for a printer.
30#
31# Revision 1.48  2003/06/25 19:52:30  jalet
32# Should be ready for testing :-)
33#
34# Revision 1.47  2003/06/25 14:10:01  jalet
35# Hey, it may work (edpykota --reset excepted) !
36#
37# Revision 1.46  2003/06/16 11:59:09  jalet
38# More work on LDAP
39#
40# Revision 1.45  2003/06/11 19:32:00  jalet
41# Severe bug wrt account balance setting should be corrected.
42#
43# Revision 1.44  2003/04/29 22:03:38  jalet
44# Better error handling.
45#
46# Revision 1.43  2003/04/23 22:13:56  jalet
47# Preliminary support for LPRng added BUT STILL UNTESTED.
48#
49# Revision 1.42  2003/04/17 13:38:47  jalet
50# Docstring corrected for better manual page
51#
52# Revision 1.41  2003/04/16 12:35:49  jalet
53# Groups quota work now !
54#
55# Revision 1.40  2003/04/16 08:22:09  jalet
56# More strict error detection.
57# Minor code rewrite to avoid some repetitive tests.
58#
59# Revision 1.39  2003/04/16 08:01:53  jalet
60# edpykota --charge command line option works now.
61#
62# Revision 1.38  2003/04/15 22:02:43  jalet
63# More complete docstring
64#
65# Revision 1.37  2003/04/15 21:58:33  jalet
66# edpykota now accepts a --delete option.
67# Preparation to allow edpykota to accept much more command line options
68# (WARNING : docstring is OK, but code isn't !)
69#
70# Revision 1.36  2003/04/15 13:55:28  jalet
71# Options --limitby and --balance added to edpykota
72#
73# Revision 1.35  2003/04/15 13:06:39  jalet
74# Allow to add a printer without any user
75#
76# Revision 1.34  2003/04/11 16:51:11  jalet
77# Bug fix for edpykota --add with users who already had a quota on the printer.
78#
79# Revision 1.33  2003/04/10 21:47:20  jalet
80# Job history added. Upgrade script neutralized for now !
81#
82# Revision 1.32  2003/04/08 21:31:39  jalet
83# (anything or 0) = anything !!! Go back to school Jerome !
84#
85# Revision 1.31  2003/04/08 21:13:44  jalet
86# Prepare --groups option to work.
87#
88# Revision 1.30  2003/04/08 21:10:18  jalet
89# Checks --groups option presence instead of --users because --users is the default.
90#
91# Revision 1.29  2003/04/05 09:28:56  jalet
92# Unnecessary message was logged
93#
94# Revision 1.28  2003/03/29 13:45:26  jalet
95# GPL paragraphs were incorrectly (from memory) copied into the sources.
96# Two README files were added.
97# Upgrade script for PostgreSQL pre 1.01 schema was added.
98#
99# Revision 1.27  2003/03/10 00:23:04  jalet
100# Bad english
101#
102# Revision 1.26  2003/03/10 00:11:27  jalet
103# Cleaner example.
104#
105# Revision 1.25  2003/03/09 23:56:21  jalet
106# Option noquota added to do accounting only.
107#
108# Revision 1.24  2003/02/27 23:48:41  jalet
109# Correctly maps PyKota's log levels to syslog log levels
110#
111# Revision 1.23  2003/02/27 22:55:20  jalet
112# WARN log priority doesn't exist.
113#
114# Revision 1.22  2003/02/27 09:37:02  jalet
115# Wildcards seem to work now
116#
117# Revision 1.21  2003/02/27 09:04:46  jalet
118# user and group names can be passed as wildcards if the --add option
119# is not set. The default is to act on all users or groups.
120#
121# Revision 1.20  2003/02/10 12:07:30  jalet
122# Now repykota should output the recorded total page number for each printer too.
123#
124# Revision 1.19  2003/02/09 13:40:29  jalet
125# typo
126#
127# Revision 1.18  2003/02/09 12:56:53  jalet
128# Internationalization begins...
129#
130# Revision 1.17  2003/02/08 22:47:23  jalet
131# Option --reset can now be used without having to use soft and hard limits
132# on the command line.
133#
134# Revision 1.16  2003/02/08 22:39:46  jalet
135# --reset command line option added
136#
137# Revision 1.15  2003/02/08 22:20:01  jalet
138# Clarification on why we don't check with /etc/passwd to see if the user
139# name is valid or not.
140#
141# Revision 1.14  2003/02/08 22:18:15  jalet
142# Now checks user and group names for validity before adding them
143#
144# Revision 1.13  2003/02/08 22:09:02  jalet
145# Only printer was added the first time.
146#
147# Revision 1.12  2003/02/08 21:44:49  jalet
148# Python 2.1 string module doesn't define ascii_letters
149#
150# Revision 1.11  2003/02/08 09:42:44  jalet
151# Better handle wrong or bad command line arguments
152#
153# Revision 1.10  2003/02/08 09:39:20  jalet
154# typos
155#
156# Revision 1.9  2003/02/08 09:38:06  jalet
157# Badly placed test
158#
159# Revision 1.8  2003/02/07 22:53:57  jalet
160# Checks if printer name is valid before adding it
161#
162# Revision 1.7  2003/02/07 22:17:58  jalet
163# Incomplete test
164#
165# Revision 1.6  2003/02/07 22:13:13  jalet
166# Perhaps edpykota is now able to add printers !!! Oh, stupid me !
167#
168# Revision 1.5  2003/02/06 14:49:04  jalet
169# edpykota should be ok now
170#
171# Revision 1.4  2003/02/06 14:28:59  jalet
172# edpykota should be ok, minus some typos
173#
174# Revision 1.3  2003/02/06 10:47:21  jalet
175# Documentation string and command line options didn't match.
176#
177# Revision 1.2  2003/02/06 10:39:23  jalet
178# Preliminary edpykota work.
179#
180# Revision 1.1  2003/02/05 21:41:09  jalet
181# Skeletons added for all command line tools
182#
183#
184#
185
186import sys
187
188from pykota import version
189from pykota.tool import PyKotaTool, PyKotaToolError
190from pykota.config import PyKotaConfigError
191from pykota.storage import PyKotaStorageError
192
193__doc__ = """edpykota v%s (C) 2003 C@LL - Conseil Internet & Logiciels Libres
194A Print Quota editor for PyKota.
195
196command line usage :
197
198  edpykota [options] user1 user2 ... userN
199 
200  edpykota [options] group1 group2 ... groupN
201
202options :
203
204  -v | --version       Prints edpykota's version number then exits.
205  -h | --help          Prints this message then exits.
206 
207  -a | --add           Adds users and/or printers if they don't
208                       exist on the Quota Storage Server.
209                       
210  -d | --delete        Deletes users/groups from the quota storage.
211                       Printers are never deleted.
212                       
213  -c | --charge p[,j]  Sets the price per page and per job to charge
214                       for a particular printer. Job price is optional.
215                       If both are to be set, separate them with a comma.
216                       Floating point values are allowed.
217 
218  -i | --ingroups g1[,g2...]  Puts the users into each of the groups
219                              listed, separated by commas. The groups
220                              must already exist in the Quota Storage.
221 
222  -u | --users         Edit users print quotas, this is the default.
223 
224  -P | --printer p     Edit quotas on printer p only. Actually p can
225                       use wildcards characters to select only
226                       some printers. The default value is *, meaning
227                       all printers.
228 
229  -g | --groups        Edit groups print quotas instead of users.
230                         
231  -p | --prototype u|g Uses user u or group g as a prototype to set
232                       print quotas
233                       
234  -n | --noquota       Doesn't set a quota but only does accounting.
235 
236  -r | --reset         Resets the printed page counter for the user
237                       or group to zero. The life time page counter
238                       is kept unchanged.
239                       
240  -l | --limitby l     Choose if the user/group is limited in printing                     
241                       by its account balance or by its page quota.
242                       The default value is 'quota'. Allowed values
243                       are 'quota' and 'balance'.
244                       
245  -b | --balance b     Sets the user's account balance to b.                     
246                       Account balance may be increase or decreased
247                       if b is prefixed with + or -.
248                       WARNING : when decreasing account balance,
249                       the total paid so far by the user is decreased
250                       too.
251                       Groups don't have a real balance, but the
252                       sum of their users' account balance.
253                       
254  -S | --softlimit sl  Sets the quota soft limit to sl pages.                       
255 
256  -H | --hardlimit hl  Sets the quota hard limit to hl pages.
257 
258  user1 through userN and group1 through groupN can use wildcards
259  if the --add option is not set.
260 
261examples :                             
262
263  $ edpykota -p jerome john paul george ringo
264 
265  This will set print quotas for the users john, paul, george and ringo
266  to the same values than user jerome. User jerome must exist.
267 
268  $ edpykota --printer lp -S 50 -H 60 jerome
269 
270  This will set jerome's print quota on the lp printer to a soft limit
271  of 50 pages, and a hard limit of 60 pages. If either user jerome or
272  printer lp doesn't exist on the Quota Storage Server then nothing is done.
273
274  $ edpykota --add --printer lp --ingroups coders,it -S 50 -H 60 jerome
275 
276  Same as above, but if either user jerome or printer lp doesn't exist
277  on the Quota Storage Server they are automatically added. Also
278  user jerome is put into the groups "coders" and "it" which must
279  already exist in the Quota Storage.
280  WARNING : the CUPS PPD file for this printer or the /etc/printcap
281            file (depending on your printing backend)  must still be
282            modified manually, as well as pykota's configuration file
283            for a new printer to be managed successfully.
284           
285  $ edpykota -g -S 500 -H 550 financial support           
286 
287  This will set print quota soft limit to 500 pages and hard limit
288  to 550 pages for groups financial and support on all printers.
289 
290  $ edpykota --reset jerome "jo*"
291 
292  This will reset jerome's page counter to zero on all printers, as
293  well as every user whose name begins with 'jo'.
294  Their life time page counter on each printer will be kept unchanged.
295 
296  $ edpykota --printer hpcolor --noquota jerome
297 
298  This will tell PyKota to not limit jerome when printing on the
299  hpcolor printer. All his jobs will be allowed on this printer, but
300  accounting of the pages he prints will still be kept.
301  Print Quotas for jerome on other printers are unchanged.
302 
303  $ edpykota --limitby balance jerome
304 
305  This will tell PyKota to limit jerome by his account's balance
306  when printing.
307 
308  $ edpykota --balance +10.0 jerome
309 
310  This will increase jerome's account balance by 10.0 (in your
311  own currency). You can decrease the account balance with a
312  dash prefix, and set it to a fixed amount with no prefix.
313 
314  $ edpykota --delete jerome rachel
315 
316  This will completely delete jerome and rachel from the Quota Storage
317  database. All their quotas and jobs will be deleted too.
318 
319  $ edpykota --printer lp --charge 0.1
320 
321  This will set the page price for printer lp to 0.1. Job price
322  will not be changed.
323
324This program is free software; you can redistribute it and/or modify
325it under the terms of the GNU General Public License as published by
326the Free Software Foundation; either version 2 of the License, or
327(at your option) any later version.
328
329This program is distributed in the hope that it will be useful,
330but WITHOUT ANY WARRANTY; without even the implied warranty of
331MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
332GNU General Public License for more details.
333
334You should have received a copy of the GNU General Public License
335along with this program; if not, write to the Free Software
336Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
337
338Please e-mail bugs to: %s""" % (version.__version__, version.__author__)
339       
340class EdPyKota(PyKotaTool) :       
341    """A class for edpykota."""
342    def main(self, names, options) :
343        """Edit user or group quotas."""
344       
345        suffix = (options["groups"] and "Group") or "User"       
346       
347        softlimit = hardlimit = None   
348        if not options["noquota"] :
349            if options["softlimit"] :
350                try :
351                    softlimit = int(options["softlimit"].strip())
352                except ValueError :   
353                    raise PyKotaToolError, _("Invalid softlimit value %s.") % options["softlimit"]
354            if options["hardlimit"] :
355                try :
356                    hardlimit = int(options["hardlimit"].strip())
357                except ValueError :   
358                    raise PyKotaToolError, _("Invalid hardlimit value %s.") % options["hardlimit"]
359            if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) :       
360                # error, exchange them
361                self.logger.log_message(_("Hard limit %i is less than soft limit %i, values will be exchanged.") % (hardlimit, softlimit))
362                (softlimit, hardlimit) = (hardlimit, softlimit)
363           
364        balance = options["balance"]
365        if balance :
366            balance = balance.strip()
367            try :
368                balancevalue = float(balance)
369            except ValueError :   
370                raise PyKotaToolError, _("Invalid balance value %s" % options["balance"])
371           
372        if options["charge"] :
373            try :
374                charges = [float(part) for part in options["charge"].split(',', 1)]
375            except ValueError :   
376                raise PyKotaToolError, _("Invalid charge amount value %s" % options["charge"])
377            else :   
378                if len(charges) > 2 :
379                    charges = charges[:2]
380                if len(charges) != 2 :
381                    charges = [charges[0], None]
382                   
383        limitby = options["limitby"]
384        if limitby :
385            limitby = limitby.strip().lower()
386        if limitby and (limitby not in ('quota', 'balance')) :   
387            raise PyKotaToolError, _("Invalid limitby value %s" % options["limitby"])
388           
389        if options["ingroups"] :   
390            groupnames = [gname.strip() for gname in options["ingroups"].split(',')]
391        else :   
392            groupnames = []
393           
394        if options["prototype"] :   
395            protoentry = getattr(self.storage, "get%s" % suffix)(options["prototype"])
396           
397        printeradded = 0
398        printers = self.storage.getMatchingPrinters(options["printer"])
399        if not printers :
400            pname = options["printer"]
401            if options["add"] and pname :
402                if self.isValidName(pname) :
403                    printers = [ self.storage.addPrinter(pname) ]
404                    if printers[0].Exists :
405                        printeradded = 1
406                    else :   
407                        raise PyKotaToolError, _("Impossible to add printer %s") % pname
408                else :   
409                    raise PyKotaToolError, _("Invalid printer name %s") % pname
410            else :
411                raise PyKotaToolError, _("There's no printer matching %s") % pname
412        if not names :   
413            if options["add"] and not printeradded :
414                raise PyKotaToolError, _("You have to pass user or group names on the command line")
415            else :   
416                names = [ "*" ] # all users
417               
418        changed = {} # tracks changes made at the user/group level
419        for printer in printers :
420            if options["charge"] :
421                (perpage, perjob) = charges
422                printer.setPrices(perpage, perjob)   
423               
424            if options["prototype"] :
425                if protoentry.Exists :
426                    protoquota = getattr(self.storage, "get%PQuota" % suffix)(protoentry, printer)
427                    if not protoquota.Exists :
428                        self.logger.log_message(_("Prototype %s not found in Quota Storage for printer %s.") % (protoentry.Name, printer.Name))
429                        continue    # skip this printer
430                    else :   
431                        (softlimit, hardlimit) = (protoquota.SoftLimit, protoquota.HardLimit)
432                else :       
433                    self.logger.log_message(_("Prototype object %s not found in Quota Storage.") % protoentry.Name)
434            if not options["noquota"] :   
435                if hardlimit is None :   
436                    hardlimit = softlimit
437                    if hardlimit is not None :
438                        self.logger.log_message(_("Undefined hard limit set to soft limit (%s) on printer %s.") % (str(hardlimit), printer.Name))
439                if softlimit is None :   
440                    softlimit = hardlimit
441                    if softlimit is not None :
442                        self.logger.log_message(_("Undefined soft limit set to hard limit (%s) on printer %s.") % (str(softlimit), printer.Name))
443                       
444            if options["add"] :   
445                allentries = []   
446                for name in names :
447                    entry = getattr(self.storage, "get%s" % suffix)(name)
448                    entrypquota = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer)
449                    allentries.append((entry, entrypquota))
450            else :   
451                allentries = getattr(self.storage, "getPrinter%ssAndQuotas" % suffix)(printer)
452               
453            for (entry, entrypquota) in [(e, q) for (e, q) in allentries if self.matchString(e.Name, names)] :
454                if not changed.has_key(entry.Name) :
455                    changed[entry.Name] = {}
456                if not entrypquota.Exists :
457                    # not found
458                    if options["add"] :
459                        # In case we want to add something, it is crucial
460                        # that we DON'T check with the system accounts files
461                        # like /etc/passwd because users may be defined
462                        # only remotely
463                        if self.isValidName(entry.Name) :
464                            if not entry.Exists :
465                                entry = getattr(self.storage, "add%s" % suffix)(entry)
466                            entrypquota = getattr(self.storage, "add%sPQuota" % suffix)(entry, printer)
467                        else :   
468                            if options["groups"] :
469                                self.logger.log_message(_("Invalid group name %s") % entry.Name)
470                            else :   
471                                self.logger.log_message(_("Invalid user name %s") % entry.Name)
472                if not entrypquota.Exists :     
473                    self.logger.log_message(_("Quota not found for object %s on printer %s.") % (entry.Name, printer.Name))
474                else :   
475                    if options["delete"] :
476                        entry.delete()
477                    else :
478                        if options["noquota"] or options["prototype"] or ((softlimit is not None) and (hardlimit is not None)) :
479                            entrypquota.setLimits(softlimit, hardlimit)
480                        if limitby :
481                            if changed[entry.Name].get("limitby") is None :
482                                entry.setLimitBy(limitby)
483                                changed[entry.Name]["limitby"] = limitby
484                       
485                        if not options["groups"] :
486                            if options["reset"] :
487                                entrypquota.reset()
488                               
489                            if balance :
490                                if changed[entry.Name].get("balance") is None :
491                                    if balance.startswith("+") or balance.startswith("-") :
492                                        newbalance = float(entry.AccountBalance or 0.0) + balancevalue
493                                        newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue
494                                        entry.setAccountBalance(newbalance, newlifetimepaid)
495                                    else :
496                                        diff = balancevalue - float(entry.AccountBalance or 0.0)
497                                        newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff
498                                        entry.setAccountBalance(balancevalue, newlifetimepaid)
499                                    changed[entry.Name]["balance"] = balance
500                            for groupname in groupnames :       
501                                if groupname not in changed[name]["ingroups"] :
502                                    group = self.storage.getGroup(groupname)
503                                    if group.Exists :
504                                        self.storage.addUserToGroup(entry, group)
505                                        changed[entry.Name]["ingroups"].append(groupname)
506                                    else :
507                                        self.logger.log_message(_("Group %s not found in the PyKota Storage.") % groupname)
508                                       
509                        getattr(self, "warn%sPQuota" % suffix)(entrypquota)   
510                     
511if __name__ == "__main__" : 
512    try :
513        defaults = { \
514                     "printer" : "*", \
515                   }
516        short_options = "vhdc:l:b:i:naugrp:P:S:H:"
517        long_options = ["help", "version", "charge=", "delete", "limitby=", "balance=", "ingroups=", "noquota", "add", "users", "groups", "reset", "prototype=", "printer=", "softlimit=", "hardlimit="]
518       
519        # Initializes the command line tool
520        editor = EdPyKota(doc=__doc__)
521       
522        # parse and checks the command line
523        (options, args) = editor.parseCommandline(sys.argv[1:], short_options, long_options)
524       
525        # sets long options
526        options["help"] = options["h"] or options["help"]
527        options["version"] = options["v"] or options["version"]
528        options["add"] = options["a"] or options["add"]
529        options["users"] = options["u"] or options["users"]
530        options["groups"] = options["g"] or options["groups"]
531        options["prototype"] = options["p"] or options["prototype"]
532        options["printer"] = options["P"] or options["printer"] or defaults["printer"]
533        options["softlimit"] = options["S"] or options["softlimit"]
534        options["hardlimit"] = options["H"] or options["hardlimit"] 
535        options["reset"] = options["r"] or options["reset"] 
536        options["noquota"] = options["n"] or options["noquota"]
537        options["limitby"] = options["l"] or options["limitby"]
538        options["balance"] = options["b"] or options["balance"] 
539        options["delete"] = options["d"] or options["delete"] 
540        options["ingroups"] = options["i"] or options["ingroups"]
541        options["charge"] = options["c"] or options["charge"]
542       
543        if options["help"] :
544            editor.display_usage_and_quit()
545        elif options["version"] :
546            editor.display_version_and_quit()
547        elif options["users"] and options["groups"] :   
548            raise PyKotaToolError, _("incompatible options, see help.")
549        elif (options["add"] or options["prototype"]) and options["delete"] :   
550            raise PyKotaToolError, _("incompatible options, see help.")
551        elif (options["softlimit"] or options["hardlimit"]) and options["prototype"] :   
552            raise PyKotaToolError, _("incompatible options, see help.")
553        elif options["noquota"] and (options["prototype"] or options["hardlimit"] or options["softlimit"]) :
554            raise PyKotaToolError, _("incompatible options, see help.")
555        elif options["groups"] and (options["balance"] or options["ingroups"]) :
556            raise PyKotaToolError, _("incompatible options, see help.")
557        else :
558            sys.exit(editor.main(args, options))
559    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError), msg :           
560        sys.stderr.write("%s\n" % msg)
561        sys.stderr.flush()
562        sys.exit(-1)
Note: See TracBrowser for help on using the browser.