Changeset 1271
- Timestamp:
- 01/12/04 00:22:42 (21 years ago)
- Location:
- pykota/trunk
- Files:
-
- 7 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/bin/cupspykota
r1257 r1271 24 24 # 25 25 # $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 # 26 30 # Revision 1.19 2004/01/08 14:10:32 jalet 27 31 # Copyright year changed. … … 108 112 from pykota.requester import PyKotaRequesterError 109 113 114 gotSigTerm = 0 115 def 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 110 122 class PyKotaPopen3(popen2.Popen3) : 111 123 """Our own class to execute real backends. … … 129 141 os._exit(1) 130 142 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 143 class 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 198 198 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.""" 221 201 # Now it becomes tricky... 202 # We must pass the unmodified job to the original backend 203 204 global gotSigTerm 222 205 223 206 # First ensure that we have a file object as input 224 207 mustclose = 0 225 if thebackend.inputfile is not None :226 if hasattr( thebackend.inputfile, "read") :227 infile = thebackend.inputfile208 if self.inputfile is not None : 209 if hasattr(self.inputfile, "read") : 210 infile = self.inputfile 228 211 else : 229 infile = open( thebackend.inputfile, "rb")212 infile = open(self.inputfile, "rb") 230 213 mustclose = 1 231 214 else : … … 233 216 234 217 # 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) 236 219 237 220 # 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:])]))) 239 222 subprocess = PyKotaPopen3([realbackend] + sys.argv[1:], capturestderr=1, bufsize=0, arg0=os.environ["DEVICE_URI"]) 240 223 … … 264 247 status = -1 265 248 inputclosed = 0 266 thebackend.logdebug("Entering streams polling loop...")249 self.logdebug("Entering streams polling loop...") 267 250 while status == -1 : 268 251 # First check if original backend is still alive … … 280 263 availablefds = pollster.poll() 281 264 for (fd, mask) in availablefds : 282 # thebackend.logdebug("file: %i mask: %04x" % (fd, mask))265 # self.logdebug("file: %i mask: %04x" % (fd, mask)) 283 266 if mask & select.POLLOUT : 284 267 # We can write … … 326 309 pollster.unregister(fromcfno) 327 310 os.close(fromcfno) 328 thebackend.logdebug("Real backend's stdout ends.")311 self.logdebug("Real backend's stdout ends.") 329 312 elif fd == cerrfno : 330 313 # Original CUPS backend has finished … … 337 320 pollster.unregister(cerrfno) 338 321 os.close(cerrfno) 339 thebackend.logdebug("Real backend's stderr ends.")322 self.logdebug("Real backend's stderr ends.") 340 323 341 324 if endinput and not inputclosed : … … 351 334 os.close(tocfno) 352 335 inputclosed = 1 353 thebackend.logdebug("Input data ends.")336 self.logdebug("Input data ends.") 354 337 355 338 # Input file was a real file, we have to close it. … … 357 340 infile.close() 358 341 359 thebackend.logdebug("Exiting streams polling loop...")342 self.logdebug("Exiting streams polling loop...") 360 343 361 344 # Check exit code of original CUPS backend. … … 363 346 retcode = os.WEXITSTATUS(status) 364 347 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") 366 349 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 390 352 if __name__ == "__main__" : 391 353 # 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 392 366 if len(sys.argv) == 1 : 393 367 # we will execute each existing backend in device enumeration mode … … 433 407 try : 434 408 # Initializes the backend 435 kotabackend = PyKota FilterOrBackend()436 retcode = main(kotabackend)409 kotabackend = PyKotaBackend() 410 retcode = kotabackend.mainWork() 437 411 except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, PyKotaRequesterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg : 438 412 sys.stderr.write("ERROR : cupspykota backend failed (%s)\n" % msg) -
pykota/trunk/bin/pykota
r1257 r1271 24 24 # 25 25 # $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 # 26 30 # Revision 1.49 2004/01/08 14:10:32 jalet 27 31 # Copyright year changed. … … 228 232 # UNKNOWN 229 233 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() 230 249 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 252 256 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) 278 298 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 300 302 if __name__ == "__main__" : 301 303 retcode = -1 … … 303 305 # Initializes the current tool 304 306 kotafilter = PyKotaFilter() 305 retcode = main(kotafilter)307 retcode = kotafilter.mainWork() 306 308 except (PyKotaToolError, PyKotaConfigError, PyKotaStorageError, PyKotaAccounterError, PyKotaRequesterError, AttributeError, KeyError, IndexError, ValueError, TypeError, IOError), msg : 307 309 sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg) -
pykota/trunk/NEWS
r1270 r1271 22 22 PyKota NEWS : 23 23 24 - 1.16alpha24 : 25 26 - Major code refactoring => It is now possible to 27 automatically add printers on first print. 28 24 29 - 1.16alpha23 : 25 30 -
pykota/trunk/pykota/accounter.py
r1257 r1271 22 22 # 23 23 # $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 # 24 28 # Revision 1.10 2004/01/08 14:10:32 jalet 25 29 # Copyright year changed. … … 69 73 self.filter = kotafilter 70 74 self.arguments = arguments 75 self.isDelayed = 0 # Accounting is immediate by default 71 76 72 77 def getLastPageCounter(self) : … … 76 81 except : 77 82 return 0 78 79 def filterInput(self, inputfile) :80 """Transparent filter."""81 mustclose = 082 if inputfile is not None :83 if hasattr(inputfile, "read") :84 infile = inputfile85 else :86 infile = open(inputfile, "rb")87 mustclose = 188 else :89 infile = sys.stdin90 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()96 83 97 def beginJob(self, printer, user) :84 def beginJob(self, userpquota) : 98 85 """Saves the computed job size.""" 99 86 # computes job's size … … 101 88 102 89 # get last job information for this printer 103 if not printer.LastJob.Exists :90 if not userpquota.Printer.LastJob.Exists : 104 91 # The printer hasn't been used yet, from PyKota's point of view 105 92 self.LastPageCounter = 0 … … 108 95 # Last lifetime page counter before actual job is 109 96 # 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) 111 98 112 def endJob(self, printer, user) :99 def endJob(self, userpquota) : 113 100 """Do nothing.""" 114 101 pass … … 121 108 return 0 122 109 123 def doAccounting(self, printer, user) :124 """Dele tgates 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. 125 112 126 113 The command must print the job size on its standard output and exit successfully. 127 114 """ 128 self.beginJob( printer, user)115 self.beginJob(userpquota) 129 116 130 117 # get the job size, which is real job size * number of copies. 118 # TODO : Double check with CUPS documentation : this is not always correct 131 119 jobsize = self.getJobSize() * self.filter.copies 132 120 133 121 # Is the current user allowed to print at all ? 134 userpquota = self.filter.storage.getUserPQuota(user, printer)135 122 action = self.filter.warnUserPQuota(userpquota) 136 123 … … 142 129 143 130 # 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) 147 134 return action 148 135 -
pykota/trunk/pykota/accounters/querying.py
r1257 r1271 22 22 # 23 23 # $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 # 24 28 # Revision 1.11 2004/01/08 14:10:32 jalet 25 29 # Copyright year changed. … … 69 73 AccounterBase.__init__(self, kotabackend, arguments) 70 74 self.requester = openRequester(kotabackend.config, kotabackend.printername) 75 self.isDelayed = 1 # With the pykota filter, accounting is delayed by one job 71 76 72 77 def getPrinterInternalPageCounter(self) : … … 89 94 return counter 90 95 91 def beginJob(self, printer, user) :96 def beginJob(self, userpquota) : 92 97 """Saves printer internal page counter at start of job.""" 93 98 # save page counter before job 94 99 self.LastPageCounter = self.counterbefore = self.getPrinterInternalPageCounter() 95 100 96 def endJob(self, printer, user) :101 def endJob(self, userpquota) : 97 102 """Saves printer internal page counter at end of job.""" 98 103 # save page counter after job … … 122 127 return jobsize 123 128 124 def doAccounting(self, printer, user) :129 def doAccounting(self, userpquota) : 125 130 """Does print accounting and returns if the job status is ALLOW or DENY.""" 126 131 # 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 130 133 131 # get last job information for this printer132 if not printer.LastJob.Exists :133 # The printer hasn't been used yet, from PyKota's point of view134 lastuser = user135 lastpagecounter = counterbeforejob136 else :137 # get last values from Quota Storage138 lastuser = printer.LastJob.User139 lastpagecounter = printer.LastJob.PrinterPageCounter140 141 # if printer is off then we assume the correct counter value is the last one142 if counterbeforejob is None :143 counterbeforejob = lastpagecounter144 145 # if the internal lifetime page counter for this printer is 0146 # then this may be a printer with a volatile counter (never147 # saved to NVRAM) which has just been switched off and then on148 # so we use the last page counter from the Quota Storage instead149 # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html150 if counterbeforejob == 0 :151 counterbeforejob = lastpagecounter152 153 # Computes the last job size as the difference between internal page154 # counter in the printer and last page counter taken from the Quota155 # Storage database for this particular printer156 try :157 jobsize = (counterbeforejob - lastpagecounter)158 except TypeError :159 # never used, and internal page counter not accessible160 jobsize = 0161 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, so165 # it may be lower than the last page counter saved in the166 # Quota Storage.167 # We unconditionnally set the last job's size to168 # 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.html171 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 printer175 lastuserquota = self.filter.storage.getUserPQuota(lastuser, printer)176 if lastuserquota.Exists :177 lastuserquota.increasePagesUsage(jobsize)178 179 # update the last job size in the history180 if printer.LastJob.Exists :181 printer.LastJob.setSize(jobsize)182 183 # warns the last user if he is over quota184 if lastuserquota.Exists :185 self.filter.warnUserPQuota(lastuserquota)186 187 134 # 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) 189 136 190 137 # 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) 192 139 return action 193 140 -
pykota/trunk/pykota/tool.py
r1257 r1271 22 22 # 23 23 # $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 # 24 28 # Revision 1.70 2004/01/08 14:10:32 jalet 25 29 # Copyright year changed. … … 775 779 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") 776 780 if os.system(commandline) : 777 # if an error occured, we die without error,778 # so that the job doesn't stop the print queue.779 781 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") 780 782 policy = "EXTERNALERROR" 781 783 break 782 784 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") 783 791 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") 784 799 return (policy, printer, user, userpquota) 785 800 786 def main (self) :801 def mainWork(self) : 787 802 """Main work is done here.""" 788 803 (policy, printer, user, userpquota) = self.getPrinterUserAndUserPQuota() 804 # TODO : check for last user's quota in case pykota filter is used with querying 789 805 if policy == "EXTERNALERROR" : 790 806 # Policy was 'EXTERNAL' and the external command returned an error code 791 pass # TODO : reject job807 return self.removeJob() 792 808 elif policy == "EXTERNAL" : 793 809 # Policy was 'EXTERNAL' and the external command wasn't able 794 810 # 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" : 806 813 # Either printer, user or user print quota doesn't exist, 807 814 # 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 22 22 # 23 23 24 __version__ = "1.16alpha2 3_unofficial"24 __version__ = "1.16alpha24_unofficial" 25 25 26 26 __doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng."""