root / pykota / trunk / bin / edpykota @ 1070

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