[CalendarServer-changes] [143]
CalendarServer/branches/users/cdaboo/quota
source_changes at macosforge.org
source_changes at macosforge.org
Mon Sep 18 08:32:01 PDT 2006
Revision: 143
Author: cdaboo at apple.com
Date: 2006-09-18 08:31:58 -0700 (Mon, 18 Sep 2006)
Log Message:
-----------
merge -r136:HEAD https://svn.opensource.apple.com/repository/calendarserver/CalendarServer/trunk .
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/quota/run
Removed Paths:
-------------
CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.basic.patch
CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.digest.patch
CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.interfaces.patch
CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.wrapper.patch
CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.resource.patch
CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.static.patch
CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch
Deleted: CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.basic.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.basic.patch 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.basic.patch 2006-09-18 15:31:58 UTC (rev 143)
@@ -1,13 +0,0 @@
-Index: twisted/web2/auth/basic.py
-===================================================================
---- twisted/web2/auth/basic.py (revision 18157)
-+++ twisted/web2/auth/basic.py (working copy)
-@@ -20,7 +20,7 @@
- def getChallenge(self, peer):
- return {'realm': self.realm}
-
-- def decode(self, response, method=None):
-+ def decode(self, response, request):
- try:
- creds = (response + '===').decode('base64')
- except:
Deleted: CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.digest.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.digest.patch 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.digest.patch 2006-09-18 15:31:58 UTC (rev 143)
@@ -1,19 +0,0 @@
-Index: twisted/web2/auth/digest.py
-===================================================================
---- twisted/web2/auth/digest.py (revision 18157)
-+++ twisted/web2/auth/digest.py (working copy)
-@@ -154,7 +154,7 @@
- 'algorithm': self.algorithm,
- 'realm': self.realm}
-
-- def decode(self, response, method='GET'):
-+ def decode(self, response, request):
- def unq(s):
- if s[0] == s[-1] == '"':
- return s[1:-1]
-@@ -172,4 +172,4 @@
-
- del self.outstanding[auth['opaque']]
-
-- return DigestedCredentials(username, method, self.realm, auth)
-+ return DigestedCredentials(username, request.method, self.realm, auth)
Deleted: CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.interfaces.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.interfaces.patch 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.interfaces.patch 2006-09-18 15:31:58 UTC (rev 143)
@@ -1,24 +0,0 @@
-Index: twisted/web2/auth/interfaces.py
-===================================================================
---- twisted/web2/auth/interfaces.py (revision 18157)
-+++ twisted/web2/auth/interfaces.py (working copy)
-@@ -13,7 +13,7 @@
- @return: dictionary of challenge arguments
- """
-
-- def decode(response, method=None):
-+ def decode(response, request):
- """Create a credentials object from the given response.
- May raise twisted.cred.error.LoginFailed if the response is invalid.
-
-@@ -20,8 +20,8 @@
- @type response: C{str}
- @param response: scheme specific response string
-
-- @type method: C{str}
-- @param method: the method by which this response was sent
-+ @type request: L{twisted.web2.server.Request}
-+ @param request: the request being processed
-
- @return: ICredentials
- """
Deleted: CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.wrapper.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.wrapper.patch 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.auth.wrapper.patch 2006-09-18 15:31:58 UTC (rev 143)
@@ -1,13 +0,0 @@
-Index: twisted/web2/auth/wrapper.py
-===================================================================
---- twisted/web2/auth/wrapper.py (revision 18157)
-+++ twisted/web2/auth/wrapper.py (working copy)
-@@ -87,7 +87,7 @@
- return UnauthorizedResource(self.credentialFactories)
-
- try:
-- creds = factory.decode(response, req.method)
-+ creds = factory.decode(response, req)
- except error.LoginFailed:
- return UnauthorizedResource(self.credentialFactories)
-
Deleted: CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.resource.patch 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.resource.patch 2006-09-18 15:31:58 UTC (rev 143)
@@ -1,304 +0,0 @@
-Index: twisted/web2/dav/resource.py
-===================================================================
---- twisted/web2/dav/resource.py (revision 18157)
-+++ twisted/web2/dav/resource.py (working copy)
-@@ -126,6 +126,8 @@
- (dav_namespace, "acl-restrictions" ), # RFC 3744, section 5.6
- (dav_namespace, "inherited-acl-set" ), # RFC 3744, section 5.7
- (dav_namespace, "principal-collection-set" ), # RFC 3744, section 5.8
-+ (dav_namespace, "quota-available-bytes" ), # RFC 4331, section 3
-+ (dav_namespace, "quota-used-bytes" ), # RFC 4331, section 4
-
- (twisted_dav_namespace, "resource-class"),
- )
-@@ -264,6 +266,32 @@
- # TODO: Merge change from original patch
- lambda: self.safeAccessControlList(request)
- )
-+
-+ if name == "quota-available-bytes":
-+ def callback(qvalue):
-+ if qvalue is None:
-+ raise HTTPError(StatusResponse(
-+ responsecode.NOT_FOUND,
-+ "Property %s does not exist." % (sname,)
-+ ))
-+ else:
-+ return davxml.QuotaAvailableBytes(str(qvalue[0]))
-+ d = self.quota(request)
-+ d.addCallback(callback)
-+ return d
-+
-+ if name == "quota-used-bytes":
-+ def callback(qvalue):
-+ if qvalue is None:
-+ raise HTTPError(StatusResponse(
-+ responsecode.NOT_FOUND,
-+ "Property %s does not exist." % (sname,)
-+ ))
-+ else:
-+ return davxml.QuotaUsedBytes(str(qvalue[1]))
-+ d = self.quota(request)
-+ d.addCallback(callback)
-+ return d
-
- if namespace == twisted_dav_namespace:
- if name == "resource-class":
-@@ -596,7 +624,7 @@
- else:
- factory = request.credentialFactories[authHeader[0]]
-
-- creds = factory.decode(authHeader[1], request.method)
-+ creds = factory.decode(authHeader[1], request)
-
- # Try to match principals in each principal collection on the resource
- def gotDetails(details):
-@@ -1502,6 +1530,219 @@
- return None
-
- ##
-+ # Quota
-+ ##
-+
-+ """
-+ The basic policy here is to define a private 'quota-root' property on a collection.
-+ That property will contain the maximum allowed bytes for the collections and all
-+ its contents.
-+
-+ In order to determine the quota property values on a resource, the server must look
-+ for the private property on that resource and any of its parents. If found on a parent,
-+ then that parent should be queried for quota information. If not found, no quota
-+ exists for the resource.
-+
-+ To determine tha actual quota in use we will cache the used byte count on the quota-root
-+ collection in another private property. It is the servers responsibility to
-+ keep that property up to date by adjusting it after every PUT, DELETE, COPY,
-+ MOVE, MKCOL, PROPPATCH, ACL, POST or any other method that may affect the size of
-+ stored data. If the private property is not present, the server will fall back to
-+ getting the size by iterating over all resources (this is done in static.py).
-+
-+ """
-+
-+ def quota(self, request):
-+ """
-+ Get current available & used quota values for this resource's quota root
-+ collection.
-+
-+ @return: an L{Defered} with result C{tuple} containing two C{int}'s the first is
-+ quota-available-bytes, the second is quota-used-bytes, or
-+ C{None} if quota is not defined on the resource.
-+ """
-+
-+ # See if already cached
-+ if hasattr(request, "quota"):
-+ yield request.quota
-+ return
-+
-+ # Check this resource first
-+ if self.isCollection():
-+ qroot = self.quotaRoot(request)
-+ if qroot is not None:
-+ used = self.currentQuotaUse(request)
-+ available = qroot - used
-+ if available < 0:
-+ available = 0
-+ request.quota = (available, used)
-+ yield request.quota
-+ return
-+
-+ # Check the next parent
-+ url = request.urlForResource(self)
-+ if url != "/":
-+ parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+ yield parent
-+ parent = parent.getResult()
-+ d = waitForDeferred(parent.quota(request))
-+ yield d
-+ request.quota = d.getResult()
-+ else:
-+ request.quota = None
-+
-+ yield request.quota
-+ return
-+
-+ quota = deferredGenerator(quota)
-+
-+ def quotaRoot(self, request):
-+ """
-+ @return: a C{int} containing the maximum allowed bytes if this collection
-+ is quota-controlled, or C{None} if not quota controlled.
-+ """
-+ if self.hasDeadProperty(TwistedQuotaRootProperty):
-+ return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
-+ else:
-+ return None
-+
-+ def quotaRootParent(self, request):
-+ """
-+ Return the next quota root above this resource.
-+
-+ @return: L{DAVResource} or C{None}
-+ """
-+
-+ # Check the next parent
-+ url = request.urlForResource(self)
-+ while (url != "/"):
-+ url = parentForURL(url)
-+ parent = waitForDeferred(request.locateResource(url))
-+ yield parent
-+ parent = parent.getResult()
-+ if parent.hasDeadProperty(TwistedQuotaRootProperty):
-+ yield parent
-+ return
-+
-+ yield None
-+
-+ quotaRootParent = deferredGenerator(quotaRootParent)
-+
-+ def setQuotaRoot(self, request, maxsize):
-+ """
-+ @param maxsize: a C{int} containing the maximum allowed bytes for the contents
-+ of this collection.
-+ """
-+ assert self.isCollection(), "Only collections can have a quota root"
-+ assert isinstance(maxsize, int), "maxsize must be an int"
-+
-+ self.writeDeadProperty(TwistedQuotaRootProperty.fromString(str(maxsize)))
-+
-+ def quotaSize(self, request):
-+ """
-+ Get the size of this resource (if its a collection get total for all children as well).
-+ TODO: Take into account size of dead-properties.
-+
-+ @return: a C{int} containing the size of the resource.
-+ """
-+ unimplemented(self)
-+
-+ def checkQuota(self, request, available):
-+ """
-+ Check to see whether all quota roots have sufficient available bytes.
-+ We currently do not use hierarchical quota checks - i.e. only the most
-+ immediate quota root parent is checked for quota.
-+
-+ @param available: a C{int} containing the additional quota required.
-+ @return: C{True} if there is sufficient quota remaining on all quota roots,
-+ C{False} otherwise.
-+ """
-+
-+ quotaroot = self
-+ while(quotaroot is not None):
-+ # Check quota on this root (if it has one)
-+ quota = quotaroot.quotaRoot(request)
-+ if quota is not None:
-+ if available > quota[0]:
-+ yield False
-+ return
-+
-+ # Check the next parent with a quota root
-+ quotaroot = waitForDeferred(quotaroot.quotaRootParent(request))
-+ yield quotaroot
-+ quotaroot = quotaroot.getResult()
-+
-+ yield True
-+
-+ checkQuota = deferredGenerator(checkQuota)
-+
-+ def quotaSizeAdjust(self, request, adjust):
-+ """
-+ Update the quota used value on all quota root parents of this resource.
-+
-+ @param adjust: a C{int} containing the number of bytes added (positive) or
-+ removed (negative) that should be used to adjust the cached total.
-+ """
-+
-+ # Check this resource first
-+ if self.isCollection():
-+ if self.hasDeadProperty(TwistedQuotaRootProperty):
-+ self.updateQuotaUse(request, adjust)
-+
-+ # Check the next parent
-+ url = request.urlForResource(self)
-+ if url != "/":
-+ parent = waitForDeferred(request.locateResource(parentForURL(url)))
-+ yield parent
-+ parent = parent.getResult()
-+ d = waitForDeferred(parent.quotaSizeAdjust(request, adjust))
-+ yield d
-+ d.getResult()
-+
-+ yield None
-+
-+ quotaSizeAdjust = deferredGenerator(quotaSizeAdjust)
-+
-+ def currentQuotaUse(self, request):
-+ """
-+ Get the cached quota use value, or if not present (or invalid) determine
-+ quota use by brute force.
-+
-+ @return: a C{int} containing the current used byte if this collection
-+ is quota-controlled, or C{None} if not quota controlled.
-+ """
-+ assert self.isCollection(), "Only collections can have a quota root"
-+ assert self.hasDeadProperty(TwistedQuotaRootProperty), "Quota use only on quota root collection"
-+
-+ # Try to get the cached value property
-+ if self.hasDeadProperty(TwistedQuotaUsedProperty):
-+ return int(str(self.readDeadProperty(TwistedQuotaUsedProperty)))
-+ else:
-+ # Do brute force size determination
-+ result = self.quotaSize(request)
-+
-+ # Cache the brute force value in the private property
-+ self.writeDeadProperty(TwistedQuotaUsedProperty.fromString(str(result)))
-+
-+ return result
-+
-+ def updateQuotaUse(self, request, adjust):
-+ """
-+ Update the quota used value on this resource.
-+
-+ @param adjust: a C{int} containing the number of bytes added (positive) or
-+ removed (negative) that should be used to adjust the cached total.
-+ @return: a C{int} containing the current used byte if this collection
-+ is quota-controlled, or C{None} if not quota controlled.
-+ """
-+ assert self.isCollection(), "Only collections can have a quota root"
-+
-+ # Get current value
-+ size = self.currentQuotaUse(request)
-+ size += adjust
-+ self.writeDeadProperty(TwistedQuotaUsedProperty.fromString(str(size)))
-+
-+ ##
- # HTTP
- ##
-
-@@ -1736,6 +1977,28 @@
-
- davxml.registerElement(TwistedAccessDisabledProperty)
-
-+"""
-+When set on a collection, this property indicates that the collection has a quota limit for
-+the size of all resources stored in the collection (and any associate meta-data such as properties).
-+The value is a number - the maximum size in bytes allowed.
-+"""
-+class TwistedQuotaRootProperty (davxml.WebDAVTextElement):
-+ namespace = twisted_private_namespace
-+ name = "quota-root"
-+
-+davxml.registerElement(TwistedQuotaRootProperty)
-+
-+"""
-+When set on a collection, this property contains the cached running total of the size of all
-+resources stored in the collection (and any associate meta-data such as properties).
-+The value is a number - the size in bytes used.
-+"""
-+class TwistedQuotaUsedProperty (davxml.WebDAVTextElement):
-+ namespace = twisted_private_namespace
-+ name = "quota-used"
-+
-+davxml.registerElement(TwistedQuotaUsedProperty)
-+
- allACL = davxml.ACL(
- davxml.ACE(
- davxml.Principal(davxml.All()),
Deleted: CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.static.patch 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.dav.static.patch 2006-09-18 15:31:58 UTC (rev 143)
@@ -1,80 +0,0 @@
-Index: twisted/web2/dav/static.py
-===================================================================
---- twisted/web2/dav/static.py (revision 18157)
-+++ twisted/web2/dav/static.py (working copy)
-@@ -29,6 +29,7 @@
- __all__ = ["DAVFile"]
-
- import os
-+import stat
-
- from twisted.python import log
- from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
-@@ -34,6 +35,8 @@
- from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
- from twisted.web2.static import File
- from twisted.web2 import dirlist
-+from twisted.web2 import http
-+from twisted.web2 import responsecode
- from twisted.web2.dav import davxml
- from twisted.web2.dav.idav import IDAVResource
- from twisted.web2.dav.resource import DAVResource, davPrivilegeSet
-@@ -98,6 +101,58 @@
- return succeed(davPrivilegeSet)
-
- ##
-+ # Quota
-+ ##
-+
-+ def quotaSize(self, request):
-+ """
-+ Get the size of this resource.
-+ TODO: Take into account size of dead-properties. Does stat
-+ include xattrs size?
-+
-+ @return: a C{int} containing the size of the resource.
-+ """
-+ if self.isCollection():
-+ return self.collectionQuotaUse(request)
-+ else:
-+ result = os.stat(self.fp.path)
-+ return result[stat.ST_SIZE]
-+
-+ def collectionQuotaUse(self, request):
-+ """
-+ Brute force determination of quota used by this collection.
-+
-+ @return: a C{int} containing the current used byte if this collection
-+ is quota-controlled, or C{None} if not quota controlled.
-+ """
-+ assert self.isCollection(), "Only collections can have a quota root"
-+
-+ def walktree(top):
-+ """
-+ Recursively descend the directory tree rooted at top,
-+ calling the callback function for each regular file
-+ """
-+
-+ total = 0
-+ for f in os.listdir(top):
-+ pathname = os.path.join(top, f)
-+ result = os.stat(pathname)
-+ mode = result[stat.ST_MODE]
-+ if stat.S_ISDIR(mode):
-+ # It's a directory, recurse into it
-+ total += walktree(pathname)
-+ elif stat.S_ISREG(mode):
-+ # It's a file, call the callback function
-+ total += result[stat.ST_SIZE]
-+ else:
-+ # Unknown file type, print a message
-+ pass
-+
-+ return total
-+
-+ return walktree(self.fp.path)
-+
-+ ##
- # Workarounds for issues with File
- ##
-
Deleted: CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/lib-patches/Twisted/twisted.web2.test.test_httpauth.patch 2006-09-18 15:31:58 UTC (rev 143)
@@ -1,71 +0,0 @@
-Index: twisted/web2/test/test_httpauth.py
-===================================================================
---- twisted/web2/test/test_httpauth.py (revision 18157)
-+++ twisted/web2/test/test_httpauth.py (working copy)
-@@ -2,6 +2,7 @@
- from twisted.internet import defer
- from twisted.cred import error
- from twisted.web2.auth import basic, digest, wrapper
-+from twisted.web2.test.test_server import SimpleRequest
-
- from twisted.web2.test import test_server
-
-@@ -25,7 +26,7 @@
- self.username,
- self.password))
-
-- creds = self.credentialFactory.decode(response)
-+ creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
- self.failUnless(creds.checkPassword(self.password))
-
- def testIncorrectPassword(self):
-@@ -33,7 +34,7 @@
- self.username,
- 'incorrectPassword'))
-
-- creds = self.credentialFactory.decode(response)
-+ creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
- self.failIf(creds.checkPassword(self.password))
-
- def testIncorrectPadding(self):
-@@ -43,7 +44,7 @@
-
- response = response.strip('=')
-
-- creds = self.credentialFactory.decode(response)
-+ creds = self.credentialFactory.decode(response, SimpleRequest(None, 'GET', '/'))
- self.failUnless(creds.checkPassword(self.password))
-
- def testInvalidCredentials(self):
-@@ -51,7 +52,7 @@
-
- self.assertRaises(error.LoginFailed,
- self.credentialFactory.decode,
-- response)
-+ response, SimpleRequest(None, 'GET', '/'))
-
- challengeResponse = ('digest', {'nonce': '178288758716122392881254770685',
- 'qop': 'auth', 'realm': 'test realm',
-@@ -74,7 +75,7 @@
- def testResponse(self):
- challenge = self.credentialFactory.getChallenge(None)
-
-- creds = self.credentialFactory.decode(authRequest, 'GET')
-+ creds = self.credentialFactory.decode(authRequest, SimpleRequest(None, 'GET', '/'))
- self.failUnless(creds.checkPassword('password'))
-
- def testFailsWithDifferentMethod(self):
-@@ -80,11 +81,11 @@
- def testFailsWithDifferentMethod(self):
- challenge = self.credentialFactory.getChallenge(None)
-
-- creds = self.credentialFactory.decode(authRequest, 'POST')
-+ creds = self.credentialFactory.decode(authRequest, SimpleRequest(None, 'POST', '/'))
- self.failIf(creds.checkPassword('password'))
-
- def testNoUsername(self):
-- self.assertRaises(error.LoginFailed, self.credentialFactory.decode, namelessAuthRequest, 'GET')
-+ self.assertRaises(error.LoginFailed, self.credentialFactory.decode, namelessAuthRequest, SimpleRequest(None, 'GET', '/'))
-
- from zope.interface import Interface, implements
- from twisted.cred import portal, checkers
Modified: CalendarServer/branches/users/cdaboo/quota/run
===================================================================
--- CalendarServer/branches/users/cdaboo/quota/run 2006-09-15 23:08:28 UTC (rev 142)
+++ CalendarServer/branches/users/cdaboo/quota/run 2006-09-18 15:31:58 UTC (rev 143)
@@ -412,7 +412,7 @@
svn_uri="svn://svn.twistedmatrix.com/svn/Twisted/branches/acl-1608-8";
fi;
- svn_get "Twisted" "${twisted}" "${svn_uri}" 18155;
+ svn_get "Twisted" "${twisted}" "${svn_uri}" 18165;
fi;
py_install "Twisted" "${twisted}";
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20060918/460f318f/attachment.html
More information about the calendarserver-changes
mailing list