root / pykota / trunk / bin / edpykota @ 1043

Revision 1043, 23.7 kB (checked in by jalet, 21 years ago)

Should be ready for testing :-)

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