[CalendarServer-changes] [4220] CalendarServer/trunk/calendarserver/tools

source_changes at macosforge.org source_changes at macosforge.org
Fri May 8 18:03:14 PDT 2009


Revision: 4220
          http://trac.macosforge.org/projects/calendarserver/changeset/4220
Author:   wsanchez at apple.com
Date:     2009-05-08 18:03:13 -0700 (Fri, 08 May 2009)
Log Message:
-----------
Rework the command line UI for calendarserver_manage_principals.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tools/export.py
    CalendarServer/trunk/calendarserver/tools/migrate.py
    CalendarServer/trunk/calendarserver/tools/principals.py
    CalendarServer/trunk/calendarserver/tools/util.py
    CalendarServer/trunk/calendarserver/tools/warmup.py

Modified: CalendarServer/trunk/calendarserver/tools/export.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/export.py	2009-05-09 00:29:42 UTC (rev 4219)
+++ CalendarServer/trunk/calendarserver/tools/export.py	2009-05-09 01:03:13 UTC (rev 4220)
@@ -36,6 +36,7 @@
 from getopt import getopt, GetoptError
 from os.path import dirname, abspath
 
+from twistedcaldav.config import ConfigurationError
 from twistedcaldav.ical import Component as iComponent, Property as iProperty
 from twistedcaldav.ical import iCalendarProductID
 from twistedcaldav.resource import isCalendarCollectionResource
@@ -76,9 +77,9 @@
     try:
         (optargs, args) = getopt(
             sys.argv[1:], "hf:o:c:H:r:u:", [
+                "help",
                 "config=",
                 "output=",
-                "help",
                 "collection=", "home=", "record=", "user=",
             ],
         )
@@ -144,12 +145,15 @@
         usage("Too many arguments: %s" % (" ".join(args),))
 
     if records:
-        config = loadConfig(configFileName)
-        directory = getDirectory()
+        try:
+            config = loadConfig(configFileName)
+        except ConfigurationError, e:
+            sys.stdout.write("%s\n" % (e,))
+            sys.exit(1)
 
     for record in records:
         recordType, shortName = record
-        calendarHome = directory.calendarHomeForShortName(recordType, shortName)
+        calendarHome = config.directory.calendarHomeForShortName(recordType, shortName)
         if not calendarHome:
             sys.stderr.write("No calendar home found for record: (%s)%s\n" % (recordType, shortName))
             sys.exit(1)

Modified: CalendarServer/trunk/calendarserver/tools/migrate.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/migrate.py	2009-05-09 00:29:42 UTC (rev 4219)
+++ CalendarServer/trunk/calendarserver/tools/migrate.py	2009-05-09 01:03:13 UTC (rev 4220)
@@ -29,7 +29,9 @@
 import sys
 from getopt import getopt, GetoptError
 
+from twistedcaldav.config import ConfigurationError
 from twistedcaldav.upgrade import upgradeData
+
 from calendarserver.tools.util import loadConfig
 
 def usage(e=None):
@@ -75,7 +77,11 @@
     if args:
         usage("Too many arguments: %s" % (" ".join(args),))
 
-    config = loadConfig(configFileName)
+    try:
+        config = loadConfig(configFileName)
+    except ConfigurationError, e:
+        sys.stdout.write("%s\n" % (e,))
+        sys.exit(1)
 
     profiling = False
 

Modified: CalendarServer/trunk/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/principals.py	2009-05-09 00:29:42 UTC (rev 4219)
+++ CalendarServer/trunk/calendarserver/tools/principals.py	2009-05-09 01:03:13 UTC (rev 4220)
@@ -21,14 +21,18 @@
 import itertools
 import operator
 from getopt import getopt, GetoptError
+from uuid import UUID
 
 from twisted.python import log
 from twisted.python.reflect import namedClass
 from twisted.internet import reactor
+from twisted.internet.defer import Deferred
 from twisted.internet.address import IPv4Address
 from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twisted.web2.dav import davxml
 
+from twext.web2.dav.davxml import sname2qname, qname2sname
+
 from twistedcaldav import caldavxml
 from twistedcaldav import memcachepool
 from twistedcaldav.config import config, defaultConfigFile
@@ -39,7 +43,7 @@
 from twistedcaldav.notify import installNotificationClient
 from twistedcaldav.static import CalendarHomeProvisioningFile
 
-from calendarserver.tools.util import UsageError
+from calendarserver.tools.util import UsageError, booleanArgument
 from calendarserver.tools.util import loadConfig, getDirectory, dummyDirectoryRecord
 from calendarserver.provision.root import RootResource
 
@@ -49,21 +53,30 @@
         print ""
 
     name = os.path.basename(sys.argv[0])
-    print "usage: %s [options]" % (name,)
+    print "usage: %s [options] actions principal [principal ...]" % (name,)
     print ""
+    print "  Performs the given actions against the giving principals."
+    print ""
+    print "  Principals are identified by one of the following:"
+    print "    Type and shortname (eg.: users:wsanchez)"
+   #print "    A principal path (eg.: /principals/users/wsanchez/)"
+    print "    A GUID (eg.: E415DBA7-40B5-49F5-A7CC-ACC81E4DEC79)"
+    print ""
     print "options:"
     print "  -h --help: print this help and exit"
     print "  -f --config <path>: Specify caldavd.plist configuration path"
-    print "  --resource <prinicpal-path>: path of the resource to use"
-    print "  --search <search-string>: search for matching resources"
-    print "  --read-property: namespace-qualified DAV property to read, e.g. 'DAV:#group-member-set'"
-    print "  --list-read-delegates: list delegates with read-only access to the current resource"
-    print "  --list-write-delegates: list delegates with read-write access to the current resource"
-    print "  --add-read-delegate <prinicpal-path>: add argument as a read-only delegate to the current resource"
-    print "  --add-write-delegate <prinicpal-path>: add argument as a read-write delegate to the current resource"
-    print "  --remove-delegate <prinicpal-path>: strip argument of delegate status for the current resource"
-    print "  --set-auto-schedule [true|false] : determines whether the current resource auto-accepts invitations"
-    print "  --get-auto-schedule : returns the current resource's auto-schedule state"
+    print ""
+    print "actions:"
+   #print "  --search <search-string>: search for matching resources"
+    print "  -P, --read-property: read DAV property to read (eg.: {DAV:}group-member-set)"
+    print "  --list-read-proxies: list proxies with read-only access"
+    print "  --list-write-proxies: list proxies with read-write access"
+    print "  --list-proxies: list all proxies"
+    print "  --add-read-proxy=principal: add a read-only proxy"
+    print "  --add-write-proxy=principal: add a read-write proxy"
+    print "  --remove-proxy=principal: remove a proxy"
+    print "  --set-auto-schedule={true|false}: set auto-accept state"
+    print "  --get-auto-schedule: read auto-schedule state"
 
     if e:
         sys.exit(64)
@@ -73,17 +86,17 @@
 def main():
     try:
         (optargs, args) = getopt(
-            sys.argv[1:], "hf:r:s:", [
+            sys.argv[1:], "hf:P:", [
+                "help",
                 "config=",
-                "help",
-                "resource=",
-                "search=",
+               #"search=",
                 "read-property=",
-                "list-read-delegates",
-                "list-write-delegates",
-                "add-read-delegate=",
-                "add-write-delegate=",
-                "remove-delegate=",
+                "list-read-proxies",
+                "list-write-proxies",
+                "list-proxies",
+                "add-read-proxy=",
+                "add-write-proxy=",
+                "remove-proxy=",
                 "set-auto-schedule=",
                 "get-auto-schedule",
             ],
@@ -91,16 +104,14 @@
     except GetoptError, e:
         usage(e)
 
-    if args:
-        usage("Too many arguments: %s" % (" ".join(args),))
+    if not args:
+        usage("No principals specified.")
 
-    logFileName = "/dev/stdout"
-    observer = log.FileLogObserver(open(logFileName, "a"))
-    log.addObserver(observer.emit)
-
-    # First pass through the args:
-
+    #
+    # Get configuration
+    #
     configFileName = None
+    actions = []
 
     for opt, arg in optargs:
         if opt in ("-h", "--help"):
@@ -109,248 +120,352 @@
         elif opt in ("-f", "--config"):
             configFileName = arg
 
-    loadConfig(configFileName)
-    directory, root = setup()
-    root = ResourceWrapper(root)
+        elif opt in ("-P", "--read-property"):
+            try:
+                qname = sname2qname(arg)
+            except ValueError, e:
+                abort(e)
+            actions.append((action_readProperty, qname))
 
-    reactor.callLater(0, run, directory, root, optargs)
-    reactor.run()
+        elif opt in ("", "--list-read-proxies"):
+            actions.append((action_listProxies, "read"))
 
- at inlineCallbacks
-def run(directory, root, optargs):
+        elif opt in ("", "--list-write-proxies"):
+            actions.append((action_listProxies, "write"))
 
-    print ""
+        elif opt in ("-L", "--list-proxies"):
+            actions.append((action_listProxies, "read", "write"))
 
-    resource = None
+        elif opt in ("--add-read-proxy", "--add-write-proxy"):
+            if "read" in opt:
+                proxyType = "read"
+            elif "write" in opt:
+                proxyType = "write"
+            else:
+                raise AssertionError("Unknown proxy type")
 
-    for opt, arg in optargs:
+            try:
+                principalForPrincipalID(arg, checkOnly=True)
+            except ValueError, e:
+                abort(e)
 
-        if opt in ("-r", "--resource",):
-            resource = root.lookupResource(arg)
-            if resource is not None:
-                print "Found resource %s at %s" % (resource.resource, arg)
-            else:
-                abort("Could not find resource at %s" % (arg,))
+            actions.append((action_addProxy, proxyType, arg))
 
-        elif opt in ("-s", "--search",):
-            fields = []
-            for fieldName in ("fullName", "firstName", "lastName",
-                "emailAddresses"):
-                fields.append((fieldName, arg, True, "contains"))
+        elif opt in ("", "--remove-proxy"):
+            try:
+                principalForPrincipalID(arg, checkOnly=True)
+            except ValueError, e:
+                abort(e)
 
-            records = list((yield directory.recordsMatchingFields(fields)))
-            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 record.guid
-                    print "   Record names: %s" % (", ".join(record.shortNames),)
-                    if record.authIDs:
-                        print "   Auth IDs: %s" % (", ".join(record.authIDs),)
-                    if record.emailAddresses:
-                        print "   Emails: %s" % (", ".join(record.emailAddresses),)
-            else:
-                print "No matches found"
+            actions.append((action_removeProxy, arg))
 
-        elif opt in ("--read-property",):
-            if resource is None: abort("No current resource.")
-
+        elif opt in ("", "--set-auto-schedule"):
             try:
-                namespace, name = arg.split("#")
-            except Exception, e:
-                abort("Can't parse --propertyToRead: %s" % (arg,))
+                autoSchedule = booleanArgument(arg)
+            except ValueError, e:
+                abort(e)
 
-            result = (yield resource.readProperty((namespace, name)))
-            print result.toxml()
+            actions.append((action_setAutoSchedule, autoSchedule))
 
-        elif opt in ("--list-write-delegates", "--list-read-delegates"):
-            if resource is None: abort("No current resource.")
+        elif opt in ("", "--get-auto-schedule"):
+            actions.append((action_getAutoSchedule,))
 
-            permission = "write" if "write" in opt else "read"
-            print "Delegates (%s) for %s:" % (permission, resource.resource)
-            paths = (yield resource.getDelegates(permission))
-            for path in paths:
-                delegate = root.getChild(path)
-                print delegate.resource
+        else:
+            raise NotImplementedError(opt)
 
-        elif opt in ("--add-write-delegate", "--add-read-delegate"):
-            if resource is None: abort("No current resource.")
+    #
+    # Get configuration
+    #
+    try:
+        loadConfig(configFileName)
+    except ConfigurationError, e:
+        abort(e)
 
-            delegate = root.lookupResource(arg)
-            if delegate is None:
-                abort("No delegate found for %s" % (arg,))
+    #
+    # Do a quick sanity check that arguments look like principal
+    # identifiers.
+    #
+    for arg in args:
+        try:
+            principalForPrincipalID(arg, checkOnly=True)
+        except ValueError, e:
+            abort(e)
 
-            permission = "write" if "write" in opt else "read"
-            result = (yield resource.addDelegate(delegate, permission))
+    #
+    # Send logging output to stdout
+    #
+    setLogLevelForNamespace(None, "warn")
+    logFileName = "/dev/stdout"
+    observer = log.FileLogObserver(open(logFileName, "a"))
+    log.addObserver(observer.emit)
 
-        elif opt == "--remove-delegate":
-            if resource is None: abort("No current resource.")
+    #
+    # Start the reactor
+    #
+    reactor.callLater(0, run, args, actions)
+    reactor.run()
 
-            delegate = root.lookupResource(arg)
-            if delegate is None:
-                abort("No delegate found for %s" % (arg,))
+ at inlineCallbacks
+def run(principalIDs, actions):
+    try:
+        #
+        # Connect to memcached, notifications
+        #
+        if config.Memcached.ClientEnabled:
+            memcachepool.installPool(
+                IPv4Address(
+                    "TCP",
+                    config.Memcached.BindAddress,
+                    config.Memcached.Port,
+                ),
+                config.Memcached.MaxClients
+            )
+        if config.Notifications.Enabled:
+            installNotificationClient(
+                config.Notifications.InternalNotificationHost,
+                config.Notifications.InternalNotificationPort,
+            )
 
-            result = (yield resource.removeDelegate(delegate, "read"))
-            result = (yield resource.removeDelegate(delegate, "write"))
+        #
+        # Wire up the resource hierarchy
+        #
+        principalCollection = config.directory.getPrincipalCollection()
+        root = RootResource(
+            config.DocumentRoot,
+            principalCollections=(principalCollection,),
+        )
+        root.putChild("principals", principalCollection)
+        calendarCollection = CalendarHomeProvisioningFile(
+            os.path.join(config.DocumentRoot, "calendars"),
+            config.directory, "/calendars/",
+        )
+        root.putChild("calendars", calendarCollection)
 
-        elif opt == "--set-auto-schedule":
-            if resource is None: abort("No current resource.")
+        #
+        # Wrap root resource
+        #
+        # FIXME: not a fan -wsanchez
+        #root = ResourceWrapper(root)
 
-            result = (yield resource.setAutoSchedule(arg.lower() in ("true", "1")))
+        for principalID in principalIDs:
+            # Resolve the given principal IDs to principals
+            try:
+                principal = principalForPrincipalID(principalID)
+            except ValueError:
+                principal = None
 
-        elif opt == "--get-auto-schedule":
-            if resource is None: abort("No current resource.")
+            if principal is None:
+                sys.stderr.write("Invalid principal ID: %s\n" % (principalID,))
+                continue
 
-            result = (yield resource.getAutoSchedule())
-            print "Auto-Schedule: %s" % ("True" if result else "False",)
+            # Performs requested actions
+            for action in actions:
+                (yield action[0](principal, *action[1:]))
+                print ""
 
-    print ""
+    finally:
+        #
+        # Stop the reactor
+        #
+        reactor.stop()
 
-    # reactor.callLater(0, reactor.stop)
-    reactor.stop()
+def principalForPrincipalID(principalID, checkOnly=False):
+    if principalID.startswith("/"):
+        raise ValueError("Can't resolve paths yet")
 
-class ResourceWrapper(object):
+        if checkOnly:
+            return None
 
-    def __init__(self, resource):
-        self.resource = resource
+    if principalID.startswith("("):
+        try:
+            i = principalID.index(")")
 
-    def readProperty(self, prop):
-        return self.resource.readProperty(prop, FakeRequest())
+            if checkOnly:
+                return None
 
-    def writeProperty(self, prop):
-        return self.resource.writeProperty(prop, FakeRequest())
+            recordType = principalID[1:i]
+            shortName = principalID[i+1:]
 
-    def lookupResource(self, specifier):
-        # For now, support GUID lookup
-        return self.getChild("principals/__uids__/%s" % (specifier,))
+            if not recordType or not shortName or "(" in recordType:
+                raise ValueError()
 
-    def getChild(self, path):
-        resource = self.resource
-        segments = path.strip("/").split("/")
-        for segment in segments:
-            resource = resource.getChild(segment)
-            if resource is None:
-                return None
-        return ResourceWrapper(resource)
+            return config.directory.principalCollection.principalForShortName(recordType, shortName)
 
-    @inlineCallbacks
-    def removeDelegate(self, delegate, permission):
-        subPrincipalName = "calendar-proxy-%s" % (permission,)
-        subPrincipal = self.getChild(subPrincipalName)
-        if subPrincipal is None:
-            abort("No proxy subprincipal found for %s" % (self.resource,))
+        except ValueError:
+            pass
 
-        namespace, name = davxml.dav_namespace, "group-member-set"
-        prop = (yield subPrincipal.readProperty((namespace, name)))
-        newChildren = []
-        for child in prop.children:
-            if str(child) != delegate.url():
-                newChildren.append(child)
+    if ":" in principalID:
+        if checkOnly:
+            return None
 
-        if len(prop.children) == len(newChildren):
-            # Nothing to do -- the delegate wasn't there
-            returnValue(False)
+        recordType, shortName = principalID.split(":", 1)
 
-        newProp = davxml.GroupMemberSet(*newChildren)
-        result = (yield subPrincipal.writeProperty(newProp))
-        returnValue(result)
+        return config.directory.principalCollection.principalForShortName(recordType, shortName)
 
-    @inlineCallbacks
-    def addDelegate(self, delegate, permission):
+    try:
+        guid = UUID(principalID)
 
-        opposite = "read" if permission == "write" else "write"
-        result = (yield self.removeDelegate(delegate, opposite))
+        if checkOnly:
+            return None
 
-        subPrincipalName = "calendar-proxy-%s" % (permission,)
-        subPrincipal = self.getChild(subPrincipalName)
+        return config.directory.principalCollection.principalForUID(guid)
+    except ValueError:
+        pass
+
+    raise ValueError("Invalid principal identifier: %s" % (principalID,))
+
+def proxySubprincipal(principal, proxyType):
+    return principal.getChild("calendar-proxy-" + proxyType)
+
+ at inlineCallbacks
+def action_readProperty(resource, qname):
+    property = (yield resource.readProperty(qname, None))
+    print "%r on %s:" % (qname2sname(qname), resource)
+    print ""
+    print property.toxml()
+
+ at inlineCallbacks
+def action_listProxies(principal, *proxyTypes):
+    for proxyType in proxyTypes:
+        subPrincipal = proxySubprincipal(principal, proxyType)
         if subPrincipal is None:
-            abort("No proxy subprincipal found for %s" % (self.resource,))
+            print "No %s proxies for %s" % (proxyType, principal)
+            continue
 
-        namespace, name = davxml.dav_namespace, "group-member-set"
-        prop = (yield subPrincipal.readProperty((namespace, name)))
-        for child in prop.children:
-            if str(child) == delegate.url():
-                # delegate is already in the group
-                break
+        membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
+
+        if membersProperty.children:
+            print "%s proxies for %s:" % (
+                {"read": "Read-only", "write": "Read/write"}[proxyType],
+                principal,
+            )
+            for member in membersProperty.children:
+                print " *", member
         else:
-            # delegate is not already in the group
-            newChildren = list(prop.children)
-            newChildren.append(davxml.HRef(delegate.url()))
-            newProp = davxml.GroupMemberSet(*newChildren)
-            result = (yield subPrincipal.writeProperty(newProp))
-            returnValue(result)
+            print "No %s proxies for %s" % (proxyType, principal)
 
-    @inlineCallbacks
-    def getDelegates(self, permission):
+ at inlineCallbacks
+def action_addProxy(principal, proxyType, *proxyIDs):
+    for proxyID in proxyIDs:
+        proxyPrincipal = principalForPrincipalID(proxyID)
+        proxyURL = proxyPrincipal.url()
 
-        subPrincipalName = "calendar-proxy-%s" % (permission,)
-        subPrincipal = self.getChild(subPrincipalName)
+        subPrincipal = proxySubprincipal(principal, proxyType)
         if subPrincipal is None:
-            abort("No proxy subprincipal found for %s" % (self.resource,))
+            sys.stderr.write("Unable to edit %s proxies for %s\n" % (proxyType, principal))
+            continue
 
-        namespace, name = davxml.dav_namespace, "group-member-set"
-        prop = (yield subPrincipal.readProperty((namespace, name)))
-        result = []
-        for child in prop.children:
-            result.append(str(child))
-        returnValue(result)
+        membersProperty = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
 
-    def setAutoSchedule(self, autoSchedule):
-        return self.resource.setAutoSchedule(autoSchedule)
+        for memberURL in membersProperty.children:
+            if str(memberURL) == proxyURL:
+                print "%s is already a %s proxy for %s" % (proxyPrincipal, proxyType, principal)
+                break
+        else:
+            memberURLs = list(membersProperty.children)
+            memberURLs.append(davxml.HRef(proxyURL))
+            membersProperty = davxml.GroupMemberSet(*memberURLs)
+            (yield subPrincipal.writeProperty(membersProperty, None))
+            print "Added %s as a %s proxy for %s" % (proxyPrincipal, proxyType, principal)
 
-    def getAutoSchedule(self):
-        return self.resource.getAutoSchedule()
+        proxyTypes = ["read", "write"]
+        proxyTypes.remove(proxyType)
 
-    def url(self):
-        return self.resource.url()
+        (yield action_removeProxy(principal, proxyID, proxyTypes=proxyTypes))
 
-class FakeRequest(object):
-    pass
+ at inlineCallbacks
+def action_removeProxy(principal, *proxyIDs, **kwargs):
+    proxyTypes = kwargs.get("proxyTypes", ("read", "write"))
 
-def abort(msg, errno=1):
-    print "ERROR:", msg
-    print "Exiting"
-    reactor.stop()
-    sys.exit(errno)
+    for proxyID in proxyIDs:
+        for proxyType in proxyTypes:
+            proxyPrincipal = principalForPrincipalID(proxyID)
+            proxyURL = proxyPrincipal.url()
 
-def setup():
-    setLogLevelForNamespace(None, "warn")
+            subPrincipal = proxySubprincipal(principal, proxyType)
+            if subPrincipal is None:
+                sys.stderr.write("Unable to edit %s proxies for %s\n" % (proxyType, principal))
+                continue
 
-    directory = getDirectory()
-    if config.Memcached["ClientEnabled"]:
-        memcachepool.installPool(
-            IPv4Address(
-                'TCP',
-                config.Memcached["BindAddress"],
-                config.Memcached["Port"]
-            ),
-            config.Memcached["MaxClients"]
-        )
-    if config.Notifications["Enabled"]:
-        installNotificationClient(
-            config.Notifications["InternalNotificationHost"],
-            config.Notifications["InternalNotificationPort"],
-        )
-    principalCollection = directory.getPrincipalCollection()
-    root = RootResource(
-        config.DocumentRoot,
-        principalCollections=(principalCollection,),
+            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
+
+            membersProperty = davxml.GroupMemberSet(*memberURLs)
+            (yield subPrincipal.writeProperty(membersProperty, None))
+            print "Removed %s as a %s proxy for %s" % (proxyPrincipal, proxyType, principal)
+
+ at inlineCallbacks
+def action_setAutoSchedule(principal, autoSchedule):
+    print "Setting auto-schedule to %s for %s" % (
+        { True: "true", False: "false" }[autoSchedule],
+        principal,
     )
-    root.putChild("principals", principalCollection)
-    calendarCollection = CalendarHomeProvisioningFile(
-        os.path.join(config.DocumentRoot, "calendars"),
-        directory, "/calendars/",
+    (yield principal.setAutoSchedule(autoSchedule))
+
+ at inlineCallbacks
+def action_getAutoSchedule(principal):
+    autoSchedule = (yield principal.getAutoSchedule())
+    print "Autoschedule for %s is %s" % (
+        principal,
+        { True: "true", False: "false" }[autoSchedule],
     )
-    root.putChild("calendars", calendarCollection)
 
-    return (directory, root)
+ at inlineCallbacks
+def _run(directory, root, optargs, principalIDs):
 
+    print ""
+
+    resource = None
+
+    for opt, arg in optargs:
+
+        if opt in ("-s", "--search",):
+            fields = []
+            for fieldName in ("fullName", "firstName", "lastName",
+                "emailAddresses"):
+                fields.append((fieldName, arg, True, "contains"))
+
+            records = list((yield config.directory.recordsMatchingFields(fields)))
+            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 record.guid
+                    print "   Record names: %s" % (", ".join(record.shortNames),)
+                    if record.authIDs:
+                        print "   Auth IDs: %s" % (", ".join(record.authIDs),)
+                    if record.emailAddresses:
+                        print "   Emails: %s" % (", ".join(record.emailAddresses),)
+            else:
+                print "No matches found"
+
+    print ""
+
+    # reactor.callLater(0, reactor.stop)
+    reactor.stop()
+
+def abort(msg, status=1):
+    sys.stdout.write("%s\n" % (msg,))
+    try:
+        reactor.stop()
+    except RuntimeError:
+        pass
+    sys.exit(status)
+
 if __name__ == "__main__":
     main()

Modified: CalendarServer/trunk/calendarserver/tools/util.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/util.py	2009-05-09 00:29:42 UTC (rev 4219)
+++ CalendarServer/trunk/calendarserver/tools/util.py	2009-05-09 01:03:13 UTC (rev 4220)
@@ -19,6 +19,7 @@
     "getDirectory",
     "dummyDirectoryRecord",
     "UsageError",
+    "booleanArgument",
 ]
 
 import sys
@@ -26,7 +27,7 @@
 
 from twisted.python.reflect import namedClass
 
-from twistedcaldav.config import config, defaultConfigFile
+from twistedcaldav.config import config, defaultConfigFile, ConfigurationError
 from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
 
 def loadConfig(configFileName):
@@ -34,10 +35,10 @@
         configFileName = defaultConfigFile
 
     if not os.path.isfile(configFileName):
-        sys.stderr.write("No config file: %s\n" % (configFileName,))
-        sys.exit(1)
+        raise ConfigurationError("No config file: %s" % (configFileName,))
 
     config.loadConfig(configFileName)
+    config.directory = getDirectory()
 
     return config
 
@@ -107,3 +108,11 @@
 
 class UsageError (StandardError):
     pass
+
+def booleanArgument(arg):
+    if   arg in ("true",  "yes", "yup",  "uh-huh", "1", "t", "y"):
+        return True
+    elif arg in ("false", "no",  "nope", "nuh-uh", "0", "f", "n"):
+        return False
+    else:
+        raise ValueError("Not a boolean: %s" % (arg,))

Modified: CalendarServer/trunk/calendarserver/tools/warmup.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/warmup.py	2009-05-09 00:29:42 UTC (rev 4219)
+++ CalendarServer/trunk/calendarserver/tools/warmup.py	2009-05-09 01:03:13 UTC (rev 4220)
@@ -32,6 +32,7 @@
 from getopt import getopt, GetoptError
 from os.path import dirname, abspath
 
+from twistedcaldav.config import ConfigurationError
 from twistedcaldav.resource import isPseudoCalendarCollectionResource
 from twistedcaldav.static import CalDAVFile, CalendarHomeFile
 from twistedcaldav.directory.directory import DirectoryService
@@ -126,20 +127,23 @@
         usage("Too many arguments: %s" % (" ".join(args),))
 
     if records or allRecords:
-        config = loadConfig(configFileName)
-        directory = getDirectory()
+        try:
+            config = loadConfig(configFileName)
+        except ConfigurationError, e:
+            sys.stdout.write("%s\n" % (e,))
+            sys.exit(1)
 
         for record in records:
             recordType, shortName = record
-            calendarHome = directory.calendarHomeForShortName(recordType, shortName)
+            calendarHome = config.directory.calendarHomeForShortName(recordType, shortName)
             if not calendarHome:
                 sys.stderr.write("No calendar home found for record: (%s)%s\n" % (recordType, shortName))
                 sys.exit(1)
             calendarHomes.add(calendarHome)
 
         if allRecords:
-            for record in directory.allRecords():
-                calendarHome = directory.calendarHomeForRecord(record)
+            for record in config.directory.allRecords():
+                calendarHome = config.directory.calendarHomeForRecord(record)
                 if not calendarHome:
                     pass
                 else:
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090508/db6567ba/attachment-0001.html>


More information about the calendarserver-changes mailing list