root / pykota / trunk / bin / edpykota @ 1031

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

More work on LDAP

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