root / pykota / trunk / bin / edpykota @ 1088

Revision 1078, 24.3 kB (checked in by jalet, 21 years ago)

Fixed typo when using edpykota --prototype

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