root / pykota / trunk / bin / edpykota @ 1023

Revision 1023, 24.6 kB (checked in by jalet, 21 years ago)

Severe bug wrt account balance setting should be corrected.

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