root / pykota / trunk / bin / edpykota @ 1089

Revision 1089, 24.7 kB (checked in by jalet, 21 years ago)

Prevents email messages to be sent at modification/creation time for
a user/group quota

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