root / pykota / trunk / bin / pkusers @ 2774

Revision 2774, 20.8 kB (checked in by jerome, 18 years ago)

Missing assignment

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