root / pykota / trunk / pykota / storages / ldapstorage.py @ 1032

Revision 1032, 30.6 kB (checked in by jalet, 21 years ago)

More work on LDAP, again. Problem detected.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1# PyKota
2#
3# PyKota : Print Quotas for CUPS and LPRng
4#
5# (c) 2003 Jerome Alet <alet@librelogiciel.com>
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19#
20# $Id$
21#
22# $Log$
23# Revision 1.10  2003/06/16 21:55:15  jalet
24# More work on LDAP, again. Problem detected.
25#
26# Revision 1.9  2003/06/16 11:59:09  jalet
27# More work on LDAP
28#
29# Revision 1.8  2003/06/15 22:26:52  jalet
30# More work on LDAP
31#
32# Revision 1.7  2003/06/14 22:44:21  jalet
33# More work on LDAP storage backend.
34#
35# Revision 1.6  2003/06/13 19:07:57  jalet
36# Two big bugs fixed, time to release something ;-)
37#
38# Revision 1.5  2003/06/10 16:37:54  jalet
39# Deletion of the second user which is not needed anymore.
40# Added a debug configuration field in /etc/pykota.conf
41# All queries can now be sent to the logger in debug mode, this will
42# greatly help improve performance when time for this will come.
43#
44# Revision 1.4  2003/06/10 10:45:32  jalet
45# Not implemented methods now raise an exception when called.
46#
47# Revision 1.3  2003/06/06 20:49:15  jalet
48# Very latest schema. UNTESTED.
49#
50# Revision 1.2  2003/06/06 14:21:08  jalet
51# New LDAP schema.
52# Small bug fixes.
53#
54# Revision 1.1  2003/06/05 11:19:13  jalet
55# More good work on LDAP storage.
56#
57#
58#
59
60#
61# My IANA assigned number, for
62# "Conseil Internet & Logiciels Libres, J�me Alet"
63# is 16868. Use this as a base to create the LDAP schema.
64#
65
66import time
67import md5
68import fnmatch
69
70from pykota.storage import PyKotaStorageError
71
72try :
73    import ldap
74    from ldap import modlist
75except ImportError :   
76    import sys
77    # TODO : to translate or not to translate ?
78    raise PyKotaStorageError, "This python version (%s) doesn't seem to have the python-ldap module installed correctly." % sys.version.split()[0]
79   
80class Storage :
81    def __init__(self, pykotatool, host, dbname, user, passwd) :
82        """Opens the LDAP connection."""
83        # raise PyKotaStorageError, "Sorry, the LDAP backend for PyKota is not yet implemented !"
84        self.closed = 1
85        self.tool = pykotatool
86        self.debug = pykotatool.config.getDebug()
87        self.info = pykotatool.config.getLDAPInfo()
88        try :
89            self.database = ldap.initialize(host) 
90            self.database.simple_bind_s(user, passwd)
91            self.basedn = dbname
92        except ldap.SERVER_DOWN :   
93            raise PyKotaStorageError, "LDAP backend for PyKota seems to be down !" # TODO : translate
94        except ldap.LDAPError :   
95            raise PyKotaStorageError, "Unable to connect to LDAP server %s as %s." % (host, user) # TODO : translate
96        else :   
97            self.closed = 0
98            if self.debug :
99                self.tool.logger.log_message("Database opened (host=%s, dbname=%s, user=%s)" % (host, dbname, user), "debug")
100           
101    def __del__(self) :       
102        """Closes the database connection."""
103        if not self.closed :
104            del self.database
105            self.closed = 1
106            if self.debug :
107                self.tool.logger.log_message("Database closed.", "debug")
108       
109    def genUUID(self) :   
110        """Generates an unique identifier.
111       
112           TODO : this one is not unique accross several print servers, but should be sufficient for testing.
113        """
114        return md5.md5("%s" % time.time()).hexdigest()
115       
116    def doSearch(self, key, fields=None, base="", scope=ldap.SCOPE_SUBTREE) :
117        """Does an LDAP search query."""
118        try :
119            base = base or self.basedn
120            if self.debug :
121                self.tool.logger.log_message("QUERY : BaseDN : %s, Scope : %s, Filter : %s, Attributes : %s" % (base, scope, key, fields), "debug")
122            result = self.database.search_s(base or self.basedn, scope, key, fields)
123        except ldap.LDAPError :   
124            raise PyKotaStorageError, _("Search for %s(%s) from %s(scope=%s) returned no answer.") % (key, fields, base, scope)
125        else :     
126            if self.debug :
127                self.tool.logger.log_message("QUERY : Result : %s" % result, "debug")
128            return result
129           
130    def doAdd(self, dn, fields) :
131        """Adds an entry in the LDAP directory."""
132        try :
133            if self.debug :
134                self.tool.logger.log_message("QUERY : ADD(%s, %s)" % (dn, str(fields)), "debug")
135            self.database.add_s(dn, modlist.addModlist(fields))
136        except ldap.LDAPError :
137            raise PyKotaStorageError, _("Problem adding LDAP entry (%s, %s)") % (dn, str(fields))
138        else :
139            return dn
140           
141    def doModify(self, dn, fields) :
142        """Modifies an entry in the LDAP directory."""
143        try :
144            oldentry = self.doSearch("objectClass=*", base=dn, scope=ldap.SCOPE_BASE)
145            if self.debug :
146                self.tool.logger.log_message("QUERY : Modify(%s, %s ==> %s)" % (dn, oldentry[0][1], fields), "debug")
147            self.database.modify_s(dn, modlist.modifyModlist(oldentry[0][1], fields, ignore_oldexistent=1))
148        except ldap.LDAPError :
149            raise PyKotaStorageError, _("Problem modifying LDAP entry (%s, %s)") % (dn, fields)
150        else :
151            return dn
152       
153    def getMatchingPrinters(self, printerpattern) :
154        """Returns the list of all printers as tuples (id, name) for printer names which match a certain pattern."""
155        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName"], base=self.info["printerbase"])
156        if result :
157            return [(printerid, printer["pykotaPrinterName"][0]) for (printerid, printer) in result if fnmatch.fnmatchcase(printer["pykotaPrinterName"][0], printerpattern)]
158           
159    def getPrinterId(self, printername) :       
160        """Returns a printerid given a printername."""
161        result = self.doSearch("(&(objectClass=pykotaPrinter)(|(pykotaPrinterName=%s)(%s=%s)))" % (printername, self.info["printerrdn"], printername), ["pykotaPrinterName"], base=self.info["printerbase"])
162        if result :
163            return result[0][0]
164           
165    def getPrinterName(self, printerid) :       
166        """Returns a printerid given a printer id."""
167        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName"], base=printerid, scope=ldap.SCOPE_BASE)
168        if result :
169            return result[0][1]["pykotaPrinterName"][0]
170           
171    def getPrinterPrices(self, printerid) :       
172        """Returns a printer prices per page and per job given a printerid."""
173        result = self.doSearch("(|(pykotaPrinterName=*)(%s=*))" % self.info["printerrdn"], ["pykotaPricePerPage", "pykotaPricePerJob"], base=printerid, scope=ldap.SCOPE_BASE)
174        if result :
175            return (float(result[0][1]["pykotaPricePerPage"][0]), float(result[0][1]["pykotaPricePerJob"][0]))
176           
177    def setPrinterPrices(self, printerid, perpage, perjob) :
178        """Sets prices per job and per page for a given printer."""
179        fields = { 
180                   "pykotaPricePerPage" : str(perpage),
181                   "pykotaPricePerJob" : str(perjob),
182                 } 
183        return self.doModify(printerid, fields)
184   
185    def getUserId(self, username) :
186        """Returns a userid given a username."""
187        result = self.doSearch("(&(objectClass=pykotaAccount)(|(pykotaUserName=%s)(%s=%s)))" % (username, self.info["userrdn"], username), [self.info["userrdn"]], base=self.info["userbase"])
188        if result :
189            return result[0][0]
190           
191    def getUserName(self, userid) :
192        """Returns a username given a userid."""
193        result = self.doSearch("objectClass=pykotaAccount", ["pykotaUserName"], base=userid, scope=ldap.SCOPE_BASE)
194        if result :
195            return result[0][1]["pykotaUserName"][0]
196           
197    def getGroupId(self, groupname) :
198        """Returns a groupid given a grupname."""
199        result = self.doSearch("(&(objectClass=pykotaGroup)(|(pykotaGroupName=%s)(%s=%s)))" % (groupname, self.info["grouprdn"], groupname), [self.info["grouprdn"]], base=self.info["groupbase"])
200        if result is not None :
201            (groupid, dummy) = result[0]
202            return groupid
203           
204    def getJobHistoryId(self, jobid, userid, printerid) :       
205        """Returns the history line's id given a (jobid, userid, printerid).
206       
207           TODO : delete because shouldn't be needed by the LDAP backend
208        """
209        raise PyKotaStorageError, "Not implemented !"
210           
211    def getPrinterUsers(self, printerid) :       
212        """Returns the list of userids and usernames which uses a given printer."""
213        # first get the printer's name from the id
214        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName", self.info["printerrdn"]], base=printerid, scope=ldap.SCOPE_BASE)
215        if result :
216            fields = result[0][1]
217            printername = (fields.get("pykotaPrinterName") or fields.get(self.info["printerrdn"]))[0]
218            result = self.doSearch("(&(objectClass=pykotaUserPQuota)(pykotaPrinterName=%s))" % printername, ["pykotaUserName"], base=self.info["userquotabase"]) 
219            if result :
220                return [(pquotauserid, fields["pykotaUserName"][0]) for (pquotauserid, fields) in result]
221       
222    def getPrinterGroups(self, printerid) :       
223        """Returns the list of groups which uses a given printer."""
224        # first get the printer's name from the id
225        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName", self.info["printerrdn"]], base=printerid, scope=ldap.SCOPE_BASE)
226        if result :
227            fields = result[0][1]
228            printername = (fields.get("pykotaPrinterName") or fields.get(self.info["printerrdn"]))[0]
229            result = self.doSearch("(&(objectClass=pykotaGroupPQuota)(pykotaPrinterName=%s))" % printername, ["pykotaGroupName"], base=self.info["groupquotabase"]) 
230            if result :
231                return [(pquotagroupid, fields["pykotaGroupName"][0]) for (pquotagroupid, fields) in result]
232       
233    def getGroupMembersNames(self, groupname) :       
234        """Returns the list of user's names which are member of this group."""
235        result = self.doSearch("(&(objectClass=pykotaGroup)(|(pykotaGroupName=%s)(%s=%s)))" % (groupname, self.info["grouprdn"], groupname), [self.info["groupmembers"]])
236        if result :
237            fields = result[0][1]
238            return fields.get(self.info["groupmembers"])
239       
240    def getUserGroupsNames(self, userid) :       
241        """Returns the list of groups' names the user is a member of."""
242        username = self.getUserName(userid)
243        if username :
244            result = self.doSearch("(&(objectClass=pykotaGroup)(%s=%s))" % (self.info["groupmembers"], username), [self.info["grouprdn"]], base=self.info["groupbase"])
245            if result :
246                return [v[self.info["grouprdn"]][0] for (k, v) in result]
247        return []       
248       
249    def addPrinter(self, printername) :       
250        """Adds a printer to the quota storage, returns its id."""
251        fields = { self.info["printerrdn"] : printername,
252                   "objectClass" : ["pykotaObject", "pykotaPrinter"],
253                   "pykotaPrinterName" : printername,
254                   "pykotaPricePerPage" : "0.0",
255                   "pykotaPricePerJob" : "0.0",
256                 } 
257        dn = "%s=%s,%s" % (self.info["printerrdn"], printername, self.info["printerbase"])
258        return self.doAdd(dn, fields)
259       
260    def addUser(self, username) :       
261        """Adds a user to the quota storage, returns its id."""
262        fields = { self.info["userrdn"] : username,
263                   "objectClass" : ["pykotaObject", "pykotaAccount", "pykotaAccountBalance"],
264                   "pykotaUserName" : username,
265                   "pykotaLimitBy" : "quota",
266                   "pykotaBalance" : "0.0",
267                   "pykotaLifeTimePaid" : "0.0",
268                 } 
269        dn = "%s=%s,%s" % (self.info["userrdn"], username, self.info["userbase"])
270        return self.doAdd(dn, fields)
271       
272    def addGroup(self, groupname) :       
273        """Adds a group to the quota storage, returns its id."""
274        fields = { self.info["grouprdn"] : groupname,
275                   "objectClass" : ["pykotaObject", "pykotaGroup"],
276                   "pykotaGroupName" : groupname,
277                   "pykotaLimitBy" : "quota",
278                 } 
279        dn = "%s=%s,%s" % (self.info["grouprdn"], groupname, self.info["groupbase"])
280        return self.doAdd(dn, fields)
281       
282    def addUserPQuota(self, username, printerid) :
283        """Initializes a user print quota on a printer, adds the user to the quota storage if needed."""
284        uuid = self.genUUID()
285        fields = { "objectClass" : ["pykotaObject", "pykotaUserPQuota"],
286                   "cn" : uuid,
287                   "pykotaUserName" : username,
288                   "pykotaPrinterName" : self.getPrinterName(printerid), 
289                   "pykotaPageCounter" : "0",
290                   "pykotaLifePageCounter" : "0",
291                   "pykotaSoftLimit" : "0",
292                   "pykotaHardLimit" : "0",
293                   "pykotaDateLimit" : "None",
294                 } 
295        dn = "cn=%s,%s" % (uuid, self.info["userquotabase"])
296        self.doAdd(dn, fields)
297        return (dn, printerid)
298       
299    def addGroupPQuota(self, groupname, printerid) :
300        """Initializes a group print quota on a printer, adds the group to the quota storage if needed."""
301        uuid = self.genUUID()
302        fields = { "objectClass" : ["pykotaObject", "pykotaGroupPQuota"],
303                   "cn" : uuid,
304                   "pykotaGroupName" : groupname,
305                   "pykotaPrinterName" : self.getPrinterName(printerid), 
306                   "pykotaSoftLimit" : "0",
307                   "pykotaHardLimit" : "0",
308                   "pykotaDateLimit" : "None",
309                 } 
310        dn = "cn=%s,%s" % (uuid, self.info["groupquotabase"])
311        self.doAdd(dn, fields)
312        return (dn, printerid)
313       
314    def increaseUserBalance(self, userquotaid, amount) :   
315        """Increases (or decreases) an user's account balance by a given amount."""
316        balance = self.getUserBalance(userquotaid)
317        if balance :
318            (newbal, newpaid) = [(float(v) + float(amount)) for v in balance]
319            result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaUserName"], base=userquotaid, scope=ldap.SCOPE_BASE)
320            if result :
321                username = result[0][1]["pykotaUserName"][0]
322                if username :
323                    result = self.doSearch("(&(objectClass=pykotaAccountBalance)(pykotaUserName=%s))" % username , [ "pykotaBalance", "pykotaLifeTimePaid"])
324                    fields = {
325                               "pykotaBalance" : str(newbal),
326                               "pykotaLifeTimePaid" : str(newpaid),
327                             }
328                    return self.doModify(result[0][0], fields)         
329       
330    def getUserBalance(self, userquotaid) :   
331        """Returns the current account balance for a given user quota identifier."""
332        # first get the user's name from the user quota id
333        result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaUserName"], base=userquotaid, scope=ldap.SCOPE_BASE)
334        if result :
335            username = result[0][1]["pykotaUserName"][0]
336            result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(%s=%s)))" % (username, self.info["userrdn"], username), ["pykotaBalance", "pykotaLifeTimePaid"])
337            if result :
338                fields = result[0][1]
339                return (float(fields["pykotaBalance"][0]), float(fields["pykotaLifeTimePaid"][0]))
340       
341    def getUserBalanceFromUserId(self, userid) :   
342        """Returns the current account balance for a given user id."""
343        # first get the user's name from the user id
344        result = self.doSearch("objectClass=pykotaAccount", ["pykotaUserName", self.info["userrdn"]], base=userid, scope=ldap.SCOPE_BASE)
345        if result :
346            fields = result[0][1]
347            username = (fields.get("pykotaUserName") or fields.get(self.info["userrdn"]))[0]
348            result = self.doSearch("(&(objectClass=pykotaAccountBalance)(|(pykotaUserName=%s)(%s=%s)))" % (username, self.info["userrdn"], username), ["pykotaBalance", "pykotaLifeTimePaid"])
349            if result :
350                fields = result[0][1]
351                return (float(fields["pykotaBalance"][0]), float(fields["pykotaLifeTimePaid"][0]))
352       
353    def getGroupBalance(self, groupquotaid) :   
354        """Returns the current account balance for a given group, as the sum of each of its users' account balance."""
355        # first get the group's name from the group quota id
356        result = self.doSearch("objectClass=pykotaGroupPQuota", ["pykotaGroupName"], base=groupquotaid, scope=ldap.SCOPE_BASE)
357        if result :
358            groupname = result[0][1]["pykotaGroupName"][0]
359            members = self.getGroupMembersNames(groupname) or []
360            balance = lifetimepaid = 0.0
361            for member in members :
362                userid = self.getUserId(member)
363                if userid :
364                    userbal = self.getUserBalanceFromUserId(userid)
365                    if userbal :
366                        (bal, paid) = userbal
367                        balance += bal
368                        lifetimepaid += paid
369            return (balance, lifetimepaid)           
370       
371    def getUserLimitBy(self, userquotaid) :   
372        """Returns the way in which user printing is limited."""
373        result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaUserName"], base=userquotaid, scope=ldap.SCOPE_BASE)
374        if result :
375            username = result[0][1]["pykotaUserName"][0]
376            result = self.doSearch("(&(objectClass=pykotaAccount)(|(pykotaUserName=%s)(%s=%s)))" % (username, self.info["userrdn"], username), ["pykotaLimitBy"])
377            if result :
378                return result[0][1]["pykotaLimitBy"][0]
379       
380    def getGroupLimitBy(self, groupquotaid) :   
381        """Returns the way in which group printing is limited."""
382        # first get the group's name from the group quota id
383        result = self.doSearch("objectClass=pykotaGroupPQuota", ["pykotaGroupName"], base=groupquotaid, scope=ldap.SCOPE_BASE)
384        if result :
385            groupname = result[0][1]["pykotaGroupName"][0]
386            result = self.doSearch("(&(objectClass=pykotaGroup)(|(pykotaGroupName=%s)(%s=%s)))" % (groupname, self.info["grouprdn"], groupname), ["pykotaLimitBy"])
387            if result :
388                return result[0][1]["pykotaLimitBy"][0]
389       
390    def setUserBalance(self, userquotaid, balance) :   
391        """Sets the account balance for a given user to a fixed value."""
392        oldbalance = self.getUserBalance(userquotaid)
393        if oldbalance :
394            (oldbal, oldpaid) = oldbalance
395            difference = balance - oldbal
396            return self.increaseUserBalance(userquotaid, difference)
397       
398    def limitUserBy(self, userquotaid, limitby) :   
399        """Limits a given user based either on print quota or on account balance."""
400        result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaUserName"], base=userquotaid, scope=ldap.SCOPE_BASE)
401        if result :
402            username = result[0][1]["pykotaUserName"][0]
403            fields = {
404                       "pykotaLimitBy" : limitby,
405                     }
406            self.doModify(self.getUserId(username), fields)
407       
408    def limitGroupBy(self, groupquotaid, limitby) :   
409        """Limits a given group based either on print quota or on sum of its users' account balances."""
410        result = self.doSearch("objectClass=pykotaGroupPQuota", ["pykotaGroupName"], base=groupquotaid, scope=ldap.SCOPE_BASE)
411        if result :
412            groupname = result[0][1]["pykotaGroupName"][0]
413            fields = {
414                       "pykotaLimitBy" : limitby,
415                     }
416            self.doModify(self.getGroupId(groupname), fields)
417       
418    def setUserPQuota(self, userquotaid, printerid, softlimit, hardlimit) :
419        """Sets soft and hard limits for a user quota on a specific printer given (userid, printerid)."""
420        fields = { 
421                   "pykotaSoftLimit" : str(softlimit),
422                   "pykotaHardLimit" : str(hardlimit),
423                   "pykotaDateLimit" : "None",
424                 } 
425        return self.doModify(userquotaid, fields)
426       
427    def setGroupPQuota(self, groupquotaid, printerid, softlimit, hardlimit) :
428        """Sets soft and hard limits for a group quota on a specific printer given (groupid, printerid)."""
429        fields = { 
430                   "pykotaSoftLimit" : str(softlimit),
431                   "pykotaHardLimit" : str(hardlimit),
432                   "pykotaDateLimit" : "None",
433                 } 
434        return self.doModify(groupquotaid, fields)
435       
436    def resetUserPQuota(self, userquotaid, printerid) :   
437        """Resets the page counter to zero for a user on a printer. Life time page counter is kept unchanged."""
438        fields = {
439                   "pykotaPageCounter" : "0",
440                   "pykotaDateLimit" : "None",
441                 }
442        return self.doModify(userquotaid, fields)     
443       
444    def resetGroupPQuota(self, groupquotaid, printerid) :   
445        """Resets the page counter to zero for a group on a printer. Life time page counter is kept unchanged."""
446        fields = {
447                   "pykotaPageCounter" : "0",
448                   "pykotaDateLimit" : "None",
449                 }
450        return self.doModify(groupquotaid, fields)     
451       
452    def updateUserPQuota(self, userquotaid, printerid, pagecount) :
453        """Updates the used user Quota information given (userid, printerid) and a job size in pages."""
454        jobprice = self.computePrinterJobPrice(printerid, pagecount)
455        result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaPageCounter", "pykotaLifePageCounter"], base=userquotaid, scope=ldap.SCOPE_BASE)
456        if result :
457            oldfields = result[0][1]
458            fields = {
459                       "pykotaPageCounter" : str(pagecount + int(oldfields["pykotaPageCounter"][0])),
460                       "pykotaLifePageCounter" : str(pagecount + int(oldfields["pykotaLifePageCounter"][0])),
461                     } 
462            return self.doModify(userquotaid, fields)         
463       
464    def getUserPQuota(self, userquotaid, printerid) :
465        """Returns the Print Quota information for a given (userquotaid, printerid)."""
466        # first get the user's name from the id
467        result = self.doSearch("objectClass=pykotaUserPQuota", ["pykotaUserName", "pykotaPageCounter", "pykotaLifePageCounter", "pykotaSoftLimit", "pykotaHardLimit", "pykotaDateLimit"], base=userquotaid, scope=ldap.SCOPE_BASE)
468        if result :
469            fields = result[0][1]
470            datelimit = fields["pykotaDateLimit"][0].strip()
471            if (not datelimit) or (datelimit.upper() == "NONE") : 
472                datelimit = None
473            return { "lifepagecounter" : int(fields["pykotaLifePageCounter"][0]), 
474                     "pagecounter" : int(fields["pykotaPageCounter"][0]),
475                     "softlimit" : int(fields["pykotaSoftLimit"][0]),
476                     "hardlimit" : int(fields["pykotaHardLimit"][0]),
477                     "datelimit" : datelimit
478                   }
479       
480    def getGroupPQuota(self, grouppquotaid, printerid) :
481        """Returns the Print Quota information for a given (grouppquotaid, printerid)."""
482        result = self.doSearch("objectClass=pykotaGroupPQuota", ["pykotaGroupName", "pykotaSoftLimit", "pykotaHardLimit", "pykotaDateLimit"], base=grouppquotaid, scope=ldap.SCOPE_BASE)
483        if result :
484            fields = result[0][1]
485            groupname = fields["pykotaGroupName"][0]
486            datelimit = fields["pykotaDateLimit"][0].strip()
487            if (not datelimit) or (datelimit.upper() == "NONE") : 
488                datelimit = None
489            quota = {
490                      "softlimit" : int(fields["pykotaSoftLimit"][0]),
491                      "hardlimit" : int(fields["pykotaHardLimit"][0]),
492                      "datelimit" : datelimit
493                    }
494            members = self.getGroupMembersNames(groupname) or []
495            pagecounter = lifepagecounter = 0
496            printerusers = self.getPrinterUsers(printerid)
497            if printerusers :
498                for (userid, username) in printerusers :
499                    if username in members :
500                        userpquota = self.getUserPQuota(userid, printerid)
501                        if userpquota :
502                            pagecounter += userpquota["pagecounter"]
503                            lifepagecounter += userpquota["lifepagecounter"]
504            quota.update({"pagecounter": pagecounter, "lifepagecounter": lifepagecounter})               
505            return quota
506       
507    def setUserDateLimit(self, userquotaid, printerid, datelimit) :
508        """Sets the limit date for a soft limit to become an hard one given (userid, printerid)."""
509        fields = {
510                   "pykotaDateLimit" : "%04i-%02i-%02i %02i:%02i:%02i" % (datelimit.year, datelimit.month, datelimit.day, datelimit.hour, datelimit.minute, datelimit.second),
511                 }
512        return self.doModify(userquotaid, fields)
513       
514    def setGroupDateLimit(self, groupquotaid, printerid, datelimit) :
515        """Sets the limit date for a soft limit to become an hard one given (groupid, printerid)."""
516        fields = {
517                   "pykotaDateLimit" : "%04i-%02i-%02i %02i:%02i:%02i" % (datelimit.year, datelimit.month, datelimit.day, datelimit.hour, datelimit.minute, datelimit.second),
518                 }
519        return self.doModify(groupquotaid, fields)
520       
521    def addJobToHistory(self, jobid, userid, printerid, pagecounter, action, jobsize=None) :
522        """Adds a job to the history: (jobid, userid, printerid, last page counter taken from requester)."""
523        uuid = self.genUUID()
524        printername = self.getPrinterName(printerid)
525        fields = {
526                   "objectClass" : ["pykotaObject", "pykotaJob"],
527                   "cn" : uuid,
528                   "pykotaUserName" : self.getUserName(userid),
529                   "pykotaPrinterName" : printername,
530                   "pykotaJobId" : jobid,
531                   "pykotaPrinterPageCounter" : str(pagecounter),
532                   "pykotaAction" : action,
533                 }
534        if jobsize is not None :         
535            fields.update({ "pykotaJobSize" : str(jobsize) })
536        dn = "cn=%s,%s" % (uuid, self.info["jobbase"])
537        self.doAdd(dn, fields)
538        result = self.doSearch("(&(objectClass=pykotaLastJob)(pykotaPrinterName=%s))" % printername, None, base=self.info["lastjobbase"])
539        if result :
540            lastjdn = result[0][0] 
541            fields = {
542                       "pykotaLastJobIdent" : uuid,
543                     }
544            self.doModify(lastjdn, fields)         
545        else :   
546            lastjuuid = self.genUUID()
547            lastjdn = "cn=%s,%s" % (lasjuuid, self.info["lastjobbase"])
548            fields = {
549                       "objectClass" : ["pykotaObject", "pykotaLastJob"],
550                       "cn" : lastjuuid,
551                       "pykotaPrinterName" : printername,
552                       "pykotaLastJobIdent" : uuid,
553                     } 
554            self.doAdd(lastjdn, fields)         
555   
556    def updateJobSizeInHistory(self, historyid, jobsize) :
557        """Updates a job size in the history given the history line's id."""
558        result = self.doSearch("(&(objectClass=pykotaJob)(cn=%s))" % historyid, ["cn"], base=self.info["jobbase"])
559        if result :
560            fields = {
561                       "pykotaJobSize" : str(jobsize),
562                     }
563            self.doModify(result[0][0], fields)         
564   
565    def getPrinterPageCounter(self, printerid) :
566        """Returns the last page counter value for a printer given its id, also returns last username, last jobid and history line id."""
567        result = self.doSearch("objectClass=pykotaPrinter", ["pykotaPrinterName", self.info["printerrdn"]], base=printerid, scope=ldap.SCOPE_BASE)
568        if result :
569            fields = result[0][1]
570            printername = (fields.get("pykotaPrinterName") or fields.get(self.info["printerrdn"]))[0]
571            result = self.doSearch("(&(objectClass=pykotaLastjob)(|(pykotaPrinterName=%s)(%s=%s)))" % (printername, self.info["printerrdn"], printername), ["pykotaLastJobIdent"], base=self.info["lastjobbase"])
572            if result :
573                lastjobident = result[0][1]["pykotaLastJobIdent"][0]
574                result = self.doSearch("(&(objectClass=pykotaJob)(cn=%s))" % lastjobident, ["pykotaUserName", "pykotaPrinterName", "pykotaJobId", "pykotaPrinterPageCounter", "pykotaJobSize", "pykotaAction", "createTimestamp"], base=self.info["jobbase"])
575                if result :
576                    fields = result[0][1]
577                    return { "id": lastjobident, 
578                             "jobid" : fields.get("pykotaJobId")[0],
579                             "userid" : self.getUserId(fields.get("pykotaUserName")[0]),
580                             "username" : fields.get("pykotaUserName")[0], 
581                             "pagecounter" : int(fields.get("pykotaPrinterPageCounter")[0]),
582                             "jobsize" : int(fields.get("pykotaJobSize")[0]),
583                           }
584       
585    def addUserToGroup(self, userid, groupid) :   
586        """Adds an user to a group."""
587        raise PyKotaStorageError, "Not implemented !"
588       
589    def deleteUser(self, userid) :   
590        """Completely deletes an user from the Quota Storage."""
591        raise PyKotaStorageError, "Not implemented !"
592       
593    def deleteGroup(self, groupid) :   
594        """Completely deletes an user from the Quota Storage."""
595        raise PyKotaStorageError, "Not implemented !"
596       
597    def computePrinterJobPrice(self, printerid, jobsize) :   
598        """Returns the price for a job on a given printer."""
599        # TODO : create a base class with things like this
600        prices = self.getPrinterPrices(printerid)
601        if prices is None :
602            perpage = perjob = 0.0
603        else :   
604            (perpage, perjob) = prices
605        return perjob + (perpage * jobsize)
Note: See TracBrowser for help on using the browser.