Changeset 1271

Show
Ignore:
Timestamp:
01/12/04 00:22:42 (20 years ago)
Author:
jalet
Message:

Major code refactoring, it's way cleaner, and now allows automated addition
of printers on first print.

Location:
pykota/trunk
Files:
7 modified

Legend:

Unmodified
Added
Removed
  • pykota/trunk/bin/cupspykota

    r1257 r1271  
    2424# 
    2525# $Log$ 
     26# Revision 1.20  2004/01/11 23:22:42  jalet 
     27# Major code refactoring, it's way cleaner, and now allows automated addition 
     28# of printers on first print. 
     29# 
    2630# Revision 1.19  2004/01/08 14:10:32  jalet 
    2731# Copyright year changed. 
     
    108112from pykota.requester import PyKotaRequesterError 
    109113 
     114gotSigTerm = 0 
     115def sigterm_handler(signum, frame) : 
     116    """Sets a global variable whenever SIGTERM is received.""" 
     117    # SIGTERM will be ignore most of the time, but during 
     118    # the call to the real backend, we have to pass it through. 
     119    global gotSigTerm 
     120    gotSigTerm = 1 
     121     
    110122class PyKotaPopen3(popen2.Popen3) : 
    111123    """Our own class to execute real backends. 
     
    129141            os._exit(1) 
    130142     
    131 gotSigTerm = 0 
    132 def sigterm_handler(signum, frame) : 
    133     """Sets a global variable whenever SIGTERM is received.""" 
    134     # SIGTERM will be ignore most of the time, but during 
    135     # the call to the real backend, we have to pass it through. 
    136     global gotSigTerm 
    137     gotSigTerm = 1 
    138      
    139 def main(thebackend) :     
    140     """Do it, and do it right !""" 
    141     # first deal with signals 
    142     # CUPS backends ignore SIGPIPE and exit(1) on SIGTERM 
    143     # Fortunately SIGPIPE is already ignored by Python 
    144     # It's there just in case this changes in the future. 
    145     # Here we have to handle SIGTERM correctly, and pass 
    146     # it to the original backend if needed. 
    147     global gotSigTerm 
    148     gotSigTerm = 0 
    149     signal.signal(signal.SIGPIPE, signal.SIG_IGN) 
    150     signal.signal(signal.SIGTERM, sigterm_handler) 
    151      
    152     # 
    153     # retrieve some informations on the current printer and user 
    154     # from the Quota Storage. 
    155     printer = thebackend.storage.getPrinter(thebackend.printername) 
    156     if not printer.Exists : 
    157         # The printer is unknown from the Quota Storage perspective 
    158         # we send an error back to CUPS, which will stop 
    159         # the print queue. 
    160         thebackend.logger.log_message(_("Printer %s not registered in the PyKota system") % thebackend.printername, "error") 
    161         return 1 
    162     else :     
    163         for dummy in range(2) : 
    164             user = thebackend.storage.getUser(thebackend.username) 
    165             if user.Exists : 
    166                 break 
    167             else :     
    168                 # The user is unknown from the Quota Storage perspective 
    169                 # Depending on the default policy for this printer, we 
    170                 # either let the job pass through or reject it, but we 
    171                 # log a message in any case. 
    172                 (policy, args) = thebackend.config.getPrinterPolicy(thebackend.printername) 
    173                 if policy == "ALLOW" : 
    174                     action = "POLICY_ALLOW" 
    175                 elif policy == "EXTERNAL" :     
    176                     commandline = thebackend.formatCommandLine(args, user, printer) 
    177                     thebackend.logger.log_message(_("User %s not registered in the PyKota system, applying external policy (%s) for printer %s") % (thebackend.username, commandline, thebackend.printername), "info") 
    178                     if os.system(commandline) : 
    179                         # if an error occured, we die without error, 
    180                         # so that the job doesn't stop the print queue. 
    181                         thebackend.logger.log_message(_("External policy %s for printer %s produced an error. Job rejected. Please check PyKota's configuration files.") % (commandline, thebackend.printername), "error") 
    182                         return 0 
    183                     else :     
    184                         # here we try a second time, because the goal 
    185                         # of the external action was to add the user 
    186                         # in the database. 
    187                         continue  
    188                 else :     
    189                     action = "POLICY_DENY" 
    190                 thebackend.logger.log_message(_("User %s not registered in the PyKota system, applying default policy (%s) for printer %s") % (thebackend.username, action, thebackend.printername), "warn") 
    191                 if action == "POLICY_DENY" : 
    192                     # if not allowed to print then die, else proceed. 
    193                     # we die without error, so that the job doesn't  
    194                     # stop the print queue. 
    195                     return 0 
    196                 # when we get there, the printer policy allows the job to pass  
    197                 break     
     143class PyKotaBackend(PyKotaFilterOrBackend) :         
     144    """A class for the pykota backend.""" 
     145    def acceptJob(self) :         
     146        """Returns the appropriate exit code to tell CUPS all is OK.""" 
     147        return 0 
     148             
     149    def removeJob(self) :             
     150        """Returns the appropriate exit code to let CUPS think all is OK. 
     151         
     152           Returning 0 (success) prevents CUPS from stopping the print queue. 
     153        """    
     154        return 0 
     155         
     156    def doWork(self, policy, printer, user, userpquota) :     
     157        """Most of the work is done here.""" 
     158        global gotSigTerm 
     159        # Two different values possible for policy here : 
     160        # ALLOW means : Either printer, user or user print quota doesn't exist, 
     161        #               but the job should be allowed anyway. 
     162        # OK means : Both printer, user and user print quota exist, job should 
     163        #            be allowed if current user is allowed to print on this printer 
     164        if policy == "OK" : 
     165            self.logdebug("Checking user %s's quota on printer %s" % (user.Name, printer.Name)) 
     166            action = self.warnUserPQuota(userpquota) 
     167            if action not in ["ALLOW", "WARN"] :     
     168                return self.removeJob() 
     169            self.logdebug("Job accounting begins.") 
     170            self.accounter.beginJob(userpquota) 
     171             
     172        # pass the job's data to the real backend     
     173        if gotSigTerm : 
     174            retcode = self.removeJob() 
     175        else :     
     176            retcode = self.handleData()         
     177         
     178        if policy == "OK" :         
     179            # stops accounting.  
     180            self.accounter.endJob(userpquota) 
     181            self.logdebug("Job accounting ends.") 
     182                 
     183            # retrieve the job size     
     184            jobsize = self.accounter.getJobSize() 
     185            self.logdebug("Job size : %i" % jobsize) 
     186             
     187            # update the quota for the current user on this printer  
     188            jobprice = (float(printer.PricePerPage or 0.0) * jobsize) + float(printer.PricePerJob or 0.0) 
     189            if jobsize : 
     190                self.logdebug("Updating user %s's quota on printer %s" % (user.Name, printer.Name)) 
     191                userpquota.increasePagesUsage(jobsize) 
     192             
     193            # adds the current job to history     
     194            printer.addJobToHistory(self.jobid, user, self.accounter.getLastPageCounter(), action, jobsize, jobprice, self.preserveinputfile, self.title, self.copies, self.options) 
     195            self.logdebug("Job added to history.") 
     196             
     197        return retcode     
    198198                     
    199         if user.Exists : 
    200             # Is the current user allowed to print at all ? 
    201             thebackend.logdebug("Checking user %s's quota on printer %s" % (user.Name, printer.Name)) 
    202             action = thebackend.warnUserPQuota(thebackend.storage.getUserPQuota(user, printer)) 
    203         elif policy == "EXTERNAL" :                 
    204             # if the extenal policy produced no error, but the  
    205             # user still doesn't exist, we die without error, 
    206             # so that the job doesn't stop the print queue. 
    207             thebackend.logger.log_message(_("External policy %s for printer %s couldn't add user %s. Job rejected.") % (commandline, thebackend.printername, thebackend.username), "error") 
    208             return 0 
    209          
    210     if action not in ["ALLOW", "WARN"] :     
    211         # if not allowed to print then die, else proceed. 
    212         # we die without error, so that the job doesn't  
    213         # stop the print queue. 
    214         retcode = 0 
    215     else : 
    216         # pass the job untouched to the underlying layer 
    217         # and starts accounting at the same time 
    218         thebackend.logdebug("Job accounting begins.") 
    219         thebackend.accounter.beginJob(printer, user) 
    220          
     199    def handleData(self) :                     
     200        """Pass the job's data to the real backend.""" 
    221201        # Now it becomes tricky... 
     202        # We must pass the unmodified job to the original backend 
     203         
     204        global gotSigTerm 
    222205         
    223206        # First ensure that we have a file object as input 
    224207        mustclose = 0     
    225         if thebackend.inputfile is not None :     
    226             if hasattr(thebackend.inputfile, "read") : 
    227                 infile = thebackend.inputfile 
     208        if self.inputfile is not None :     
     209            if hasattr(self.inputfile, "read") : 
     210                infile = self.inputfile 
    228211            else :     
    229                 infile = open(thebackend.inputfile, "rb") 
     212                infile = open(self.inputfile, "rb") 
    230213            mustclose = 1 
    231214        else :     
     
    233216             
    234217        # Find the real backend pathname     
    235         realbackend = os.path.join(os.path.split(sys.argv[0])[0], thebackend.originalbackend) 
     218        realbackend = os.path.join(os.path.split(sys.argv[0])[0], self.originalbackend) 
    236219         
    237220        # And launch it 
    238         thebackend.logdebug("Starting real backend %s with args %s" % (realbackend, " ".join(['"%s"' % a for a in ([os.environ["DEVICE_URI"]] + sys.argv[1:])]))) 
     221        self.logdebug("Starting real backend %s with args %s" % (realbackend, " ".join(['"%s"' % a for a in ([os.environ["DEVICE_URI"]] + sys.argv[1:])]))) 
    239222        subprocess = PyKotaPopen3([realbackend] + sys.argv[1:], capturestderr=1, bufsize=0, arg0=os.environ["DEVICE_URI"]) 
    240223         
     
    264247        status = -1 
    265248        inputclosed = 0 
    266         thebackend.logdebug("Entering streams polling loop...") 
     249        self.logdebug("Entering streams polling loop...") 
    267250        while status == -1 : 
    268251            # First check if original backend is still alive 
     
    280263            availablefds = pollster.poll() 
    281264            for (fd, mask) in availablefds : 
    282                 # thebackend.logdebug("file: %i    mask: %04x" % (fd, mask)) 
     265                # self.logdebug("file: %i    mask: %04x" % (fd, mask)) 
    283266                if mask & select.POLLOUT : 
    284267                    # We can write 
     
    326309                        pollster.unregister(fromcfno)         
    327310                        os.close(fromcfno) 
    328                         thebackend.logdebug("Real backend's stdout ends.") 
     311                        self.logdebug("Real backend's stdout ends.") 
    329312                    elif fd == cerrfno :     
    330313                        # Original CUPS backend has finished  
     
    337320                        pollster.unregister(cerrfno)         
    338321                        os.close(cerrfno) 
    339                         thebackend.logdebug("Real backend's stderr ends.") 
     322                        self.logdebug("Real backend's stderr ends.") 
    340323                         
    341324            if endinput and not inputclosed :             
     
    351334                os.close(tocfno) 
    352335                inputclosed = 1 
    353                 thebackend.logdebug("Input data ends.") 
     336                self.logdebug("Input data ends.") 
    354337                 
    355338        # Input file was a real file, we have to close it.     
     
    357340            infile.close() 
    358341             
    359         thebackend.logdebug("Exiting streams polling loop...") 
     342        self.logdebug("Exiting streams polling loop...") 
    360343             
    361344        # Check exit code of original CUPS backend.     
     
    363346            retcode = os.WEXITSTATUS(status) 
    364347        else :     
    365             thebackend.logger.log_message(_("CUPS backend %s died abnormally.") % realbackend, "error") 
     348            self.logger.log_message(_("CUPS backend %s died abnormally.") % realbackend, "error") 
    366349            retcode = -1 
    367      
    368     # stops accounting.  
    369     thebackend.accounter.endJob(printer, user) 
    370     thebackend.logdebug("Job accounting ends.") 
    371          
    372     # retrieve the job size     
    373     jobsize = thebackend.accounter.getJobSize() 
    374     thebackend.logdebug("Job size : %i" % jobsize) 
    375      
    376     # update the quota for the current user on this printer  
    377     jobprice = (float(printer.PricePerPage or 0.0) * jobsize) + float(printer.PricePerJob or 0.0) 
    378     if jobsize : 
    379         userquota = thebackend.storage.getUserPQuota(user, printer) 
    380         if userquota.Exists : 
    381             thebackend.logdebug("Updating user %s's quota on printer %s" % (user.Name, printer.Name)) 
    382             userquota.increasePagesUsage(jobsize) 
    383      
    384     # adds the current job to history     
    385     printer.addJobToHistory(thebackend.jobid, user, thebackend.accounter.getLastPageCounter(), action, jobsize, jobprice, thebackend.preserveinputfile, thebackend.title, thebackend.copies, thebackend.options) 
    386     thebackend.logdebug("Job added to history.") 
    387      
    388     return retcode # return (retcode or gotSigTerm) shouldn't be needed 
    389  
     350        return retcode     
     351     
    390352if __name__ == "__main__" :     
    391353    # This is a CUPS backend, we should act and die like a CUPS backend 
     354     
     355    # first deal with signals 
     356    # CUPS backends ignore SIGPIPE and exit(1) on SIGTERM 
     357    # Fortunately SIGPIPE is already ignored by Python 
     358    # It's there just in case this changes in the future. 
     359    # Here we have to handle SIGTERM correctly, and pass 
     360    # it to the original backend if needed. 
     361    global gotSigTerm 
     362    gotSigTerm = 0 
     363    signal.signal(signal.SIGPIPE, signal.SIG_IGN) 
     364    signal.signal(signal.SIGTERM, sigterm_handler) 
     365     
    392366    if len(sys.argv) == 1 : 
    393367        # we will execute each existing backend in device enumeration mode 
     
    433407        try : 
    434408            # Initializes the backend 
    435             kotabackend = PyKotaFilterOrBackend()     
    436             retcode = main(kotabackend) 
     409            kotabackend = PyKotaBackend()     
     410            retcode = kotabackend.mainWork() 
    437411        except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, PyKotaRequesterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg : 
    438412            sys.stderr.write("ERROR : cupspykota backend failed (%s)\n" % msg) 
  • pykota/trunk/bin/pykota

    r1257 r1271  
    2424# 
    2525# $Log$ 
     26# Revision 1.50  2004/01/11 23:22:42  jalet 
     27# Major code refactoring, it's way cleaner, and now allows automated addition 
     28# of printers on first print. 
     29# 
    2630# Revision 1.49  2004/01/08 14:10:32  jalet 
    2731# Copyright year changed. 
     
    228232            # UNKNOWN 
    229233            return -1 
     234         
     235    def doWork(self, policy, printer, user, userpquota) :     
     236        """Most of the work is done here.""" 
     237        # Two different values possible for policy here : 
     238        # ALLOW means : Either printer, user or user print quota doesn't exist, 
     239        #               but the job should be allowed anyway. 
     240        # OK means : Both printer, user and user print quota exist, job should 
     241        #            be allowed if current user is allowed to print on this printer 
     242        if policy == "OK" : 
     243            self.logdebug("Does accounting for user %s on printer %s." % (user.Name, printer.Name)) 
     244            action = self.accounter.doAccounting(userpquota) 
     245            if action == "DENY" : 
     246                # Just die. 
     247                self.logdebug("Printing is denied.") 
     248                return self.removeJob() 
    230249             
    231 def main(thefilter) :     
    232     """Do it, and do it right !""" 
    233     # 
    234     # If this is a CUPS filter, we should act and die like a CUPS filter when needed 
    235     if thefilter.printingsystem == "CUPS" : 
    236         if len(sys.argv) not in (6, 7) :     
    237             sys.stderr.write("ERROR: %s job-id user title copies options [file]\n" % sys.argv[0]) 
    238             return thefilter.removeJob() 
    239      
    240     # Get the last page counter and last username from the Quota Storage backend 
    241     printer = thefilter.storage.getPrinter(thefilter.printername) 
    242     if not printer.Exists : 
    243         # The printer is unknown from the Quota Storage perspective. 
    244         # we just cancel the job. 
    245         thefilter.logger.log_message(_("Printer %s not registered in the PyKota system") % thefilter.printername, "error") 
    246         return thefilter.removeJob() 
    247     else :     
    248         for dummy in range(2) : 
    249             user = thefilter.storage.getUser(thefilter.username) 
    250             if user.Exists : 
    251                 break 
     250        # pass the job's data to the next filter 
     251        self.logdebug("Passing input data to next filter.") 
     252        mustclose = 0     
     253        if self.inputfile is not None :     
     254            if hasattr(self.inputfile, "read") : 
     255                infile = self.inputfile 
    252256            else :     
    253                 # The user is unknown from the Quota Storage perspective 
    254                 # Depending on the default policy for this printer, we 
    255                 # either let the job pass through or reject it, but we 
    256                 # log a message in any case. 
    257                 (policy, args) = thefilter.config.getPrinterPolicy(thefilter.printername) 
    258                 if policy == "ALLOW" : 
    259                     action = "POLICY_ALLOW" 
    260                 elif policy == "EXTERNAL" :     
    261                     commandline = thefilter.formatCommandLine(args, user, printer) 
    262                     thefilter.logger.log_message(_("User %s not registered in the PyKota system, applying external policy (%s) for printer %s") % (thefilter.username, commandline, thefilter.printername), "info") 
    263                     if os.system(commandline) : 
    264                         thefilter.logger.log_message(_("External policy %s for printer %s produced an error. Job rejected. Please check PyKota's configuration files.") % (commandline, thefilter.printername), "error") 
    265                         return thefilter.removeJob() 
    266                     else :     
    267                         # here we try a second time, because the goal 
    268                         # of the external action was to add the user 
    269                         # in the database. 
    270                         continue  
    271                 else :     
    272                     action = "POLICY_DENY" 
    273                 thefilter.logger.log_message(_("User %s not registered in the PyKota system, applying default policy (%s) for printer %s") % (thefilter.username, action, thefilter.printername), "warn") 
    274                 if action == "POLICY_DENY" : 
    275                     return thefilter.removeJob() 
    276                 # when we get there, the printer policy allows the job to pass  
    277                 break     
     257                infile = open(self.inputfile, "rb") 
     258            mustclose = 1 
     259        else :     
     260            infile = sys.stdin 
     261        data = infile.read(256*1024)     
     262        while data : 
     263            sys.stdout.write(data) 
     264            data = infile.read(256*1024) 
     265        if mustclose :     
     266            infile.close() 
     267         
     268        return self.acceptJob() 
     269         
     270    def mainWork(self) :     
     271        # If this is a CUPS filter, we should act and die like a CUPS filter when needed 
     272        if self.printingsystem == "CUPS" : 
     273            if len(sys.argv) not in (6, 7) :     
     274                sys.stderr.write("ERROR: %s job-id user title copies options [file]\n" % sys.argv[0]) 
     275                return self.removeJob() 
     276                 
     277        if self.accounter.isDelayed :         
     278            # Here we need to update the last user's print quota         
     279            # because hardware accounting is delayed by one print job 
     280            # in the pykota filter. 
     281            printer = self.storage.getPrinter(self.printername)         
     282            if not printer.Exists : 
     283                self.logger.log_message(_("Printer %s not registered in the PyKota system") % self.printername, "info") 
     284            else :     
     285                if not printer.LastJob.Exists : 
     286                    self.logger.log_message(_("Printer %s was never used") % self.printername, "info") 
     287                else : 
     288                    self.logdebug("Updating print quota for last user %s on printer %s" % (printer.LastJob.User.Name, printer.Name)) 
     289                    lastuserpquota = self.storage.getUserPQuota(printer.LastJob.User, printer) 
     290                    self.accounter.counterbefore = printer.LastJob.PrinterPageCounter 
     291                    self.accounter.counterafter = self.accounter.getPrinterInternalPageCounter() or 0 
     292                    lastjobsize = self.accounter.getJobSize() 
     293                    self.logdebug("Last Job size : %i" % lastjobsize) 
     294                    printer.LastJob.setSize(lastjobsize) 
     295                    self.logdebug("Updating user %s's quota on printer %s" % (lastuserpquota.User.Name, printer.Name)) 
     296                    lastuserpquota.increasePagesUsage(lastjobsize) 
     297                    self.warnUserPQuota(lastuserpquota) 
    278298                     
    279         # if user exists, do accounting 
    280         if user.Exists : 
    281             # Now does the accounting and act depending on the result 
    282             thefilter.logdebug("Does accounting for user %s on printer %s." % (user.Name, printer.Name)) 
    283             action = thefilter.accounter.doAccounting(printer, user) 
    284          
    285             # if not allowed to print then die, else proceed. 
    286             if action == "DENY" : 
    287                 # No, just die cleanly 
    288                 thefilter.logdebug("Printing is denied.") 
    289                 return thefilter.removeJob() 
    290         elif policy == "EXTERNAL" :                 
    291             thefilter.logger.log_message(_("External policy %s for printer %s couldn't add user %s. Job rejected.") % (commandline, thefilter.printername, thefilter.username), "error") 
    292             return thefilter.removeJob() 
    293          
    294         # pass the job untouched to the underlying layer 
    295         thefilter.logdebug("Passing input data to next filter.") 
    296         thefilter.accounter.filterInput(thefilter.inputfile)       
    297      
    298         return thefilter.acceptJob() 
    299  
     299        # then deal with current print job as usual             
     300        PyKotaFilterOrBackend.mainWork(self)         
     301             
    300302if __name__ == "__main__" :     
    301303    retcode = -1 
     
    303305        # Initializes the current tool 
    304306        kotafilter = PyKotaFilter()     
    305         retcode = main(kotafilter) 
     307        retcode = kotafilter.mainWork() 
    306308    except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, PyKotaRequesterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg : 
    307309        sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg) 
  • pykota/trunk/NEWS

    r1270 r1271  
    2222PyKota NEWS : 
    2323 
     24    - 1.16alpha24 : 
     25     
     26        - Major code refactoring => It is now possible to 
     27          automatically add printers on first print. 
     28         
    2429    - 1.16alpha23 : 
    2530     
  • pykota/trunk/pykota/accounter.py

    r1257 r1271  
    2222# 
    2323# $Log$ 
     24# Revision 1.11  2004/01/11 23:22:42  jalet 
     25# Major code refactoring, it's way cleaner, and now allows automated addition 
     26# of printers on first print. 
     27# 
    2428# Revision 1.10  2004/01/08 14:10:32  jalet 
    2529# Copyright year changed. 
     
    6973        self.filter = kotafilter 
    7074        self.arguments = arguments 
     75        self.isDelayed = 0      # Accounting is immediate by default 
    7176         
    7277    def getLastPageCounter(self) :     
     
    7681        except :     
    7782            return 0 
    78          
    79     def filterInput(self, inputfile) : 
    80         """Transparent filter.""" 
    81         mustclose = 0     
    82         if inputfile is not None :     
    83             if hasattr(inputfile, "read") : 
    84                 infile = inputfile 
    85             else :     
    86                 infile = open(inputfile, "rb") 
    87             mustclose = 1 
    88         else :     
    89             infile = sys.stdin 
    90         data = infile.read(256*1024)     
    91         while data : 
    92             sys.stdout.write(data) 
    93             data = infile.read(256*1024) 
    94         if mustclose :     
    95             infile.close() 
    9683             
    97     def beginJob(self, printer, user) :     
     84    def beginJob(self, userpquota) :     
    9885        """Saves the computed job size.""" 
    9986        # computes job's size 
     
    10188         
    10289        # get last job information for this printer 
    103         if not printer.LastJob.Exists : 
     90        if not userpquota.Printer.LastJob.Exists : 
    10491            # The printer hasn't been used yet, from PyKota's point of view 
    10592            self.LastPageCounter = 0 
     
    10895            # Last lifetime page counter before actual job is  
    10996            # last page counter + last job size 
    110             self.LastPageCounter = int(printer.LastJob.PrinterPageCounter or 0) + int(printer.LastJob.JobSize or 0) 
     97            self.LastPageCounter = int(userpquota.Printer.LastJob.PrinterPageCounter or 0) + int(userpquota.Printer.LastJob.JobSize or 0) 
    11198         
    112     def endJob(self, printer, user) :     
     99    def endJob(self, userpquota) :     
    113100        """Do nothing.""" 
    114101        pass 
     
    121108            return 0 
    122109         
    123     def doAccounting(self, printer, user) : 
    124         """Deletgates the computation of the job size to an external command. 
     110    def doAccounting(self, userpquota) : 
     111        """Delegates the computation of the job size to an external command. 
    125112         
    126113           The command must print the job size on its standard output and exit successfully. 
    127114        """ 
    128         self.beginJob(printer, user) 
     115        self.beginJob(userpquota) 
    129116         
    130117        # get the job size, which is real job size * number of copies. 
     118        # TODO : Double check with CUPS documentation : this is not always correct 
    131119        jobsize = self.getJobSize() * self.filter.copies 
    132120             
    133121        # Is the current user allowed to print at all ? 
    134         userpquota = self.filter.storage.getUserPQuota(user, printer) 
    135122        action = self.filter.warnUserPQuota(userpquota) 
    136123         
     
    142129         
    143130        # adds the current job to history     
    144         jobprice = (float(printer.PricePerPage or 0.0) * jobsize) + float(printer.PricePerJob or 0.0) 
    145         printer.addJobToHistory(self.filter.jobid, user, self.getLastPageCounter(), action, jobsize, jobprice, self.filter.preserveinputfile, self.filter.title, self.filter.copies, self.filter.options) 
    146         self.endJob(printer, user) 
     131        jobprice = (float(userpquota.Printer.PricePerPage or 0.0) * jobsize) + float(userpquota.Printer.PricePerJob or 0.0) 
     132        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) 
     133        self.endJob(userpquota) 
    147134        return action 
    148135         
  • pykota/trunk/pykota/accounters/querying.py

    r1257 r1271  
    2222# 
    2323# $Log$ 
     24# Revision 1.12  2004/01/11 23:22:42  jalet 
     25# Major code refactoring, it's way cleaner, and now allows automated addition 
     26# of printers on first print. 
     27# 
    2428# Revision 1.11  2004/01/08 14:10:32  jalet 
    2529# Copyright year changed. 
     
    6973        AccounterBase.__init__(self, kotabackend, arguments) 
    7074        self.requester = openRequester(kotabackend.config, kotabackend.printername) 
     75        self.isDelayed = 1 # With the pykota filter, accounting is delayed by one job 
    7176         
    7277    def getPrinterInternalPageCounter(self) :     
     
    8994        return counter     
    9095         
    91     def beginJob(self, printer, user) :     
     96    def beginJob(self, userpquota) :     
    9297        """Saves printer internal page counter at start of job.""" 
    9398        # save page counter before job 
    9499        self.LastPageCounter = self.counterbefore = self.getPrinterInternalPageCounter() 
    95100         
    96     def endJob(self, printer, user) :     
     101    def endJob(self, userpquota) :     
    97102        """Saves printer internal page counter at end of job.""" 
    98103        # save page counter after job 
     
    122127        return jobsize 
    123128         
    124     def doAccounting(self, printer, user) : 
     129    def doAccounting(self, userpquota) : 
    125130        """Does print accounting and returns if the job status is ALLOW or DENY.""" 
    126131        # Get the page counter directly from the printer itself 
    127         # Tries MAXTRIES times, sleeping two seconds each time, in case the printer is sleeping. 
    128         # This was seen with my Apple LaserWriter 16/600 PS which doesn't answer before having warmed up. 
    129         counterbeforejob = self.getPrinterInternalPageCounter() 
     132        counterbeforejob = self.getPrinterInternalPageCounter() or 0 
    130133         
    131         # get last job information for this printer 
    132         if not printer.LastJob.Exists : 
    133             # The printer hasn't been used yet, from PyKota's point of view 
    134             lastuser = user 
    135             lastpagecounter = counterbeforejob 
    136         else :     
    137             # get last values from Quota Storage 
    138             lastuser = printer.LastJob.User 
    139             lastpagecounter = printer.LastJob.PrinterPageCounter 
    140              
    141         # if printer is off then we assume the correct counter value is the last one 
    142         if counterbeforejob is None : 
    143             counterbeforejob = lastpagecounter 
    144              
    145         # if the internal lifetime page counter for this printer is 0     
    146         # then this may be a printer with a volatile counter (never 
    147         # saved to NVRAM) which has just been switched off and then on 
    148         # so we use the last page counter from the Quota Storage instead 
    149         # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html 
    150         if counterbeforejob == 0 : 
    151             counterbeforejob = lastpagecounter 
    152              
    153         # Computes the last job size as the difference between internal page 
    154         # counter in the printer and last page counter taken from the Quota 
    155         # Storage database for this particular printer 
    156         try : 
    157             jobsize = (counterbeforejob - lastpagecounter)     
    158         except TypeError :     
    159             # never used, and internal page counter not accessible 
    160             jobsize = 0 
    161              
    162         if jobsize < 0 : 
    163             # Probably an HP printer which was switched off and back on,  
    164             # its primary counter is only saved in a 10 increment, so 
    165             # it may be lower than the last page counter saved in the 
    166             # Quota Storage.  
    167             # We unconditionnally set the last job's size to  
    168             # abs(int((10 - abs(lastcounter(snmp) - lastcounter(storage)) / 2)) 
    169             # For more accurate accounting, don't switch off your HP printers ! 
    170             # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html 
    171             self.filter.logger.log_message(_("Error in page count value %i for user %s on printer %s") % (jobsize, lastuser.Name, self.filter.printername), "error") 
    172             jobsize = abs(int((10 - abs(jobsize)) / 2))     # Workaround for HP printers' feature ! 
    173              
    174         # update the quota for the previous user on this printer  
    175         lastuserquota = self.filter.storage.getUserPQuota(lastuser, printer) 
    176         if lastuserquota.Exists : 
    177             lastuserquota.increasePagesUsage(jobsize) 
    178          
    179         # update the last job size in the history 
    180         if printer.LastJob.Exists : 
    181             printer.LastJob.setSize(jobsize) 
    182          
    183         # warns the last user if he is over quota 
    184         if lastuserquota.Exists : 
    185             self.filter.warnUserPQuota(lastuserquota) 
    186              
    187134        # Is the current user allowed to print at all ? 
    188         action = self.filter.warnUserPQuota(self.filter.storage.getUserPQuota(user, printer)) 
     135        action = self.filter.warnUserPQuota(userpquota) 
    189136         
    190137        # adds the current job to history     
    191         printer.addJobToHistory(self.filter.jobid, user, counterbeforejob, action, filename=self.filter.preserveinputfile, title=self.filter.title, copies=self.filter.copies, options=self.filter.options) 
     138        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) 
    192139        return action 
    193140             
  • pykota/trunk/pykota/tool.py

    r1257 r1271  
    2222# 
    2323# $Log$ 
     24# Revision 1.71  2004/01/11 23:22:42  jalet 
     25# Major code refactoring, it's way cleaner, and now allows automated addition 
     26# of printers on first print. 
     27# 
    2428# Revision 1.70  2004/01/08 14:10:32  jalet 
    2529# Copyright year changed. 
     
    775779                    self.logger.log_message(_("User %s doesn't have quota on printer %s in the PyKota system, applying external policy (%s) for printer %s") % (self.username, self.printername, commandline, self.printername), "info") 
    776780                if os.system(commandline) : 
    777                     # if an error occured, we die without error, 
    778                     # so that the job doesn't stop the print queue. 
    779781                    self.logger.log_message(_("External policy %s for printer %s produced an error. Job rejected. Please check PyKota's configuration files.") % (commandline, self.printername), "error") 
    780782                    policy = "EXTERNALERROR" 
    781783                    break 
    782784            else :         
     785                if not printer.Exists : 
     786                    self.logger.log_message(_("Printer %s not registered in the PyKota system, applying default policy (%s)") % (self.printername, policy), "info") 
     787                if not user.Exists : 
     788                    self.logger.log_message(_("User %s not registered in the PyKota system, applying default policy (%s) for printer %s") % (self.username, policy, self.printername), "info") 
     789                if not userpquota.Exists : 
     790                    self.logger.log_message(_("User %s doesn't have quota on printer %s in the PyKota system, applying default policy (%s)") % (self.username, self.printername, policy), "info") 
    783791                break 
     792        if policy == "EXTERNAL" :     
     793            if not printer.Exists : 
     794                self.logger.log_message(_("Printer %s still not registered in the PyKota system, job will be rejected") % self.printername, "info") 
     795            if not user.Exists : 
     796                self.logger.log_message(_("User %s still not registered in the PyKota system, job will be rejected on printer %s") % (self.username, self.printername), "info") 
     797            if not userpquota.Exists : 
     798                self.logger.log_message(_("User %s still doesn't have quota on printer %s in the PyKota system, job will be rejected") % (self.username, self.printername), "info") 
    784799        return (policy, printer, user, userpquota) 
    785800         
    786     def main(self) :     
     801    def mainWork(self) :     
    787802        """Main work is done here.""" 
    788803        (policy, printer, user, userpquota) = self.getPrinterUserAndUserPQuota() 
     804        # TODO : check for last user's quota in case pykota filter is used with querying 
    789805        if policy == "EXTERNALERROR" : 
    790806            # Policy was 'EXTERNAL' and the external command returned an error code 
    791             pass # TODO : reject job 
     807            return self.removeJob() 
    792808        elif policy == "EXTERNAL" : 
    793809            # Policy was 'EXTERNAL' and the external command wasn't able 
    794810            # to add either the printer, user or user print quota 
    795             pass # TODO : reject job 
    796         elif policy == "ALLOW": 
    797             # Either printer, user or user print quota doesn't exist, 
    798             # but the job should be allowed anyway. 
    799             pass # TODO : accept job 
    800         elif policy == "OK" : 
    801             # Both printer, user and user print quota exist, job should 
    802             # be allowed if current user is allowed to print on this 
    803             # printer 
    804             pass # TODO : decide what to do based on user quota. 
    805         else : # DENY 
     811            return self.removeJob() 
     812        elif policy == "DENY" :     
    806813            # Either printer, user or user print quota doesn't exist, 
    807814            # and the job should be rejected. 
    808             pass # TODO : reject job 
     815            return self.removeJob() 
     816        else : 
     817            if policy not in ("OK", "ALLOW") : 
     818                self.logger.log_message(_("Invalid policy %s for printer %s") % (policy, self.printername)) 
     819                return self.removeJob() 
     820            else : 
     821                return self.doWork(policy, printer, user, userpquota) 
  • pykota/trunk/pykota/version.py

    r1269 r1271  
    2222# 
    2323 
    24 __version__ = "1.16alpha23_unofficial" 
     24__version__ = "1.16alpha24_unofficial" 
    2525 
    2626__doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""