[CalendarServer-changes] [2645] CalendarServer/branches/sqlpropstore-2629

source_changes at macosforge.org source_changes at macosforge.org
Mon Jun 30 14:12:04 PDT 2008


Revision: 2645
          http://trac.macosforge.org/projects/calendarserver/changeset/2645
Author:   cdaboo at apple.com
Date:     2008-06-30 14:12:03 -0700 (Mon, 30 Jun 2008)
Log Message:
-----------
Add mechanism to upgrade xattrs->sqlite properties triggered by an explicit -U/--upgrade
command line option. This also merges in the code that does proxy-db, principal hierarchy
upgrade/clean-up.

Modified Paths:
--------------
    CalendarServer/branches/sqlpropstore-2629/bin/caldavd
    CalendarServer/branches/sqlpropstore-2629/run
    CalendarServer/branches/sqlpropstore-2629/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/branches/sqlpropstore-2629/twistedcaldav/tap.py
    CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_tap.py

Added Paths:
-----------
    CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_upgrade.py
    CalendarServer/branches/sqlpropstore-2629/twistedcaldav/upgrade.py

Modified: CalendarServer/branches/sqlpropstore-2629/bin/caldavd
===================================================================
--- CalendarServer/branches/sqlpropstore-2629/bin/caldavd	2008-06-30 18:12:25 UTC (rev 2644)
+++ CalendarServer/branches/sqlpropstore-2629/bin/caldavd	2008-06-30 21:12:03 UTC (rev 2645)
@@ -84,6 +84,7 @@
     echo "Options:";
     echo "        -h Print this help and exit";
     echo "        -X Do not daemonize";
+    echo "        -U Upgrade Only";
     echo "        -u User name to run as";
     echo "        -g Group name to run as";
     echo "        -f Configuration file to read";

Modified: CalendarServer/branches/sqlpropstore-2629/run
===================================================================
--- CalendarServer/branches/sqlpropstore-2629/run	2008-06-30 18:12:25 UTC (rev 2644)
+++ CalendarServer/branches/sqlpropstore-2629/run	2008-06-30 21:12:03 UTC (rev 2645)
@@ -43,6 +43,7 @@
      read_key="";
       profile="";
       reactor="";
+      upgrade="";
 
 usage ()
 {
@@ -52,13 +53,13 @@
 
   echo "Usage: ${program} [-hvgsfnpdkrR] [-K key] [-iI dst] [-t type] [-S statsdirectory] [-P plugin]";
   echo "Options:";
-  echo "	-h  Print this help and exit";
-  echo "	-v  Be verbose";
-  echo "	-g  Get dependencies only; don't run setup or run the server.";
-  echo "	-s  Run setup only; don't run server";
-  echo "	-f  Force setup to run";
-  echo "	-n  Do not run setup";
-  echo "	-p  Print PYTHONPATH value for server and exit";
+  echo "        -h  Print this help and exit";
+  echo "        -v  Be verbose";
+  echo "        -g  Get dependencies only; don't run setup or run the server";
+  echo "        -s  Run setup only; don't run server";
+  echo "        -f  Force setup to run";
+  echo "        -n  Do not run setup";
+  echo "        -p  Print PYTHONPATH value for server and exit";
   echo "        -d  Run caldavd as a daemon";
   echo "        -k  Stop caldavd";
   echo "        -r  Restart caldavd";
@@ -67,8 +68,9 @@
   echo "        -I  Perform a home install into dst; implies -s";
   echo "        -t  Select the server process type (Master, Slave or Combined) [${service_type}]";
   echo "        -S  Write a pstats object for each process to the given directory when the server is stopped.";
-  echo "	-P  Select the twistd plugin name [${plugin_name}]";
+  echo "        -P  Select the twistd plugin name [${plugin_name}]";
   echo "        -R  Twisted Reactor plugin to execute [${reactor}]";
+  echo "        -U  Upgrade the server's data only when running it";
 
   if [ "${1-}" == "-" ]; then return 0; fi;
   exit 64;
@@ -85,6 +87,7 @@
     'd')     daemonize=""; ;;
     'P')   plugin_name="${OPTARG}"; ;;
     'R')       reactor="-R ${OPTARG}"; ;;
+    'U')       upgrade="-U"; ;;
     't')  service_type="${OPTARG}"; ;;
     'K')      read_key="${OPTARG}"; ;;
     'S')       profile="-p ${OPTARG}"; ;;
@@ -251,7 +254,8 @@
         -T "${twisted}/bin/twistd"           \
         -P "${plugin_name}"                  \
         -t "${service_type}"                 \
-        ${reactor}                         \
+        ${reactor}                           \
+        ${upgrade}                           \
         ${profile};
     cd /;
   fi;

Modified: CalendarServer/branches/sqlpropstore-2629/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/sqlpropstore-2629/twistedcaldav/directory/calendaruserproxy.py	2008-06-30 18:12:25 UTC (rev 2644)
+++ CalendarServer/branches/sqlpropstore-2629/twistedcaldav/directory/calendaruserproxy.py	2008-06-30 21:12:03 UTC (rev 2645)
@@ -336,6 +336,7 @@
 
     dbType = "CALENDARUSERPROXY"
     dbFilename = "calendaruserproxy.sqlite"
+    dbOldFilename = db_prefix + "calendaruserproxy"
     dbFormatVersion = "4"
 
     class ProxyDBMemcacher(Memcacher):

Modified: CalendarServer/branches/sqlpropstore-2629/twistedcaldav/tap.py
===================================================================
--- CalendarServer/branches/sqlpropstore-2629/twistedcaldav/tap.py	2008-06-30 18:12:25 UTC (rev 2644)
+++ CalendarServer/branches/sqlpropstore-2629/twistedcaldav/tap.py	2008-06-30 21:12:03 UTC (rev 2645)
@@ -54,6 +54,7 @@
 from twistedcaldav.static import CalendarHomeProvisioningFile
 from twistedcaldav.static import TimezoneServiceFile
 from twistedcaldav.timezones import TimezoneCache
+from twistedcaldav.upgrade import UpgradeTheServer, UpgradeError
 from twistedcaldav import pdmonster
 from twistedcaldav import memcachepool
 
@@ -80,9 +81,12 @@
 
 
 class CalDAVOptions(Options):
-    optParameters = [[
-        "config", "f", "/etc/caldavd/caldavd.plist", "Path to configuration file."
-    ]]
+    optFlags = [
+        ["upgradeonly", "U", "Do server upgrade only."],
+    ]
+    optParameters = [
+        ["config", "f", "/etc/caldavd/caldavd.plist", "Path to configuration file."],
+    ]
 
     zsh_actions = {"config" : "_files -g '*.plist'"}
 
@@ -708,6 +712,13 @@
     makeService_Single   = makeService_Slave
 
     def makeService(self, options):
+
+        # Check for upgrade only
+        if options.get('upgradeonly', False):
+            # Now do any on disk upgrades we might need.
+            UpgradeTheServer.doUpgrade()
+            raise UpgradeError("Upgrade Completed Successfully")
+            
         serverType = config.ProcessType
 
         serviceMethod = getattr(self, "makeService_%s" % (serverType,), None)

Modified: CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_tap.py
===================================================================
--- CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_tap.py	2008-06-30 18:12:25 UTC (rev 2644)
+++ CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_tap.py	2008-06-30 21:12:03 UTC (rev 2645)
@@ -39,6 +39,7 @@
 from twistedcaldav.directory.sudo import SudoDirectoryService
 from twistedcaldav.directory.directory import UnknownRecordTypeError
 
+from twistedcaldav.upgrade import UpgradeError
 
 class TestCalDAVOptions(CalDAVOptions):
     """
@@ -237,6 +238,30 @@
         return service.services[0].args[1].protocolArgs['requestFactory']
 
 
+class UpgradeTests(BaseServiceMakerTests):
+    """
+    Test the supgrade behavior
+    """
+
+    def test_upgrade_U(self):
+        """
+        Test the default options of the dispatching makeService
+        """
+
+        self.options.parseOptions(['-f', self.configFile, "-U"])
+
+        self.assertRaises(UpgradeError, CalDAVServiceMaker().makeService, self.options)
+
+    def test_upgrade_dashdash(self):
+        """
+        Test the default options of the dispatching makeService
+        """
+
+        self.options.parseOptions(['-f', self.configFile, "--upgrade"])
+
+        self.assertRaises(UpgradeError, CalDAVServiceMaker().makeService, self.options)
+
+
 class CalDAVServiceMakerTests(BaseServiceMakerTests):
     """
     Test the service maker's behavior

Added: CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_upgrade.py
===================================================================
--- CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_upgrade.py	                        (rev 0)
+++ CalendarServer/branches/sqlpropstore-2629/twistedcaldav/test/test_upgrade.py	2008-06-30 21:12:03 UTC (rev 2645)
@@ -0,0 +1,320 @@
+##
+# Copyright (c) 2005-2007 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 twisted.web2.dav import davxml
+from twisted.web2.dav.resource import TwistedGETContentMD5
+from twistedcaldav import caldavxml, customxml
+from twistedcaldav.config import config
+from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyDatabase
+from twistedcaldav.sqlprops import SQLPropertiesDatabase
+from twistedcaldav.static import CalDAVFile, CalendarHomeFile
+from twistedcaldav.upgrade import UpgradeXattrsToSqlite, UpgradePrincipalCollectionInMemory, UpgradeError
+import os
+import twistedcaldav.test.util
+import xattr
+
+class ProxyDBUpgradeTests(twistedcaldav.test.util.TestCase):
+    
+    def setUpInitialStates(self):
+        
+        self.setUpOldDocRoot()
+        self.setUpOldDocRootWithoutDB()
+        self.setUpNewDocRoot()
+        
+        self.setUpNewDataRoot()
+        self.setUpDataRootWithProxyDB()
+
+    def setUpOldDocRoot(self):
+        
+        # Set up doc root
+        self.olddocroot = self.mktemp()
+        os.mkdir(self.olddocroot)
+
+        principals = os.path.join(self.olddocroot, "principals")
+        os.mkdir(principals)
+        os.mkdir(os.path.join(principals, "__uids__"))
+        os.mkdir(os.path.join(principals, "users"))
+        os.mkdir(os.path.join(principals, "groups"))
+        os.mkdir(os.path.join(principals, "locations"))
+        os.mkdir(os.path.join(principals, "resources"))
+        os.mkdir(os.path.join(principals, "sudoers"))
+        os.mkdir(os.path.join(self.olddocroot, "calendars"))
+
+        proxyDB = CalendarUserProxyDatabase(principals)
+        proxyDB._db()
+        os.rename(
+            os.path.join(principals, CalendarUserProxyDatabase.dbFilename),
+            os.path.join(principals, CalendarUserProxyDatabase.dbOldFilename),
+        )
+
+    def setUpOldDocRootWithoutDB(self):
+        
+        # Set up doc root
+        self.olddocrootnodb = self.mktemp()
+        os.mkdir(self.olddocrootnodb)
+
+        principals = os.path.join(self.olddocrootnodb, "principals")
+        os.mkdir(principals)
+        os.mkdir(os.path.join(principals, "__uids__"))
+        os.mkdir(os.path.join(principals, "users"))
+        os.mkdir(os.path.join(principals, "groups"))
+        os.mkdir(os.path.join(principals, "locations"))
+        os.mkdir(os.path.join(principals, "resources"))
+        os.mkdir(os.path.join(principals, "sudoers"))
+        os.mkdir(os.path.join(self.olddocrootnodb, "calendars"))
+
+    def setUpNewDocRoot(self):
+        
+        # Set up doc root
+        self.newdocroot = self.mktemp()
+        os.mkdir(self.newdocroot)
+
+        os.mkdir(os.path.join(self.newdocroot, "calendars"))
+
+    def setUpNewDataRoot(self):
+        
+        # Set up data root
+        self.newdataroot = self.mktemp()
+        os.mkdir(self.newdataroot)
+
+    def setUpDataRootWithProxyDB(self):
+        
+        # Set up data root
+        self.existingdataroot = self.mktemp()
+        os.mkdir(self.existingdataroot)
+
+        proxyDB = CalendarUserProxyDatabase(self.existingdataroot)
+        proxyDB._db()
+
+    def test_normalUpgrade(self):
+        """
+        Test the behavior of normal upgrade from old server to new.
+        """
+
+        self.setUpInitialStates()
+
+        config.DocumentRoot = self.olddocroot
+        config.DataRoot = self.newdataroot
+        
+        # Check pre-conditions
+        self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.isdir(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals", CalendarUserProxyDatabase.dbOldFilename)))
+        self.assertFalse(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+        UpgradePrincipalCollectionInMemory.doUpgrade()
+        
+        # Check post-conditions
+        self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals",)))
+        self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+    def test_partialUpgrade(self):
+        """
+        Test the behavior of a partial upgrade (one where /principals exists but the proxy db does not) from old server to new.
+        """
+
+        self.setUpInitialStates()
+
+        config.DocumentRoot = self.olddocrootnodb
+        config.DataRoot = self.newdataroot
+        
+        # Check pre-conditions
+        self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.isdir(os.path.join(config.DocumentRoot, "principals")))
+        self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals", CalendarUserProxyDatabase.dbOldFilename)))
+        self.assertFalse(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+        UpgradePrincipalCollectionInMemory.doUpgrade()
+        
+        # Check post-conditions
+        self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals",)))
+        self.assertFalse(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+    def test_noUpgrade(self):
+        """
+        Test the behavior of running on a new server (i.e. no upgrade needed).
+        """
+
+        self.setUpInitialStates()
+
+        config.DocumentRoot = self.newdocroot
+        config.DataRoot = self.existingdataroot
+        
+        # Check pre-conditions
+        self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+        UpgradePrincipalCollectionInMemory.doUpgrade()
+        
+        # Check post-conditions
+        self.assertFalse(os.path.exists(os.path.join(config.DocumentRoot, "principals",)))
+        self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+    def test_failedUpgrade(self):
+        """
+        Test the behavior of failed upgrade from old server to new where proxy DB exists in two locations.
+        """
+
+        self.setUpInitialStates()
+
+        config.DocumentRoot = self.olddocroot
+        config.DataRoot = self.existingdataroot
+        
+        # Check pre-conditions
+        self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.isdir(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals", CalendarUserProxyDatabase.dbOldFilename)))
+        self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+        self.assertRaises(UpgradeError, UpgradePrincipalCollectionInMemory.doUpgrade)
+        
+        # Check post-conditions
+        self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.isdir(os.path.join(config.DocumentRoot, "principals")))
+        self.assertTrue(os.path.exists(os.path.join(config.DocumentRoot, "principals", CalendarUserProxyDatabase.dbOldFilename)))
+        self.assertTrue(os.path.exists(os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)))
+
+class UpgradeSQLProps (twistedcaldav.test.util.TestCase):
+    """
+    SQL properties tests
+    """
+    data_dir = os.path.join(os.path.dirname(__file__), "data")
+
+    props = (
+        davxml.DisplayName.fromString("My Name"),
+        davxml.ACL(
+            davxml.ACE(
+                davxml.Principal(davxml.Authenticated()),
+                davxml.Grant(davxml.Privilege(davxml.Read())),
+                davxml.Protected(),
+            ),
+        ),
+        caldavxml.CalendarDescription.fromString("My Calendar"),
+    )
+    
+    def setUp(self):
+        
+        super(UpgradeSQLProps, self).setUp()
+
+        # First setup an xattr document root
+        self._setUpTestDocuments()
+
+        # Set to use sqlite prop store
+        config.PropertyStore = "SQL"
+        config.updatePropertyStore()
+        
+        # Do the upgrade
+        UpgradeXattrsToSqlite.doUpgrade()
+        
+    def _setUpTestDocuments(self):
+
+        config.PropertyStore = "xattr"
+        config.updatePropertyStore()
+
+        self.xattr_root, _ignore_url = self.mkdtemp("docs")
+        config.DocumentRoot = self.xattr_root
+        os.mkdir(os.path.join(self.xattr_root, "calendars"))
+        os.mkdir(os.path.join(self.xattr_root, "calendars", "__uids__"))
+        
+        os.mkdir(os.path.join(self.xattr_root, "calendars", "__uids__", "12"))
+        os.mkdir(os.path.join(self.xattr_root, "calendars", "__uids__", "12", "34"))
+        self.user_home = os.path.join(self.xattr_root, "calendars", "__uids__", "12", "34", "12345-6789-10")
+        os.mkdir(self.user_home)
+        
+        self.user_calendar = os.path.join(self.user_home, "calendar")
+        os.mkdir(self.user_calendar)
+
+        self.user_event = os.path.join(self.user_calendar, "12345.ics")
+        f = open(self.user_event, "w")
+        f.close()
+        
+        # xattrs on user home
+        resource = CalDAVFile(self.user_home)
+        resource.deadProperties().set(davxml.QuotaAvailableBytes.fromString("1000"))
+        resource.deadProperties().set(davxml.QuotaUsedBytes.fromString("500"))
+        
+        # xattrs on user calendar
+        resource = CalDAVFile(self.user_calendar)
+        resource.deadProperties().set(davxml.ResourceType.calendar) #@UndefinedVariable
+        resource.deadProperties().set(davxml.DisplayName.fromString("A Calendar"))
+        resource.deadProperties().set(caldavxml.CalendarDescription.fromString("A Calendar"))
+        resource.deadProperties().set(customxml.GETCTag.fromString("2008-06-30T13:00:00-123456"))
+        
+        # xattr on calendar resource
+        resource = CalDAVFile(self.user_event)
+        resource.deadProperties().set(davxml.GETContentType.fromString("text/calendar"))
+        resource.deadProperties().set(TwistedGETContentMD5.fromString("ABCDEF-12345"))
+        
+    def _testProperty(self, path, prop, description = ""):
+
+        if path == self.user_home:
+            class DummyCalendarHomeFile(CalendarHomeFile):
+                
+                def __init__(self, path):
+                    CalDAVFile.__init__(self, path)
+
+            resource = DummyCalendarHomeFile(path)
+        else:
+            resource = CalDAVFile(path)
+
+        self.assertTrue(resource.deadProperties().contains(prop.qname()),
+                        msg="Could not find property %s %r." % (description, prop,))
+        self.assertTrue(resource.deadProperties().get(prop.qname()) == prop,
+                        msg="Could not get property %s %r." % (description, prop,))
+    
+    def _testNoXattrs(self, path):
+        
+        x = xattr.xattr(path)
+        self.assertTrue(len(x) == 0)
+
+    def test_root(self):
+        self.assertFalse(os.path.exists(os.path.join(self.xattr_root, SQLPropertiesDatabase.dbFilename)))
+        self._testNoXattrs(self.xattr_root)
+
+    def test_calendars(self):
+        self.assertFalse(os.path.exists(os.path.join(self.xattr_root, "calendars", SQLPropertiesDatabase.dbFilename)))
+        self._testNoXattrs(os.path.join(self.xattr_root, "calendars"))
+
+    def test_uids(self):
+        self.assertFalse(os.path.exists(os.path.join(self.xattr_root, "calendars", "__uids__", SQLPropertiesDatabase.dbFilename)))
+        self._testNoXattrs(os.path.join(self.xattr_root, "calendars", "__uids__"))
+
+    def test_hashed(self):
+        self.assertFalse(os.path.exists(os.path.join(self.xattr_root, "calendars", "__uids__", "12", SQLPropertiesDatabase.dbFilename)))
+        self._testNoXattrs(os.path.join(self.xattr_root, "calendars", "__uids__", "12"))
+        self.assertFalse(os.path.exists(os.path.join(self.xattr_root, "calendars", "__uids__", "12", "34", SQLPropertiesDatabase.dbFilename)))
+        self._testNoXattrs(os.path.join(self.xattr_root, "calendars", "__uids__", "12", "34"))
+
+    def test_user_home(self):
+        self.assertTrue(os.path.exists(os.path.join(self.user_home, SQLPropertiesDatabase.dbFilename)))
+        self._testProperty(self.user_home, davxml.QuotaAvailableBytes.fromString("1000"), "on user home") #@UndefinedVariable
+        self._testProperty(self.user_home, davxml.QuotaUsedBytes.fromString("500"), "on user home")
+        self._testNoXattrs(self.user_home)
+
+    def test_user_calendar(self):
+        self.assertTrue(os.path.exists(os.path.join(self.user_home, SQLPropertiesDatabase.dbFilename)))
+        self.assertTrue(os.path.exists(os.path.join(self.user_calendar, SQLPropertiesDatabase.dbFilename)))
+        self._testProperty(self.user_calendar, davxml.ResourceType.calendar, "on user calendar") #@UndefinedVariable
+        self._testProperty(self.user_calendar, davxml.DisplayName.fromString("A Calendar"), "on user calendar")
+        self._testProperty(self.user_calendar, caldavxml.CalendarDescription.fromString("A Calendar"), "on user calendar")
+        self._testProperty(self.user_calendar, customxml.GETCTag.fromString("2008-06-30T13:00:00-123456"), "on user calendar")
+        self._testNoXattrs(self.user_calendar)
+
+    def test_user_event(self):
+        self.assertTrue(os.path.exists(os.path.join(self.user_calendar, SQLPropertiesDatabase.dbFilename)))
+        self._testProperty(self.user_event, davxml.GETContentType.fromString("text/calendar"), "on user event")
+        self._testProperty(self.user_event, TwistedGETContentMD5.fromString("ABCDEF-12345"), "on user event")
+        self._testNoXattrs(self.user_event)

Added: CalendarServer/branches/sqlpropstore-2629/twistedcaldav/upgrade.py
===================================================================
--- CalendarServer/branches/sqlpropstore-2629/twistedcaldav/upgrade.py	                        (rev 0)
+++ CalendarServer/branches/sqlpropstore-2629/twistedcaldav/upgrade.py	2008-06-30 21:12:03 UTC (rev 2645)
@@ -0,0 +1,145 @@
+##
+# Copyright (c) 2006-2007 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 twistedcaldav.config import config
+from twisted.python.filepath import FilePath
+from twisted.web2.dav.xattrprops import xattrPropertyStore
+from twistedcaldav.sqlprops import sqlPropertyStore
+from twistedcaldav.log import Logger
+from twisted.web2.dav.fileop import rmdir
+from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyDatabase
+import os
+
+log = Logger()
+
+class UpgradeTheServer(object):
+    
+    @staticmethod
+    def doUpgrade():
+        
+        UpgradePrincipalCollectionInMemory.doUpgrade()
+        UpgradeXattrsToSqlite.doUpgrade()
+
+class UpgradePrincipalCollectionInMemory(object):
+
+    @classmethod
+    def doUpgrade(cls):
+        
+        # Look for the /principals/ directory on disk
+        old_principals = os.path.join(config.DocumentRoot, "principals")
+        if os.path.exists(old_principals):
+            # First move the proxy database and rename it
+            cls._doProxyDatabaseMoveUpgrade()
+        
+            # Now delete the on disk representation of principals
+            rmdir(old_principals)
+            log.info(
+                "Removed the old principal directory at '%s'."
+                % (old_principals,)
+            )
+
+    @classmethod
+    def _doProxyDatabaseMoveUpgrade(cls):
+        
+        # See if the old DB is present
+        old_db_path = os.path.join(config.DocumentRoot, "principals", CalendarUserProxyDatabase.dbOldFilename)
+        if not os.path.exists(old_db_path):
+            # Nothing to be done
+            return
+        
+        # See if the new one is already present
+        new_db_path = os.path.join(config.DataRoot, CalendarUserProxyDatabase.dbFilename)
+        if os.path.exists(new_db_path):
+            # We have a problem - both the old and new ones exist. Stop the server from starting
+            # up and alert the admin to this condition
+            raise UpgradeError(
+                "Upgrade Error: unable to move the old calendar user proxy database at '%s' to '%s' because the new database already exists."
+                % (old_db_path, new_db_path,)
+            )
+        
+        # Now move the old one to the new location
+        try:
+            os.rename(old_db_path, new_db_path)
+        except Exception, e:
+            raise UpgradeError(
+                "Upgrade Error: unable to move the old calendar user proxy database at '%s' to '%s' due to %s."
+                % (old_db_path, new_db_path, str(e))
+            )
+            
+        log.info(
+            "Moved the calendar user proxy database from '%s' to '%s'."
+            % (old_db_path, new_db_path,)
+        )
+
+class UpgradeXattrsToSqlite(object):
+
+    @classmethod
+    def doUpgrade(cls):
+        """
+        Upgrade xattr properties to sqlite properties.
+        """
+        
+        if config.PropertyStore == "SQL":
+            log.info("Doing xattr->sqlite property upgrade")
+            docroot = config.DocumentRoot
+            cls._upgradeItem(docroot, "")
+    
+    @classmethod
+    def _upgradeItem(cls, docroot, path):
+        
+        # Upgrade the properties at this path first
+        cls._upgradeXAttrs(docroot, path)
+        
+        fullpath = os.path.join(docroot, path)
+        if os.path.isdir(fullpath):
+            for child in os.listdir(fullpath):
+                if child[0] == '.':
+                    continue
+                childpath = os.path.join(path, child)
+                cls._upgradeItem(docroot, childpath)
+    
+    @classmethod
+    def _upgradeXAttrs(cls, docroot, path):
+        
+        class DummyResource:
+            
+            def __init__(self, path):
+                self.fp = FilePath(path)
+        
+            def isCollection(self):
+                return self.fp.isdir()
+    
+        log.debug("Doing xattr->sqlite property upgrade for: %s" % (path,))
+        resource = DummyResource(os.path.join(docroot, path))
+        xprops = xattrPropertyStore(resource)
+        
+        # Detect some special cases for sql properties
+        root_resource = (len(path) == 0)
+        avoid_contention = path.startswith("calendars/__uids__/") and len(path.split("/")) == 5
+        sqlprops = sqlPropertyStore(resource, root_resource=root_resource, avoid_contention=avoid_contention)
+        
+        for propname in list(xprops.list()):
+            try:
+                sqlprops.set(xprops.get(propname))
+            except Exception, e:
+                log.debug("Unable to upgrade property '%s' on '%s' because of %s" % (propname, path, e,))
+            xprops.delete(propname)
+
+
+class UpgradeError(RuntimeError):
+    """
+    Generic upgrade error.
+    """
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080630/115debac/attachment-0001.html 


More information about the calendarserver-changes mailing list