root / pykota / trunk / bin / edpykota @ 975

Revision 975, 23.5 kB (checked in by jalet, 21 years ago)

Better error handling.

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