Changeset 1495 for pykota/trunk/pykota

Show
Ignore:
Timestamp:
05/25/04 00:45:49 (20 years ago)
Author:
jalet
Message:

New 'enforcement' directive added
Polling loop improvements

Location:
pykota/trunk/pykota
Files:
6 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/pykota/accounter.py

    r1483 r1495  
    2222# 
    2323# $Log$ 
     24# Revision 1.15  2004/05/24 22:45:49  jalet 
     25# New 'enforcement' directive added 
     26# Polling loop improvements 
     27# 
    2428# Revision 1.14  2004/05/18 14:49:19  jalet 
    2529# Big code changes to completely remove the need for "requester" directives, 
     
    8488        self.filter = kotafilter 
    8589        self.arguments = arguments 
    86         self.isDelayed = 0      # Accounting is immediate by default 
    8790        self.firstPassSize = None 
    8891         
     
    138141            return 0 
    139142         
    140     def doAccounting(self, userpquota) : 
    141         """Does accounting for current job.""" 
    142         self.beginJob(userpquota) 
    143              
    144         # Is the current user allowed to print at all ? 
    145         action = self.filter.warnUserPQuota(userpquota) 
    146          
    147         # update the quota for the current user on this printer, if allowed to print 
    148         if action == "DENY" : 
    149             jobsize = 0 
    150         else :     
    151             # get the job size 
    152             jobsize = self.getJobSize() 
    153             userpquota.increasePagesUsage(jobsize) 
    154          
    155         # adds the current job to history     
    156         jobprice = userpquota.computeJobPrice(jobsize) 
    157         userpquota.Printer.addJobToHistory(self.filter.jobid, userpquota.User, self.getLastPageCounter(), action, jobsize, jobprice, self.filter.preserveinputfile, self.filter.title, self.filter.copies, self.filter.options) 
    158         self.endJob(userpquota) 
    159         return action 
    160          
    161143    def computeJobSize(self) :     
    162144        """Must be overriden in children classes.""" 
    163145        raise RuntimeError, "AccounterBase.computeJobSize() must be overriden !" 
    164          
    165146         
    166147def openAccounter(kotafilter) : 
  • pykota/trunk/pykota/accounters/hardware.py

    r1494 r1495  
    2222# 
    2323# $Log$ 
     24# Revision 1.4  2004/05/24 22:45:49  jalet 
     25# New 'enforcement' directive added 
     26# Polling loop improvements 
     27# 
    2428# Revision 1.3  2004/05/24 14:36:40  jalet 
    2529# Revert to old polling loop. Will need optimisations 
     
    4448        """Initializes querying accounter.""" 
    4549        AccounterBase.__init__(self, kotabackend, arguments) 
    46         self.isDelayed = 1 # With the pykota filter, accounting is delayed by one job 
    4750         
    4851    def getPrinterInternalPageCounter(self) :     
     
    9295        return jobsize 
    9396         
    94     def doAccounting(self, userpquota) : 
    95         """Does print accounting and returns if the job status is ALLOW or DENY.""" 
    96         # Get the page counter directly from the printer itself 
    97         counterbeforejob = self.getPrinterInternalPageCounter() or 0 
    98          
    99         # Is the current user allowed to print at all ? 
    100         action = self.filter.warnUserPQuota(userpquota) 
    101          
    102         # adds the current job to history     
    103         userpquota.Printer.addJobToHistory(self.filter.jobid, userpquota.User, counterbeforejob, action, filename=self.filter.preserveinputfile, title=self.filter.title, copies=self.filter.copies, options=self.filter.options) 
    104         return action 
    105          
    10697    def askPrinterPageCounter(self, printer) : 
    10798        """Returns the page counter from the printer via an external command. 
  • pykota/trunk/pykota/accounters/software.py

    r1483 r1495  
    2222# 
    2323# $Log$ 
     24# Revision 1.3  2004/05/24 22:45:49  jalet 
     25# New 'enforcement' directive added 
     26# Polling loop improvements 
     27# 
    2428# Revision 1.2  2004/05/18 14:49:23  jalet 
    2529# Big code changes to completely remove the need for "requester" directives, 
     
    3034# 
    3135# 
    32 # 
    3336 
    3437import sys 
    3538import os 
    3639import popen2 
    37 import tempfile 
    3840from pykota.accounter import AccounterBase, PyKotaAccounterError 
    3941 
     
    4143    def computeJobSize(self) :     
    4244        """Feeds an external command with our datas to let it compute the job size, and return its value.""" 
    43         temporary = None     
    44         if self.filter.inputfile is None :     
    45             infile = sys.stdin 
    46             # we will have to duplicate our standard input 
    47             temporary = tempfile.TemporaryFile() 
    48         else :     
    49             infile = open(self.filter.inputfile, "rb") 
    50              
    51         # launches software accounter 
    52         # TODO : USE tempfile.mkstemp() instead ! Needs some work ! 
    53         infilename = tempfile.mktemp() 
    54         outfilename = tempfile.mktemp() 
    55         errfilename = tempfile.mktemp() 
     45        self.filter.logdebug("Launching software accounter %s" % self.arguments) 
     46        MEGABYTE = 1024*1024 
     47        self.filter.jobdatastream.seek(0) 
     48        child = popen2.Popen4(self.arguments) 
     49        try : 
     50            data = self.filter.jobdatastream.read(MEGABYTE)     
     51            while data : 
     52                child.tochild.write(data) 
     53                data = self.filter.jobdatastream.read(MEGABYTE) 
     54            child.tochild.flush() 
     55            child.tochild.close()     
     56        except (IOError, OSError), msg :     
     57            msg = "%s : %s" % (self.arguments, msg)  
     58            self.filter.logger.log_message(_("Unable to compute job size with accounter %s") % msg) 
     59         
     60        pagecount = 0 
     61        try : 
     62            pagecount = int(child.fromchild.readline().strip()) 
     63        except (AttributeError, ValueError) : 
     64            self.filter.logger.log_message(_("Unable to compute job size with accounter %s") % self.arguments) 
     65        except (IOError, OSError), msg :     
     66            msg = "%s : %s" % (self.arguments, msg)  
     67            self.filter.logger.log_message(_("Unable to compute job size with accounter %s") % msg) 
     68        child.fromchild.close() 
    5669         
    5770        try : 
    58             # feed it with our data 
    59             fakeinput = open(infilename, "wb") 
    60             data = infile.read(256*1024)     
    61             while data : 
    62                 fakeinput.write(data) 
    63                 if temporary is not None : 
    64                     temporary.write(data) 
    65                 data = infile.read(256*1024) 
    66             fakeinput.close() 
    67          
    68             # launches child process 
    69             command = "%s <%s >%s 2>%s" % (self.arguments, infilename, outfilename, errfilename) 
    70             retcode = os.system(command) 
    71              
    72             # check exit status 
    73             if (os.WIFEXITED(retcode) and not os.WEXITSTATUS(retcode)) or os.stat(errfilename) : 
    74                 # tries to extract the job size from the software accounter's 
    75                 # standard output 
    76                 childoutput = open(outfilename, "r") 
    77                 try : 
    78                     pagecount = int(childoutput.readline().strip()) 
    79                 except (AttributeError, ValueError) : 
    80                     self.filter.logger.log_message(_("Unable to compute job size with accounter %s") % self.arguments) 
    81                     pagecount = 0 
    82                 childoutput.close()     
    83             else : 
    84                 self.filter.logger.log_message(_("Unable to compute job size with accounter %s") % self.arguments) 
    85                 pagecount = 0 
    86             os.remove(infilename) 
    87             os.remove(outfilename) 
    88             os.remove(errfilename) 
    89         except IOError, msg :     
    90             # TODO : temporary files may remain on the filesystem... 
    91             msg = "%s : %s" % (self.arguments, msg)  
    92             self.filter.logger.log_message(_("Unable to compute job size with accounter %s") % msg) 
    93             pagecount = 0 
    94              
    95         if temporary is not None :     
    96             # this is a copy of our previous standard input 
    97             # flush, then rewind 
    98             temporary.flush() 
    99             temporary.seek(0, 0) 
    100             # our temporary file will be used later if the 
    101             # job is allowed. 
    102             self.filter.inputfile = temporary 
    103         else : 
    104             infile.close() 
     71            retcode = child.wait() 
     72        except OSError, msg :     
     73            self.filter.logger.log_message(_("Problem while waiting for software accounter pid %s to exit") % child.pid) 
     74        else :     
     75            if os.WIFEXITED(retcode) : 
     76                status = os.WEXITSTATUS(retcode) 
     77            else :     
     78                status = retcode 
     79            self.filter.logger.log_message(_("Software accounter %s exit code is %s") % (self.arguments, repr(retcode))) 
     80        self.filter.logdebug("Software accounter %s said job is %s pages long." % (self.arguments, pagecount)) 
    10581        return pagecount     
    10682             
  • pykota/trunk/pykota/config.py

    r1483 r1495  
    2222# 
    2323# $Log$ 
     24# Revision 1.48  2004/05/24 22:45:49  jalet 
     25# New 'enforcement' directive added 
     26# Polling loop improvements 
     27# 
    2428# Revision 1.47  2004/05/18 14:49:20  jalet 
    2529# Big code changes to completely remove the need for "requester" directives, 
     
    343347            return      # No command to launch in the post-hook 
    344348             
     349    def getPrinterEnforcement(self, printername) :     
     350        """Returns if quota enforcement should be strict or laxist for the current printer.""" 
     351        validenforcements = [ "STRICT", "LAXIST" ]      
     352        try : 
     353            enforcement = self.getPrinterOption(printername, "enforcement") 
     354        except PyKotaConfigError :     
     355            return "LAXIST" 
     356        else :     
     357            enforcement = enforcement.upper() 
     358            if enforcement not in validenforcements : 
     359                raise PyKotaConfigError, _("Option enforcement in section %s only supports values in %s") % (printername, str(validenforcements)) 
     360            return enforcement     
     361             
    345362    def getPrinterPolicy(self, printername) :     
    346363        """Returns the default policy for the current printer.""" 
  • pykota/trunk/pykota/tool.py

    r1492 r1495  
    2222# 
    2323# $Log$ 
     24# Revision 1.90  2004/05/24 22:45:49  jalet 
     25# New 'enforcement' directive added 
     26# Polling loop improvements 
     27# 
    2428# Revision 1.89  2004/05/21 22:02:52  jalet 
    2529# Preliminary work on pre-accounting 
     
    379383        self.smtpserver = self.config.getSMTPServer() 
    380384        self.maildomain = self.config.getMailDomain() 
     385        self.softwareJobPrice = 0.0 
    381386         
    382387    def logdebug(self, message) :     
     
    513518        user = userpquota.User 
    514519        printer = userpquota.Printer 
     520        enforcement = self.config.getPrinterEnforcement(printer.Name) 
    515521        self.logdebug("Checking user %s's quota on printer %s" % (user.Name, printer.Name)) 
    516522        (policy, dummy) = self.config.getPrinterPolicy(userpquota.Printer.Name) 
     
    524530        else :     
    525531            pagecounter = int(userpquota.PageCounter or 0) 
     532            if enforcement == "STRICT" : 
     533                pagecounter += self.softwareJobSize 
    526534            if userpquota.SoftLimit is not None : 
    527535                softlimit = int(userpquota.SoftLimit) 
     
    564572        group = grouppquota.Group 
    565573        printer = grouppquota.Printer 
     574        enforcement = self.config.getPrinterEnforcement(printer.Name) 
    566575        self.logdebug("Checking group %s's quota on printer %s" % (group.Name, printer.Name)) 
    567576        if group.LimitBy and (group.LimitBy.lower() == "balance") :  
    568             if group.AccountBalance <= 0.0 : 
     577            val = group.AccountBalance 
     578            if enforcement == "STRICT" :  
     579                val -= self.softwareJobPrice # use precomputed size. 
     580            if val <= 0.0 : 
    569581                action = "DENY" 
    570             elif group.AccountBalance <= self.config.getPoorMan() :     
     582            elif val <= self.config.getPoorMan() :     
    571583                action = "WARN" 
    572584            else :     
    573585                action = "ALLOW" 
    574586        else : 
     587            val = grouppquota.PageCounter 
     588            if enforcement == "STRICT" : 
     589                val += self.softwareJobSize 
    575590            if grouppquota.SoftLimit is not None : 
    576591                softlimit = int(grouppquota.SoftLimit) 
    577                 if grouppquota.PageCounter < softlimit : 
     592                if val < softlimit : 
    578593                    action = "ALLOW" 
    579594                else :     
     
    583598                    else :     
    584599                        hardlimit = int(grouppquota.HardLimit) 
    585                         if softlimit <= grouppquota.PageCounter < hardlimit :     
     600                        if softlimit <= val < hardlimit :     
    586601                            now = DateTime.now() 
    587602                            if grouppquota.DateLimit is not None : 
     
    600615                    # no soft limit, only a hard one. 
    601616                    hardlimit = int(grouppquota.HardLimit) 
    602                     if grouppquota.PageCounter < hardlimit : 
     617                    if val < hardlimit : 
    603618                        action = "ALLOW" 
    604619                    else :       
     
    643658            else :     
    644659                val = float(user.AccountBalance or 0.0) 
     660                if self.config.getPrinterEnforcement(printer.Name) == "STRICT" :  
     661                    val -= self.softwareJobPrice # use precomputed size. 
    645662                if val <= 0.0 : 
    646663                    return "DENY" 
     
    820837    def precomputeJobSize(self) :     
    821838        """Computes the job size with a software method.""" 
     839        self.logdebug("Precomputing job's size with generic PDL analyzer...") 
    822840        try : 
    823841            parser = pdlanalyzer.PDLAnalyzer(self.jobdatastream) 
    824             return parser.getJobSize() 
     842            jobsize = parser.getJobSize() 
    825843        except pdlanalyzer.PDLAnalyzerError, msg :     
    826844            # Here we just log the failure, but 
     
    830848            self.logger.log_message(_("Unable to precompute the job's size with the generic PDL analyzer."), "warn") 
    831849            return 0 
     850        else :     
     851            if ((self.printingsystem == "CUPS") \ 
     852                and (self.preserveinputfile is not None)) \ 
     853                or (self.printingsystem != "CUPS") : 
     854                return jobsize * self.copies 
     855            else :         
     856                return jobsize 
    832857             
    833858    def sigterm_handler(self, signum, frame) : 
  • pykota/trunk/pykota/version.py

    r1494 r1495  
    2222# 
    2323 
    24 __version__ = "1.19alpha13_unofficial" 
     24__version__ = "1.19alpha14_unofficial" 
    2525 
    2626__doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""