~ubuntu-branches/ubuntu/utopic/calendarserver/utopic

« back to all changes in this revision

Viewing changes to contrib/migration/59_calendarmigrator.py

  • Committer: Package Import Robot
  • Author(s): Rahul Amaram
  • Date: 2012-05-29 18:12:12 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20120529181212-mxjdfncopy6vou0f
Tags: 3.2+dfsg-1
* New upstream release
* Moved from using cdbs to dh sequencer
* Modified calenderserver init.d script based on /etc/init.d/skeleton script
* Removed ldapdirectory.patch as the OpenLDAP directory service has been 
  merged upstream
* Moved package to section "net" as calendarserver is more service than 
  library (Closes: #665859)
* Changed Architecture of calendarserver package to any as the package
  now includes compiled architecture dependent Python extensions
* Unowned files are no longer left on the system upon purging
  (Closes: #668731)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
#
3
 
# MigrationExtra script to maintain the enabled/disabled state of the
4
 
# calendar server.
5
 
#
6
 
# This script examines the launchd preferences from the previous system
7
 
# (also taking into account the overrides.plist) and then invokes serveradmin
8
 
# to start/stop calendar server.
9
 
#
10
 
# The only argument this script currently cares about is --sourceRoot, which
11
 
# should point to the root of the previous system.
12
 
#
13
 
# Copyright (c) 2005-2009 Apple Inc.  All Rights Reserved.
14
 
#
15
 
# IMPORTANT NOTE:  This file is licensed only for use on Apple-labeled
16
 
# computers and is subject to the terms and conditions of the Apple
17
 
# Software License Agreement accompanying the package this file is a
18
 
# part of.  You may not port this file to another platform without
19
 
# Apple's written consent.
20
 
 
21
 
from __future__ import with_statement
22
 
 
23
 
import datetime
24
 
import optparse
25
 
import os
26
 
import plistlib
27
 
import shutil
28
 
import subprocess
29
 
import sys
30
 
 
31
 
LAUNCHD_KEY = "org.calendarserver.calendarserver"
32
 
LOG = "/Library/Logs/Migration/calendarmigrator.log"
33
 
SERVICE_NAME = "calendar"
34
 
LAUNCHD_OVERRIDES = "var/db/launchd.db/com.apple.launchd/overrides.plist"
35
 
LAUNCHD_PREFS_DIR = "System/Library/LaunchDaemons"
36
 
CALDAVD_CONFIG_DIR = "private/etc/caldavd"
37
 
 
38
 
def main():
39
 
 
40
 
    optionParser = optparse.OptionParser()
41
 
 
42
 
    optionParser.add_option('--purge', choices=('0', '1'),
43
 
        metavar='[0|1]',
44
 
        help='remove old files after migration (IGNORED)')
45
 
 
46
 
    optionParser.add_option('--sourceRoot', type='string',
47
 
        metavar='DIR',
48
 
        help='path to the root of the system to migrate')
49
 
 
50
 
    optionParser.add_option('--sourceType', type='string',
51
 
        metavar='[System|TimeMachine]',
52
 
        help='migration source type (IGNORED)')
53
 
 
54
 
    optionParser.add_option('--sourceVersion', type='string',
55
 
        metavar='10.X.X',
56
 
        help='version number of previous system (IGNORED)')
57
 
 
58
 
    optionParser.add_option('--targetRoot', type='string',
59
 
        metavar='DIR',
60
 
        help='path to the root of the new system',
61
 
        default='/')
62
 
 
63
 
    optionParser.add_option('--language', choices=('en', 'fr', 'de', 'ja'),
64
 
        metavar='[en|fr|de|ja]',
65
 
        help='language identifier (IGNORED)')
66
 
 
67
 
    (options, args) = optionParser.parse_args()
68
 
 
69
 
    if options.sourceRoot:
70
 
 
71
 
        if os.path.exists(options.sourceRoot):
72
 
            migrateConfiguration(options)
73
 
            migrateRunState(options)
74
 
 
75
 
    else:
76
 
        log("ERROR: --sourceRoot must be specified")
77
 
        sys.exit(1)
78
 
 
79
 
 
80
 
def migrateRunState(options):
81
 
    """
82
 
    Try to determine whether server was running in previous system, then
83
 
    modify the launchd settings in the new system.
84
 
    """
85
 
 
86
 
    try:
87
 
        disabled = isServiceDisabled(options.sourceRoot, LAUNCHD_KEY)
88
 
        log("Service '%s' was previously %s" %
89
 
            (LAUNCHD_KEY, "disabled" if disabled else "enabled"))
90
 
    except ServiceStateError, e:
91
 
        log("Couldn't determine previous state of service '%s': %s" %
92
 
            (LAUNCHD_KEY, e))
93
 
        return
94
 
 
95
 
    setServiceStateDisabled(options.targetRoot, LAUNCHD_KEY, disabled)
96
 
 
97
 
 
98
 
def migrateConfiguration(options):
99
 
    """
100
 
    Copy files/directories/symlinks from previous system's /etc/caldavd
101
 
 
102
 
    Skips anything ending in ".default".
103
 
    Regular files overwrite copies in new system.
104
 
    Directories and symlinks only copied over if they don't overwrite anything.
105
 
    """
106
 
 
107
 
    oldConfigDir = os.path.join(options.sourceRoot, CALDAVD_CONFIG_DIR)
108
 
    if not os.path.exists(oldConfigDir):
109
 
        log("Old configuration directory does not exist: %s" % (oldConfigDir,))
110
 
        return
111
 
 
112
 
    newConfigDir = os.path.join(options.targetRoot, CALDAVD_CONFIG_DIR)
113
 
    if not os.path.exists(newConfigDir):
114
 
        log("New configuration directory does not exist: %s" % (newConfigDir,))
115
 
        return
116
 
 
117
 
    log("Copying configuration files from %s to %s" % (oldConfigDir, newConfigDir))
118
 
 
119
 
    for name in os.listdir(oldConfigDir):
120
 
 
121
 
        if not name.endswith(".default"):
122
 
 
123
 
            oldPath = os.path.join(oldConfigDir, name)
124
 
            newPath = os.path.join(newConfigDir, name)
125
 
 
126
 
            if os.path.islink(oldPath) and not os.path.exists(newPath):
127
 
                # Recreate the symlink if it won't overwrite an existing file
128
 
                link = os.readlink(oldPath)
129
 
                log("Symlinking %s to %s" % (newPath, link))
130
 
                os.symlink(link, newPath)
131
 
 
132
 
            elif os.path.isfile(oldPath):
133
 
 
134
 
                if name == "caldavd.plist":
135
 
                    # Migrate certain settings from the old plist to new:
136
 
                    log("Parsing %s" % (oldPath,))
137
 
                    oldPlist = plistlib.readPlist(oldPath)
138
 
                    if os.path.exists(newPath):
139
 
                        log("Parsing %s" % (newPath,))
140
 
                        newPlist = plistlib.readPlist(newPath)
141
 
                        log("Removing %s" % (newPath,))
142
 
                        os.remove(newPath)
143
 
                    else:
144
 
                        newPlist = { }
145
 
                    log("Processing %s" % (oldPath,))
146
 
                    mergePlist(oldPlist, newPlist)
147
 
                    log("Writing %s" % (newPath,))
148
 
                    plistlib.writePlist(newPlist, newPath)
149
 
 
150
 
                else:
151
 
                    # Copy the file over, overwriting copy in newConfigDir
152
 
                    log("Copying file %s to %s" % (oldPath, newConfigDir))
153
 
                    shutil.copy2(oldPath, newConfigDir)
154
 
 
155
 
 
156
 
            elif os.path.isdir(oldPath) and not os.path.exists(newPath):
157
 
                # Copy the dir over, but only if new one doesn't exist
158
 
                log("Copying directory %s to %s" % (oldPath, newPath))
159
 
                shutil.copytree(oldPath, newPath, symlinks=True)
160
 
 
161
 
def mergePlist(oldPlist, newPlist):
162
 
 
163
 
    # The following CalendarServer v1.x keys are ignored:
164
 
    # EnableNotifications, Verbose
165
 
 
166
 
    # These keys are copied verbatim:
167
 
    for key in (
168
 
        "AccessLogFile", "AdminPrincipals", "BindAddresses", "BindHTTPPorts",
169
 
        "BindSSLPorts", "ControlSocket", "DocumentRoot", "EnableDropBox",
170
 
        "EnableProxyPrincipals", "EnableSACLs", "ErrorLogFile", "GroupName",
171
 
        "HTTPPort", "MaximumAttachmentSize", "MultiProcess", "PIDFile",
172
 
        "ProcessType", "ResponseCompression", "RotateAccessLog",
173
 
        "SSLAuthorityChain", "SSLCertificate", "SSLPort", "SSLPrivateKey",
174
 
        "ServerHostName", "ServerStatsFile", "SudoersFile", "UserName",
175
 
        "UserQuota",
176
 
    ):
177
 
        if key in oldPlist:
178
 
            newPlist[key] = oldPlist[key]
179
 
 
180
 
    # "Wiki" is a new authentication in v2.x; copy all "Authentication" sub-keys    # over, and "Wiki" will be picked up from the new plist:
181
 
    if "Authentication" in oldPlist:
182
 
        for key in oldPlist["Authentication"]:
183
 
            newPlist["Authentication"][key] = oldPlist["Authentication"][key]
184
 
 
185
 
    # Strip out any unknown params from the DirectoryService:
186
 
    if "DirectoryService" in oldPlist:
187
 
        newPlist["DirectoryService"] = oldPlist["DirectoryService"]
188
 
        for key in newPlist["DirectoryService"]["params"].keys():
189
 
            if key not in (
190
 
                "node", "restrictEnabledRecords", "restrictToGroup",
191
 
                "cacheTimeout", "xmlFile"
192
 
            ):
193
 
                del newPlist["DirectoryService"]["params"][key]
194
 
 
195
 
    # Place DataRoot as a sibling of DocumentRoot:
196
 
    parent = os.path.dirname(newPlist["DocumentRoot"].rstrip("/"))
197
 
    newPlist["DataRoot"] = os.path.join(parent, "Data")
198
 
 
199
 
 
200
 
def isServiceDisabled(source, service):
201
 
    """
202
 
    Returns whether or not a service is disabled
203
 
 
204
 
    @param source: System root to examine
205
 
    @param service: launchd key representing service
206
 
    @return: True if service is disabled, False if enabled
207
 
    """
208
 
 
209
 
    overridesPath = os.path.join(source, LAUNCHD_OVERRIDES)
210
 
    if os.path.isfile(overridesPath):
211
 
        overrides = plistlib.readPlist(overridesPath)
212
 
        try:
213
 
            return overrides[service]['Disabled']
214
 
        except KeyError:
215
 
            # Key is not in the overrides.plist, continue on
216
 
            pass
217
 
 
218
 
    prefsPath = os.path.join(source, LAUNCHD_PREFS_DIR, "%s.plist" % service)
219
 
    if os.path.isfile(prefsPath):
220
 
        prefs = plistlib.readPlist(prefsPath)
221
 
        try:
222
 
            return prefs['Disabled']
223
 
        except KeyError:
224
 
            return False
225
 
 
226
 
    raise ServiceStateError("Neither %s nor %s exist" %
227
 
        (overridesPath, prefsPath))
228
 
 
229
 
 
230
 
def setServiceStateDisabled(target, service, disabled):
231
 
    """
232
 
    Modifies launchd settings for a service
233
 
 
234
 
    @param target: System root
235
 
    @param service: launchd key representing service
236
 
    @param disabled: boolean
237
 
    """
238
 
 
239
 
    overridesPath = os.path.join(target, LAUNCHD_OVERRIDES)
240
 
    if os.path.isfile(overridesPath):
241
 
        overrides = plistlib.readPlist(overridesPath)
242
 
        if not overrides.has_key(service):
243
 
            overrides[service] = { }
244
 
        overrides[service]['Disabled'] = disabled
245
 
        plistlib.writePlist(overrides, overridesPath)
246
 
 
247
 
 
248
 
class ServiceStateError(Exception):
249
 
    """
250
 
    Could not determine service state
251
 
    """
252
 
 
253
 
 
254
 
def log(msg):
255
 
    try:
256
 
        with open(LOG, 'a') as output:
257
 
            timestamp = datetime.datetime.now().strftime("%b %d %H:%M:%S")
258
 
            output.write("%s %s\n" % (timestamp, msg))
259
 
    except IOError:
260
 
        # Could not write to log
261
 
        pass
262
 
 
263
 
 
264
 
if __name__ == '__main__':
265
 
    main()