root / pykota / trunk / bin / edpykota @ 927

Revision 927, 22.9 kB (checked in by jalet, 21 years ago)

Groups quota work now !

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