[CalendarServer-changes] [5047] CalendarServer/branches/users/sagen/locations-resources
source_changes at macosforge.org
source_changes at macosforge.org
Sat Feb 6 11:46:23 PST 2010
Revision: 5047
http://trac.macosforge.org/projects/calendarserver/changeset/5047
Author: sagen at apple.com
Date: 2010-02-06 11:46:22 -0800 (Sat, 06 Feb 2010)
Log Message:
-----------
Adds a new command line utility for use by servermgr_calendar to carry out various location/resource manipulation commands
Modified Paths:
--------------
CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/principals.py
CalendarServer/branches/users/sagen/locations-resources/setup.py
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py
Added Paths:
-----------
CalendarServer/branches/users/sagen/locations-resources/bin/calendarserver_command_gateway
CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/gateway.py
Added: CalendarServer/branches/users/sagen/locations-resources/bin/calendarserver_command_gateway
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/bin/calendarserver_command_gateway (rev 0)
+++ CalendarServer/branches/users/sagen/locations-resources/bin/calendarserver_command_gateway 2010-02-06 19:46:22 UTC (rev 5047)
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2010 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.
+##
+
+import sys
+
+#PYTHONPATH
+
+if __name__ == "__main__":
+ if "PYTHONPATH" in globals():
+ sys.path.insert(0, PYTHONPATH)
+ else:
+ from os.path import dirname, abspath, join
+ from subprocess import Popen, PIPE
+
+ home = dirname(dirname(abspath(__file__)))
+ run = join(home, "run")
+
+ child = Popen((run, "-p"), stdout=PIPE)
+ path, stderr = child.communicate()
+
+ path = path.rstrip("\n")
+
+ if child.wait() == 0:
+ sys.path[0:0] = path.split(":")
+
+ sys.argv[1:1] = ["-f", join(home, "conf", "caldavd-dev.plist")]
+
+ from calendarserver.tools.gateway import main
+ main()
Property changes on: CalendarServer/branches/users/sagen/locations-resources/bin/calendarserver_command_gateway
___________________________________________________________________
Added: svn:executable
+ *
Added: CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/gateway.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/gateway.py (rev 0)
+++ CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/gateway.py 2010-02-06 19:46:22 UTC (rev 5047)
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+
+##
+# Copyright (c) 2006-2010 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.
+##
+
+import sys
+import os
+import plistlib
+import xml
+
+import operator
+from getopt import getopt, GetoptError
+from pwd import getpwnam
+from grp import getgrnam
+
+from twisted.python.util import switchUID
+
+from twistedcaldav.config import config, ConfigurationError
+from twistedcaldav.directory.directory import DirectoryError
+
+from calendarserver.tools.util import loadConfig, getDirectory, autoDisableMemcached
+
+def usage(e=None):
+
+ name = os.path.basename(sys.argv[0])
+ print "usage: %s [options]" % (name,)
+ print ""
+ print " TODO: describe usage"
+ print ""
+ print "options:"
+ print " -h --help: print this help and exit"
+ print " -f --config <path>: Specify caldavd.plist configuration path"
+ print ""
+
+ if e:
+ sys.exit(64)
+ else:
+ sys.exit(0)
+
+
+def main():
+
+ try:
+ (optargs, args) = getopt(
+ sys.argv[1:], "hf:", [
+ "help",
+ "config=",
+ ],
+ )
+ except GetoptError, e:
+ usage(e)
+
+ #
+ # Get configuration
+ #
+ configFileName = None
+
+ for opt, arg in optargs:
+ if opt in ("-h", "--help"):
+ usage()
+
+ elif opt in ("-f", "--config"):
+ configFileName = arg
+
+ else:
+ raise NotImplementedError(opt)
+
+ try:
+ loadConfig(configFileName)
+
+ # Shed privileges
+ if config.UserName and config.GroupName and os.getuid() == 0:
+ uid = getpwnam(config.UserName).pw_uid
+ gid = getgrnam(config.GroupName).gr_gid
+ switchUID(uid, uid, gid)
+
+ os.umask(config.umask)
+
+ try:
+ config.directory = getDirectory()
+ except DirectoryError, e:
+ abort(e)
+ autoDisableMemcached(config)
+ except ConfigurationError, e:
+ abort(e)
+
+ #
+ # Read commands from stdin
+ #
+ rawInput = sys.stdin.read()
+ try:
+ plist = plistlib.readPlistFromString(rawInput)
+ except xml.parsers.expat.ExpatError, e:
+ abort(str(e))
+
+ # If the plist is an array, each element of the array is a separate
+ # command dictionary.
+ if isinstance(plist, list):
+ commands = plist
+ else:
+ commands = [plist]
+
+ # Make sure 'command' is specified
+ for command in commands:
+ if not command.has_key('command'):
+ abort("'command' missing from plist")
+
+ run(commands)
+
+def run(commands):
+ for command in commands:
+ commandName = command['command']
+
+ methodName = "command_%s" % (commandName,)
+ if hasattr(Commands, methodName):
+ getattr(Commands, methodName)(command)
+ else:
+ abort("Unknown command '%s'" % (commandName,))
+
+class Commands(object):
+
+ @classmethod
+ def command_getLocationList(cls, command):
+ directory = config.directory
+ result = []
+ for record in directory.listRecords("locations"):
+ result.append( {
+ 'GeneratedUID' : record.guid,
+ 'RecordName' : [n for n in record.shortNames],
+ 'RealName' : record.fullName,
+ 'AutoSchedule' : record.autoSchedule,
+ } )
+ respond(command, result)
+
+ @classmethod
+ def command_createLocation(cls, command):
+ directory = config.directory
+
+ try:
+ directory.createRecord("locations", guid=command['GeneratedUID'],
+ shortNames=command['RecordName'], fullName=command['RealName'])
+ except DirectoryError, e:
+ abort(str(e))
+
+ result = []
+ for record in directory.listRecords("locations"):
+ result.append( {
+ 'GeneratedUID' : record.guid,
+ 'RecordName' : [n for n in record.shortNames],
+ 'RealName' : record.fullName,
+ 'AutoSchedule' : record.autoSchedule,
+ } )
+ respond(command, result)
+
+
+ @classmethod
+ def command_getResourceList(cls, command):
+ directory = config.directory
+ result = []
+ for record in directory.listRecords("resources"):
+ result.append( {
+ 'GeneratedUID' : record.guid,
+ 'RecordName' : [n for n in record.shortNames],
+ 'RealName' : record.fullName,
+ 'AutoSchedule' : record.autoSchedule,
+ } )
+ respond(command, result)
+
+def respond(command, result):
+ sys.stdout.write(plistlib.writePlistToString( { 'command' : command['command'], 'result' : result } ) )
+
+def abort(msg, status=1):
+ sys.stdout.write(plistlib.writePlistToString( { 'error' : msg, } ) )
+ sys.exit(status)
+
+if __name__ == "__main__":
+ main()
Property changes on: CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/gateway.py
___________________________________________________________________
Added: svn:executable
+ *
Modified: CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/principals.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/principals.py 2010-02-05 19:37:39 UTC (rev 5046)
+++ CalendarServer/branches/users/sagen/locations-resources/calendarserver/tools/principals.py 2010-02-06 19:46:22 UTC (rev 5047)
@@ -103,7 +103,6 @@
#"search=",
"list-principal-types",
"list-principals=",
- "create-principal=",
"read-property=",
"list-read-proxies",
"list-write-proxies",
@@ -124,7 +123,6 @@
configFileName = None
listPrincipalTypes = False
listPrincipals = None
- createPrincipal = None
principalActions = []
for opt, arg in optargs:
@@ -140,9 +138,6 @@
elif opt in ("", "--list-principals"):
listPrincipals = arg
- elif opt in ("", "--create-principal"):
- createPrincipal = arg
-
elif opt in ("", "--read-property"):
try:
qname = sname2qname(arg)
@@ -245,18 +240,6 @@
return
#
- # Create principal
- #
- if createPrincipal:
- if args:
- usage("Too many arguments")
-
- recordType, shortName = createPrincipal.split(":", 1)
- config.directory.createRecord(recordType, shortNames=(shortName,))
- return
-
-
- #
# Do a quick sanity check that arguments look like principal
# identifiers.
#
Modified: CalendarServer/branches/users/sagen/locations-resources/setup.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/setup.py 2010-02-05 19:37:39 UTC (rev 5046)
+++ CalendarServer/branches/users/sagen/locations-resources/setup.py 2010-02-06 19:46:22 UTC (rev 5047)
@@ -109,6 +109,7 @@
"bin/caldavd",
"bin/calendarserver_export",
"bin/calendarserver_manage_principals"
+ "bin/calendarserver_command_gateway"
],
data_files = [ ("caldavd", ["conf/caldavd.plist"]) ],
ext_modules = extensions,
Modified: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py 2010-02-05 19:37:39 UTC (rev 5046)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py 2010-02-06 19:46:22 UTC (rev 5047)
@@ -19,6 +19,7 @@
from twistedcaldav.test.util import TestCase
from calendarserver.tools.util import getDirectory
from twisted.python.filepath import FilePath
+from twistedcaldav.directory.directory import DirectoryError
class ModificationTestCase(TestCase):
@@ -119,3 +120,9 @@
# Make sure old records are still there:
record = directory.recordWithUID("location01")
self.assertNotEquals(record, None)
+
+ def test_createDuplicateRecord(self):
+ directory = getDirectory()
+
+ directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+ self.assertRaises(DirectoryError, directory.createRecord, "resources", "resource01", shortNames=("resource01",), uid="resource01")
Modified: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py 2010-02-05 19:37:39 UTC (rev 5046)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py 2010-02-06 19:46:22 UTC (rev 5047)
@@ -30,7 +30,7 @@
from twisted.python.filepath import FilePath
from twistedcaldav.directory import augment
-from twistedcaldav.directory.directory import DirectoryService
+from twistedcaldav.directory.directory import DirectoryService, DirectoryError
from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
CachingDirectoryRecord
from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser, XMLAccountRecord
@@ -81,13 +81,24 @@
return self._recordTypes
def listRecords(self, recordType):
+ self._lastCheck = 0
for xmlPrincipal in self._accounts()[recordType].itervalues():
record = self.recordWithGUID(xmlPrincipal.guid)
if record is not None:
yield record
def queryDirectory(self, recordTypes, indexType, indexKey):
-
+ """
+ If the query is a miss, re-read from the XML file and try again
+ """
+ if not self._queryDirectory(recordTypes, indexType, indexKey):
+ self._lastCheck = 0
+ self._queryDirectory(recordTypes, indexType, indexKey)
+
+ def _queryDirectory(self, recordTypes, indexType, indexKey):
+
+ anyMatches = False
+
for recordType in recordTypes:
for xmlPrincipal in self._accounts()[recordType].itervalues():
record = XMLDirectoryRecord(
@@ -112,7 +123,10 @@
matched = indexKey in record.calendarUserAddresses
if matched:
+ anyMatches = True
self.recordCacheForType(recordType).addRecord(record, indexType, indexKey)
+
+ return anyMatches
def recordsMatchingFields(self, fields, operand="or", recordType=None):
# Default, brute force method search of underlying XML data
@@ -221,7 +235,26 @@
return element
+
def _persistRecords(self, element):
+
+ def indent(elem, level=0):
+ i = "\n" + level*" "
+ if len(elem):
+ if not elem.text or not elem.text.strip():
+ elem.text = i + " "
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ for elem in elem:
+ indent(elem, level+1)
+ if not elem.tail or not elem.tail.strip():
+ elem.tail = i
+ else:
+ if level and (not elem.tail or not elem.tail.strip()):
+ elem.tail = i
+
+ indent(element)
+
# TODO: make this robust:
ET.ElementTree(element).write(self.xmlFile.path)
@@ -252,6 +285,8 @@
accountsElement = ET.Element("accounts", realm=self.realmName)
for recType in self.recordTypes():
for xmlPrincipal in accounts[recType].itervalues():
+ if xmlPrincipal.guid == guid:
+ raise DirectoryError("Duplicate guid: %s" % (guid,))
self._addElement(accountsElement, xmlPrincipal)
xmlPrincipal = XMLAccountRecord(recordType)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100206/f5abff73/attachment-0001.html>
More information about the calendarserver-changes
mailing list