root / pykota / trunk / bin / pkusers @ 2765

Revision 2765, 19.9 kB (checked in by jerome, 19 years ago)

Optimized pkbcodes like edpykota.

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKota Users Manager
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003, 2004, 2005, 2006 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22#
23# $Id$
24#
25#
26
27import os
28import sys
29import pwd
30import grp
31
32from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
33
34__doc__ = N_("""pkusers v%(__version__)s (c) %(__years__)s %(__author__)s
35
36An Users and Groups Manager for PyKota.
37
38command line usage :
39
40  pkusers [options] user1 user2 user3 ... userN
41 
42or : 
43
44  pkusers --groups [options] group1 group2 group3 ... groupN
45
46options :
47
48  -v | --version       Prints pkusers's version number then exits.
49  -h | --help          Prints this message then exits.
50 
51  -a | --add           Adds users if they don't exist on the database.
52                       If they exist, they are modified unless
53                       -s|--skipexisting is also used.
54                       
55  -d | --delete        Deletes users from the quota storage.
56 
57  -D | --description d Adds a textual description to users or groups.
58                       
59  -g | --groups        Edit users groups instead of users.
60                         
61  -o | --overcharge f  Sets the overcharging factor applied to the user
62                       when computing the cost of a print job. Positive or
63                       negative floating point values are allowed,
64                       this allows you to do some really creative
65                       things like giving money to an user whenever
66                       he prints. The number of pages in a print job
67                       is not modified by this coefficient, only the
68                       cost of the job for a particular user.
69                       Only users have such a coefficient.
70 
71  -i | --ingroups g1[,g2...]  Puts the users into each of the groups
72                              listed, separated by commas. The groups
73                              must already exist in the Quota Storage.
74                       
75  -L | --list          Lists users or groups.
76 
77  -l | --limitby l     Choose if the user/group is limited in printing                     
78                       by its account balance or by its page quota.
79                       The default value is 'quota'. Allowed values
80                       are 'quota' 'balance' 'noquota' 'noprint' 
81                       and 'nochange' :
82                       
83                         - quota : limit by number of pages per printer.
84                         - balance : limit by number of credits in account.
85                         - noquota : no limit, accounting still done.
86                         - nochange : no limit, accounting not done.
87                         - noprint : printing is denied.
88                       NB : nochange and noprint are not supported for groups.
89                       
90  -b | --balance b     Sets the user's account balance to b.                     
91                       Account balance may be increase or decreased
92                       if b is prefixed with + or -.
93                       WARNING : when decreasing account balance,
94                       the total paid so far by the user is decreased
95                       too.
96                       Groups don't have a real balance, but the
97                       sum of their users' account balance.
98                       
99  -C | --comment txt   Defines some informational text to be associated
100                       with a change to an user's account balance.
101                       Only meaningful if -b | --balance is also used.
102                       
103                       
104  -r | --remove        In combination with the --ingroups option above,                       
105                       remove users from the specified users groups.
106                       
107  -s | --skipexisting  In combination with the --add option above, tells
108                       pkusers to not modify existing users.
109                       
110  user1 through userN and group1 through groupN can use wildcards
111  if the --add option is not set.
112 
113examples :                             
114
115  $ pkusers --add john paul george ringo/ringo@example.com
116 
117  This will add users john, paul, george and ringo to the quota
118  database. User ringo's email address will also be set to
119  'ringo@example.com'
120 
121  $ pkusers --ingroups coders,it jerome
122 
123  User jerome is put into the groups "coders" and "it" which must
124  already exist in the quota database.
125           
126  $ pkusers --limitby balance jerome
127 
128  This will tell PyKota to limit jerome by his account's balance
129  when printing.
130 
131  $ pkusers --balance +10.0 --comment "He paid with his blood !" jerome
132 
133  This will increase jerome's account balance by 10.0 (in your
134  own currency). You can decrease the account balance with a
135  dash prefix, and set it to a fixed amount with no prefix.
136  A comment will be stored for this balance change.
137 
138  $ pkusers --delete jerome rachel
139 
140  This will completely delete jerome and rachel from the quota
141  database. All their quotas and jobs will be deleted too.
142 
143  $ pkusers --overcharge 2.5 poorstudent
144 
145  This will overcharge the poorstudent user by a factor of 2.5.
146 
147  $ pkusers --overcharge -1 jerome
148 
149  User jerome will actually earn money whenever he prints.
150 
151  $ pkusers --overcharge 0 boss
152 
153  User boss can print at will, it won't cost him anything because the
154  cost of each print job will be multiplied by zero before charging
155  his account.
156""")
157       
158class PKUsers(PyKotaTool) :       
159    """A class for a users and users groups manager."""
160    def main(self, names, options) :
161        """Manage users or groups."""
162        names = self.sanitizeNames(options, names)
163        suffix = (options["groups"] and "Group") or "User"       
164       
165        if options["delete"] :   
166            self.display("%s...\n" % _("Deletion"))
167            todelete = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
168            nbtotal = len(todelete)
169            for i in range(nbtotal) :
170                entry = todelete[i]
171                if entry.Exists :
172                    entry.delete()
173                percent = 100.0 * float(i) / float(nbtotal)
174                self.display("\r%.02f%%" % percent)
175        else :
176            if options["add"] :   
177                self.display("%s...\n" % _("Creation"))
178                rejectunknown = self.config.getRejectUnknown()   
179                entries = []
180                nbtotal = len(names)
181                for i in range(nbtotal) :
182                    ename = names[i]
183                    email = ""
184                    if not options["groups"] :
185                        splitname = ename.split('/', 1)     # username/email
186                        if len(splitname) == 1 :
187                            splitname.append("")
188                        (ename, email) = splitname
189                        if email and (email.count('@') != 1) :
190                            self.printInfo(_("Invalid email address %s") % email)
191                            email = ""
192                    entry = getattr(self.storage, "get%s" % suffix)(ename)
193                    if entry.Exists :
194                        if options["skipexisting"] :
195                            self.printInfo(_("%s %s already exists, skipping.") % (suffix, entry.Name))
196                        else :   
197                            self.printInfo(_("%s %s already exists, will be modified.") % (suffix, entry.Name))
198                            entries.append(entry)
199                    else :
200                        if self.isValidName(entry.Name) :
201                            reject = 0
202                            if rejectunknown :
203                                if options["groups"] :
204                                    try :
205                                        grp.getgrnam(entry.Name)
206                                    except KeyError :   
207                                        self.printInfo(_("Unknown group %s") % entry.Name, "error")
208                                        reject = 1
209                                else :   
210                                    try :
211                                        pwd.getpwnam(entry.Name)
212                                    except KeyError :   
213                                        self.printInfo(_("Unknown user %s") % entry.Name, "error")
214                                        reject = 1
215                            if not reject :       
216                                if email :
217                                    entry.Email = email
218                                entry = getattr(self.storage, "add%s" % suffix)(entry)
219                                entries.append(entry)
220                        else :   
221                            if options["groups"] :
222                                self.printInfo(_("Invalid group name %s") % entry.Name)
223                            else :   
224                                self.printInfo(_("Invalid user name %s") % entry.Name)
225                               
226                    percent = 100.0 * float(i) / float(nbtotal)
227                    self.display("\r%.02f%%" % percent)
228                self.done()
229            else :       
230                entries = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
231                if not entries :
232                    raise PyKotaCommandLineError, _("There's no %s matching %s") % (suffix.lower(), " ".join(names))
233                       
234            if options["list"] :
235                if suffix == "User" :
236                    maildomain = self.config.getMailDomain()
237                    smtpserver = self.config.getSMTPServer()
238                    for entry in entries :
239                        if entry.Exists :
240                            email = entry.Email
241                            if not email :
242                                if maildomain :     
243                                    email = "%s@%s" % (entry.Name, maildomain)
244                                elif smtpserver :   
245                                    email = "%s@%s" % (entry.Name, smtpserver)
246                                else :   
247                                    email = "%s@%s" % (entry.Name, "localhost")
248                            msg = "%s - <%s>" % (entry.Name, email)
249                            if entry.Description :
250                                msg += " - %s" % entry.Description
251                            print msg   
252                            print "    %s" % (_("Limited by : %s") % entry.LimitBy)
253                            print "    %s" % (_("Account balance : %.2f") % (entry.AccountBalance or 0.0))
254                            print "    %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))
255                            print "    %s" % (_("Overcharging factor : %.2f") % entry.OverCharge)
256                            print
257                else :   
258                    for entry in entries :
259                        if entry.Exists :
260                            msg = "%s" % entry.Name
261                            if entry.Description :
262                                msg += " - %s" % entry.Description
263                            print msg   
264                            print "    %s" % (_("Limited by : %s") % entry.LimitBy)
265                            print "    %s" % (_("Group balance : %.2f") % (entry.AccountBalance or 0.0))
266                            print "    %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))
267                            print
268            else :
269                self.display("%s...\n" % _("Modification"))
270               
271                limitby = options["limitby"]
272                if limitby :
273                    limitby = limitby.strip().lower()
274                if limitby :
275                    if limitby not in ('quota', 'balance', 'noquota', \
276                                                'noprint', 'nochange') :
277                        raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"]
278                    if (limitby in ('nochange', 'noprint')) and options["groups"] :   
279                        raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"]
280                   
281                overcharge = options["overcharge"]
282                if overcharge :
283                    try :
284                        overcharge = float(overcharge.strip())
285                    except (ValueError, AttributeError) :   
286                        raise PyKotaCommandLineError, _("Invalid overcharge value %s") % options["overcharge"]
287                       
288                balance = options["balance"]
289                if balance :
290                    balance = balance.strip()
291                    try :
292                        balancevalue = float(balance)
293                    except ValueError :   
294                        raise PyKotaCommandLineError, _("Invalid balance value %s") % options["balance"]
295                   
296                if options["ingroups"] :
297                    usersgroups = self.storage.getMatchingGroups(options["ingroups"])
298                    if not usersgroups :
299                        raise PyKotaCommandLineError, _("There's no users group matching %s") % " ".join(options["ingroups"].split(','))
300                else :         
301                    usersgroups = []
302                       
303                description = options["description"]
304                if description :
305                    description = options["description"].strip()
306                   
307                comment = options["comment"]
308                if comment :
309                    comment = options["comment"].strip()
310                   
311                nbtotal = len(entries)
312                for i in range(nbtotal) :       
313                    entry = entries[i]
314                   
315                    # We need a transaction because we may have to create a record
316                    # in the payments' table at the same time we modify the user's balance.
317                    self.storage.beginTransaction()
318                    try :
319                        if description is not None : # NB : "" is allowed !
320                            entry.setDescription(description)
321                        if limitby :   
322                            entry.setLimitBy(limitby)
323                           
324                        if suffix == "User" :   
325                            if overcharge is not None : # NB : 0 is allowed !     
326                                entry.setOverChargeFactor(overcharge)
327                       
328                        if suffix == "User" :
329                            if balance :
330                                if balance.startswith("+") or balance.startswith("-") :
331                                    newbalance = float(entry.AccountBalance or 0.0) + balancevalue
332                                    newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue
333                                    entry.setAccountBalance(newbalance, newlifetimepaid, comment)
334                                else :
335                                    diff = balancevalue - float(entry.AccountBalance or 0.0)
336                                    newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff
337                                    entry.setAccountBalance(balancevalue, newlifetimepaid, comment)
338                                           
339                            for ugroup in usersgroups :
340                                if options["remove"] :
341                                    ugroup.delUserFromGroup(entry)
342                                else :
343                                    ugroup.addUserToGroup(entry)
344                        entry.save()   
345                    except :
346                        self.storage.rollbackTransaction()
347                        raise
348                    else :   
349                        self.storage.commitTransaction()
350
351                    percent = 100.0 * float(i) / float(nbtotal)
352                    self.display("\r%.02f%%" % percent)
353                               
354        if not options["list"] :               
355            self.done()
356                     
357if __name__ == "__main__" : 
358    retcode = 0
359    try :
360        defaults = { \
361                     "comment" : "", \
362                   }
363        short_options = "hvaD:dgl:rso:i:b:C:L"
364        long_options = ["help", "version", "add", "description=", \
365                        "delete", "groups", "list", "remove", \
366                        "skipexisting", "overcharge=", \
367                        "ingroups=", "limitby=", "balance=", "comment=", \
368                       ]
369                       
370       
371        # Initializes the command line tool
372        manager = PKUsers(doc=__doc__)
373        manager.deferredInit()
374       
375        # parse and checks the command line
376        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
377       
378        # sets long options
379        options["help"] = options["h"] or options["help"]
380        options["version"] = options["v"] or options["version"]
381        options["add"] = options["a"] or options["add"]
382        options["description"] = options["D"] or options["description"]
383        options["delete"] = options["d"] or options["delete"] 
384        options["groups"] = options["g"] or options["groups"]
385        options["list"] = options["L"] or options["list"]
386        options["remove"] = options["r"] or options["remove"]
387        options["skipexisting"] = options["s"] or options["skipexisting"]
388        options["limitby"] = options["l"] or options["limitby"]
389        options["balance"] = options["b"] or options["balance"] 
390        options["ingroups"] = options["i"] or options["ingroups"]
391        options["overcharge"] = options["o"] or options["overcharge"]
392        options["comment"] = options["C"] or options["comment"] or defaults["comment"]
393       
394        if options["help"] :
395            manager.display_usage_and_quit()
396        elif options["version"] :
397            manager.display_version_and_quit()
398        elif (options["delete"] and (options["add"] or options["remove"] or options["description"])) \
399           or (options["skipexisting"] and not options["add"]) \
400           or (options["list"] and (options["add"] or options["delete"] or options["remove"] or options["description"])) \
401           or (options["groups"] and (options["balance"] or options["ingroups"] or options["overcharge"])) :
402            raise PyKotaCommandLineError, _("incompatible options, see help.")
403        elif options["remove"] and not options["ingroups"] :   
404            raise PyKotaCommandLineError, _("You have to pass user groups names on the command line")
405        elif (not args) and (options["add"] or options["delete"]) :
406            raise PyKotaCommandLineError, _("You have to pass user or group names on the command line")
407        else :
408            retcode = manager.main(args, options)
409    except KeyboardInterrupt :       
410        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
411        retcode = -3
412    except PyKotaCommandLineError, msg :   
413        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
414        retcode = -2
415    except SystemExit :       
416        pass
417    except :
418        try :
419            manager.crashed("pkusers failed")
420        except :   
421            crashed("pkusers failed")
422        retcode = -1
423
424    try :
425        manager.storage.close()
426    except (TypeError, NameError, AttributeError) :   
427        pass
428       
429    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.