root / pykota / trunk / bin / edpykota @ 1069

Revision 1069, 24.0 kB (checked in by jalet, 21 years ago)

Small fix

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