root / pykota / trunk / bin / edpykota @ 1120

Revision 1120, 25.4 kB (checked in by jalet, 21 years ago)

Comment added.

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