Changeset 3426
- Timestamp:
- 10/04/08 22:29:34 (16 years ago)
- Location:
- pykota/trunk
- Files:
-
- 2 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/bin/pkusers
r3413 r3426 22 22 # 23 23 24 """A users and groups manager for PyKota.""" 25 24 26 import sys 25 27 import pwd … … 27 29 28 30 import pykota.appinit 29 from pykota.utils import *30 31 from pykota.utils import run 32 from pykota.commandline import PyKotaOptionParser 31 33 from pykota.errors import PyKotaCommandLineError 32 34 from pykota.tool import Percent, PyKotaTool 33 35 from pykota.storage import StorageUser, StorageGroup 34 36 35 __doc__ = N_("""pkusers v%(__version__)s (c) %(__years__)s %(__author__)s 36 37 An Users and Groups Manager for PyKota. 38 39 command line usage : 40 41 pkusers [options] user1 user2 user3 ... userN 42 43 or : 44 45 pkusers --groups [options] group1 group2 group3 ... groupN 46 47 options : 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 -e | --email addr Sets the email address for the users. 59 If the addr parameter begins with @, then 60 the username is prepended to addr to form 61 a valid email address. 62 63 -D | --description d Adds a textual description to users or groups. 64 65 -g | --groups Edit users groups instead of users. 66 67 -o | --overcharge f Sets the overcharging factor applied to the user 68 when computing the cost of a print job. Positive or 69 negative floating point values are allowed, 70 this allows you to do some really creative 71 things like giving money to an user whenever 72 he prints. The number of pages in a print job 73 is not modified by this coefficient, only the 74 cost of the job for a particular user. 75 Only users have such a coefficient. 76 77 -i | --ingroups g1[,g2...] Puts the users into each of the groups 78 listed, separated by commas. The groups 79 must already exist in the Quota Storage. 80 81 -L | --list Lists users or groups. 82 83 -l | --limitby l Choose if the user/group is limited in printing 84 by its account balance or by its page quota. 85 The default value is 'quota'. Allowed values 86 are 'quota' 'balance' 'noquota' 'noprint' 87 and 'nochange' : 88 89 - quota : limit by number of pages per printer. 90 - balance : limit by number of credits in account. 91 - noquota : no limit, accounting still done. 92 - nochange : no limit, accounting not done. 93 - noprint : printing is denied. 94 NB : nochange and noprint are not supported for groups. 95 96 -b | --balance b Sets the user's account balance to b. 97 Account balance may be increase or decreased 98 if b is prefixed with + or -. 99 WARNING : when decreasing account balance, 100 the total paid so far by the user is decreased 101 too. 102 Groups don't have a real balance, but the 103 sum of their users' account balance. 104 105 -C | --comment txt Defines some informational text to be associated 106 with a change to an user's account balance. 107 Only meaningful if -b | --balance is also used. 108 109 110 -r | --remove In combination with the --ingroups option above, 111 remove users from the specified users groups. 112 113 -s | --skipexisting In combination with the --add option above, tells 114 pkusers to not modify existing users. 115 116 user1 through userN and group1 through groupN can use wildcards 117 if the --add option is not set. 118 119 examples : 120 121 $ pkusers --add john paul george ringo/ringo@example.com 122 123 This will add users john, paul, george and ringo to the quota 124 database. User ringo's email address will also be set to 125 'ringo@example.com' 126 127 $ pkusers --ingroups coders,it jerome 128 129 User jerome is put into the groups "coders" and "it" which must 130 already exist in the quota database. 131 132 $ pkusers --limitby balance jerome 133 134 This will tell PyKota to limit jerome by his account's balance 135 when printing. 136 137 $ pkusers --balance +10.0 --comment "He paid with his blood !" jerome 138 139 This will increase jerome's account balance by 10.0 (in your 140 own currency). You can decrease the account balance with a 141 dash prefix, and set it to a fixed amount with no prefix. 142 A comment will be stored for this balance change. 143 144 $ pkusers --delete jerome rachel 145 146 This will completely delete jerome and rachel from the quota 147 database. All their quotas and jobs will be deleted too. 148 149 $ pkusers --overcharge 2.5 poorstudent 150 151 This will overcharge the poorstudent user by a factor of 2.5. 152 153 $ pkusers --overcharge -1 jerome 154 155 User jerome will actually earn money whenever he prints. 156 157 $ pkusers --overcharge 0 boss 158 159 User boss can print at will, it won't cost him anything because the 160 cost of each print job will be multiplied by zero before charging 161 his account. 162 163 $ pkusers --email @example.com 164 165 This will set the email address for each user to username@example.com 166 """) 37 167 38 168 39 class PKUsers(PyKotaTool) : … … 201 72 ugroup.addUserToGroup(user) 202 73 74 def sanitizeNames(self, names, isgroups) : 75 """Sanitize users and groups names if needed.""" 76 if not self.config.isAdmin : 77 username = pwd.getpwuid(os.geteuid())[0] 78 if isgroups : 79 user = self.storage.getUser(username) 80 if user.Exists : 81 return [ g.Name for g in self.storage.getUserGroups(user) ] 82 return [ username ] 83 return names 84 203 85 def main(self, names, options) : 204 86 """Manage users or groups.""" 205 names = self.sanitizeNames(options, names) 206 suffix = (options["groups"] and "Group") or "User" 207 208 if not options["list"] : 87 islist = (options.action == "list") 88 isadd = (options.action == "add") 89 isdelete = (options.action == "delete") 90 91 if not islist : 92 self.adminOnly() 93 94 names = self.sanitizeNames(names, options.groups) 95 if not names : 96 if isdelete or isadd : 97 raise PyKotaCommandLineError, _("You must specify users or groups names on the command line.") 98 names = [u"*"] 99 100 if options.remove and not options.ingroups : 101 raise PyKotaCommandLineError, _("You must specify users groups names on the command line.") 102 elif (((islist or isdelete) and (options.limitby \ 103 or options.balance \ 104 or options.email \ 105 or options.remove \ 106 or options.overcharge \ 107 or options.ingroups \ 108 or options.description \ 109 or options.skipexisting \ 110 or options.comment))) \ 111 or (options.groups and (options.ingroups \ 112 or options.balance \ 113 or options.email \ 114 or options.remove \ 115 or options.overcharge \ 116 or options.comment)) : 117 raise PyKotaCommandLineError, _("Incompatible command line options. Please look at the online help or manual page.") 118 119 suffix = (options.groups and "Group") or "User" 120 121 if not islist : 209 122 percent = Percent(self) 210 123 211 if not options["add"]:212 if not options["list"]:124 if not isadd : 125 if not islist : 213 126 percent.display("%s..." % _("Extracting datas")) 214 if not names : # NB : can't happen for --delete because it's catched earlier215 names = ["*"]216 127 entries = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names)) 217 128 if not entries : 218 if not options["list"]:129 if not islist : 219 130 percent.display("\n") 220 raise PyKotaCommandLineError, _("There's no %s matching %s") % (_(suffix.lower()), " ".join(names)) 221 if not options["list"] : 131 raise PyKotaCommandLineError, _("There's no %s matching %s") \ 132 % (_(suffix.lower()), " ".join(names)) 133 if not islist : 222 134 percent.setSize(len(entries)) 223 135 224 if options["list"]:136 if islist : 225 137 if suffix == "User" : 226 138 maildomain = self.config.getMailDomain() … … 238 150 if entry.Description : 239 151 msg += " - %s" % entry.Description 240 print msg241 print " %s" % (_("Limited by : %s") % entry.LimitBy)242 print " %s" % (_("Account balance : %.2f") % (entry.AccountBalance or 0.0))243 print " %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))244 print " %s" % (_("Overcharging factor : %.2f") % entry.OverCharge)245 print152 self.display("%s\n" % msg) 153 self.display(" %s\n" % (_("Limited by : %s") % entry.LimitBy)) 154 self.display(" %s\n" % (_("Account balance : %.2f") % (entry.AccountBalance or 0))) 155 self.display(" %s\n" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0))) 156 self.display(" %s\n" % (_("Overcharging factor : %.2f") % entry.OverCharge)) 157 self.display("\n") 246 158 else : 247 159 for entry in entries : … … 249 161 if entry.Description : 250 162 msg += " - %s" % entry.Description 251 print msg252 print " %s" % (_("Limited by : %s") % entry.LimitBy)253 print " %s" % (_("Group balance : %.2f") % (entry.AccountBalance or 0.0))254 print " %s" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0.0))255 print256 elif options["delete"]:163 self.display("%s\n" % msg) 164 self.display(" %s\n" % (_("Limited by : %s") % entry.LimitBy)) 165 self.display(" %s\n" % (_("Group balance : %.2f") % (entry.AccountBalance or 0))) 166 self.display(" %s\n" % (_("Total paid so far : %.2f") % (entry.LifeTimePaid or 0))) 167 self.display("\n") 168 elif isdelete : 257 169 percent.display("\n%s..." % _("Deletion")) 258 170 getattr(self.storage, "deleteMany%ss" % suffix)(entries) 259 171 percent.display("\n") 260 172 else : 261 limitby = options ["limitby"]173 limitby = options.limitby 262 174 if limitby : 263 175 limitby = limitby.strip().lower() 264 176 if limitby : 265 if limitby not in ('quota', 'balance', 'noquota', \ 266 'noprint', 'nochange') : 267 raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 268 if (limitby in ('nochange', 'noprint')) and options["groups"] : 269 raise PyKotaCommandLineError, _("Invalid limitby value %s") % options["limitby"] 270 271 overcharge = options["overcharge"] 272 if overcharge : 273 try : 274 overcharge = float(overcharge.strip()) 275 except (ValueError, AttributeError) : 276 raise PyKotaCommandLineError, _("Invalid overcharge value %s") % options["overcharge"] 277 278 balance = options["balance"] 177 if limitby not in ('quota', 178 'balance', 179 'noquota', 180 'noprint', 181 'nochange') : 182 raise PyKotaCommandLineError, _("Invalid limitby value %s") \ 183 % options.limitby 184 if (limitby in ('nochange', 'noprint')) and options.groups : 185 raise PyKotaCommandLineError, _("Invalid limitby value %s") \ 186 % options.limitby 187 188 balance = options.balance 279 189 if balance : 280 190 balance = balance.strip() … … 282 192 balancevalue = float(balance) 283 193 except ValueError : 284 raise PyKotaCommandLineError, _("Invalid balance value %s") % options["balance"] 194 raise PyKotaCommandLineError, _("Invalid balance value %s") \ 195 % options.balance 285 196 else : 286 197 balancevalue = None 287 198 288 if options ["ingroups"]:289 usersgroups = self.storage.getMatchingGroups(options ["ingroups"])199 if options.ingroups : 200 usersgroups = self.storage.getMatchingGroups(options.ingroups) 290 201 if not usersgroups : 291 raise PyKotaCommandLineError, _("There's no users group matching %s") % " ".join(options["ingroups"].split(',')) 202 raise PyKotaCommandLineError, _("There's no users group matching %s") \ 203 % " ".join(options.ingroups.split(',')) 292 204 else : 293 205 usersgroups = [] 294 206 295 description = options["description"] 296 if description : 297 description = description.strip() 298 299 comment = options["comment"] 300 if comment : 301 comment = comment.strip() 302 email = options["email"] 303 if email : 304 email = email.strip() 305 skipexisting = options["skipexisting"] 306 groups = options["groups"] 307 remove = options["remove"] 207 if options.description : 208 options.description = options.description.strip() 209 210 if options.comment : 211 options.comment = options.comment.strip() 212 213 if options.email : 214 options.email = options.email.strip() 215 308 216 self.storage.beginTransaction() 309 217 try : 310 if options["add"]:218 if isadd : 311 219 rejectunknown = self.config.getRejectUnknown() 312 220 percent.display("%s...\n" % _("Creation")) … … 314 222 for ename in names : 315 223 useremail = None 316 if not groups :224 if not options.groups : 317 225 splitname = ename.split('/', 1) # username/email 318 226 if len(splitname) == 1 : … … 322 230 reject = 0 323 231 if rejectunknown : 324 if groups :232 if options.groups : 325 233 try : 326 234 grp.getgrnam(ename) … … 336 244 if not reject : 337 245 entry = globals()["Storage%s" % suffix](self.storage, ename) 338 if groups : 339 self.modifyEntry(entry, groups, limitby, \ 340 description) 246 if options.groups : 247 self.modifyEntry(entry, 248 options.groups, 249 limitby, 250 options.description) 341 251 else : 342 self.modifyEntry(entry, groups, limitby, \ 343 description, overcharge,\ 344 balance, balancevalue, \ 345 comment, useremail or email) 252 self.modifyEntry(entry, 253 options.groups, 254 limitby, 255 options.description, 256 options.overcharge, 257 balance, 258 balancevalue, 259 options.comment, 260 useremail or options.email) 346 261 oldentry = getattr(self.storage, "add%s" % suffix)(entry) 347 262 if oldentry is not None : 348 if skipexisting : 349 self.logdebug(_("%s %s already exists, skipping.") % (_(suffix), ename)) 263 if options.skipexisting : 264 self.logdebug(_("%s %s already exists, skipping.") \ 265 % (_(suffix), ename)) 350 266 else : 351 self.logdebug(_("%s %s already exists, will be modified.") % (_(suffix), ename)) 352 if groups : 353 self.modifyEntry(oldentry, groups, \ 354 limitby, description) 267 self.logdebug(_("%s %s already exists, will be modified.") \ 268 % (_(suffix), ename)) 269 if options.groups : 270 self.modifyEntry(oldentry, 271 options.groups, 272 limitby, 273 options.description) 355 274 else : 356 self.modifyEntry(oldentry, groups, limitby, \ 357 description, overcharge,\ 358 balance, balancevalue, \ 359 comment, useremail or email) 275 self.modifyEntry(oldentry, 276 options.groups, 277 limitby, 278 options.description, 279 options.overcharge, 280 balance, 281 balancevalue, 282 options.comment, 283 useremail or options.email) 360 284 oldentry.save() 361 if not groups : 362 self.manageUsersGroups(usersgroups, oldentry, remove) 363 elif usersgroups and not groups : 285 if not options.groups : 286 self.manageUsersGroups(usersgroups, 287 oldentry, 288 options.remove) 289 elif usersgroups and not options.groups : 364 290 self.manageUsersGroups(usersgroups, \ 365 291 self.storage.getUser(ename), \ 366 remove)292 options.remove) 367 293 else : 368 294 raise PyKotaCommandLineError, _("Invalid name %s") % ename … … 371 297 percent.display("\n%s...\n" % _("Modification")) 372 298 for entry in entries : 373 if groups : 374 self.modifyEntry(entry, groups, limitby, description) 299 if options.groups : 300 self.modifyEntry(entry, 301 options.groups, 302 limitby, 303 options.description) 375 304 else : 376 self.modifyEntry(entry, groups, limitby, description, \ 377 overcharge, balance, balancevalue, \ 378 comment, email) 379 self.manageUsersGroups(usersgroups, entry, remove) 305 self.modifyEntry(entry, 306 options.groups, 307 limitby, 308 options.description, 309 options.overcharge, 310 balance, 311 balancevalue, 312 options.comment, 313 options.email) 314 self.manageUsersGroups(usersgroups, 315 entry, 316 options.remove) 380 317 entry.save() 381 318 percent.oneMore() … … 386 323 self.storage.commitTransaction() 387 324 388 if not options["list"]:325 if not islist : 389 326 percent.done() 390 327 391 328 if __name__ == "__main__" : 392 retcode = 0 393 try : 394 defaults = { \ 395 "comment" : "", \ 396 } 397 short_options = "hvaD:dgl:rso:i:b:C:Le:" 398 long_options = ["help", "version", "add", "description=", \ 399 "delete", "groups", "list", "remove", \ 400 "skipexisting", "overcharge=", "email=", \ 401 "ingroups=", "limitby=", "balance=", "comment=", \ 402 ] 403 404 405 # Initializes the command line tool 406 manager = PKUsers(doc=__doc__) 407 manager.deferredInit() 408 409 # parse and checks the command line 410 (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options) 411 412 # sets long options 413 options["help"] = options["h"] or options["help"] 414 options["version"] = options["v"] or options["version"] 415 options["add"] = options["a"] or options["add"] 416 options["description"] = options["D"] or options["description"] 417 options["delete"] = options["d"] or options["delete"] 418 options["groups"] = options["g"] or options["groups"] 419 options["list"] = options["L"] or options["list"] 420 options["remove"] = options["r"] or options["remove"] 421 options["skipexisting"] = options["s"] or options["skipexisting"] 422 options["limitby"] = options["l"] or options["limitby"] 423 options["balance"] = options["b"] or options["balance"] 424 options["ingroups"] = options["i"] or options["ingroups"] 425 options["overcharge"] = options["o"] or options["overcharge"] 426 options["comment"] = options["C"] or options["comment"] or defaults["comment"] 427 options["email"] = options["e"] or options["email"] 428 429 if options["help"] : 430 manager.display_usage_and_quit() 431 elif options["version"] : 432 manager.display_version_and_quit() 433 elif (options["delete"] and (options["add"] or options["remove"] or options["description"] or options["email"])) \ 434 or (options["skipexisting"] and not options["add"]) \ 435 or (options["list"] and (options["add"] or options["delete"] or options["remove"] or options["description"] or options["email"])) \ 436 or (options["groups"] and (options["balance"] or options["ingroups"] or options["overcharge"])) : 437 raise PyKotaCommandLineError, _("incompatible options, see help.") 438 elif options["remove"] and not options["ingroups"] : 439 raise PyKotaCommandLineError, _("You have to pass user groups names on the command line") 440 elif (not args) and (options["add"] or options["delete"]) : 441 raise PyKotaCommandLineError, _("You have to pass user or group names on the command line") 442 else : 443 retcode = manager.main(args, options) 444 except KeyboardInterrupt : 445 logerr("\nInterrupted with Ctrl+C !\n") 446 retcode = -3 447 except PyKotaCommandLineError, msg : 448 logerr("%s : %s\n" % (sys.argv[0], msg)) 449 retcode = -2 450 except SystemExit : 451 pass 452 except : 453 try : 454 manager.crashed("pkusers failed") 455 except : 456 crashed("pkusers failed") 457 retcode = -1 458 459 try : 460 manager.storage.close() 461 except (TypeError, NameError, AttributeError) : 462 pass 463 464 sys.exit(retcode) 329 parser = PyKotaOptionParser(description=_("Manages PyKota users or users groups."), 330 usage="pkusers [options] name1 name2 ... nameN") 331 parser.add_option("-a", "--add", 332 action="store_const", 333 const="add", 334 dest="action", 335 help=_("Add new, or modify existing, users or groups.")) 336 parser.add_option("-b", "--balance", 337 dest="balance", 338 help=_("Set an user's account balance. The value can also be increased or decreased when the value is prefixed with '+' or '-'. Users groups don't have a real account balance, instead the sum of their members' account balances is used.")) 339 parser.add_option("-C", "--comment", 340 dest="comment", 341 default="", 342 help=_("Associate a textual comment with a change in an user's account balance. Only meaningful when --balance is also used.")) 343 parser.add_option("-d", "--delete", 344 action="store_const", 345 const="delete", 346 dest="action", 347 help=_("Delete the specified users or groups. Also purge the print quota entries and printing history matching the specified users or groups.")) 348 parser.add_option("-D", "--description", 349 dest="description", 350 help=_("Set a textual description for the specified users or groups.")) 351 parser.add_option("-e", "--email", 352 dest="email", 353 help=_("Set an user's email address. If this parameter begins with '@' then the username is prepended to this parameter to form a valid email address.")) 354 parser.add_option("-g", "--groups", 355 action="store_true", 356 dest="groups", 357 help=_("Manage users groups instead of users.")) 358 parser.add_option("-i", "--ingroups", 359 dest="ingroups", 360 help=_("Put the specified users into the specified groups. When combined with the --remove option, users are removed from the specified groups instead.")) 361 parser.add_option("-l", "--limitby", 362 dest="limitby", 363 help=_("Set the limiting factor for the specified users or groups. Can be any of 'quota' (limit by number of pages per printer), 'balance' (limit by number of credits), 'noquota' (no limit but accounting done), 'nochange' (no limit and not accounting), or 'noprint' (printing is denied). The two latter ones are not supported for groups.")) 364 parser.add_option("-L", "--list", 365 action="store_const", 366 const="list", 367 dest="action", 368 help=_("Display detailed informations about the specified users or groups.")) 369 parser.add_option("-o", "--overcharge", 370 type="float", 371 dest="overcharge", 372 help=_("Set the overcharging factor applied to the specified users when computing the cost of a print job. Any floating point value can be used, allowing you to express your creativity...")) 373 parser.add_option("-r", "--remove", 374 action="store_true", 375 dest="remove", 376 help=_("When combined with the --ingroups option, remove users from the specified users groups.")) 377 parser.add_option("-s", "--skipexisting", 378 action="store_true", 379 dest="skipexisting", 380 help=_("If --add is used, ensure that existing users or groups won't be modified.")) 381 382 parser.add_example("--add john paul george ringo/ringo@example.com", 383 _("Would make users 'john', 'paul', 'george' and 'ringo' be known to PyKota. User 'ringo''s email address would be set to 'ringo@example.com'.")) 384 parser.add_example("--add --groups coders it", 385 _("Would create two users groups named 'coders' and 'it'.")) 386 parser.add_example("--add --ingroups coders,it jerome", 387 _("Would add user 'jerome' and put him into the 'coders' and 'it' groups. Both groups would have to be existing.")) 388 parser.add_example("--limitby balance --balance 10.0 john", 389 _("Would give 10.0 credits to 'john' and make his printing be limited by his account balance.")) 390 parser.add_example('--balance +10.0 --comment "He paid with his blood." jerome', 391 _("Would add 10.0 credits to 'jerome''s account and register a comment associated with his payment.")) 392 parser.add_example('--delete "jer*" "rach*"', 393 _("Would delete all user accounts whose names begin with either 'jer' or 'rach'.")) 394 parser.add_example("--overcharge -1.50 theboss", 395 _("Would make the boss earn money whenever he prints.")) 396 parser.add_example("--email @example.com", 397 _("Would set the email address for each existing user to username@example.com")) 398 parser.add_example("--list", 399 _("Would list all users.")) 400 run(parser, PKUsers) -
pykota/trunk/pykota/version.py
r3413 r3426 22 22 """This module defines some application level constants.""" 23 23 24 __version__ = "1.27alpha 5_unofficial"24 __version__ = "1.27alpha6_unofficial" 25 25 26 26 __doc__ = "PyKota : a complete Printing Quota Solution for CUPS."