root / pykota / trunk / bin / pkusers @ 2703

Revision 2703, 19.1 kB (checked in by jerome, 18 years ago)

Updated pkusers' documentation.
Added pkusers' manual pages.

  • 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
30
31from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_
32
33__doc__ = N_("""pkusers v%(__version__)s (c) %(__years__)s %(__author__)s
34
35An Users and Groups Manager for PyKota.
36
37command line usage :
38
39  pkusers [options] user1 user2 user3 ... userN
40 
41or : 
42
43  pkusers --groups [options] group1 group2 group3 ... groupN
44
45options :
46
47  -v | --version       Prints pkusers's version number then exits.
48  -h | --help          Prints this message then exits.
49 
50  -a | --add           Adds users if they don't exist on the database.
51                       If they exist, they are modified unless
52                       -s|--skipexisting is also used.
53                       
54  -d | --delete        Deletes users from the quota storage.
55 
56  -D | --description d Adds a textual description to users or groups.
57                       
58  -g | --groups        Edit users groups instead of users.
59                         
60  -p | --prototype u|g Uses user u or group g as a prototype to create
61                       or modify datas.
62                       
63  -o | --overcharge f  Sets the overcharging factor applied to the user
64                       when computing the cost of a print job. Positive or
65                       negative floating point values are allowed,
66                       this allows you to do some really creative
67                       things like giving money to an user whenever
68                       he prints. The number of pages in a print job
69                       is not modified by this coefficient, only the
70                       cost of the job for a particular user.
71                       Only users have such a coefficient.
72 
73  -i | --ingroups g1[,g2...]  Puts the users into each of the groups
74                              listed, separated by commas. The groups
75                              must already exist in the Quota Storage.
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 --groups 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 -p jerome john paul george ringo/ringo@example.com
116 
117  This will add users john, paul, george and ringo to the quota
118  database, and set their account information based on user
119  jerome's own account. User jerome must already exist.
120  User ringo's email address will also be set to '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 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 
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        if not self.config.isAdmin :
163            raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command."))
164           
165        suffix = (options["groups"] and "Group") or "User"       
166       
167        if options["delete"] :   
168            self.display("%s...\n" % _("Deletion"))
169            todelete = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
170            nbtotal = len(todelete)
171            for i in range(nbtotal) :
172                todelete[i].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.display("\r100.00%%\r        \r%s\n" % _("Done."))
229            else :       
230                if not names :
231                    names = ["*"]
232                entries = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names))
233                if not entries :
234                    raise PyKotaCommandLineError, _("There's no %s matching %s") % (suffix.lower(), " ".join(names))
235                       
236            if options["list"] :
237                if suffix == "User" :
238                    maildomain = self.config.getMailDomain()
239                    smtpserver = self.config.getSMTPServer()
240                    for entry in entries :
241                        email = entry.Email
242                        if not email :
243                            if maildomain :     
244                                email = "%s@%s" % (entry.Name, maildomain)
245                            elif smtpserver :   
246                                email = "%s@%s" % (entry.Name, smtpserver)
247                            else :   
248                                email = "%s@%s" % (entry.Name, "localhost")
249                        msg = "%s - <%s>" % (entry.Name, email)
250                        if entry.Description :
251                            msg += " - %s" % entry.Description
252                        print msg   
253                        print "    %s" % (_("Limited by : %s") % entry.LimitBy)
254                        print "    %s" % (_("Account balance : %.2f") % (entry.AccountBalance or 0.0))
255                        print "    %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))
256                        print "    %s" % (_("Overcharging factor : %.2f") % entry.OverCharge)
257                else :   
258                    for entry in entries :
259                        msg = "%s" % entry.Name
260                        if entry.Description :
261                            msg += " - %s" % entry.Description
262                        print msg   
263                        print "    %s" % (_("Limited by : %s") % entry.LimitBy)
264                        print "    %s" % (_("Group balance : %.2f") % (entry.AccountBalance or 0.0))
265                        print "    %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))
266            else :
267                self.display("%s...\n" % _("Modification"))
268               
269                limitby = options["limitby"]
270                if limitby :
271                    limitby = limitby.strip().lower()
272                if limitby :
273                    if limitby not in ('quota', 'balance', 'noquota', \
274                                                'noprint', 'nochange') :
275                        raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"]
276                    if limitby in ('noquota', 'nochange') :   
277                        options["noquota"] = 1
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                if options["description"] :
304                    description = options["description"].strip()
305                   
306                if options["prototype"] :   
307                    protoentry = getattr(self.storage, "get%s" % suffix)(options["prototype"])
308                    if not protoentry.Exists :
309                        raise PyKotaCommandLineError, _("Prototype object %s not found in Quota Storage.") % protoentry.Name
310                    else :   
311                        limitby = protoentry.LimitBy
312                        balancevalue = protoentry.AccountBalance
313                        if balancevalue is not None :
314                            balance = str(abs(balancevalue))
315                        else :   
316                            balance = None
317                        overcharge = getattr(protoentry, "OverCharge", None)
318                   
319                nbtotal = len(entries)
320                for i in range(nbtotal) :       
321                    entry = entries[i]
322                    if description is not None :        # NB : "" is allowed !
323                        entry.setDescription(description)
324                    entry.save()   
325                    if not options["groups"] :
326                        for ugroup in usersgroups :
327                            if options["remove"] :
328                                ugroup.delUserFromGroup(entry)
329                            else :
330                                ugroup.addUserToGroup(entry)
331                    percent = 100.0 * float(i) / float(nbtotal)
332                    self.display("\r%.02f%%" % percent)
333                               
334        if not options["list"] :               
335            self.display("\r100.00%%\r        \r%s\n" % _("Done."))
336                     
337if __name__ == "__main__" : 
338    retcode = 0
339    try :
340        defaults = { \
341                     "comment" : "", \
342                   }
343        short_options = "hvaD:dgl:rsp:o:i:b:C:L"
344        long_options = ["help", "version", "add", "description=", \
345                        "delete", "groups", "list", "remove", \
346                        "skipexisting", "overcharge=", "prototype=", \
347                        "ingroups=", "limitby=", "balance=", "comment=", \
348                       ]
349                       
350       
351        # Initializes the command line tool
352        manager = PKUsers(doc=__doc__)
353        manager.deferredInit()
354       
355        # parse and checks the command line
356        (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options)
357       
358        # sets long options
359        options["help"] = options["h"] or options["help"]
360        options["version"] = options["v"] or options["version"]
361        options["add"] = options["a"] or options["add"]
362        options["description"] = options["D"] or options["description"]
363        options["delete"] = options["d"] or options["delete"] 
364        options["groups"] = options["g"] or options["groups"]
365        options["list"] = options["L"] or options["list"]
366        options["remove"] = options["r"] or options["remove"]
367        options["skipexisting"] = options["s"] or options["skipexisting"]
368        options["prototype"] = options["p"] or options["prototype"]
369        options["limitby"] = options["l"] or options["limitby"]
370        options["balance"] = options["b"] or options["balance"] 
371        options["ingroups"] = options["i"] or options["ingroups"]
372        options["overcharge"] = options["o"] or options["overcharge"]
373        options["comment"] = options["C"] or options["comment"] or defaults["comment"]
374       
375        if options["help"] :
376            manager.display_usage_and_quit()
377        elif options["version"] :
378            manager.display_version_and_quit()
379        elif (options["delete"] and (options["add"] or options["groups"] or options["remove"] or options["description"])) \
380           or (options["skipexisting"] and not options["add"]) \
381           or (options["list"] and (options["add"] or options["delete"] or options["remove"] or options["description"])) \
382           or (options["groups"] and (options["balance"] or options["ingroups"] or options["overcharge"])) :
383            raise PyKotaCommandLineError, _("incompatible options, see help.")
384        elif options["remove"] and not options["ingroups"] :   
385            raise PyKotaCommandLineError, _("You have to pass user groups names on the command line")
386        elif (not args) and options["add"] :
387            raise PyKotaCommandLineError, _("You have to pass user or group names on the command line")
388        else :
389            retcode = manager.main(args, options)
390    except KeyboardInterrupt :       
391        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
392        retcode = -3
393    except PyKotaCommandLineError, msg :   
394        sys.stderr.write("%s : %s\n" % (sys.argv[0], msg))
395        retcode = -2
396    except SystemExit :       
397        pass
398    except :
399        try :
400            manager.crashed("pkusers failed")
401        except :   
402            crashed("pkusers failed")
403        retcode = -1
404
405    try :
406        manager.storage.close()
407    except (TypeError, NameError, AttributeError) :   
408        pass
409       
410    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.