[CalendarServer-changes] [10982] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Apr 2 18:26:13 PDT 2013
Revision: 10982
http://trac.calendarserver.org//changeset/10982
Author: sagen at apple.com
Date: 2013-04-02 18:26:13 -0700 (Tue, 02 Apr 2013)
Log Message:
-----------
Group Cacher sidecar is gone, and calendarserver_manage_principals has access to the store in order to schedule group cacher updates.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tap/caldav.py
CalendarServer/trunk/calendarserver/tap/util.py
CalendarServer/trunk/calendarserver/tools/cmdline.py
CalendarServer/trunk/calendarserver/tools/gateway.py
CalendarServer/trunk/calendarserver/tools/principals.py
CalendarServer/trunk/calendarserver/tools/purge.py
CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist
CalendarServer/trunk/calendarserver/tools/test/test_principals.py
CalendarServer/trunk/calendarserver/tools/util.py
CalendarServer/trunk/calendarserver/webadmin/resource.py
CalendarServer/trunk/conf/auth/augments-test.xml
CalendarServer/trunk/conf/auth/resources-test.xml
CalendarServer/trunk/twext/enterprise/queue.py
CalendarServer/trunk/twext/enterprise/test/test_queue.py
CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
CalendarServer/trunk/twistedcaldav/directory/directory.py
CalendarServer/trunk/twistedcaldav/scheduling/imip/test/test_inbound.py
CalendarServer/trunk/twistedcaldav/test/util.py
Property Changed:
----------------
CalendarServer/trunk/conf/auth/resources-test.xml
Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -78,6 +78,7 @@
from twistedcaldav.upgrade import UpgradeFileSystemFormatService, PostDBImportService
from calendarserver.tap.util import pgServiceFromConfig, getDBPool, MemoryLimitService
+from calendarserver.tap.util import directoryFromConfig
from twext.enterprise.ienterprise import POSTGRES_DIALECT
from twext.enterprise.ienterprise import ORACLE_DIALECT
@@ -1054,9 +1055,10 @@
if observers:
pushDistributor = PushDistributor(observers)
+ directory = result.rootResource.getDirectory()
+
# Optionally set up mail retrieval
if config.Scheduling.iMIP.Enabled:
- directory = result.rootResource.getDirectory()
mailRetriever = MailRetriever(store, directory,
config.Scheduling.iMIP.Receiving)
mailRetriever.setServiceParent(result)
@@ -1445,7 +1447,41 @@
spawner.setServiceParent(multi)
if config.UseMetaFD:
cl.setServiceParent(multi)
+
+ directory = directoryFromConfig(config)
+ rootResource = getRootResource(config, store, [])
+
+ # Optionally set up mail retrieval
+ if config.Scheduling.iMIP.Enabled:
+ mailRetriever = MailRetriever(store, directory,
+ config.Scheduling.iMIP.Receiving)
+ mailRetriever.setServiceParent(multi)
+ else:
+ mailRetriever = None
+
+ # Optionally set up group cacher
+ if config.GroupCaching.Enabled:
+ groupCacher = GroupMembershipCacheUpdater(
+ calendaruserproxy.ProxyDBService,
+ directory,
+ config.GroupCaching.UpdateSeconds,
+ config.GroupCaching.ExpireSeconds,
+ namespace=config.GroupCaching.MemcachedPool,
+ useExternalProxies=config.GroupCaching.UseExternalProxies
+ )
+ else:
+ groupCacher = None
+
+ def decorateTransaction(txn):
+ txn._pushDistributor = None
+ txn._rootResource = rootResource
+ txn._mailRetriever = mailRetriever
+ txn._groupCacher = groupCacher
+
+ store.callWithNewTransactions(decorateTransaction)
+
return multi
+
ssvc = self.storageService(spawnerSvcCreator, uid, gid)
ssvc.setServiceParent(s)
return s
Modified: CalendarServer/trunk/calendarserver/tap/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/util.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tap/util.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -650,6 +650,7 @@
config.WebCalendarRoot,
root,
directory,
+ newStore,
principalCollections=(principalCollection,),
)
root.putChild("admin", webAdmin)
Modified: CalendarServer/trunk/calendarserver/tools/cmdline.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/cmdline.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tools/cmdline.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -24,9 +24,14 @@
from twext.python.log import StandardIOObserver
from twistedcaldav.config import ConfigurationError
+from twisted.internet.defer import inlineCallbacks
import os
import sys
+from calendarserver.tap.util import getRootResource
+from twisted.application.service import Service
+from errno import ENOENT, EACCES
+from twext.enterprise.queue import NonPerformingQueuer
# TODO: direct unit tests for these functions.
@@ -85,6 +90,10 @@
autoDisableMemcached(config)
maker = serviceMaker()
+
+ # Only perform post-import duties if someone has explicitly said to
+ maker.doPostImport = getattr(maker, "doPostImport", False)
+
options = CalDAVOptions
service = maker.makeService(options)
@@ -98,3 +107,49 @@
return
reactor.run()
+
+
+
+class WorkerService(Service):
+
+ def __init__(self, store):
+ self._store = store
+ # Work can be queued but will not be performed by the command line tool
+ store.queuer = NonPerformingQueuer()
+
+
+ def rootResource(self):
+ try:
+ from twistedcaldav.config import config
+ rootResource = getRootResource(config, self._store)
+ except OSError, e:
+ if e.errno == ENOENT:
+ # Trying to re-write resources.xml but its parent directory does
+ # not exist. The server's never been started, so we're missing
+ # state required to do any work.
+ raise ConfigurationError(
+ "It appears that the server has never been started.\n"
+ "Please start it at least once before running this tool.")
+ elif e.errno == EACCES:
+ # Trying to re-write resources.xml but it is not writable by the
+ # current user. This most likely means we're in a system
+ # configuration and the user doesn't have sufficient privileges
+ # to do the other things the tool might need to do either.
+ raise ConfigurationError("You must run this tool as root.")
+ else:
+ raise
+ return rootResource
+
+ @inlineCallbacks
+ def startService(self):
+ from twisted.internet import reactor
+ try:
+ yield self.doWork()
+ except ConfigurationError, ce:
+ sys.stderr.write("Error: %s\n" % (str(ce),))
+ except Exception, e:
+ sys.stderr.write("Error: %s\n" % (e,))
+ raise
+ finally:
+ reactor.stop()
+
Modified: CalendarServer/trunk/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/gateway.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tools/gateway.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -28,10 +28,11 @@
from twistedcaldav.directory.directory import DirectoryError
from txdav.xml import element as davxml
-from calendarserver.tools.principals import (
+from calendarserver.tools.util import (
principalForPrincipalID, proxySubprincipal, addProxy, removeProxy,
- getProxies, setProxies, ProxyError, ProxyWarning, updateRecord
+ ProxyError, ProxyWarning
)
+from calendarserver.tools.principals import getProxies, setProxies, updateRecord
from calendarserver.tools.purge import WorkerService, PurgeOldEventsService, DEFAULT_BATCH_SIZE, DEFAULT_RETAIN_DAYS
from calendarserver.tools.cmdline import utilityMain
@@ -212,7 +213,7 @@
readProxies = command.get("ReadProxies", None)
writeProxies = command.get("WriteProxies", None)
principal = principalForPrincipalID(record.guid, directory=self.dir)
- (yield setProxies(principal, readProxies, writeProxies, directory=self.dir))
+ (yield setProxies(self.store, principal, readProxies, writeProxies, directory=self.dir))
respondWithRecordsOfType(self.dir, command, "locations")
@@ -260,7 +261,7 @@
readProxies = command.get("ReadProxies", None)
writeProxies = command.get("WriteProxies", None)
principal = principalForPrincipalID(record.guid, directory=self.dir)
- (yield setProxies(principal, readProxies, writeProxies, directory=self.dir))
+ (yield setProxies(self.store, principal, readProxies, writeProxies, directory=self.dir))
yield self.command_getLocationAttributes(command)
@@ -300,7 +301,7 @@
readProxies = command.get("ReadProxies", None)
writeProxies = command.get("WriteProxies", None)
principal = principalForPrincipalID(record.guid, directory=self.dir)
- (yield setProxies(principal, readProxies, writeProxies, directory=self.dir))
+ (yield setProxies(self.store, principal, readProxies, writeProxies, directory=self.dir))
respondWithRecordsOfType(self.dir, command, "resources")
@@ -328,7 +329,7 @@
readProxies = command.get("ReadProxies", None)
writeProxies = command.get("WriteProxies", None)
principal = principalForPrincipalID(record.guid, directory=self.dir)
- (yield setProxies(principal, readProxies, writeProxies, directory=self.dir))
+ (yield setProxies(self.store, principal, readProxies, writeProxies, directory=self.dir))
yield self.command_getResourceAttributes(command)
@@ -370,7 +371,7 @@
respondWithError("Proxy not found: %s" % (command['Proxy'],))
return
try:
- (yield addProxy(principal, "write", proxy))
+ (yield addProxy(self.root, self.dir, self.store, principal, "write", proxy))
except ProxyError, e:
respondWithError(str(e))
return
@@ -390,7 +391,7 @@
respondWithError("Proxy not found: %s" % (command['Proxy'],))
return
try:
- (yield removeProxy(principal, proxy, proxyTypes=("write",)))
+ (yield removeProxy(self.root, self.dir, self.store, principal, proxy, proxyTypes=("write",)))
except ProxyError, e:
respondWithError(str(e))
return
@@ -419,7 +420,7 @@
respondWithError("Proxy not found: %s" % (command['Proxy'],))
return
try:
- (yield addProxy(principal, "read", proxy))
+ (yield addProxy(self.root, self.dir, self.store, principal, "read", proxy))
except ProxyError, e:
respondWithError(str(e))
return
@@ -439,7 +440,7 @@
respondWithError("Proxy not found: %s" % (command['Proxy'],))
return
try:
- (yield removeProxy(principal, proxy, proxyTypes=("read",)))
+ (yield removeProxy(self.root, self.dir, self.store, principal, proxy, proxyTypes=("read",)))
except ProxyError, e:
respondWithError(str(e))
return
Modified: CalendarServer/trunk/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/principals.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tools/principals.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -20,31 +20,33 @@
import sys
import os
import operator
-import signal
from getopt import getopt, GetoptError
from uuid import UUID
from twisted.internet import reactor
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from txdav.xml import element as davxml
from txdav.xml.base import decodeXMLName, encodeXMLName
from twistedcaldav.config import config
from twistedcaldav.directory.directory import UnknownRecordTypeError, DirectoryError
+from twistedcaldav.directory.directory import scheduleNextGroupCachingUpdate
-from calendarserver.tools.util import booleanArgument
+from calendarserver.tools.util import (
+ booleanArgument, proxySubprincipal, action_addProxyPrincipal,
+ principalForPrincipalID, prettyPrincipal, ProxyError,
+ action_removeProxyPrincipal
+)
from twistedcaldav.directory.augment import allowedAutoScheduleModes
from calendarserver.tools.cmdline import utilityMain
# FIXME: Move WorkerService to a util module?
from calendarserver.tools.purge import WorkerService
-__all__ = [
- "principalForPrincipalID", "proxySubprincipal", "addProxy", "removeProxy",
- "ProxyError", "ProxyWarning", "updateRecord"
-]
+from calendarserver.tools.cmdline import utilityMain, WorkerService
+
def usage(e=None):
if e:
if isinstance(e, UnknownRecordTypeError):
@@ -100,17 +102,22 @@
class PrincipalService(WorkerService):
"""
+ Executes principals-related functions in a context which has access to the store
"""
+ function = None
params = []
@inlineCallbacks
def doWork(self):
"""
+ Calls the function that's been assigned to "function" and passes the root
+ resource, directory, store, and whatever has been assigned to "params".
"""
- rootResource = self.rootResource()
- directory = rootResource.getDirectory()
- yield self.command(rootResource, directory, self._store, *self.params)
+ if self.function is not None:
+ rootResource = self.rootResource()
+ directory = rootResource.getDirectory()
+ yield self.function(rootResource, directory, self._store, *self.params)
def main():
@@ -265,10 +272,9 @@
if args:
usage("Too many arguments")
- for recordType in config.directory.recordTypes():
- print(recordType)
+ function = runListPrincipalTypes
+ params = ()
- return
elif addType:
@@ -289,7 +295,8 @@
else:
shortNames = ()
- params = (runAddPrincipal, addType, guid, shortNames, fullName)
+ function = runAddPrincipal
+ params = (addType, guid, shortNames, fullName)
elif listPrincipals:
@@ -303,19 +310,13 @@
if args:
usage("Too many arguments")
- try:
- records = list(config.directory.listRecords(listPrincipals))
- if records:
- printRecordList(records)
- else:
- print("No records of type %s" % (listPrincipals,))
- except UnknownRecordTypeError, e:
- usage(e)
+ function = runListPrincipals
+ params = (listPrincipals,)
- return
elif searchPrincipals:
- params = (runSearch, searchPrincipals)
+ function = runSearch
+ params = (searchPrincipals,)
else:
#
@@ -331,157 +332,96 @@
except ValueError, e:
abort(e)
- params = (runPrincipalActions, args, principalActions)
+ function = runPrincipalActions
+ params = (args, principalActions)
+ PrincipalService.function = function
PrincipalService.params = params
utilityMain(configFileName, PrincipalService, verbose=verbose)
- at inlineCallbacks
-def runPrincipalActions(rootResource, directory, store, principalIDs, actions):
- try:
- for principalID in principalIDs:
- # Resolve the given principal IDs to principals
- try:
- principal = principalForPrincipalID(principalID)
- except ValueError:
- principal = None
+def runListPrincipalTypes(service, rootResource, directory, store):
+ for recordType in directory.recordTypes():
+ print(recordType)
+ return succeed(None)
- if principal is None:
- sys.stderr.write("Invalid principal ID: %s\n" % (principalID,))
- continue
- # Performs requested actions
- for action in actions:
- (yield action[0](rootResource, directory, store, principal,
- *action[1:]))
- print("")
-
- finally:
- #
- # Stop the reactor
- #
- reactor.stop()
-
- at inlineCallbacks
-def runSearch(rootResource, directory, store, searchTerm):
-
+def runListPrincipals(service, rootResource, directory, store, listPrincipals):
try:
- fields = []
- for fieldName in ("fullName", "firstName", "lastName", "emailAddresses"):
- fields.append((fieldName, searchTerm, True, "contains"))
-
- records = list((yield directory.recordsMatchingTokens(searchTerm.strip().split())))
+ records = list(directory.listRecords(listPrincipals))
if records:
- records.sort(key=operator.attrgetter('fullName'))
- print("%d matches found:" % (len(records),))
- for record in records:
- print("\n%s (%s)" % (record.fullName,
- { "users" : "User",
- "groups" : "Group",
- "locations" : "Place",
- "resources" : "Resource",
- }.get(record.recordType),
- ))
- print(" GUID: %s" % (record.guid,))
- print(" Record name(s): %s" % (", ".join(record.shortNames),))
- if record.authIDs:
- print(" Auth ID(s): %s" % (", ".join(record.authIDs),))
- if record.emailAddresses:
- print(" Email(s): %s" % (", ".join(record.emailAddresses),))
+ printRecordList(records)
else:
- print("No matches found")
+ print("No records of type %s" % (listPrincipals,))
+ except UnknownRecordTypeError, e:
+ usage(e)
+ return succeed(None)
- print("")
- finally:
- #
- # Stop the reactor
- #
- reactor.stop()
-
@inlineCallbacks
-def runAddPrincipal(addType, guid, shortNames, fullName):
- try:
+def runPrincipalActions(service, rootResource, directory, store, principalIDs,
+ actions):
+ for principalID in principalIDs:
+ # Resolve the given principal IDs to principals
try:
- yield updateRecord(True, config.directory, addType, guid=guid,
- shortNames=shortNames, fullName=fullName)
- print("Added '%s'" % (fullName,))
- except DirectoryError, e:
- print(e)
+ principal = principalForPrincipalID(principalID, directory=directory)
+ except ValueError:
+ principal = None
- finally:
- #
- # Stop the reactor
- #
- reactor.stop()
+ if principal is None:
+ sys.stderr.write("Invalid principal ID: %s\n" % (principalID,))
+ continue
+ # Performs requested actions
+ for action in actions:
+ (yield action[0](rootResource, directory, store, principal,
+ *action[1:]))
+ print("")
-def principalForPrincipalID(principalID, checkOnly=False, directory=None):
-
- # Allow a directory parameter to be passed in, but default to config.directory
- # But config.directory isn't set right away, so only use it when we're doing more
- # than checking.
- if not checkOnly and not directory:
- directory = config.directory
- if principalID.startswith("/"):
- segments = principalID.strip("/").split("/")
- if (len(segments) == 3 and
- segments[0] == "principals" and segments[1] == "__uids__"):
- uid = segments[2]
- else:
- raise ValueError("Can't resolve all paths yet")
+ at inlineCallbacks
+def runSearch(service, rootResource, directory, store, searchTerm):
- if checkOnly:
- return None
+ fields = []
+ for fieldName in ("fullName", "firstName", "lastName", "emailAddresses"):
+ fields.append((fieldName, searchTerm, True, "contains"))
- return directory.principalCollection.principalForUID(uid)
+ records = list((yield directory.recordsMatchingTokens(searchTerm.strip().split())))
+ if records:
+ records.sort(key=operator.attrgetter('fullName'))
+ print("%d matches found:" % (len(records),))
+ for record in records:
+ print("\n%s (%s)" % (record.fullName,
+ { "users" : "User",
+ "groups" : "Group",
+ "locations" : "Place",
+ "resources" : "Resource",
+ }.get(record.recordType),
+ ))
+ print(" GUID: %s" % (record.guid,))
+ print(" Record name(s): %s" % (", ".join(record.shortNames),))
+ if record.authIDs:
+ print(" Auth ID(s): %s" % (", ".join(record.authIDs),))
+ if record.emailAddresses:
+ print(" Email(s): %s" % (", ".join(record.emailAddresses),))
+ else:
+ print("No matches found")
+ print("")
- if principalID.startswith("("):
- try:
- i = principalID.index(")")
- if checkOnly:
- return None
-
- recordType = principalID[1:i]
- shortName = principalID[i+1:]
-
- if not recordType or not shortName or "(" in recordType:
- raise ValueError()
-
- return directory.principalCollection.principalForShortName(recordType, shortName)
-
- except ValueError:
- pass
-
- if ":" in principalID:
- if checkOnly:
- return None
-
- recordType, shortName = principalID.split(":", 1)
-
- return directory.principalCollection.principalForShortName(recordType, shortName)
-
+ at inlineCallbacks
+def runAddPrincipal(service, rootResource, directory, store, addType, guid,
+ shortNames, fullName):
try:
- UUID(principalID)
+ yield updateRecord(True, directory, addType, guid=guid,
+ shortNames=shortNames, fullName=fullName)
+ print("Added '%s'" % (fullName,))
+ except DirectoryError, e:
+ print(e)
- if checkOnly:
- return None
- x = directory.principalCollection.principalForUID(principalID)
- return x
- except ValueError:
- pass
-
- raise ValueError("Invalid principal identifier: %s" % (principalID,))
-
-def proxySubprincipal(principal, proxyType):
- return principal.getChild("calendar-proxy-" + proxyType)
-
def action_removePrincipal(rootResource, directory, store, principal):
record = principal.record
fullName = record.fullName
@@ -530,57 +470,17 @@
@inlineCallbacks
def action_addProxy(rootResource, directory, store, principal, proxyType, *proxyIDs):
for proxyID in proxyIDs:
- proxyPrincipal = principalForPrincipalID(proxyID)
+ proxyPrincipal = principalForPrincipalID(proxyID, directory=directory)
if proxyPrincipal is None:
print("Invalid principal ID: %s" % (proxyID,))
else:
- (yield action_addProxyPrincipal(principal, proxyType, proxyPrincipal))
+ (yield action_addProxyPrincipal(rootResource, directory, store,
+ principal, proxyType, proxyPrincipal))
- at inlineCallbacks
-def action_addProxyPrincipal(rootResource, directory, store, principal, proxyType, proxyPrincipal):
- try:
- (yield addProxy(principal, proxyType, proxyPrincipal))
- print("Added %s as a %s proxy for %s" % (
- prettyPrincipal(proxyPrincipal), proxyType,
- prettyPrincipal(principal)))
- except ProxyError, e:
- print("Error:", e)
- except ProxyWarning, e:
- print(e)
- at inlineCallbacks
-def addProxy(principal, proxyType, proxyPrincipal):
- proxyURL = proxyPrincipal.url()
- subPrincipal = proxySubprincipal(principal, proxyType)
- if subPrincipal is None:
- raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType,
- prettyPrincipal(principal)))
-
- membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
-
- for memberURL in membersProperty.children:
- if str(memberURL) == proxyURL:
- raise ProxyWarning("%s is already a %s proxy for %s" % (
- prettyPrincipal(proxyPrincipal), proxyType,
- prettyPrincipal(principal)))
-
- else:
- memberURLs = list(membersProperty.children)
- memberURLs.append(davxml.HRef(proxyURL))
- membersProperty = davxml.GroupMemberSet(*memberURLs)
- (yield subPrincipal.writeProperty(membersProperty, None))
-
- proxyTypes = ["read", "write"]
- proxyTypes.remove(proxyType)
-
- (yield action_removeProxyPrincipal(principal, proxyPrincipal, proxyTypes=proxyTypes))
-
- triggerGroupCacherUpdate(config)
-
-
@inlineCallbacks
-def setProxies(principal, readProxyPrincipals, writeProxyPrincipals, directory=None):
+def setProxies(store, principal, readProxyPrincipals, writeProxyPrincipals, directory=None):
"""
Set read/write proxies en masse for a principal
@param principal: DirectoryPrincipalResource
@@ -605,8 +505,9 @@
proxyURL = proxyPrincipal.url()
memberURLs.append(davxml.HRef(proxyURL))
membersProperty = davxml.GroupMemberSet(*memberURLs)
- (yield subPrincipal.writeProperty(membersProperty, None))
- triggerGroupCacherUpdate(config)
+ yield subPrincipal.writeProperty(membersProperty, None)
+ if store is not None:
+ yield scheduleNextGroupCachingUpdate(store, 0)
@inlineCallbacks
@@ -635,59 +536,16 @@
@inlineCallbacks
def action_removeProxy(rootResource, directory, store, principal, *proxyIDs, **kwargs):
for proxyID in proxyIDs:
- proxyPrincipal = principalForPrincipalID(proxyID)
+ proxyPrincipal = principalForPrincipalID(proxyID, directory=directory)
if proxyPrincipal is None:
print("Invalid principal ID: %s" % (proxyID,))
else:
- (yield action_removeProxyPrincipal(principal, proxyPrincipal, **kwargs))
+ (yield action_removeProxyPrincipal(rootResource, directory, store,
+ principal, proxyPrincipal, **kwargs))
- at inlineCallbacks
-def action_removeProxyPrincipal(rootResource, directory, store, principal, proxyPrincipal, **kwargs):
- try:
- removed = (yield removeProxy(principal, proxyPrincipal, **kwargs))
- if removed:
- print("Removed %s as a proxy for %s" % (
- prettyPrincipal(proxyPrincipal),
- prettyPrincipal(principal)))
- except ProxyError, e:
- print("Error:", e)
- except ProxyWarning, e:
- print(e)
- at inlineCallbacks
-def removeProxy(principal, proxyPrincipal, **kwargs):
- removed = False
- proxyTypes = kwargs.get("proxyTypes", ("read", "write"))
- for proxyType in proxyTypes:
- proxyURL = proxyPrincipal.url()
- subPrincipal = proxySubprincipal(principal, proxyType)
- if subPrincipal is None:
- raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType,
- prettyPrincipal(principal)))
-
- membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
-
- memberURLs = [
- m for m in membersProperty.children
- if str(m) != proxyURL
- ]
-
- if len(memberURLs) == len(membersProperty.children):
- # No change
- continue
- else:
- removed = True
-
- membersProperty = davxml.GroupMemberSet(*memberURLs)
- (yield subPrincipal.writeProperty(membersProperty, None))
-
- if removed:
- triggerGroupCacherUpdate(config)
- returnValue(removed)
-
-
@inlineCallbacks
def action_setAutoSchedule(rootResource, directory, store, principal, autoSchedule):
if principal.record.recordType == "groups":
@@ -759,7 +617,7 @@
print("Setting auto-accept-group for %s is not allowed." % (principal,))
else:
- groupPrincipal = principalForPrincipalID(autoAcceptGroup)
+ groupPrincipal = principalForPrincipalID(autoAcceptGroup, directory=directory)
if groupPrincipal is None or groupPrincipal.record.recordType != "groups":
print("Invalid principal ID: %s" % (autoAcceptGroup,))
else:
@@ -802,17 +660,7 @@
pass
sys.exit(status)
-class ProxyError(Exception):
- """
- Raised when proxy assignments cannot be performed
- """
-class ProxyWarning(Exception):
- """
- Raised for harmless proxy assignment failures such as trying to add a
- duplicate or remove a non-existent assignment.
- """
-
def parseCreationArgs(args):
"""
Look at the command line arguments for --add, and figure out which
@@ -865,10 +713,6 @@
for fullName, shortName, guid in results:
print(format % (fullName, shortName, guid))
-def prettyPrincipal(principal):
- record = principal.record
- return "\"%s\" (%s:%s)" % (record.fullName, record.recordType,
- record.shortNames[0])
@inlineCallbacks
@@ -949,28 +793,6 @@
returnValue(record)
-def triggerGroupCacherUpdate(config, killMethod=None):
- """
- Look up the pid of the group cacher sidecar and HUP it to trigger an update
- """
- if killMethod is None:
- killMethod = os.kill
- pidFilename = os.path.join(config.RunRoot, "groupcacher.pid")
- if os.path.exists(pidFilename):
- pidFile = open(pidFilename, "r")
- pid = pidFile.read().strip()
- pidFile.close()
- try:
- pid = int(pid)
- except ValueError:
- return
- try:
- killMethod(pid, signal.SIGHUP)
- except OSError:
- pass
-
-
-
if __name__ == "__main__":
main()
Modified: CalendarServer/trunk/calendarserver/tools/purge.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/purge.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tools/purge.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -18,12 +18,10 @@
from __future__ import print_function
from calendarserver.tap.util import FakeRequest
-from calendarserver.tap.util import getRootResource
from calendarserver.tools import tables
-from calendarserver.tools.cmdline import utilityMain
-from calendarserver.tools.principals import removeProxy
+from calendarserver.tools.cmdline import utilityMain, WorkerService
+from calendarserver.tools.util import removeProxy
-from errno import ENOENT, EACCES
from getopt import getopt, GetoptError
from pycalendar.datetime import PyCalendarDateTime
@@ -31,13 +29,10 @@
from twext.python.log import Logger
from twext.web2.responsecode import NO_CONTENT
-from twisted.application.service import Service
-from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue
from twistedcaldav import caldavxml
from twistedcaldav.caldavxml import TimeRange
-from twistedcaldav.config import config, ConfigurationError
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.directory.directory import DirectoryRecord
from twistedcaldav.method.put_common import StoreCalendarObjectResource
@@ -45,6 +40,7 @@
from txdav.xml import element as davxml
+
import collections
import os
import sys
@@ -54,49 +50,9 @@
DEFAULT_BATCH_SIZE = 100
DEFAULT_RETAIN_DAYS = 365
-class WorkerService(Service):
- def __init__(self, store):
- self._store = store
- def rootResource(self):
- try:
- rootResource = getRootResource(config, self._store)
- except OSError, e:
- if e.errno == ENOENT:
- # Trying to re-write resources.xml but its parent directory does
- # not exist. The server's never been started, so we're missing
- # state required to do any work. (Plus, what would be the point
- # of purging stuff from a server that's completely empty?)
- raise ConfigurationError(
- "It appears that the server has never been started.\n"
- "Please start it at least once before purging anything.")
- elif e.errno == EACCES:
- # Trying to re-write resources.xml but it is not writable by the
- # current user. This most likely means we're in a system
- # configuration and the user doesn't have sufficient privileges
- # to do the other things the tool might need to do either.
- raise ConfigurationError("You must run this tool as root.")
- else:
- raise
- return rootResource
-
-
- @inlineCallbacks
- def startService(self):
- try:
- yield self.doWork()
- except ConfigurationError, ce:
- sys.stderr.write("Error: %s\n" % (str(ce),))
- except Exception, e:
- sys.stderr.write("Error: %s\n" % (e,))
- raise
- finally:
- reactor.stop()
-
-
-
class PurgeOldEventsService(WorkerService):
cutoff = None
@@ -1163,9 +1119,8 @@
return cls.CANCELEVENT_NOT_MODIFIED
- @classmethod
@inlineCallbacks
- def _purgeProxyAssignments(cls, principal):
+ def _purgeProxyAssignments(self, principal):
assignments = []
@@ -1174,7 +1129,7 @@
proxyFor = (yield principal.proxyFor(proxyType == "write"))
for other in proxyFor:
assignments.append((principal.record.uid, proxyType, other.record.uid))
- (yield removeProxy(other, principal))
+ (yield removeProxy(self.root, self.directory, self._store, other, principal))
subPrincipal = principal.getChild("calendar-proxy-" + proxyType)
proxies = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
Modified: CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tools/test/principals/caldavd.plist 2013-04-03 01:26:13 UTC (rev 10982)
@@ -79,23 +79,23 @@
<!-- Data root -->
<key>DataRoot</key>
- <string>Data</string>
+ <string>%(DataRoot)s</string>
<!-- Document root -->
<key>DocumentRoot</key>
- <string>Documents</string>
+ <string>%(DocumentRoot)s</string>
<!-- Configuration root -->
<key>ConfigRoot</key>
- <string>/etc/caldavd</string>
+ <string>Config</string>
<!-- Log root -->
<key>LogRoot</key>
- <string>/var/log/caldavd</string>
+ <string>%(LogRoot)s</string>
<!-- Run root -->
<key>RunRoot</key>
- <string>/var/run</string>
+ <string>%(LogRoot)s</string>
<!-- Child aliases -->
<key>Aliases</key>
@@ -279,7 +279,7 @@
-->
<key>ProxyLoadFromFile</key>
- <string>conf/auth/proxies-test.xml</string>
+ <string></string>
<!--
Special principals
Modified: CalendarServer/trunk/calendarserver/tools/test/test_principals.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_principals.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tools/test/test_principals.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -15,7 +15,6 @@
##
import os
-import signal
import sys
from twext.python.filepath import CachingFilePath as FilePath
@@ -31,8 +30,7 @@
from calendarserver.tap.util import directoryFromConfig
from calendarserver.tools.principals import (parseCreationArgs, matchStrings,
- updateRecord, principalForPrincipalID, getProxies, setProxies,
- triggerGroupCacherUpdate)
+ updateRecord, principalForPrincipalID, getProxies, setProxies)
class ManagePrincipalsTestCase(TestCase):
@@ -53,6 +51,9 @@
newConfig = template % {
"ServerRoot" : os.path.abspath(config.ServerRoot),
+ "DataRoot" : os.path.abspath(config.DataRoot),
+ "DocumentRoot" : os.path.abspath(config.DocumentRoot),
+ "LogRoot" : os.path.abspath(config.LogRoot),
}
configFilePath = FilePath(os.path.join(config.ConfigRoot, "caldavd.plist"))
configFilePath.setContent(newConfig)
@@ -339,36 +340,13 @@
self.assertEquals(readProxies, []) # initially empty
self.assertEquals(writeProxies, []) # initially empty
- (yield setProxies(principal, ["users:user03", "users:user04"], ["users:user05"], directory=directory))
+ (yield setProxies(None, principal, ["users:user03", "users:user04"], ["users:user05"], directory=directory))
readProxies, writeProxies = (yield getProxies(principal, directory=directory))
self.assertEquals(set(readProxies), set(["user03", "user04"]))
self.assertEquals(set(writeProxies), set(["user05"]))
# Using None for a proxy list indicates a no-op
- (yield setProxies(principal, [], None, directory=directory))
+ (yield setProxies(None, principal, [], None, directory=directory))
readProxies, writeProxies = (yield getProxies(principal, directory=directory))
self.assertEquals(readProxies, []) # now empty
self.assertEquals(set(writeProxies), set(["user05"])) # unchanged
-
-
- def test_triggerGroupCacherUpdate(self):
- """
- Verify triggerGroupCacherUpdate can read a pidfile and send a SIGHUP
- """
-
- self.calledArgs = None
- def killMethod(pid, sig):
- self.calledArgs = (pid, sig)
-
- class StubConfig(object):
- def __init__(self, runRootPath):
- self.RunRoot = runRootPath
-
- runRootDir = FilePath(self.mktemp())
- runRootDir.createDirectory()
- pidFile = runRootDir.child("groupcacher.pid")
- pidFile.setContent("1234")
- testConfig = StubConfig(runRootDir.path)
- triggerGroupCacherUpdate(testConfig, killMethod=killMethod)
- self.assertEquals(self.calledArgs, (1234, signal.SIGHUP))
- runRootDir.remove()
Modified: CalendarServer/trunk/calendarserver/tools/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/util.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/tools/util.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -31,21 +31,27 @@
import socket
from pwd import getpwnam
from grp import getgrnam
+from uuid import UUID
+from twistedcaldav.config import config, ConfigurationError
+from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
+
+
from twisted.python.filepath import FilePath
from twisted.python.reflect import namedClass
from twext.python.log import Logger
+from twisted.internet.defer import inlineCallbacks, returnValue
+from txdav.xml import element as davxml
from calendarserver.provision.root import RootResource
from twistedcaldav import memcachepool
-from twistedcaldav.config import config, ConfigurationError
from twistedcaldav.directory import calendaruserproxy
from twistedcaldav.directory.aggregate import AggregateDirectoryService
from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
+from twistedcaldav.directory.directory import scheduleNextGroupCachingUpdate
from calendarserver.push.notifier import NotifierFactory
-from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
from txdav.common.datastore.file import CommonDataStore
@@ -312,3 +318,176 @@
+def principalForPrincipalID(principalID, checkOnly=False, directory=None):
+
+ # Allow a directory parameter to be passed in, but default to config.directory
+ # But config.directory isn't set right away, so only use it when we're doing more
+ # than checking.
+ if not checkOnly and not directory:
+ directory = config.directory
+
+ if principalID.startswith("/"):
+ segments = principalID.strip("/").split("/")
+ if (len(segments) == 3 and
+ segments[0] == "principals" and segments[1] == "__uids__"):
+ uid = segments[2]
+ else:
+ raise ValueError("Can't resolve all paths yet")
+
+ if checkOnly:
+ return None
+
+ return directory.principalCollection.principalForUID(uid)
+
+
+ if principalID.startswith("("):
+ try:
+ i = principalID.index(")")
+
+ if checkOnly:
+ return None
+
+ recordType = principalID[1:i]
+ shortName = principalID[i+1:]
+
+ if not recordType or not shortName or "(" in recordType:
+ raise ValueError()
+
+ return directory.principalCollection.principalForShortName(recordType, shortName)
+
+ except ValueError:
+ pass
+
+ if ":" in principalID:
+ if checkOnly:
+ return None
+
+ recordType, shortName = principalID.split(":", 1)
+
+ return directory.principalCollection.principalForShortName(recordType, shortName)
+
+ try:
+ UUID(principalID)
+
+ if checkOnly:
+ return None
+
+ x = directory.principalCollection.principalForUID(principalID)
+ return x
+ except ValueError:
+ pass
+
+ raise ValueError("Invalid principal identifier: %s" % (principalID,))
+
+def proxySubprincipal(principal, proxyType):
+ return principal.getChild("calendar-proxy-" + proxyType)
+
+ at inlineCallbacks
+def action_addProxyPrincipal(rootResource, directory, store, principal, proxyType, proxyPrincipal):
+ try:
+ (yield addProxy(rootResource, directory, store, principal, proxyType, proxyPrincipal))
+ print("Added %s as a %s proxy for %s" % (
+ prettyPrincipal(proxyPrincipal), proxyType,
+ prettyPrincipal(principal)))
+ except ProxyError, e:
+ print("Error:", e)
+ except ProxyWarning, e:
+ print(e)
+
+ at inlineCallbacks
+def action_removeProxyPrincipal(rootResource, directory, store, principal, proxyPrincipal, **kwargs):
+ try:
+ removed = (yield removeProxy(rootResource, directory, store,
+ principal, proxyPrincipal, **kwargs))
+ if removed:
+ print("Removed %s as a proxy for %s" % (
+ prettyPrincipal(proxyPrincipal),
+ prettyPrincipal(principal)))
+ except ProxyError, e:
+ print("Error:", e)
+ except ProxyWarning, e:
+ print(e)
+
+ at inlineCallbacks
+def addProxy(rootResource, directory, store, principal, proxyType, proxyPrincipal):
+ proxyURL = proxyPrincipal.url()
+
+ subPrincipal = proxySubprincipal(principal, proxyType)
+ if subPrincipal is None:
+ raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType,
+ prettyPrincipal(principal)))
+
+ membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
+
+ for memberURL in membersProperty.children:
+ if str(memberURL) == proxyURL:
+ raise ProxyWarning("%s is already a %s proxy for %s" % (
+ prettyPrincipal(proxyPrincipal), proxyType,
+ prettyPrincipal(principal)))
+
+ else:
+ memberURLs = list(membersProperty.children)
+ memberURLs.append(davxml.HRef(proxyURL))
+ membersProperty = davxml.GroupMemberSet(*memberURLs)
+ (yield subPrincipal.writeProperty(membersProperty, None))
+
+ proxyTypes = ["read", "write"]
+ proxyTypes.remove(proxyType)
+
+ (yield action_removeProxyPrincipal(rootResource, directory, store,
+ principal, proxyPrincipal, proxyTypes=proxyTypes))
+
+ yield scheduleNextGroupCachingUpdate(store, 0)
+
+ at inlineCallbacks
+def removeProxy(rootResource, directory, store, principal, proxyPrincipal, **kwargs):
+ removed = False
+ proxyTypes = kwargs.get("proxyTypes", ("read", "write"))
+ for proxyType in proxyTypes:
+ proxyURL = proxyPrincipal.url()
+
+ subPrincipal = proxySubprincipal(principal, proxyType)
+ if subPrincipal is None:
+ raise ProxyError("Unable to edit %s proxies for %s\n" % (proxyType,
+ prettyPrincipal(principal)))
+
+ membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
+
+ memberURLs = [
+ m for m in membersProperty.children
+ if str(m) != proxyURL
+ ]
+
+ if len(memberURLs) == len(membersProperty.children):
+ # No change
+ continue
+ else:
+ removed = True
+
+ membersProperty = davxml.GroupMemberSet(*memberURLs)
+ (yield subPrincipal.writeProperty(membersProperty, None))
+
+ if removed:
+ yield scheduleNextGroupCachingUpdate(store, 0)
+ returnValue(removed)
+
+
+
+def prettyPrincipal(principal):
+ record = principal.record
+ return "\"%s\" (%s:%s)" % (record.fullName, record.recordType,
+ record.shortNames[0])
+
+class ProxyError(Exception):
+ """
+ Raised when proxy assignments cannot be performed
+ """
+
+class ProxyWarning(Exception):
+ """
+ Raised for harmless proxy assignment failures such as trying to add a
+ duplicate or remove a non-existent assignment.
+ """
+
+
+
Modified: CalendarServer/trunk/calendarserver/webadmin/resource.py
===================================================================
--- CalendarServer/trunk/calendarserver/webadmin/resource.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/calendarserver/webadmin/resource.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -28,7 +28,7 @@
import operator
import urlparse
-from calendarserver.tools.principals import (
+from calendarserver.tools.util import (
principalForPrincipalID, proxySubprincipal, action_addProxyPrincipal,
action_removeProxyPrincipal
)
@@ -569,9 +569,10 @@
Web administration HTTP resource.
"""
- def __init__(self, path, root, directory, principalCollections=()):
+ def __init__(self, path, root, directory, store, principalCollections=()):
self.root = root
self.directory = directory
+ self.store = store
super(WebAdminResource, self).__init__(path,
principalCollections=principalCollections)
@@ -642,16 +643,18 @@
# Update the proxies if specified.
for proxyId in removeProxies:
proxy = self.getResourceById(request, proxyId)
- (yield action_removeProxyPrincipal(principal, proxy,
- proxyTypes=["read", "write"]))
+ (yield action_removeProxyPrincipal(self.root, self.directory, self.store,
+ principal, proxy, proxyTypes=["read", "write"]))
for proxyId in makeReadProxies:
proxy = self.getResourceById(request, proxyId)
- (yield action_addProxyPrincipal(principal, "read", proxy))
+ (yield action_addProxyPrincipal(self.root, self.directory, self.store,
+ principal, "read", proxy))
for proxyId in makeWriteProxies:
proxy = self.getResourceById(request, proxyId)
- (yield action_addProxyPrincipal(principal, "write", proxy))
+ (yield action_addProxyPrincipal(self.root, self.directory, self.store,
+ principal, "write", proxy))
@inlineCallbacks
Modified: CalendarServer/trunk/conf/auth/augments-test.xml
===================================================================
--- CalendarServer/trunk/conf/auth/augments-test.xml 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/conf/auth/augments-test.xml 2013-04-03 01:26:13 UTC (rev 10982)
@@ -1,21 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2009-2013 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- -->
-
<!DOCTYPE augments SYSTEM "augments.dtd">
<augments>
@@ -111,4 +94,46 @@
<enable-addressbook>false</enable-addressbook>
<auto-schedule>false</auto-schedule>
</record>
+ <record>
+ <uid>03DFF660-8BCC-4198-8588-DD77F776F518</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>true</auto-schedule>
+ </record>
+ <record>
+ <uid>80689D41-DAF8-4189-909C-DB017B271892</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>true</auto-schedule>
+ </record>
+ <record>
+ <uid>C38BEE7A-36EE-478C-9DCB-CBF4612AFE65</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>true</auto-schedule>
+ <auto-schedule-mode>default</auto-schedule-mode>
+ <auto-accept-group>group01</auto-accept-group>
+ </record>
+ <record>
+ <uid>CCE95217-A57B-481A-AC3D-FEC9AB6CE3A9</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>true</auto-schedule>
+ </record>
+ <record>
+ <uid>0CE0BF31-5F9E-4801-A489-8C70CF287F5F</uid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <enable-addressbook>true</enable-addressbook>
+ <enable-login>true</enable-login>
+ <auto-schedule>true</auto-schedule>
+ </record>
</augments>
Modified: CalendarServer/trunk/conf/auth/resources-test.xml
===================================================================
--- CalendarServer/trunk/conf/auth/resources-test.xml 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/conf/auth/resources-test.xml 2013-04-03 01:26:13 UTC (rev 10982)
@@ -1,94 +1,227 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2013 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- -->
-
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-
<accounts realm="Test Realm">
- <location repeat="10">
- <uid>location%02d</uid>
- <guid>location%02d</guid>
- <password>location%02d</password>
- <name>Room %02d</name>
+ <location>
+ <uid>jupiter</uid>
+ <guid>jupiter</guid>
+ <name>Jupiter Conference Room, Building 2, 1st Floor</name>
</location>
- <resource repeat="20">
- <uid>resource%02d</uid>
- <guid>resource%02d</guid>
- <password>resource%02d</password>
- <name>Resource %02d</name>
- </resource>
<location>
+ <uid>uranus</uid>
+ <guid>uranus</guid>
+ <name>Uranus Conference Room, Building 3, 1st Floor</name>
+ </location>
+ <location>
+ <uid>morgensroom</uid>
+ <guid>03DFF660-8BCC-4198-8588-DD77F776F518</guid>
+ <name>Morgen's Room</name>
+ </location>
+ <location>
<uid>mercury</uid>
<guid>mercury</guid>
- <password>test</password>
<name>Mercury Conference Room, Building 1, 2nd Floor</name>
</location>
<location>
- <uid>venus</uid>
- <guid>venus</guid>
- <password>test</password>
- <name>Venus Conference Room, Building 1, 2nd Floor</name>
+ <uid>location09</uid>
+ <guid>location09</guid>
+ <name>Room 09</name>
</location>
<location>
- <uid>Earth</uid>
- <guid>Earth</guid>
- <password>test</password>
- <name>Earth Conference Room, Building 1, 1st Floor</name>
+ <uid>location08</uid>
+ <guid>location08</guid>
+ <name>Room 08</name>
</location>
<location>
+ <uid>location07</uid>
+ <guid>location07</guid>
+ <name>Room 07</name>
+ </location>
+ <location>
+ <uid>location06</uid>
+ <guid>location06</guid>
+ <name>Room 06</name>
+ </location>
+ <location>
+ <uid>location05</uid>
+ <guid>location05</guid>
+ <name>Room 05</name>
+ </location>
+ <location>
+ <uid>location04</uid>
+ <guid>location04</guid>
+ <name>Room 04</name>
+ </location>
+ <location>
+ <uid>location03</uid>
+ <guid>location03</guid>
+ <name>Room 03</name>
+ </location>
+ <location>
+ <uid>location02</uid>
+ <guid>location02</guid>
+ <name>Room 02</name>
+ </location>
+ <location>
+ <uid>location01</uid>
+ <guid>location01</guid>
+ <name>Room 01</name>
+ </location>
+ <location>
+ <uid>delegatedroom</uid>
+ <guid>delegatedroom</guid>
+ <name>Delegated Conference Room</name>
+ </location>
+ <location>
<uid>mars</uid>
<guid>redplanet</guid>
- <password>test</password>
<name>Mars Conference Room, Building 1, 1st Floor</name>
</location>
<location>
- <uid>jupiter</uid>
- <guid>jupiter</guid>
- <password>test</password>
- <name>Jupiter Conference Room, Building 2, 1st Floor</name>
+ <uid>sharissroom</uid>
+ <guid>80689D41-DAF8-4189-909C-DB017B271892</guid>
+ <name>Shari's Room</name>
</location>
<location>
- <uid>neptune</uid>
- <guid>neptune</guid>
- <password>test</password>
- <name>Neptune Conference Room, Building 2, 1st Floor</name>
- </location>
- <location>
<uid>pluto</uid>
<guid>pluto</guid>
- <password>test</password>
<name>Pluto Conference Room, Building 2, 1st Floor</name>
</location>
<location>
<uid>saturn</uid>
<guid>saturn</guid>
- <password>test</password>
<name>Saturn Conference Room, Building 2, 1st Floor</name>
</location>
<location>
- <uid>uranus</uid>
- <guid>uranus</guid>
- <password>test</password>
- <name>Uranus Conference Room, Building 3, 1st Floor</name>
+ <uid>location10</uid>
+ <guid>location10</guid>
+ <name>Room 10</name>
</location>
<location>
- <uid>delegatedroom</uid>
- <guid>delegatedroom</guid>
- <password>delegatedroom</password>
- <name>Delegated Conference Room</name>
+ <uid>neptune</uid>
+ <guid>neptune</guid>
+ <name>Neptune Conference Room, Building 2, 1st Floor</name>
</location>
+ <location>
+ <uid>Earth</uid>
+ <guid>Earth</guid>
+ <name>Earth Conference Room, Building 1, 1st Floor</name>
+ </location>
+ <location>
+ <uid>venus</uid>
+ <guid>venus</guid>
+ <name>Venus Conference Room, Building 1, 2nd Floor</name>
+ </location>
+ <resource>
+ <uid>sharisotherresource</uid>
+ <guid>CCE95217-A57B-481A-AC3D-FEC9AB6CE3A9</guid>
+ <name>Shari's Other Resource</name>
+ </resource>
+ <resource>
+ <uid>resource15</uid>
+ <guid>resource15</guid>
+ <name>Resource 15</name>
+ </resource>
+ <resource>
+ <uid>resource14</uid>
+ <guid>resource14</guid>
+ <name>Resource 14</name>
+ </resource>
+ <resource>
+ <uid>resource17</uid>
+ <guid>resource17</guid>
+ <name>Resource 17</name>
+ </resource>
+ <resource>
+ <uid>resource16</uid>
+ <guid>resource16</guid>
+ <name>Resource 16</name>
+ </resource>
+ <resource>
+ <uid>resource11</uid>
+ <guid>resource11</guid>
+ <name>Resource 11</name>
+ </resource>
+ <resource>
+ <uid>resource10</uid>
+ <guid>resource10</guid>
+ <name>Resource 10</name>
+ </resource>
+ <resource>
+ <uid>resource13</uid>
+ <guid>resource13</guid>
+ <name>Resource 13</name>
+ </resource>
+ <resource>
+ <uid>resource12</uid>
+ <guid>resource12</guid>
+ <name>Resource 12</name>
+ </resource>
+ <resource>
+ <uid>resource19</uid>
+ <guid>resource19</guid>
+ <name>Resource 19</name>
+ </resource>
+ <resource>
+ <uid>resource18</uid>
+ <guid>resource18</guid>
+ <name>Resource 18</name>
+ </resource>
+ <resource>
+ <uid>sharisresource</uid>
+ <guid>C38BEE7A-36EE-478C-9DCB-CBF4612AFE65</guid>
+ <name>Shari's Resource</name>
+ </resource>
+ <resource>
+ <uid>resource20</uid>
+ <guid>resource20</guid>
+ <name>Resource 20</name>
+ </resource>
+ <resource>
+ <uid>resource06</uid>
+ <guid>resource06</guid>
+ <name>Resource 06</name>
+ </resource>
+ <resource>
+ <uid>resource07</uid>
+ <guid>resource07</guid>
+ <name>Resource 07</name>
+ </resource>
+ <resource>
+ <uid>resource04</uid>
+ <guid>resource04</guid>
+ <name>Resource 04</name>
+ </resource>
+ <resource>
+ <uid>resource05</uid>
+ <guid>resource05</guid>
+ <name>Resource 05</name>
+ </resource>
+ <resource>
+ <uid>resource02</uid>
+ <guid>resource02</guid>
+ <name>Resource 02</name>
+ </resource>
+ <resource>
+ <uid>resource03</uid>
+ <guid>resource03</guid>
+ <name>Resource 03</name>
+ </resource>
+ <resource>
+ <uid>resource01</uid>
+ <guid>resource01</guid>
+ <name>Resource 01</name>
+ </resource>
+ <resource>
+ <uid>sharisotherresource1</uid>
+ <guid>0CE0BF31-5F9E-4801-A489-8C70CF287F5F</guid>
+ <name>Shari's Other Resource1</name>
+ </resource>
+ <resource>
+ <uid>resource08</uid>
+ <guid>resource08</guid>
+ <name>Resource 08</name>
+ </resource>
+ <resource>
+ <uid>resource09</uid>
+ <guid>resource09</guid>
+ <name>Resource 09</name>
+ </resource>
</accounts>
Property changes on: CalendarServer/trunk/conf/auth/resources-test.xml
___________________________________________________________________
Added: svn:executable
+ *
Modified: CalendarServer/trunk/twext/enterprise/queue.py
===================================================================
--- CalendarServer/trunk/twext/enterprise/queue.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/twext/enterprise/queue.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -87,7 +87,7 @@
from twisted.application.service import MultiService
from twisted.internet.protocol import Factory
from twisted.internet.defer import (
- inlineCallbacks, returnValue, Deferred, passthru
+ inlineCallbacks, returnValue, Deferred, passthru, succeed
)
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.protocols.amp import AMP, Command, Integer, Argument, String
@@ -865,6 +865,9 @@
+
+
+
class WorkerFactory(Factory, object):
"""
Factory, to be used as the client to connect from the worker to the
@@ -1446,4 +1449,41 @@
"""
Choose to perform the work locally.
"""
- return LocalPerformer(self.txnFactory)
\ No newline at end of file
+ return LocalPerformer(self.txnFactory)
+
+
+
+class NonPerformer(object):
+ """
+ Implementor of C{performWork} that doesn't actual perform any work. This
+ is used in the case where you want to be able to enqueue work for someone
+ else to do, but not take on any work yourself (such as a command line tool).
+ """
+ implements(_IWorkPerformer)
+
+ def performWork(self, table, workID):
+ """
+ Don't perform work.
+ """
+ return succeed(None)
+
+
+class NonPerformingQueuer(_BaseQueuer):
+ """
+ When work is enqueued with this queuer, it is never executed locally.
+ It's expected that the polling machinery will find the work and perform it.
+ """
+ implements(IQueuer)
+
+ def __init__(self, reactor=None):
+ super(NonPerformingQueuer, self).__init__()
+ if reactor is None:
+ from twisted.internet import reactor
+ self.reactor = reactor
+
+
+ def choosePerformer(self):
+ """
+ Choose to perform the work locally.
+ """
+ return NonPerformer()
\ No newline at end of file
Modified: CalendarServer/trunk/twext/enterprise/test/test_queue.py
===================================================================
--- CalendarServer/trunk/twext/enterprise/test/test_queue.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/twext/enterprise/test/test_queue.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -54,7 +54,7 @@
from zope.interface.verify import verifyObject
from twisted.test.proto_helpers import StringTransport
-from twext.enterprise.queue import _BaseQueuer
+from twext.enterprise.queue import _BaseQueuer, NonPerformingQueuer
import twext.enterprise.queue
class Clock(_Clock):
@@ -654,3 +654,14 @@
queuer.enqueueWork(None, None)
self.assertNotEqual(self.proposal, None)
+
+class NonPerformingQueuerTests(TestCase):
+
+ @inlineCallbacks
+ def test_choosePerformer(self):
+ queuer = NonPerformingQueuer()
+ performer = queuer.choosePerformer()
+ result = (yield performer.performWork(None, None))
+ self.assertEquals(result, None)
+
+
Modified: CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -594,7 +594,8 @@
@param principalUID: the UID of the principal to remove.
"""
-
+ # FIXME: This method doesn't appear to be used anywhere. Still needed?
+
if delay:
# We are going to remove the principal only after <delay> seconds
# has passed since we first chose to remove it, to protect against
Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -49,7 +49,6 @@
from twext.python.log import Logger, LoggingMixIn
from twistedcaldav.config import config
-from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
from twistedcaldav.directory.idirectory import IDirectoryService, IDirectoryRecord
from twistedcaldav.directory.util import uuidFromName, normalizeUUID
@@ -935,7 +934,7 @@
# Delete all other work items
yield Delete(From=self.table, Where=None).on(self.transaction)
- groupCacher = self.transaction._groupCacher
+ groupCacher = getattr(self.transaction, "_groupCacher", None)
if groupCacher is not None:
try:
yield groupCacher.updateCache()
@@ -947,6 +946,12 @@
log.debug("Scheduling next group cacher update: %s" % (notBefore,))
yield self.transaction.enqueue(GroupCacherPollingWork,
notBefore=notBefore)
+ else:
+ notBefore = (datetime.datetime.utcnow() +
+ datetime.timedelta(seconds=10))
+ log.debug("Rescheduling group cacher update: %s" % (notBefore,))
+ yield self.transaction.enqueue(GroupCacherPollingWork,
+ notBefore=notBefore)
@inlineCallbacks
Modified: CalendarServer/trunk/twistedcaldav/scheduling/imip/test/test_inbound.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/imip/test/test_inbound.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/twistedcaldav/scheduling/imip/test/test_inbound.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -15,6 +15,7 @@
##
+from twistedcaldav.test.util import TestCase
import email
from twisted.internet.defer import inlineCallbacks
from twisted.python.modules import getModule
@@ -24,7 +25,6 @@
from twistedcaldav.scheduling.imip.inbound import injectMessage
from twistedcaldav.scheduling.imip.inbound import IMIPReplyWork
from twistedcaldav.scheduling.itip import iTIPRequestStatus
-from twistedcaldav.test.util import TestCase
from twistedcaldav.test.util import xmlFile
from txdav.common.datastore.test.util import buildStore
from calendarserver.tap.util import getRootResource
Modified: CalendarServer/trunk/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/util.py 2013-04-03 01:26:07 UTC (rev 10981)
+++ CalendarServer/trunk/twistedcaldav/test/util.py 2013-04-03 01:26:13 UTC (rev 10982)
@@ -19,7 +19,7 @@
import os
import xattr
-from calendarserver.provision.root import RootResource
+from twistedcaldav.stdconfig import config
from twisted.python.failure import Failure
from twisted.internet.base import DelayedCall
@@ -34,7 +34,6 @@
from twistedcaldav import memcacher
from twistedcaldav.bind import doBind
-from twistedcaldav.config import config
from twistedcaldav.directory import augment
from twistedcaldav.directory.addressbook import DirectoryAddressBookHomeProvisioningResource
from twistedcaldav.directory.calendar import (
@@ -48,6 +47,8 @@
from txdav.common.datastore.test.util import deriveQuota
from txdav.common.datastore.file import CommonDataStore
+from calendarserver.provision.root import RootResource
+
from twext.python.log import Logger
log = Logger()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130402/e562e799/attachment-0001.html>
More information about the calendarserver-changes
mailing list