root / pykota / trunk / bin / edpykota @ 1063

Revision 1063, 23.8 kB (checked in by jalet, 21 years ago)

Ensure that the user don't pass more than two prices for a printer.

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