Changeset 1495

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
Files:
9 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/bin/cupspykota

    r1494 r1495  
    2424# 
    2525# $Log$ 
     26# Revision 1.49  2004/05/24 22:45:48  jalet 
     27# New 'enforcement' directive added 
     28# Polling loop improvements 
     29# 
    2630# Revision 1.48  2004/05/24 14:36:24  jalet 
    2731# Revert to old polling loop. Will need optimisations 
     
    252256             
    253257            # checks the user's quota 
     258            self.softwareJobPrice = userpquota.computeJobPrice(self.softwareJobSize) 
    254259            action = self.warnUserPQuota(userpquota) 
    255260             
     
    311316        return retcode     
    312317                
    313     def setNonBlocking(self, fno) : 
    314         """Sets a file handle to be non-blocking.""" 
    315         flags = fcntl.fcntl(fno, fcntl.F_GETFL, 0) 
    316         fcntl.fcntl(fno, fcntl.F_SETFL, flags | os.O_NONBLOCK) 
    317  
    318318    def unregisterFileNo(self, pollobj, fileno) :                 
    319319        """Removes a file handle from the polling object.""" 
     
    327327            self.logdebug("File number %s unregistered from polling object." % fileno) 
    328328             
    329     def formatFileEvent(self, fd, mask, ins, outs) :         
     329    def formatFileEvent(self, fd, mask) :         
    330330        """Formats file debug info.""" 
    331         try : 
    332             name = ins.get(fd, outs.get(fd))["name"] 
    333         except KeyError :     
    334             self.logdebug("File %s not found in %s or %s" % (fd, repr(ins), repr(outs))) 
    335         else :     
    336             maskval = [] 
    337             if mask & select.POLLIN : 
    338                 maskval.append("POLLIN") 
    339             if mask & select.POLLOUT : 
    340                 maskval.append("POLLOUT") 
    341             if mask & select.POLLPRI : 
    342                 maskval.append("POLLPRI") 
    343             if mask & select.POLLERR : 
    344                 maskval.append("POLLERR") 
    345             if mask & select.POLLHUP : 
    346                 maskval.append("POLLHUP") 
    347             if mask & select.POLLNVAL : 
    348                 maskval.append("POLLNVAL") 
    349             return "%s (%s)" % (name, " | ".join(maskval)) 
     331        maskval = [] 
     332        if mask & select.POLLIN : 
     333            maskval.append("POLLIN") 
     334        if mask & select.POLLOUT : 
     335            maskval.append("POLLOUT") 
     336        if mask & select.POLLPRI : 
     337            maskval.append("POLLPRI") 
     338        if mask & select.POLLERR : 
     339            maskval.append("POLLERR") 
     340        if mask & select.POLLHUP : 
     341            maskval.append("POLLHUP") 
     342        if mask & select.POLLNVAL : 
     343            maskval.append("POLLNVAL") 
     344        return "%s (%s)" % (fd, " | ".join(maskval)) 
    350345         
    351346    def handleData(self) :                     
     
    378373        if self.preserveinputfile is None : 
    379374            # this is not a real file, we read the job's data 
    380             # from stdin  
     375            # from our temporary file which is a copy of stdin  
    381376            infno = self.jobdatastream.fileno() 
    382377            self.jobdatastream.seek(0) 
     
    389384            endinput = 1 
    390385         
    391         self.logdebug("Catching SIGTERM.") 
     386        self.logdebug("Capturing SIGTERM events.") 
    392387        signal.signal(signal.SIGTERM, self.sigterm_handler) 
    393388         
     389        self.logdebug("Entering streams polling loop...") 
     390        MEGABYTE = 1024*1024 
    394391        killed = 0 
    395         self.logdebug("Entering streams polling loop...") 
    396392        status = -1 
    397         while status == -1 : 
     393        while (status == -1) and (not killed) and not (inputclosed and outputclosed) : 
    398394            # First check if original backend is still alive 
    399395            status = subprocess.poll() 
     
    404400                try : 
    405401                    os.kill(subprocess.pid, signal.SIGTERM) 
     402                except OSError, msg : # ignore but logs if process was already killed. 
     403                    self.logdebug("Error while sending signal to pid %s" % subprocess.pid) 
     404                else :     
    406405                    self.logger.log_message(_("SIGTERM was sent to real backend %s (pid: %s)") % (realbackend, subprocess.pid), "info") 
    407406                    killed = 1 
    408                 except : # ignore if process was already killed. 
    409                     pass 
    410407             
    411408            # In any case, deal with any remaining I/O 
    412409            availablefds = pollster.poll(5000) 
    413             for (fd, mask) in availablefds : 
    414                 # self.logdebug("file: %i    mask: %04x" % (fd, mask)) 
    415                 try : 
    416                     if mask & select.POLLOUT : 
    417                         # We can write 
    418                         if fd == tocfno : 
    419                             if indata : 
    420                                 os.write(fd, indata)     
    421                                 try : 
    422                                     os.fsync(fd) 
    423                                 except OSError : 
    424                                     pass 
    425                                 indata = "" 
    426                             if endinput :     
    427                                 self.unregisterFileNo(pollster, tocfno)         
    428                                 self.logdebug("Closing real backend's stdin.") 
    429                                 os.close(tocfno) 
    430                                 inputclosed = 1 
    431                         elif fd == stderrfno : 
    432                             if outdata : 
    433                                 os.write(fd, outdata) 
    434                                 try : 
    435                                     os.fsync(fd) 
    436                                 except OSError :     
    437                                     pass 
    438                                 outdata = "" 
    439                             if endoutput :     
    440                                 self.unregisterFileNo(pollster, stderrfno)         
    441                                 outputclosed = 1 
    442                     if (mask & select.POLLIN) or (mask & select.POLLPRI) :      
    443                         # We have something to read 
    444                         try : 
    445                             data = os.read(fd, 256 * 1024) 
    446                         except OSError, msg :     
    447                             self.logdebug("Error while reading file %s : %s" % (fd, msg)) 
    448                         else : 
     410            if not availablefds : 
     411                self.logdebug("Nothing to do, sleeping a bit...") 
     412                time.sleep(0.01) # give some time to the system 
     413            else : 
     414                for (fd, mask) in availablefds : 
     415                    # self.logdebug(self.formatFileEvent(fd, mask)) 
     416                    try : 
     417                        if mask & select.POLLOUT : 
     418                            # We can write 
     419                            if fd == tocfno : 
     420                                if indata : 
     421                                    try : 
     422                                        os.write(fd, indata)     
     423                                    except IOError, msg :     
     424                                        self.logdebug("Error while writing to real backend's stdin %s : %s" % (fd, msg)) 
     425                                    else :     
     426                                        indata = "" 
     427                                if endinput :     
     428                                    self.unregisterFileNo(pollster, tocfno)         
     429                                    self.logdebug("Closing real backend's stdin.") 
     430                                    os.close(tocfno) 
     431                                    inputclosed = 1 
     432                            elif fd == stderrfno : 
     433                                if outdata : 
     434                                    try : 
     435                                        os.write(fd, outdata) 
     436                                    except IOError, msg :     
     437                                        self.logdebug("Error while writing to CUPS back channel (stderr) %s : %s" % (fd, msg)) 
     438                                    else : 
     439                                        outdata = "" 
     440                                if endoutput :     
     441                                    self.unregisterFileNo(pollster, stderrfno)         
     442                                    outputclosed = 1 
     443                        if mask & (select.POLLIN | select.POLLPRI) :      
     444                            # We have something to read 
     445                            try : 
     446                                data = os.read(fd, MEGABYTE) 
     447                            except (IOError, OSError), msg :     
     448                                self.logdebug("Error while reading file %s : %s" % (fd, msg)) 
     449                            else : 
     450                                if fd == infno : 
     451                                    indata += data 
     452                                    if not data :    # If yes, then no more input data 
     453                                        self.unregisterFileNo(pollster, infno) 
     454                                        self.logdebug("Input data ends.") 
     455                                        endinput = 1 # this happens with real files. 
     456                                elif fd == fromcfno : 
     457                                    outdata += data 
     458                        if mask & (select.POLLHUP | select.POLLERR) : 
     459                            # Treat POLLERR as an EOF. 
     460                            # Some standard I/O stream has no more datas 
     461                            self.unregisterFileNo(pollster, fd) 
    449462                            if fd == infno : 
    450                                 indata += data 
    451                                 if not data :    # If yes, then no more input data 
    452                                     self.unregisterFileNo(pollster, infno) 
    453                                     self.logdebug("Input data ends.") 
    454                                     endinput = 1 # this happens with real files. 
    455                             elif fd == fromcfno : 
    456                                 outdata += data 
    457                     if (mask & select.POLLHUP) or (mask & select.POLLERR) : 
    458                         # I've never seen POLLERR myself, but this probably 
    459                         # can't hurt to treat an error condition just like  
    460                         # an EOF. 
    461                         #  
    462                         # Some standard I/O stream has no more datas 
    463                         self.unregisterFileNo(pollster, fd) 
    464                         if fd == infno : 
    465                             # Here we are in the case where the input file is stdin. 
    466                             # which has no more data to be read. 
    467                             self.logdebug("Input data ends.") 
    468                             endinput = 1 
    469                         elif fd == fromcfno :     
    470                             # We are no more interested in this file descriptor         
    471                             self.logdebug("Closing real backend's stdout+stderr.") 
    472                             os.close(fromcfno) 
    473                             endoutput = 1 
    474                 except IOError :             
    475                     pass # we got signalled during an I/O it seems 
    476             if killed or (inputclosed and outputclosed) : 
    477                 break 
     463                                # Here we are in the case where the input file is stdin. 
     464                                # which has no more data to be read. 
     465                                self.logdebug("Input data ends.") 
     466                                endinput = 1 
     467                            elif fd == fromcfno :     
     468                                # We are no more interested in this file descriptor         
     469                                self.logdebug("Closing real backend's stdout+stderr.") 
     470                                os.close(fromcfno) 
     471                                endoutput = 1 
     472                                 
     473                        if mask & select.POLLNVAL :         
     474                            self.logdebug("File %s was closed. Unregistering from polling object." % fd) 
     475                            self.unregisterFileNo(pollster, fd) 
     476                    except IOError, msg :             
     477                        self.logdebug("Got an IOError : %s" % msg) # we got signalled during an I/O 
    478478                 
    479479        # We must close the real backend's input stream 
     
    491491            # we exited the loop before the real backend exited 
    492492            # now we have to wait for it to finish and get its status 
     493            self.logdebug("Waiting for real backend to exit...") 
    493494            try : 
    494495                status = subprocess.wait() 
  • pykota/trunk/conf/pykota.conf.sample

    r1483 r1495  
    425425# PYKOTAPHASE environment variable. 
    426426# Pre and Post Hooks can be defined either globally, per printer, 
    427 # or both if both are defined, the printer specific hook has 
     427# or both. If both are defined, the printer specific hook has 
    428428# priority. 
    429429# 
     
    476476#posthook: /usr/bin/printenv >/tmp/after 
    477477 
     478# How should enforcement be done for this printer ? 
     479# 
     480# "laxist" is the default if value is not set, and allows users 
     481# to be over quota on their last job.  
     482# 
     483# "strict" tries to prevent users from ever being over quota. 
     484# 
     485# Enforcement can be defined either globally, per printer, 
     486# or both. If both are defined, the printer specific enforcement  
     487# setting has priority.  
     488# 
     489# valid values : "strict" or "laxist" 
     490# 
     491# default value 
     492# enforcement : laxist 
     493enforcement : strict 
  • pykota/trunk/NEWS

    r1494 r1495  
    2222PyKota NEWS : 
    2323 
     24    - 1.19alpha14 : 
     25     
     26        - New 'enforcement' directive which accepts either 
     27          STRICT or LAXIST. See sample configuration file 
     28          for details. 
     29           
    2430    - 1.19alpha13 : 
    2531     
  • 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."""