#! /usr/bin/env python # -*- coding: ISO-8859-15 -*- # PyKota Print Quota Editor # # PyKota - Print Quotas for CUPS and LPRng # # (c) 2003, 2004, 2005, 2006 Jerome Alet # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # $Id$ # # import sys import os import pwd import grp from pykota.tool import PyKotaTool, PyKotaToolError, PyKotaCommandLineError, crashed, N_ from pykota.config import PyKotaConfigError from pykota.storage import PyKotaStorageError __doc__ = N_("""edpykota v%(__version__)s (c) %(__years__)s %(__author__)s A Print Quota editor for PyKota. command line usage : edpykota [options] user1 user2 ... userN edpykota [options] group1 group2 ... groupN options : -v | --version Prints edpykota's version number then exits. -h | --help Prints this message then exits. -a | --add Adds users or groups print quota entries if they don't exist in database. -d | --delete Deletes users or groups print quota entries. Users or groups are never deleted, you have to use the pkusers command to delete them. The history will be purge from all matching jobs, unless -g | --groups is used. -P | --printer p Edit quotas on printer p only. Actually p can use wildcards characters to select only some printers. The default value is *, meaning all printers. You can specify several names or wildcards, by separating them with commas. -g | --groups Edit groups print quota entries instead of users print quota entries. -L | --list Lists users or groups print quota entries. -n | --noquota Sets both soft and hard limits to None for users or groups print quota entries. -r | --reset Resets the actual page counter for the user or group to zero on the specified printers. The life time page counter is kept unchanged. -R | --hardreset Resets the actual and life time page counters for the user or group to zero on the specified printers. This is a shortcut for '--used 0'. -s | --skipexisting In combination with the --add option above, tells edpykota to not modify existing print quota entries. -S | --softlimit sl Sets the quota soft limit to sl pages. -H | --hardlimit hl Sets the quota hard limit to hl pages. -I | --increase v Increase existing Soft and Hard limits by the value of v. You can prefix v with + or -, if no sign is used, + is assumed. -U | --used u Sets the page counters for the user u pages on the selected printers. Doesn't work for groups, since their page counters are the sum of all their members' page counters. Useful for migrating users from a different system where they have already used some pages. Actual and Life Time page counters may be increased or decreased if u is prefixed with + or -. WARNING : BOTH page counters are modified in all cases, so be careful. NB : if u equals '0', then the action taken is the same as if --hardreset was used. user1 through userN and group1 through groupN can use wildcards if the --add option is not set. examples : $ edpykota --add john paul george ringo This will create print quota entries for users john, paul, george and ringo on all printers. These print quota entries will have no limit set. $ edpykota --printer lp -S 50 -H 60 jerome This will set jerome's print quota on the lp printer to a soft limit of 50 pages, and a hard limit of 60 pages. Both user jerome and printer lp have been previously created with the pkusers and pkprinters commands, respectively. $ edpykota -g -S 500 -H 550 financial support This will set print quota soft limit to 500 pages and hard limit to 550 pages for groups financial and support on all printers. $ edpykota --reset jerome "jo*" This will reset jerome's page counter to zero on all printers, as well as every user whose name begins with 'jo'. Their life time page counter on each printer will be kept unchanged. You can also reset the life time page counters by using the --hardreset | -R command line option. $ edpykota --printer hpcolor --noquota jerome This will tell PyKota to not limit jerome when printing on the hpcolor printer. All his jobs will be allowed on this printer, but accounting of the pages he prints will still be kept. Print Quotas for jerome on other printers are unchanged. $ edpykota --delete --printer "HP*,XER*" jerome rachel This will delete users jerome and rachel's print quota entries on all printers which name begin with 'HP' or 'XER'. The jobs printed by these users on these printers will be deleted from the history. """) class EdPyKota(PyKotaTool) : """A class for edpykota.""" def main(self, names, options) : """Edit user or group quotas.""" if not self.config.isAdmin : raise PyKotaCommandLineError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0], _("You're not allowed to use this command.")) suffix = (options["groups"] and "Group") or "User" printernames = options["printer"].split(",") if not names : names = ["*"] (printers, entries, pqentries) = getattr(self.storage, "getPrinters%ssAndPQuotas" % suffix)(printernames, names) if options["delete"] : self.display("%s...\n" % _("Deletion")) nbtotal = len(pqentries) for i in range(nbtotal) : (pqkey, pqentry) = pqentries.popitem() if pqentry.Exists : pqentry.delete() percent = 100.0 * float(i) / float(nbtotal) self.display("\r%.02f%%" % percent) else : skipexisting = options["skipexisting"] if options["add"] : self.display("%s...\n" % _("Creation")) mprinters = self.storage.getMatchingPrinters(options["printer"]) mentries = getattr(self.storage, "getMatching%ss" % suffix)(",".join(names)) nbtotal = len(mprinters) * len(mentries) i = 0 for printer in mprinters : pname = printer.Name for entry in mentries : ename = entry.Name pqkey = "%s@%s" % (ename, pname) if pqentries.has_key(pqkey) and pqentries[pqkey].Exists : if skipexisting : self.printInfo(_("%s print quota entry %s@%s already exists, skipping.") % (suffix, ename, pname)) del pqentries[pqkey] else : self.printInfo(_("%s print quota entry %s@%s already exists, will be modified.") % (suffix, ename, pname)) else : newpqentry = getattr(self.storage, "add%sPQuota" % suffix)(entry, printer) if not newpqentry.Exists : self.printInfo(_("Impossible to create a print quota entry for %s@%s") % (ename, pname)) else : pqentries[pqkey] = newpqentry percent = 100.0 * float(i) / float(nbtotal) self.display("\r%.02f%%" % percent) i += 1 self.display("\r100.00%%\r \r%s\n" % _("Done.")) if not pqentries : raise PyKotaCommandLineError, _("There's no %s print quota entry matching %s") % (suffix.lower(), " ".join(names)) if options["list"] : for (name, entry) in pqentries.items() : if entry.Exists : print name print " %s" % (_("Page counter : %s") % entry.PageCounter) print " %s" % (_("Lifetime page counter : %s") % entry.LifePageCounter) print " %s" % (_("Soft limit : %s") % entry.SoftLimit) print " %s" % (_("Hard limit : %s") % entry.HardLimit) print " %s" % (_("Date limit : %s") % entry.DateLimit) # TODO : print " %s" % (_("Maximum job size : %s") % ((entry.MaxJobSize and (_("%s pages") % entry.MaxJobSize)) or _("Unlimited"))) if hasattr(entry, "WarnCount") : print " %s" % (_("Warning banners printed : %s") % entry.WarnCount) print else : self.display("%s...\n" % _("Modification")) raise PyKotaCommandLineError, "Not implemented yet." softlimit = hardlimit = None used = options["used"] if used : used = used.strip() try : int(used) except ValueError : raise PyKotaCommandLineError, _("Invalid used value %s.") % used increase = options["increase"] if increase : try : increase = int(increase.strip()) except ValueError : raise PyKotaCommandLineError, _("Invalid increase value %s.") % increase if not options["noquota"] : if options["softlimit"] : try : softlimit = int(options["softlimit"].strip()) if softlimit < 0 : raise ValueError except ValueError : raise PyKotaCommandLineError, _("Invalid softlimit value %s.") % options["softlimit"] if options["hardlimit"] : try : hardlimit = int(options["hardlimit"].strip()) if hardlimit < 0 : raise ValueError except ValueError : raise PyKotaCommandLineError, _("Invalid hardlimit value %s.") % options["hardlimit"] if (softlimit is not None) and (hardlimit is not None) and (hardlimit < softlimit) : # error, exchange them self.printInfo(_("Hard limit %i is less than soft limit %i, values will be exchanged.") % (hardlimit, softlimit)) (softlimit, hardlimit) = (hardlimit, softlimit) sys.stdout.write(_("Managing print quotas... (this may take a lot of time)")) sys.stdout.flush() missingusers = {} missinggroups = {} changed = {} # tracks changes made at the user/group level for printer in printers : if not options["noquota"] : if hardlimit is None : hardlimit = softlimit if hardlimit is not None : self.printInfo(_("Undefined hard limit set to soft limit (%s) on printer %s.") % (str(hardlimit), printer.Name)) if softlimit is None : softlimit = hardlimit if softlimit is not None : self.printInfo(_("Undefined soft limit set to hard limit (%s) on printer %s.") % (str(softlimit), printer.Name)) if options["add"] : allentries = [] for name in names : email = "" if not options["groups"] : splitname = name.split('/', 1) # username/email if len(splitname) == 1 : splitname.append("") (name, email) = splitname if email and (email.count('@') != 1) : self.printInfo(_("Invalid email address %s") % email) email = "" entry = getattr(self.storage, "get%s" % suffix)(name) if email and not options["groups"] : entry.Email = email entrypquota = getattr(self.storage, "get%sPQuota" % suffix)(entry, printer) allentries.append((entry, entrypquota)) else : allentries = getattr(self.storage, "getPrinter%ssAndQuotas" % suffix)(printer, names) for (entry, entrypquota) in allentries : if not changed.has_key(entry.Name) : changed[entry.Name] = {} if not options["groups"] : changed[entry.Name]["ingroups"] = [] if entry.Exists and (not entrypquota.Exists) : # not found if options["add"] : entrypquota = getattr(self.storage, "add%sPQuota" % suffix)(entry, printer) if not entrypquota.Exists : self.printInfo(_("Quota not found for object %s on printer %s.") % (entry.Name, printer.Name)) else : if options["noquota"] \ or ((softlimit is not None) and (hardlimit is not None)) : entrypquota.setLimits(softlimit, hardlimit) if increase : if (entrypquota.SoftLimit is None) \ or (entrypquota.HardLimit is None) : self.printInfo(_("You can't increase limits by %s when no limit is set.") % increase, "error") else : newsoft = entrypquota.SoftLimit + increase newhard = entrypquota.HardLimit + increase if (newsoft >= 0) and (newhard >= 0) : entrypquota.setLimits(newsoft, newhard) else : self.printInfo(_("You can't set negative limits."), "error") if options["reset"] : entrypquota.reset() if options["hardreset"] : entrypquota.hardreset() if not options["groups"] : if used : entrypquota.setUsage(used) if not options["list"] : self.display("\r100.00%%\r \r%s\n" % _("Done.")) if __name__ == "__main__" : retcode = 0 try : defaults = { \ "printer" : "*", \ } short_options = "vhdnagrLP:S:H:G:RU:I:s" long_options = ["help", "version", \ "delete", "list", \ "noquota", "add", \ "groups", "reset", "hardreset", \ "printer=", "softlimit=", "hardlimit=", \ "increase=", "used=", "skipexisting"] # Initializes the command line tool manager = EdPyKota(doc=__doc__) manager.deferredInit() # parse and checks the command line (options, args) = manager.parseCommandline(sys.argv[1:], short_options, long_options) # sets long options options["help"] = options["h"] or options["help"] options["version"] = options["v"] or options["version"] options["add"] = options["a"] or options["add"] options["groups"] = options["g"] or options["groups"] options["printer"] = options["P"] or options["printer"] or defaults["printer"] options["softlimit"] = options["S"] or options["softlimit"] options["hardlimit"] = options["H"] or options["hardlimit"] options["reset"] = options["r"] or options["reset"] options["noquota"] = options["n"] or options["noquota"] options["delete"] = options["d"] or options["delete"] options["hardreset"] = options["R"] or options["hardreset"] options["used"] = options["U"] or options["used"] options["increase"] = options["I"] or options["increase"] options["list"] = options["L"] or options["list"] options["skipexisting"] = options["s"] or options["skipexisting"] if options["help"] : manager.display_usage_and_quit() elif options["version"] : manager.display_version_and_quit() elif (options["add"] and options["delete"]) \ or (options["noquota"] and (options["hardlimit"] or options["softlimit"])) \ or (options["groups"] and options["used"]) \ or (options["skipexisting"] and not options["add"]) : raise PyKotaCommandLineError, _("incompatible options, see help.") elif options["delete"] and not args : raise PyKotaCommandLineError, _("You have to pass user or group names on the command line") else : retcode = manager.main(args, options) except KeyboardInterrupt : sys.stderr.write("\nInterrupted with Ctrl+C !\n") retcode = -3 except PyKotaCommandLineError, msg : sys.stderr.write("%s : %s\n" % (sys.argv[0], msg)) retcode = -2 except SystemExit : pass except : try : manager.crashed("edpykota failed") except : crashed("edpykota failed") retcode = -1 try : manager.storage.close() except (TypeError, NameError, AttributeError) : pass sys.exit(retcode)