[CalendarServer-changes] [5432] CalendarServer/branches/users/sagen/deprovision
source_changes at macosforge.org
source_changes at macosforge.org
Wed Mar 31 15:56:08 PDT 2010
Revision: 5432
http://trac.macosforge.org/projects/calendarserver/changeset/5432
Author: sagen at apple.com
Date: 2010-03-31 15:56:07 -0700 (Wed, 31 Mar 2010)
Log Message:
-----------
Adds calendarserver_purge_principals
Modified Paths:
--------------
CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_events
CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/purge.py
CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/test/test_purge.py
Added Paths:
-----------
CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_principals
CalendarServer/branches/users/sagen/deprovision/doc/calendarserver_purge_principals.8
Modified: CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_events
===================================================================
--- CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_events 2010-03-31 22:25:31 UTC (rev 5431)
+++ CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_events 2010-03-31 22:56:07 UTC (rev 5432)
@@ -40,5 +40,5 @@
sys.argv[1:1] = ["-f", join(home, "conf", "caldavd-dev.plist")]
- from calendarserver.tools.purge import main_purge
- main_purge()
+ from calendarserver.tools.purge import main_purge_events
+ main_purge_events()
Added: CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_principals
===================================================================
--- CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_principals (rev 0)
+++ CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_principals 2010-03-31 22:56:07 UTC (rev 5432)
@@ -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.purge import main_purge_principals
+ main_purge_principals()
Property changes on: CalendarServer/branches/users/sagen/deprovision/bin/calendarserver_purge_principals
___________________________________________________________________
Added: svn:executable
+ *
Modified: CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/purge.py
===================================================================
--- CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/purge.py 2010-03-31 22:25:31 UTC (rev 5431)
+++ CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/purge.py 2010-03-31 22:56:07 UTC (rev 5432)
@@ -37,11 +37,10 @@
from twistedcaldav.method.delete_common import DeleteResource
import os
import sys
-import tarfile
log = Logger()
-def usage_purge(e=None):
+def usage_purge_events(e=None):
name = os.path.basename(sys.argv[0])
print "usage: %s [options]" % (name,)
@@ -61,10 +60,64 @@
else:
sys.exit(0)
+def usage_purge_principal(e=None):
-def main_purge():
+ name = os.path.basename(sys.argv[0])
+ print "usage: %s [options]" % (name,)
+ print ""
+ print " Remove a principal's events from the calendar server"
+ print ""
+ print "options:"
+ print " -f --config <path>: Specify caldavd.plist configuration path"
+ print " -h --help: print this help and exit"
+ print " -n --dry-run: only calculate how many events to purge"
+ print " -v --verbose: print progress information"
+ print ""
+ if e:
+ sys.exit(64)
+ else:
+ sys.exit(0)
+
+
+
+def shared_main(configFileName, method, *args, **kwds):
+
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:
+ rootResource = getRootResource(config)
+ directory = rootResource.getDirectory()
+ except DirectoryError, e:
+ print "Error: %s" % (e,)
+ return
+ setupMemcached(config)
+ setupNotifications(config)
+ except ConfigurationError, e:
+ print "Error: %s" % (e,)
+ return
+
+
+ #
+ # Start the reactor
+ #
+ reactor.callLater(0.1, callThenStop, method, directory,
+ rootResource, *args, **kwds)
+
+ reactor.run()
+
+def main_purge_events():
+
+ try:
(optargs, args) = getopt(
sys.argv[1:], "d:f:hnv", [
"days=",
@@ -75,7 +128,7 @@
],
)
except GetoptError, e:
- usage_purge(e)
+ usage_purge_events(e)
#
# Get configuration
@@ -87,14 +140,14 @@
for opt, arg in optargs:
if opt in ("-h", "--help"):
- usage_purge()
+ usage_purge_events()
elif opt in ("-d", "--days"):
try:
days = int(arg)
except ValueError, e:
print "Invalid value for --days: %s" % (arg,)
- usage_purge(e)
+ usage_purge_events(e)
elif opt in ("-v", "--verbose"):
verbose = True
@@ -110,38 +163,52 @@
cutoff = (date.today()-timedelta(days=days)).strftime("%Y%m%dT000000Z")
- try:
- loadConfig(configFileName)
+ shared_main(configFileName, purgeOldEvents, cutoff, verbose=verbose,
+ dryrun=dryrun)
- # 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)
+def main_purge_principals():
- try:
- rootResource = getRootResource(config)
- directory = rootResource.getDirectory()
- except DirectoryError, e:
- print "Error: %s" % (e,)
- return
- setupMemcached(config)
- setupNotifications(config)
- except ConfigurationError, e:
- print "Error: %s" % (e,)
- return
+ try:
+ (optargs, args) = getopt(
+ sys.argv[1:], "f:hnv", [
+ "dry-run",
+ "config=",
+ "help",
+ "verbose",
+ ],
+ )
+ except GetoptError, e:
+ usage_purge_principal(e)
-
#
- # Start the reactor
+ # Get configuration
#
- reactor.callLater(0.1, callThenStop, purgeOldEvents, directory,
- rootResource, cutoff, verbose=verbose, dryrun=dryrun)
+ configFileName = None
+ dryrun = False
+ verbose = False
- reactor.run()
+ for opt, arg in optargs:
+ if opt in ("-h", "--help"):
+ usage_purge_principal()
+ elif opt in ("-v", "--verbose"):
+ verbose = True
+
+ elif opt in ("-n", "--dry-run"):
+ dryrun = True
+
+ elif opt in ("-f", "--config"):
+ configFileName = arg
+
+ else:
+ raise NotImplementedError(opt)
+
+ # args is a list of guids
+
+ shared_main(configFileName, purgeGUIDs, args, verbose=verbose, dryrun=dryrun)
+
+
@inlineCallbacks
def callThenStop(method, *args, **kwds):
try:
@@ -261,8 +328,25 @@
@inlineCallbacks
-def purgeGUID(guid, directory, root, tarPath=None):
+def purgeGUIDs(directory, root, guids, verbose=False, dryrun=False):
+ total = 0
+ for guid in guids:
+ count, allAssignments[guid] = (yield purgeGUID(guid, directory, root,
+ verbose=verbose, dryrun=dryrun))
+ total += count
+
+
+ # TODO: figure out what to do with the purged proxy assignments...
+ # ...print to stdout?
+ # ...save in a file?
+
+ returnValue(total)
+
+
+ at inlineCallbacks
+def purgeGUID(guid, directory, root, verbose=False, dryrun=False):
+
# Does the record exist?
record = directory.recordWithGUID(guid)
if record is None:
@@ -281,12 +365,7 @@
principal = principalCollection.principalForRecord(record)
calendarHome = principal.calendarHome()
- if tarPath:
- tarFile = tarfile.open(tarPath, mode="w:gz")
- else:
- tarFile = None
-
- # Anything in the past should be deleted without implicit scheduling
+ # Anything in the past is left alone
now = datetime.utcnow().strftime("%Y%m%dT%H%M%SZ")
filter = caldavxml.Filter(
caldavxml.ComponentFilter(
@@ -304,47 +383,24 @@
collection = calendarHome.getChild(collName)
if collection.isCalendarCollection():
- # To compute past and ongoing events...
-
- # ...first start with all events...
- allEvents = set(collection.listChildren())
-
- ongoingEvents = set()
-
- # ...and find those that appear *after* the given cutoff
for name, uid, type in collection.index().indexedSearch(filter):
- ongoingEvents.add(name)
-
- for name in allEvents:
resource = collection.getChild(name)
-
- if tarFile:
- data = resource.iCalendarText()
- tarInfo = tarfile.TarInfo(name="%s/%s" % (collName, name))
- tarInfo.size = len(data)
- tarFile.addfile(tarInfo, fileobj=StringIO(data))
-
uri = "/calendars/__uids__/%s/%s/%s" % (
record.uid,
collName,
name
)
-
- (yield deleteResource(root, collection, resource,
- uri, guid, implicit=(name in ongoingEvents)))
+ if not dryrun:
+ (yield deleteResource(root, collection, resource,
+ uri, guid, implicit=True))
count += 1
- assignments = (yield purgeProxyAssignments(principal))
+ if not dryrun:
+ assignments = (yield purgeProxyAssignments(principal))
- if tarFile:
- tarInfo = tarfile.TarInfo(name="ProxyAssignments")
- tarInfo.size = len(assignments)
- tarFile.addfile(tarInfo, fileobj=StringIO(assignments))
- tarFile.close()
+ returnValue((count, assignments))
- returnValue(count)
-
@inlineCallbacks
def purgeProxyAssignments(principal):
@@ -354,19 +410,16 @@
proxyFor = (yield principal.proxyFor(proxyType == "write"))
for other in proxyFor:
- assignments.append("%s\t%s\t%s\n" %
- (principal.record.guid, proxyType, other.record.guid))
+ assignments.append((principal.record.guid, proxyType, other.record.guid))
(yield removeProxy(other, principal))
subPrincipal = principal.getChild("calendar-proxy-" + proxyType)
proxies = (yield subPrincipal.readProperty(davxml.GroupMemberSet, None))
for other in proxies.children:
- assignments.append("%s\t%s\t%s\n" %
- (str(other).split("/")[3], proxyType, principal.record.guid))
+ assignments.append((str(other).split("/")[3], proxyType, principal.record.guid))
(yield subPrincipal.writeProperty(davxml.GroupMemberSet(), None))
- assignments = "".join(assignments)
returnValue(assignments)
Modified: CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/test/test_purge.py
===================================================================
--- CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/test/test_purge.py 2010-03-31 22:25:31 UTC (rev 5431)
+++ CalendarServer/branches/users/sagen/deprovision/calendarserver/tools/test/test_purge.py 2010-03-31 22:56:07 UTC (rev 5432)
@@ -459,8 +459,8 @@
# Purging the guid should clear out proxy assignments
assignments = (yield purgeProxyAssignments(purgingPrincipal))
- self.assertTrue("5D6ABA3C-3446-4340-8083-7E37C5BC0B26\twrite\t291C2C29-B663-4342-8EA1-A055E6A04D65" in assignments)
- self.assertTrue("291C2C29-B663-4342-8EA1-A055E6A04D65\twrite\t5D6ABA3C-3446-4340-8083-7E37C5BC0B26" in assignments)
+ self.assertTrue(("5D6ABA3C-3446-4340-8083-7E37C5BC0B26", "write", "291C2C29-B663-4342-8EA1-A055E6A04D65") in assignments)
+ self.assertTrue(("291C2C29-B663-4342-8EA1-A055E6A04D65", "write", "5D6ABA3C-3446-4340-8083-7E37C5BC0B26") in assignments)
membersProperty = (yield getProxies(keepingPrincipal, "write"))
self.assertEquals(len(membersProperty.children), 0)
@@ -519,16 +519,13 @@
},
}
self.createHierarchy(before, config.DocumentRoot)
- tarPath = os.path.join(config.DocumentRoot, "calendars", "tarball.tgz")
- count = (yield purgeGUID("E9E78C86-4829-4520-A35D-70DDADAB2092",
- self.directory, self.rootResource, tarPath=tarPath))
+ count, assignments = (yield purgeGUID(
+ "E9E78C86-4829-4520-A35D-70DDADAB2092",
+ self.directory, self.rootResource))
- self.assertEquals(count, 3)
+ self.assertEquals(count, 2)
after = {
- "tarball.tgz" : {
- "@contents" : None, # ignore contents
- },
"__uids__" : {
"E9" : {
"E7" : {
@@ -537,6 +534,9 @@
".db.sqlite": {
"@contents" : None, # ignore contents
},
+ "noninvite.ics": {
+ "@contents" : NON_INVITE_ICS,
+ },
},
},
},
@@ -600,15 +600,25 @@
{
resourceAttr : collectionType,
},
- "noninvite.ics": {
- "@contents" : NON_INVITE_ICS_2,
+ # non-repeating, non-invite, in the past
+ # = untouched
+ "noninvite_past.ics": {
+ "@contents" : NON_INVITE_PAST_ICS,
},
+ # non-repeating, non-invite, in the future
+ # = removed
+ "noninvite_future.ics": {
+ "@contents" : NON_INVITE_FUTURE_ICS,
+ },
"organizer.ics": {
"@contents" : ORGANIZER_ICS_2,
},
"attendee.ics": {
"@contents" : ATTENDEE_ICS_2,
},
+ "repeating_organizer.ics": {
+ "@contents" : REPEATING_ORGANIZER_ICS,
+ },
},
},
},
@@ -627,6 +637,9 @@
"attendee.ics": {
"@contents" : ATTENDEE_ICS_2,
},
+ "repeating_organizer.ics": {
+ "@contents" : REPEATING_ORGANIZER_ICS,
+ },
},
},
},
@@ -635,10 +648,11 @@
},
}
self.createHierarchy(before, config.DocumentRoot)
- count = (yield purgeGUID("1CB4378B-DD76-462D-B4D4-BD131FE89243",
+ count, assignments = (yield purgeGUID(
+ "1CB4378B-DD76-462D-B4D4-BD131FE89243",
self.directory, self.rootResource))
- self.assertEquals(count, 3)
+ self.assertEquals(count, 4)
after = {
"__uids__" : {
@@ -649,6 +663,9 @@
".db.sqlite": {
"@contents" : None, # ignore contents
},
+ "noninvite_past.ics": {
+ "@contents" : NON_INVITE_PAST_ICS,
+ },
},
},
},
@@ -671,6 +688,11 @@
"ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=DECLINED:urn:uuid:1CB4378B-DD76-462D-B\r\n 4D4-BD131FE89243",
),
},
+ "*.ics/UID:8ED97931-9A19-4596-9D4D-52B36D6AB803": {
+ "@contents" : (
+ "METHOD:CANCEL",
+ ),
+ },
},
"calendar": {
".db.sqlite": {
@@ -686,6 +708,11 @@
"ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=DECLINED;SCHEDULE-STATUS=2.0:urn:uuid:\r\n 1CB4378B-DD76-462D-B4D4-BD131FE89243",
),
},
+ "repeating_organizer.ics": {
+ "@contents" : (
+ "STATUS:CANCELLED",
+ ),
+ },
},
},
},
@@ -775,20 +802,24 @@
},
}
self.createHierarchy(before, config.DocumentRoot)
- count = (yield purgeGUID("767F9EB0-8A58-4F61-8163-4BE0BB72B873",
+ count, assignments = (yield purgeGUID(
+ "767F9EB0-8A58-4F61-8163-4BE0BB72B873",
self.directory, self.rootResource))
- self.assertEquals(count, 4)
+ self.assertEquals(count, 3)
after = {
"__uids__" : {
- "76" : { # Non-existent -- all items purged
+ "76" : { # Non-existent
"7F" : {
"767F9EB0-8A58-4F61-8163-4BE0BB72B873" : {
"calendar": {
".db.sqlite": {
"@contents" : None, # ignore contents
},
+ "noninvite.ics": { # event in the past
+ "@contents" : NON_INVITE_ICS_3,
+ },
},
},
},
@@ -919,8 +950,8 @@
# For test_purgeNonExistentGUID
-# No organizer/attendee
-NON_INVITE_ICS_2 = """BEGIN:VCALENDAR
+# No organizer/attendee, in the past
+NON_INVITE_PAST_ICS = """BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
UID:151AFC76-6036-40EF-952B-97D1840760BF
@@ -931,6 +962,19 @@
END:VCALENDAR
""".replace("\n", "\r\n") % (past,)
+# No organizer/attendee, in the future
+NON_INVITE_FUTURE_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:251AFC76-6036-40EF-952B-97D1840760BF
+SUMMARY:Non Invitation
+DTSTART:%s
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % (future,)
+
+
# Purging non-existent organizer; has existing attendee
ORGANIZER_ICS_2 = """BEGIN:VCALENDAR
VERSION:2.0
@@ -961,7 +1005,23 @@
END:VCALENDAR
""".replace("\n", "\r\n") % (future,)
+# Purging non-existent organizer; has existing attendee; repeating
+REPEATING_ORGANIZER_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+UID:8ED97931-9A19-4596-9D4D-52B36D6AB803
+SUMMARY:Repeating Organizer
+DTSTART:%s
+DURATION:PT1H
+RRULE:FREQ=DAILY;COUNT=400
+ORGANIZER:urn:uuid:1CB4378B-DD76-462D-B4D4-BD131FE89243
+ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:urn:uuid:1CB4378B-DD76-462D-B4D4-BD131FE89243
+ATTENDEE;CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:urn:uuid:291C2C29-B663-4342-8EA1-A055E6A04D65
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % (past,)
+
# For test_purgeMultipleNonExistentGUIDs
# No organizer/attendee
Added: CalendarServer/branches/users/sagen/deprovision/doc/calendarserver_purge_principals.8
===================================================================
--- CalendarServer/branches/users/sagen/deprovision/doc/calendarserver_purge_principals.8 (rev 0)
+++ CalendarServer/branches/users/sagen/deprovision/doc/calendarserver_purge_principals.8 2010-03-31 22:56:07 UTC (rev 5432)
@@ -0,0 +1,56 @@
+.\"
+.\" 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.
+.\"
+.\" The following requests are required for all man pages.
+.Dd June 17, 2009
+.Dt CALENDARSERVER_PURGE_PRINCIPALS 8
+.Os
+.Sh NAME
+.Nm calendarserver_purge_principals
+.Nd Darwin Calendar Server deprovisioned user clean-up utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl -config Ar file
+.Op Fl -dry-run
+.Op Fl -verbose
+.Op Fl -help
+.Ar guid
+.Op guid ...
+.Sh DESCRIPTION
+.Nm
+is a tool for removing one or more principals' future events and proxy assignments from the calendar server. Events in the past are retained, but any ongoing events are canceled.
+.Pp
+.Nm
+should be run as a user with the same priviledges as the Calendar
+Server itself, as it needs to read and write data that belongs to the
+server.
+.Sh OPTIONS
+.Bl -tag -width flag
+.It Fl h, -help
+Display usage information
+.It Fl f, -config Ar FILE
+Use the Calendar Server configuration specified in the given file. Defaults to /etc/caldavd/caldavd.plist.
+.It Fl n, -dry-run
+Calculate and display how many events would be removed, but don't actually remove them.
+.It Fl v, -verbose
+Print progress information.
+.El
+.Sh FILES
+.Bl -tag -width flag
+.It /etc/caldavd/caldavd.plist
+The Calendar Server configuration file.
+.El
+.Sh SEE ALSO
+.Xr caldavd 8
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100331/547f6fc2/attachment-0001.html>
More information about the calendarserver-changes
mailing list