[CalendarServer-changes] [5969] CalendarServer/branches/users/wsanchez/deployment

source_changes at macosforge.org source_changes at macosforge.org
Mon Aug 2 19:13:55 PDT 2010


Revision: 5969
          http://trac.macosforge.org/projects/calendarserver/changeset/5969
Author:   cdaboo at apple.com
Date:     2010-08-02 19:13:55 -0700 (Mon, 02 Aug 2010)
Log Message:
-----------
Allow importing of old proxy database into new DB types.

Modified Paths:
--------------
    CalendarServer/branches/users/wsanchez/deployment/twistedcaldav/directory/calendaruserproxy.py

Added Paths:
-----------
    CalendarServer/branches/users/wsanchez/deployment/bin/caldav_load_proxydb
    CalendarServer/branches/users/wsanchez/deployment/calendarserver/tools/loadproxydb.py

Added: CalendarServer/branches/users/wsanchez/deployment/bin/caldav_load_proxydb
===================================================================
--- CalendarServer/branches/users/wsanchez/deployment/bin/caldav_load_proxydb	                        (rev 0)
+++ CalendarServer/branches/users/wsanchez/deployment/bin/caldav_load_proxydb	2010-08-03 02:13:55 UTC (rev 5969)
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+##
+# Copyright (c) 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()
+
+        if child.wait() == 0:
+            sys.path[0:0] = path.split(":")
+
+	print sys.path
+    from calendarserver.tools.loadproxydb import main
+    main()


Property changes on: CalendarServer/branches/users/wsanchez/deployment/bin/caldav_load_proxydb
___________________________________________________________________
Added: svn:executable
   + *

Added: CalendarServer/branches/users/wsanchez/deployment/calendarserver/tools/loadproxydb.py
===================================================================
--- CalendarServer/branches/users/wsanchez/deployment/calendarserver/tools/loadproxydb.py	                        (rev 0)
+++ CalendarServer/branches/users/wsanchez/deployment/calendarserver/tools/loadproxydb.py	2010-08-03 02:13:55 UTC (rev 5969)
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+##
+# Copyright (c) 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.
+##
+
+from calendarserver.tools.util import loadConfig, getDirectory,\
+    autoDisableMemcached
+from getopt import getopt, GetoptError
+from grp import getgrnam
+from pwd import getpwnam
+from sys import stdout, stderr
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks
+from twisted.python.log import addObserver, removeObserver
+from twisted.python.util import switchUID
+from twistedcaldav.config import config, ConfigurationError
+from twistedcaldav.directory import calendaruserproxy
+from twistedcaldav.log import setLogLevelForNamespace
+import os
+import sys
+from twistedcaldav.directory.calendaruserproxy import ProxySqliteDB,\
+    OldCalendarUserProxyDatabase
+
+class UsageError (StandardError):
+    pass
+
+class StandardIOObserver (object):
+    """
+    Log observer that writes to standard I/O.
+    """
+    def emit(self, eventDict):
+        text = None
+
+        if eventDict["isError"]:
+            output = stderr
+            if "failure" in eventDict:
+                text = eventDict["failure"].getTraceback()
+        else:
+            output = stdout
+
+        if not text:
+            text = " ".join([str(m) for m in eventDict["message"]]) + "\n"
+
+        output.write(text)
+        output.flush()
+
+    def start(self):
+        addObserver(self.emit)
+
+    def stop(self):
+        removeObserver(self.emit)
+
+def usage(e=None):
+    if e:
+        print e
+        print ""
+
+    name = os.path.basename(sys.argv[0])
+    print "usage: %s [options] FILE" % (name,)
+    print ""
+    print "Populate an sqlite or PostgreSQL proxy database with values"
+    print "from an sqlite proxy database file."
+    print ""
+    print "FILE: Specify sqlite proxy database file path"
+    print ""
+    print "options:"
+    print "  -h --help: print this help and exit"
+    print "  -f --config: Specify caldavd.plist configuration path"
+
+    if e:
+        sys.exit(64)
+    else:
+        sys.exit(0)
+
+def main():
+    try:
+        (optargs, args) = getopt(
+            sys.argv[1:], "hf:", [
+                "config=",
+                "help",
+            ],
+        )
+    except GetoptError, e:
+        usage(e)
+
+    configFileName = None
+
+    for opt, arg in optargs:
+        if opt in ("-h", "--help"):
+            usage()
+
+        elif opt in ("-f", "--config"):
+            configFileName = arg
+
+
+    if len(args) != 1:
+        usage("Wrong number of arguments: %s" % (" ".join(args),))
+    oldDB = args[0]
+    if not os.path.exists(oldDB):
+        usage("Database does not exist: %s" % (oldDB,))
+
+    observer = StandardIOObserver()
+    observer.start()
+
+    #
+    # Get configuration
+    #
+    try:
+        loadConfig(configFileName)
+        setLogLevelForNamespace(None, "warn")
+
+        # 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)
+
+        config.directory = getDirectory()
+        autoDisableMemcached(config)
+    except ConfigurationError, e:
+        usage("Unable to start: %s" % (e,))
+
+    #
+    # Start the reactor
+    #
+    reactor.callLater(0, run, oldDB)
+    reactor.run()
+
+ at inlineCallbacks
+def run(oldDB):
+    
+    try:
+        # Read in old records
+        olddb = OldCalendarUserProxyDatabase(oldDB)
+        items = olddb.exportAll()
+        print "Read %d items" % (len(items),)
+        
+        # Write to new records
+        db = calendaruserproxy.ProxyDBService
+        yield db.importAll(items)
+            
+        print "Added %d items" % (len(items),)
+    finally:
+        #
+        # Stop the reactor
+        #
+        reactor.stop()


Property changes on: CalendarServer/branches/users/wsanchez/deployment/calendarserver/tools/loadproxydb.py
___________________________________________________________________
Added: svn:executable
   + *

Modified: CalendarServer/branches/users/wsanchez/deployment/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/users/wsanchez/deployment/twistedcaldav/directory/calendaruserproxy.py	2010-08-02 22:54:55 UTC (rev 5968)
+++ CalendarServer/branches/users/wsanchez/deployment/twistedcaldav/directory/calendaruserproxy.py	2010-08-03 02:13:55 UTC (rev 5969)
@@ -44,8 +44,10 @@
 from twistedcaldav.resource import CalDAVComplianceMixIn
 from twistedcaldav.directory.util import NotFilePath
 from twistedcaldav.log import LoggingMixIn
+from twistedcaldav.sql import AbstractSQLDatabase
 
 import itertools
+import os
 import time
 
 class PermissionsMixIn (ReadOnlyWritePropertiesResourceMixIn):
@@ -764,7 +766,19 @@
         
         yield super(ProxyDB, self).clean()
 
+    @inlineCallbacks
+    def exportAll(self):
+        
+        # Get all members and memberships from old DB
+        allItems = (yield self.query("select GROUPNAME, MEMBER from GROUPS"))
+        returnValue(allItems) 
 
+    @inlineCallbacks
+    def importAll(self, items):
+        
+        for group, member in items:
+            yield self._add_to_db_one(group, member)
+
 ProxyDBService = None   # Global proxyDB service
 
 
@@ -788,7 +802,244 @@
         ADBAPIPostgreSQLMixin.__init__(self, )
         ProxyDB.__init__(self, "Proxies", "pgdb", (), host=host, database=database, user=user, password=password)
 
+class OldCalendarUserProxyDatabase(AbstractSQLDatabase):
+    """
+    A database to maintain calendar user proxy group memberships.
 
+    SCHEMA:
+
+    Group Database:
+
+    ROW: GROUPNAME, MEMBER
+
+    """
+
+    dbType = "CALENDARUSERPROXY"
+    dbFilename = "calendaruserproxy.sqlite"
+    dbFormatVersion = "4"
+
+    class ProxyDBMemcacher(Memcacher):
+        
+        def setMembers(self, guid, members):
+            return self.set("members:%s" % (guid,), str(",".join(members)))
+
+        def setMemberships(self, guid, memberships):
+            return self.set("memberships:%s" % (guid,), str(",".join(memberships)))
+
+        def getMembers(self, guid):
+            def _value(value):
+                if value:
+                    return set(value.split(","))
+                elif value is None:
+                    return None
+                else:
+                    return set()
+            d = self.get("members:%s" % (guid,))
+            d.addCallback(_value)
+            return d
+
+        def getMemberships(self, guid):
+            def _value(value):
+                if value:
+                    return set(value.split(","))
+                elif value is None:
+                    return None
+                else:
+                    return set()
+            d = self.get("memberships:%s" % (guid,))
+            d.addCallback(_value)
+            return d
+
+        def deleteMember(self, guid):
+            return self.delete("members:%s" % (guid,))
+
+        def deleteMembership(self, guid):
+            return self.delete("memberships:%s" % (guid,))
+
+    def __init__(self, path):
+        super(OldCalendarUserProxyDatabase, self).__init__(path, True)
+        
+        self._memcacher = OldCalendarUserProxyDatabase.ProxyDBMemcacher("proxyDB")
+
+    @inlineCallbacks
+    def setGroupMembers(self, principalUID, members):
+        """
+        Add a group membership record.
+
+        @param principalUID: the UID of the group principal to add.
+        @param members: a list UIDs of principals that are members of this group.
+        """
+
+        # Get current members before we change them
+        current_members = yield self.getMembers(principalUID)
+        if current_members is None:
+            current_members = ()
+        current_members = set(current_members)
+
+        # Remove what is there, then add it back.
+        self._delete_from_db(principalUID)
+        self._add_to_db(principalUID, members)
+        self._db_commit()
+        
+        # Update cache
+        update_members = set(members)
+        
+        remove_members = current_members.difference(update_members)
+        add_members = update_members.difference(current_members)
+        for member in itertools.chain(remove_members, add_members,):
+            _ignore = yield self._memcacher.deleteMembership(member)
+        _ignore = yield self._memcacher.deleteMember(principalUID)
+
+    @inlineCallbacks
+    def removeGroup(self, principalUID):
+        """
+        Remove a group membership record.
+
+        @param principalUID: the UID of the group principal to remove.
+        """
+
+        self._delete_from_db(principalUID)
+        self._db_commit()
+        
+        # Update cache
+        members = yield self.getMembers(principalUID)
+        if members:
+            for member in members:
+                yield self._memcacher.deleteMembership(member)
+            yield self._memcacher.deleteMember(principalUID)
+
+    @inlineCallbacks
+    def getMembers(self, principalUID):
+        """
+        Return the list of group member UIDs for the specified principal.
+        
+        @return: a deferred returning a C{set} of members.
+        """
+
+        def _members():
+            members = set()
+            for row in self._db_execute("select MEMBER from GROUPS where GROUPNAME = :1", principalUID):
+                members.add(row[0])
+            return members
+
+        # Pull from cache
+        result = yield self._memcacher.getMembers(principalUID)
+        if result is None:
+            result = _members()
+            yield self._memcacher.setMembers(principalUID, result)
+        returnValue(result)
+
+    @inlineCallbacks
+    def getMemberships(self, principalUID):
+        """
+        Return the list of group principal UIDs the specified principal is a member of.
+        
+        @return: a deferred returning a C{set} of memberships.
+        """
+
+        def _members():
+            members = set()
+            for row in self._db_execute("select GROUPNAME from GROUPS where MEMBER = :1", principalUID):
+                members.add(row[0])
+            return members
+
+        # Pull from cache
+        result = yield self._memcacher.getMemberships(principalUID)
+        if result is None:
+            result = _members()
+            yield self._memcacher.setMemberships(principalUID, result)
+        returnValue(result)
+
+    def exportAll(self):
+        
+        # Get all members and memberships from old DB
+        return tuple(self._db_execute("select GROUPNAME, MEMBER from GROUPS"))
+
+    def _add_to_db(self, principalUID, members):
+        """
+        Insert the specified entry into the database.
+
+        @param principalUID: the UID of the group principal to add.
+        @param members: a list of UIDs or principals that are members of this group.
+        """
+        for member in members:
+            self._db_execute(
+                """
+                insert into GROUPS (GROUPNAME, MEMBER)
+                values (:1, :2)
+                """, principalUID, member
+            )
+
+    def _delete_from_db(self, principalUID):
+        """
+        Deletes the specified entry from the database.
+
+        @param principalUID: the UID of the group principal to remove.
+        """
+        self._db_execute("delete from GROUPS where GROUPNAME = :1", principalUID)
+
+    def _db_version(self):
+        """
+        @return: the schema version assigned to this index.
+        """
+        return OldCalendarUserProxyDatabase.dbFormatVersion
+
+    def _db_type(self):
+        """
+        @return: the collection type assigned to this index.
+        """
+        return OldCalendarUserProxyDatabase.dbType
+
+    def _db_init_data_tables(self, q):
+        """
+        Initialise the underlying database tables.
+        @param q:           a database cursor to use.
+        """
+
+        #
+        # GROUPS table
+        #
+        q.execute(
+            """
+            create table GROUPS (
+                GROUPNAME   text,
+                MEMBER      text
+            )
+            """
+        )
+        q.execute(
+            """
+            create index GROUPNAMES on GROUPS (GROUPNAME)
+            """
+        )
+        q.execute(
+            """
+            create index MEMBERS on GROUPS (MEMBER)
+            """
+        )
+
+    def _db_upgrade_data_tables(self, q, old_version):
+        """
+        Upgrade the data from an older version of the DB.
+        @param q: a database cursor to use.
+        @param old_version: existing DB's version number
+        @type old_version: str
+        """
+
+        # Add index if old version is less than "4"
+        if int(old_version) < 4:
+            q.execute(
+                """
+                create index GROUPNAMES on GROUPS (GROUPNAME)
+                """
+            )
+            q.execute(
+                """
+                create index MEMBERS on GROUPS (MEMBER)
+                """
+            )
+
+
 ##
 # Utilities
 ##
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100802/8ba05768/attachment-0001.html>


More information about the calendarserver-changes mailing list