Changeset 973
- Timestamp:
- 04/29/03 20:37:54 (22 years ago)
- Location:
- pykota/trunk
- Files:
-
- 4 added
- 11 modified
Legend:
- Unmodified
- Added
- Removed
-
pykota/trunk/bin/pykota
r966 r973 23 23 # 24 24 # $Log$ 25 # Revision 1.29 2003/04/29 18:37:54 jalet 26 # Pluggable accounting methods (actually doesn't support external scripts) 27 # 25 28 # Revision 1.28 2003/04/26 08:41:24 jalet 26 29 # Small code reorganisation (UNTESTED) to allow pluggable accounting … … 133 136 134 137 from pykota.tool import PyKotaTool, PyKotaToolError 135 from pykota.requester import openRequester, PyKotaRequesterError 136 137 MAXTRIES = 6 # maximum number of tries to get the printer's internal page counter 138 TIMETOSLEEP = 10 # number of seconds to sleep between two tries to get the printer's internal page counter 138 from pykota.storage import PyKotaStorageError 139 from pykota.accounter import openAccounter, PyKotaAccounterError 139 140 140 141 class PyKotaFilter(PyKotaTool) : … … 143 144 PyKotaTool.__init__(self) 144 145 (self.printingsystem, self.printerhostname, self.printername, self.username, self.jobid, self.inputfile) = self.extractInfoFromCupsOrLprng() 145 self.requester = openRequester(self.config, self.printername) 146 146 self.accounter = openAccounter(self) 147 148 def extractInfoFromCupsOrLprng(self) : 149 """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename) depending on the printing system in use (as seen by the print filter). 150 151 Returns (None, None, None, None, None, None) if no printing system is recognized. 152 """ 153 # Try to detect CUPS 154 if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) : 155 if len(sys.argv) == 7 : 156 inputfile = sys.argv[6] 157 else : 158 inputfile = None 159 160 device_uri = os.environ.get("DEVICE_URI", "") 161 # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp 162 try : 163 (backend, destination) = device_uri.split(":", 1) 164 except ValueError : 165 raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri 166 while destination.startswith("/") : 167 destination = destination[1:] 168 printerhostname = destination.split("/")[0].split(":")[0] 169 return ("CUPS", printerhostname, os.environ.get("PRINTER"), sys.argv[2].strip(), sys.argv[1].strip(), inputfile) 170 else : 171 # Try to detect LPRng 172 jseen = Jseen = Pseen = nseen = rseen = None 173 for arg in sys.argv : 174 if arg.startswith("-j") : 175 jseen = arg[2:].strip() 176 elif arg.startswith("-J") : 177 Jseen = arg[2:].strip() 178 if Jseen == "(STDIN)" : 179 Jseen = None 180 elif arg.startswith("-n") : 181 nseen = arg[2:].strip() 182 elif arg.startswith("-P") : 183 Pseen = arg[2:].strip() 184 elif arg.startswith("-r") : 185 rseen = arg[2:].strip() 186 if jseen and Pseen and nseen and rseen : 187 return ("LPRNG", rseen, Pseen, nseen, jseen, Jseen) 188 return (None, None, None, None, None, None) # Unknown printing system 189 147 190 def filterInput(self, inputfile) : 148 191 """Transparent filter.""" … … 182 225 def main() : 183 226 """Do it, and do it right !""" 184 global MAXTRIES, TIMETOSLEEP185 186 227 # Initializes the current tool 187 228 kotafilter = PyKotaFilter() … … 216 257 return kotafilter.removeJob() 217 258 else : 218 # Get the page counter directly from the printer itself 219 # Tries MAXTRIES times, sleeping two seconds each time, in case the printer is sleeping. 220 # This was seen with my Apple LaserWriter 16/600 PS which doesn't answer before having warmed up. 221 for i in range(MAXTRIES) : 222 try : 223 counterbeforejob = kotafilter.requester.getPrinterPageCounter(kotafilter.printerhostname) 224 except PyKotaRequesterError, msg : 225 # can't get actual page counter, assume printer is off or warming up 226 # log the message anyway. 227 kotafilter.logger.log_message("%s" % msg, "warn") 228 counterbeforejob = None 229 printerIsOff = 1 230 else : 231 # printer answered, it is on so we can exit the loop 232 printerIsOff = 0 233 break 234 time.sleep(TIMETOSLEEP) 235 236 # get last job information for this printer 237 pgc = kotafilter.storage.getPrinterPageCounter(printerid) 238 if pgc is None : 239 # The printer hasn't been used yet, from PyKota's point of view 240 lasthistoryid = None 241 lastjobid = kotafilter.jobid 242 lastuserid = userid 243 lastusername = kotafilter.username 244 lastpagecounter = counterbeforejob 245 else : 246 # get last values from Quota Storage 247 (lasthistoryid, lastjobid, lastuserid, lastusername, lastpagecounter) = (pgc["id"], pgc["jobid"], pgc["userid"], pgc["username"], pgc["pagecounter"]) 248 249 # if printer is off then we assume the correct counter value is the last one 250 if printerIsOff : 251 counterbeforejob = lastpagecounter 252 253 # if the internal lifetime page counter for this printer is 0 254 # then this may be a printer with a volatile counter (never 255 # saved to NVRAM) which has just been switched off and then on 256 # so we use the last page counter from the Quota Storage instead 257 # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html 258 if counterbeforejob == 0 : 259 counterbeforejob = lastpagecounter 260 261 # Computes the last job size as the difference between internal page 262 # counter in the printer and last page counter taken from the Quota 263 # Storage database for this particular printer 264 try : 265 jobsize = (counterbeforejob - lastpagecounter) 266 except TypeError : 267 # never used, and internal page counter not accessible 268 jobsize = 0 269 270 if jobsize < 0 : 271 # Probably an HP printer which was switched off and back on, 272 # its primary counter is only saved in a 10 increment, so 273 # it may be lower than the last page counter saved in the 274 # Quota Storage. 275 # We unconditionnally set the last job's size to 276 # abs(int((10 - abs(lastcounter(snmp) - lastcounter(storage)) / 2)) 277 # For more accurate accounting, don't switch off your HP printers ! 278 # explanation at : http://web.mit.edu/source/third/lprng/doc/LPRng-HOWTO-15.html 279 kotafilter.logger.log_message(_("Error in page count value %i for user %s on printer %s") % (jobsize, lastusername, kotafilter.printername), "error") 280 jobsize = abs(int((10 - abs(jobsize)) / 2)) # Workaround for HP printers' feature ! 281 282 # update the quota for the previous user on this printer 283 kotafilter.storage.updateUserPQuota(lastuserid, printerid, jobsize) 284 285 # update the last job size in the history 286 kotafilter.storage.updateJobSizeInHistory(lasthistoryid, jobsize) 287 288 # warns the last user if he is over quota 289 kotafilter.warnUserPQuota(lastusername, kotafilter.printername) 290 291 # Is the current user allowed to print at all ? 292 action = kotafilter.warnUserPQuota(kotafilter.username, kotafilter.printername) 293 294 # adds the current job to history 295 kotafilter.storage.addJobToHistory(kotafilter.jobid, kotafilter.storage.getUserId(kotafilter.username), printerid, counterbeforejob, action) 259 # Now does the accounting and act depending on the result 260 action = kotafilter.accounter.doAccounting(printerid, userid) 296 261 297 262 # if not allowed to print then die, else proceed. … … 306 271 307 272 if __name__ == "__main__" : 308 sys.exit(main()) 273 try : 274 retcode = main() 275 except (PyKotaToolError, PyKotaStorageError, PyKotaAccounterError), msg : 276 sys.stderr.write("ERROR : PyKota filter failed (%s)\n" % msg) 277 sys.stderr.flush() 278 retcode = -1 279 sys.exit(retcode) 280 -
pykota/trunk/conf/pykota.conf.sample
r964 r973 87 87 [lp] 88 88 89 # How to query the lp printer for its page counter 89 # What is the accounting backend to use 90 # 91 # supported values : 92 # 93 # - querying : asks the printer for its lifetime page counter 94 # - more to be added in the future 95 # 96 accounter: querying 97 98 # How to query the lp printer for its page counter, if needed 90 99 # Only snmp(community, oid) and external(command) are supported 91 100 # … … 113 122 requester: external(snmpget -c public -Ov %(printer)s 43.10.2.1.4.1.1 | cut -f 2,2 -d " ") 114 123 124 115 125 # Default policy for inexistant users (e.g. root) 116 126 # either allow or deny -
pykota/trunk/NEWS
r970 r973 22 22 PyKota NEWS : 23 23 24 - 1.05alpha2 : 25 26 - Pluggable accounting methods. 27 24 28 - 1.05alpha1 : 25 29 -
pykota/trunk/po/en/pykota.po
r952 r973 21 21 # 22 22 # $Log$ 23 # Revision 1.22 2003/04/29 18:37:54 jalet 24 # Pluggable accounting methods (actually doesn't support external scripts) 25 # 23 26 # Revision 1.21 2003/04/23 22:13:56 jalet 24 27 # Preliminary support for LPRng added BUT STILL UNTESTED. … … 325 328 msgid "Group %s not found in the PyKota Storage." 326 329 msgstr "" 330 331 msgid "Unsupported accounter backend %s" 332 msgstr "" 333 334 msgid "Option accounter in section %s only supports values in %s" 335 msgstr "" 336 337 msgid "Option requester for printer %s was not set" 338 msgstr "" -
pykota/trunk/po/fr/pykota.po
r952 r973 21 21 # 22 22 # $Log$ 23 # Revision 1.21 2003/04/29 18:37:54 jalet 24 # Pluggable accounting methods (actually doesn't support external scripts) 25 # 23 26 # Revision 1.20 2003/04/23 22:13:57 jalet 24 27 # Preliminary support for LPRng added BUT STILL UNTESTED. … … 338 341 msgid "Group %s not found in the PyKota Storage." 339 342 msgstr "Groupe %s non trouv�ans le Quota Storage." 343 344 msgid "Unsupported accounter backend %s" 345 msgstr "Backend accounter %s non support� 346 msgid "Option accounter in section %s only supports values in %s" 347 msgstr "L'option accounter pour l'imprimante %s supporte seulement les valeurs %s" 348 349 msgid "Option requester for printer %s was not set" 350 msgstr "L'option requester pour l'imprimante %s n'a pas � d�nie" -
pykota/trunk/po/pykota.pot
r952 r973 21 21 # 22 22 # $Log$ 23 # Revision 1.22 2003/04/29 18:37:54 jalet 24 # Pluggable accounting methods (actually doesn't support external scripts) 25 # 23 26 # Revision 1.21 2003/04/23 22:13:56 jalet 24 27 # Preliminary support for LPRng added BUT STILL UNTESTED. … … 325 328 msgid "Group %s not found in the PyKota Storage." 326 329 msgstr "" 330 331 msgid "Unsupported accounter backend %s" 332 msgstr "" 333 334 msgid "Option accounter in section %s only supports values in %s" 335 msgstr "" 336 337 msgid "Option requester for printer %s was not set" 338 msgstr "" -
pykota/trunk/pykota/config.py
r956 r973 21 21 # 22 22 # $Log$ 23 # Revision 1.24 2003/04/29 18:37:54 jalet 24 # Pluggable accounting methods (actually doesn't support external scripts) 25 # 23 26 # Revision 1.23 2003/04/24 11:53:48 jalet 24 27 # Default policy for unknown users/groups is to DENY printing instead … … 139 142 self.config = ConfigParser.ConfigParser() 140 143 self.config.read([self.filename]) 141 self.checkConfiguration()142 143 def checkConfiguration(self) :144 """Checks if configuration is correct.145 146 raises PyKotaConfigError in case a problem is detected147 """148 validmethods = [ "lazy" ] # TODO add more methods149 if self.config.get("global", "method", raw=1).lower() not in validmethods :150 raise PyKotaConfigError, _("Option method only supports values in %s") % str(validmethods)151 144 152 145 def getPrinterNames(self) : … … 198 191 return logger 199 192 193 def getAccounterBackend(self, printer) : 194 """Returns the accounter backend to use for a given printer. 195 196 if it is not set, it defaults to 'querying' which means ask printer 197 for its internal lifetime page counter. 198 """ 199 validaccounters = [ "querying" ] 200 try : 201 accounter = self.getPrinterOption(printer, "accounter").lower() 202 except PyKotaConfigError : 203 accounter = "querying" 204 if accounter not in validaccounters : 205 raise PyKotaConfigError, _("Option accounter in section %s only supports values in %s") % (printer, str(validaccounters)) 206 return accounter 207 200 208 def getRequesterBackend(self, printer) : 201 209 """Returns the requester backend to use for a given printer, with its arguments.""" 202 fullrequester = self.getPrinterOption(printer, "requester") 203 try : 204 (requester, args) = [x.strip() for x in fullrequester.split('(', 1)] 205 except ValueError : 206 raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer) 207 if args.endswith(')') : 208 args = args[:-1] 209 if not args : 210 raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer) 211 validrequesters = [ "snmp", "external" ] # TODO : add more requesters 212 if requester not in validrequesters : 213 raise PyKotaConfigError, _("Option requester for printer %s only supports values in %s") % (printer, str(validrequesters)) 214 return (requester, args) 210 try : 211 fullrequester = self.getPrinterOption(printer, "requester") 212 except PyKotaConfigError : 213 # No requester defined, maybe it is not needed if accounting method 214 # is not set to 'querying', but if we are called, then the accounting 215 # method really IS 'querying', and so there's a big problem. 216 raise PyKotaConfigError, _("Option requester for printer %s was not set") % printer 217 else : 218 try : 219 (requester, args) = [x.strip() for x in fullrequester.split('(', 1)] 220 except ValueError : 221 raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer) 222 if args.endswith(')') : 223 args = args[:-1] 224 if not args : 225 raise PyKotaConfigError, _("Invalid requester %s for printer %s") % (fullrequester, printer) 226 validrequesters = [ "snmp", "external" ] # TODO : add more requesters 227 if requester not in validrequesters : 228 raise PyKotaConfigError, _("Option requester for printer %s only supports values in %s") % (printer, str(validrequesters)) 229 return (requester, args) 215 230 216 231 def getPrinterPolicy(self, printer) : -
pykota/trunk/pykota/tool.py
r956 r973 21 21 # 22 22 # $Log$ 23 # Revision 1.39 2003/04/29 18:37:54 jalet 24 # Pluggable accounting methods (actually doesn't support external scripts) 25 # 23 26 # Revision 1.38 2003/04/24 11:53:48 jalet 24 27 # Default policy for unknown users/groups is to DENY printing instead … … 200 203 self.storage = storage.openConnection(self.config, asadmin=asadmin) 201 204 self.smtpserver = self.config.getSMTPServer() 202 203 def extractInfoFromCupsOrLprng(self) :204 """Returns a tuple (printingsystem, printerhostname, printername, username, jobid, filename) depending on the printing system in use (as seen by the print filter).205 206 Returns (None, None, None, None, None, None) if no printing system is recognized.207 """208 # Try to detect CUPS209 if os.environ.has_key("CUPS_SERVERROOT") and os.path.isdir(os.environ.get("CUPS_SERVERROOT", "")) :210 if len(sys.argv) == 7 :211 inputfile = sys.argv[6]212 else :213 inputfile = None214 215 device_uri = os.environ.get("DEVICE_URI", "")216 # TODO : check this for more complex urls than ipp://myprinter.dot.com:631/printers/lp217 try :218 (backend, destination) = device_uri.split(":", 1)219 except ValueError :220 raise PyKotaToolError, "Invalid DEVICE_URI : %s\n" % device_uri221 while destination.startswith("/") :222 destination = destination[1:]223 printerhostname = destination.split("/")[0].split(":")[0]224 return ("CUPS", printerhostname, os.environ.get("PRINTER"), sys.argv[2].strip(), sys.argv[1].strip(), inputfile)225 else :226 # Try to detect LPRng227 jseen = Jseen = Pseen = nseen = rseen = None228 for arg in sys.argv :229 if arg.startswith("-j") :230 jseen = arg[2:].strip()231 elif arg.startswith("-J") :232 Jseen = arg[2:].strip()233 if Jseen == "(STDIN)" :234 Jseen = None235 elif arg.startswith("-n") :236 nseen = arg[2:].strip()237 elif arg.startswith("-P") :238 Pseen = arg[2:].strip()239 elif arg.startswith("-r") :240 rseen = arg[2:].strip()241 if jseen and Pseen and nseen and rseen :242 return ("LPRNG", rseen, Pseen, nseen, jseen, Jseen)243 return (None, None, None, None, None, None) # Unknown printing system244 205 245 206 def display_version_and_quit(self) : -
pykota/trunk/pykota/version.py
r962 r973 21 21 # 22 22 23 __version__ = "1.05alpha 1-unofficial"23 __version__ = "1.05alpha2-unofficial" 24 24 25 25 __doc__ = """PyKota : a complete Printing Quota Solution for CUPS and LPRng.""" -
pykota/trunk/setup.py
r952 r973 23 23 # 24 24 # $Log$ 25 # Revision 1.13 2003/04/29 18:37:54 jalet 26 # Pluggable accounting methods (actually doesn't support external scripts) 27 # 25 28 # Revision 1.12 2003/04/23 22:13:56 jalet 26 29 # Preliminary support for LPRng added BUT STILL UNTESTED. … … 73 76 import shutil 74 77 from distutils.core import setup 78 import ConfigParser 75 79 76 80 sys.path.insert(0, "pykota") … … 155 159 else : 156 160 sys.stderr.write("WARNING : PyKota won't run without a configuration file !\n") 161 else : 162 # Configuration file already exists. Check if this is an old version or not 163 # if the 'method: lazy' line is present, then the configuration file 164 # has to be updated. 165 oldconf = ConfigParser.ConfigParser() 166 oldconf.read(["/etc/pykota.conf"]) 167 try : 168 if oldconf.get("global", "method", raw=1).lower().strip() == "lazy" : 169 sys.stdout.write("You have got an OLD PyKota configuration file !\n") 170 sys.stdout.write("The 'method' statement IS NOT SUPPORTED ANYMORE\nand was replaced with the 'accounter' statement.\n") 171 sys.stdout.write("You have to manually set an 'accounter' statement,\neither globally or for each printer.\n") 172 sys.stdout.write("Please read the sample configuration file conf/pykota.conf.sample\n") 173 sys.stdout.write("to learn how to MANUALLY apply the modifications needed,\nafter the installation is done.\n") 174 sys.stdout.write("If you don't do this, then PyKota will stop working !\n") 175 answer = raw_input("Please, press ENTER when you'll have read the above paragraph.") 176 except ConfigParser.NoOptionError : 177 # New configuration file, OK 178 pass 157 179 158 180 # checks if some needed Python modules are there or not. … … 189 211 author_email = "alet@librelogiciel.com", 190 212 url = "http://www.librelogiciel.com/software/", 191 packages = [ "pykota", "pykota.storages", "pykota.requesters", "pykota.loggers" ],213 packages = [ "pykota", "pykota.storages", "pykota.requesters", "pykota.loggers", "pykota.accounters" ], 192 214 scripts = [ "bin/pykota", "bin/edpykota", "bin/repykota", "bin/warnpykota" ], 193 215 data_files = data_files)