~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to setup/configure.py

ivle.config.Config: __init__ now takes a 'blank' argument, which allows it to
    create a blank config without trying to load an existing one.
setup.configure: Now instantiates an ivle.config.Config(blank=True), rather
    than a generic configobj.ConfigObj.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
1
# IVLE - Informatics Virtual Learning Environment
3
2
# Copyright (C) 2007-2009 The University of Melbourne
4
3
#
16
15
# along with this program; if not, write to the Free Software
17
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
17
 
 
18
# Module: setup/config
19
19
# Author: Matt Giuca, Refactored by David Coles
 
20
# Date:   03/07/2008
20
21
 
21
22
'''Configures IVLE with machine-specific details, most notably, various paths.
22
23
Either prompts the administrator for these details or accepts them as
23
24
command-line args.
24
25
 
25
 
Creates etc/ivle.conf
 
26
Creates ivle/conf/conf.py and bin/trampoline/trampoline/conf.h.
26
27
'''
27
28
 
28
29
import optparse
29
30
import getopt
30
31
import os
31
32
import sys
32
 
import stat
33
33
import hashlib
34
34
import uuid
35
35
 
 
36
from setup.util import query_user
36
37
import ivle.config
37
38
 
38
39
import configobj
39
40
 
 
41
# conf_options maps option names to values
 
42
conf_options = {}
 
43
 
40
44
class ConfigOption:
41
45
    """A configuration option; one of the things written to conf.py."""
42
46
    def __init__(self, option_name, default, prompt, comment, ask=True):
75
79
# ('/usr/local' for the usual install, '/usr' for distribution packages)""",
76
80
    ask=False))
77
81
 
 
82
config_options.append(ConfigOption("paths/site_packages",
 
83
    None,
 
84
    """site-packages directory in Python, where Python libraries are to be
 
85
installed. May be left as the default, in which case the value will be
 
86
computed from prefix and the current Python version:""",
 
87
    """
 
88
# 'site-packages' directory in Python, where Python libraries are to be
 
89
# installed. May be None (recommended), in which case the value will be
 
90
# computed from prefix and the current Python version.""", ask=False))
 
91
 
78
92
config_options.append(ConfigOption("paths/data",
79
93
    "/var/lib/ivle",
80
94
    "In the local file system, where user-modifiable data files should be "
92
106
# In the local file system, where IVLE error logs should be located.""",
93
107
    ask=False))
94
108
 
95
 
config_options.append(ConfigOption("urls/public_host",
96
 
    "public.ivle.localhost",
 
109
config_options.append(ConfigOption("urls/public_host", "public.localhost",
97
110
    """Hostname which will cause the server to go into "public mode",
98
111
providing login-free access to student's published work:""",
99
112
    """
104
117
# Private mode (normal mode) requires login, and only serves files relevant to
105
118
# the logged-in user."""))
106
119
 
107
 
config_options.append(ConfigOption("media/version", None,
108
 
    """Version of IVLE media resources (must change on each upgrade):""",
 
120
config_options.append(ConfigOption("os/allowed_uids", "33",
 
121
    """UID of the web server process which will run IVLE.
 
122
Only this user may execute the trampoline. May specify multiple users as
 
123
a comma-separated list.
 
124
    (eg. "1002,78")""",
109
125
    """
110
 
# Version string for IVLE media resource URLs. When set, they are aggressively
111
 
# cached by the browser, so it must be either left unset or changed each time
112
 
# a media file is changed.""", ask=False))
 
126
# The User-ID of the web server process which will run IVLE, and any other
 
127
# users who are allowed to run the trampoline. This is stores as a string of
 
128
# comma-separated integers, simply because it is not used within Python, only
 
129
# used by the setup program to write to conf.h (see setup.py config).""",
 
130
    ask=False))
113
131
 
114
132
config_options.append(ConfigOption("database/host", "localhost",
115
133
    """PostgreSQL Database config
128
146
    """
129
147
# Database name"""))
130
148
 
 
149
config_options.append(ConfigOption("plugins/forum/dbname", "ivle_forum",
 
150
    """Forum Database name:""",
 
151
    """
 
152
# Forum Database name"""))
 
153
 
131
154
config_options.append(ConfigOption("database/username", "postgres",
132
155
    """Username for DB server login:""",
133
156
    """
135
158
 
136
159
config_options.append(ConfigOption("database/password", "",
137
160
    """Password for DB server login:
138
 
    (Caution: This password is stored in plaintext!)""",
 
161
    (Caution: This password is stored in plaintext in ivle/conf/conf.py)""",
139
162
    """
140
163
# Database password"""))
141
164
 
178
201
# other modules may be plugged in to pulldown against organisation-specific
179
202
# pulldown backends.""", ask=False))
180
203
 
181
 
config_options.append(ConfigOption("urls/svn_addr",
182
 
    "http://svn.ivle.localhost/",
 
204
config_options.append(ConfigOption("urls/svn_addr", "http://svn.localhost/",
183
205
    """Subversion config
184
206
=================
185
207
The base url for accessing subversion repositories:""",
203
225
    """
204
226
# The password for the usrmgt-server.""", ask=False))
205
227
 
206
 
def query_user(default, prompt):
207
 
    """Prompts the user for a string, which is read from a line of stdin.
208
 
    Exits silently if EOF is encountered. Returns the string, with spaces
209
 
    removed from the beginning and end.
210
 
 
211
 
    Returns default if a 0-length line (after spaces removed) was read.
212
 
    """
213
 
    if default is None:
214
 
        # A default of None means the value will be computed specially, so we
215
 
        # can't really tell you what it is
216
 
        defaultstr = "computed"
217
 
    elif isinstance(default, basestring):
218
 
        defaultstr = '"%s"' % default
219
 
    else:
220
 
        defaultstr = repr(default)
221
 
    sys.stdout.write('%s\n    (default: %s)\n>' % (prompt, defaultstr))
222
 
    try:
223
 
        val = sys.stdin.readline()
224
 
    except KeyboardInterrupt:
225
 
        # Ctrl+C
226
 
        sys.stdout.write("\n")
227
 
        sys.exit(1)
228
 
    sys.stdout.write("\n")
229
 
    # If EOF, exit
230
 
    if val == '': sys.exit(1)
231
 
    # If empty line, return default
232
 
    val = val.strip()
233
 
    if val == '': return default
234
 
    return val
235
 
 
236
228
def configure(args):
 
229
    # Call the real function
 
230
    return __configure(args)
 
231
 
 
232
def __configure(args):
237
233
    # Try importing existing conf, but if we can't just set up defaults
238
234
    # The reason for this is that these settings are used by other phases
239
235
    # of setup besides conf, so we need to know them.
240
236
    # Also this allows you to hit Return to accept the existing value.
241
 
    try:
242
 
        conf = ivle.config.Config()
243
 
    except ivle.config.ConfigError:
244
 
        # Couldn't find a config file anywhere.
245
 
        # Create a new blank config object (not yet bound to a file)
246
 
        # All lookups (below) will fail, so it will be initialised with all
247
 
        # the default values.
248
 
        conf = ivle.config.Config(blank=True)
249
 
 
250
 
    # Check that all the options are present, and if not, load the default
 
237
    conf = ivle.config.Config()
251
238
    for opt in config_options:
252
239
        try:
253
 
            conf.get_by_path(opt.option_name)
 
240
            conf_options[opt.option_name] = conf.get_by_path(opt.option_name)
254
241
        except KeyError:
255
 
            # If the default is None, omit it
256
 
            # Else ConfigObj will write the string 'None' to the conf file
257
 
            if opt.default is not None:
258
 
                conf.set_by_path(opt.option_name, opt.default)
259
 
 
260
 
    # Store comments in the conf object
261
 
    for opt in config_options:
262
 
        # Omitted if the key doesn't exist
263
 
        conf.set_by_path(opt.option_name, comment=opt.comment)
 
242
            conf_options[opt.option_name] = opt.default
264
243
 
265
244
    # Set up some variables
266
245
    cwd = os.getcwd()
267
246
 
268
247
    # the files that will be created/overwritten
269
 
    try:
270
 
        confdir = os.environ['IVLECONF']
271
 
    except KeyError:
272
 
        confdir = '/etc/ivle'
273
 
 
274
 
    conffile = os.path.join(confdir, 'ivle.conf')
275
 
    plugindefaultfile = os.path.join(confdir, 'plugins.d/000default.conf')
 
248
    conffile = os.path.join(cwd, "etc/ivle.conf")
 
249
    conf_hfile = os.path.join(cwd, "bin/trampoline/conf.h")
 
250
    phpBBconffile = os.path.join(cwd, "www/php/phpBB3/config.php")
276
251
 
277
252
    # Get command-line arguments to avoid asking questions.
278
253
 
288
263
    if opts == []:
289
264
        # Interactive mode. Prompt the user for all the values.
290
265
 
291
 
        print """This tool will create %s, prompting you for details about
292
 
your configuration. The file will be updated with modified options if it already
293
 
exists. If it does not already exist, it will be created with sane defaults and
294
 
restrictive permissions.
295
 
 
296
 
%s will also be overwritten with the default list of plugins.
 
266
        print """This tool will create the following files:
 
267
    %s
 
268
    %s
 
269
    %s
 
270
prompting you for details about your configuration. The file will be
 
271
overwritten if it already exists. It will *not* install or deploy IVLE.
297
272
 
298
273
Please hit Ctrl+C now if you do not wish to do this.
299
 
""" % (conffile, plugindefaultfile)
 
274
""" % (conffile, conf_hfile, phpBBconffile)
300
275
 
301
276
        # Get information from the administrator
302
277
        # If EOF is encountered at any time during the questioning, just exit
304
279
 
305
280
        for opt in config_options:
306
281
            if opt.ask:
307
 
                conf.set_by_path(opt.option_name,
308
 
                    query_user(conf.get_by_path(opt.option_name), opt.prompt))
 
282
                conf_options[opt.option_name] = \
 
283
                    query_user(conf_options[opt.option_name], opt.prompt)
309
284
    else:
310
285
        opts = dict(opts)
311
286
        # Non-interactive mode. Parse the options.
312
287
        for opt in config_options:
313
288
            if '--' + opt.option_name in opts:
314
 
                conf.set_by_path(opt.option_name,
315
 
                                 opts['--' + opt.option_name])
 
289
                conf_options[opt.option_name] = opts['--' + opt.option_name]
316
290
 
317
291
    # Error handling on input values
318
292
    try:
319
 
        conf['database']['port'] = int(conf['database']['port'])
320
 
        if (conf['database']['port'] < 0
321
 
            or conf['database']['port'] >= 65536):
 
293
        allowed_uids_list = map(int,
 
294
                                conf_options['os/allowed_uids'].split(','))
 
295
    except ValueError:
 
296
        print >>sys.stderr, (
 
297
        "Invalid UID list (%s).\n"
 
298
        "Must be a comma-separated list of integers." %
 
299
            conf_options['os/allowed_uids'])
 
300
        return 1
 
301
    try:
 
302
        conf_options['database/port'] = int(conf_options['database/port'])
 
303
        if (conf_options['database/port'] < 0
 
304
            or conf_options['database/port'] >= 65536):
322
305
            raise ValueError()
323
306
    except ValueError:
324
 
        if conf['database']['port'] == '' or conf['database']['port'] is None:
325
 
            pass
326
 
        else:
327
 
            print >>sys.stderr, (
328
 
            "Invalid DB port (%s).\n"
329
 
            "Must be an integer between 0 and 65535." %
330
 
                repr(conf['database']['port']))
331
 
            return 1
 
307
        print >>sys.stderr, (
 
308
        "Invalid DB port (%s).\n"
 
309
        "Must be an integer between 0 and 65535." %
 
310
            repr(conf_options['database/port']))
 
311
        return 1
332
312
    try:
333
 
        conf['usrmgt']['port'] = int(conf['usrmgt']['port'])
334
 
        if (conf['usrmgt']['port'] < 0 or conf['usrmgt']['port'] >= 65536):
 
313
        conf_options['usrmgt/port'] = int(conf_options['usrmgt/port'])
 
314
        if (conf_options['usrmgt/port'] < 0
 
315
            or conf_options['usrmgt/port'] >= 65536):
335
316
            raise ValueError()
336
317
    except ValueError:
337
318
        print >>sys.stderr, (
338
319
        "Invalid user management port (%s).\n"
339
320
        "Must be an integer between 0 and 65535." %
340
 
            repr(conf['usrmgt']['port']))
 
321
            repr(conf_options['usrmgt/port']))
341
322
        return 1
342
323
 
343
324
    # By default we generate the magic randomly.
344
 
    try:
345
 
        conf['usrmgt']['magic']     # Throw away; just check for KeyError
346
 
    except KeyError:
347
 
        conf['usrmgt']['magic'] = hashlib.md5(uuid.uuid4().bytes).hexdigest()
348
 
 
349
 
    clobber_permissions = not os.path.exists(conffile)
350
 
 
351
 
    # Write ./etc/ivle.conf (even if we loaded from a different filename)
 
325
    if conf_options['usrmgt/magic'] is None:
 
326
        conf_options['usrmgt/magic'] = \
 
327
            hashlib.md5(uuid.uuid4().bytes).hexdigest()
 
328
 
 
329
    # Generate the forum secret
 
330
    forum_secret = hashlib.md5(uuid.uuid4().bytes).hexdigest()
 
331
 
 
332
    # Write ./etc/ivle.conf
 
333
 
 
334
    conf = ivle.config.Config(blank=True)
352
335
    conf.filename = conffile
 
336
 
353
337
    conf.initial_comment = ["# IVLE Configuration File"]
 
338
 
 
339
    # Add the forum secret to the config file (regenerated each config)
 
340
    config_options.append(ConfigOption('plugins/forum/secret', None, '', ''))
 
341
    conf_options['plugins/forum/secret'] = forum_secret
 
342
 
 
343
    for opt in config_options:
 
344
        newopt_path = opt.option_name.split('/')
 
345
        # Iterate over each segment of the path, and find the section in conf
 
346
        # file to insert the value into (use all but the last path segment)
 
347
        conf_section = conf
 
348
        for seg in newopt_path[:-1]:
 
349
            # Create the section if it isn't there
 
350
            if seg not in conf_section:
 
351
                conf_section[seg] = {}
 
352
            conf_section = conf_section[seg]
 
353
        # The final path segment names the key to insert into
 
354
        keyname = newopt_path[-1]
 
355
        value = conf_options[opt.option_name]
 
356
        if value is not None:
 
357
            conf_section[keyname] = value
 
358
            conf_section.comments[keyname] = opt.comment.split('\n')
 
359
 
354
360
    conf.write()
355
361
 
356
 
    # We need to restrict permissions on a new file, as it contains
357
 
    # a nice database password.
358
 
    if clobber_permissions:
359
 
        os.chown(conffile, 33, 33) # chown to www-data
360
 
        os.chmod(conffile, stat.S_IRUSR | stat.S_IWUSR) # No g/o perms!
361
 
 
362
362
    print "Successfully wrote %s" % conffile
363
363
 
364
 
    plugindefault = open(plugindefaultfile, 'w')
365
 
    plugindefault.write("""# IVLE default plugin configuration file
366
 
[ivle.webapp.core#Plugin]
367
 
[ivle.webapp.admin.user#Plugin]
368
 
[ivle.webapp.tutorial#Plugin]
369
 
[ivle.webapp.admin.subject#Plugin]
370
 
[ivle.webapp.filesystem.browser#Plugin]
371
 
[ivle.webapp.filesystem.diff#Plugin]
372
 
[ivle.webapp.filesystem.svnlog#Plugin]
373
 
[ivle.webapp.filesystem.serve#Plugin]
374
 
[ivle.webapp.groups#Plugin]
375
 
[ivle.webapp.console#Plugin]
376
 
[ivle.webapp.security#Plugin]
377
 
[ivle.webapp.media#Plugin]
378
 
[ivle.webapp.help#Plugin]
379
 
[ivle.webapp.tos#Plugin]
380
 
[ivle.webapp.userservice#Plugin]
381
 
[ivle.webapp.fileservice#Plugin]
382
 
[ivle.webapp.submit#Plugin]
383
 
""")
384
 
    plugindefault.close()
385
 
    print "Successfully wrote %s" % plugindefaultfile
386
 
 
387
 
    print
388
 
    print "You may modify the configuration at any time by editing " + conffile
 
364
    # Write bin/trampoline/conf.h
 
365
 
 
366
    conf = open(conf_hfile, "w")
 
367
 
 
368
    # XXX Compute jail_base, jail_src_base and jail_system. These will
 
369
    # ALSO be done by the boilerplate code, but we need them here in order
 
370
    # to write to the C file.
 
371
    jail_base = os.path.join(conf_options['paths/data'], 'jailmounts')
 
372
    jail_src_base = os.path.join(conf_options['paths/data'], 'jails')
 
373
    jail_system = os.path.join(jail_src_base, '__base__')
 
374
 
 
375
    conf.write("""/* IVLE Configuration File
 
376
 * conf.h
 
377
 * Administrator settings required by trampoline.
 
378
 * Note: trampoline will have to be rebuilt in order for changes to this file
 
379
 * to take effect.
 
380
 */
 
381
 
 
382
#define IVLE_AUFS_JAILS
 
383
 
 
384
/* In the local file system, where are the jails located.
 
385
 * The trampoline does not allow the creation of a jail anywhere besides
 
386
 * jail_base or a subdirectory of jail_base.
 
387
 */
 
388
static const char* jail_base = "%s";
 
389
static const char* jail_src_base = "%s";
 
390
static const char* jail_system = "%s";
 
391
 
 
392
/* Which user IDs are allowed to run the trampoline.
 
393
 * This list should be limited to the web server user.
 
394
 * (Note that root is an implicit member of this list).
 
395
 */
 
396
static const int allowed_uids[] = { %s };
 
397
""" % (repr(jail_base)[1:-1], repr(jail_src_base)[1:-1],
 
398
       repr(jail_system)[1:-1], repr(allowed_uids_list)[1:-1]))
 
399
    # Note: The above uses PYTHON reprs, not C reprs
 
400
    # However they should be the same with the exception of the outer
 
401
    # characters, which are stripped off and replaced
 
402
 
 
403
    conf.close()
 
404
 
 
405
    print "Successfully wrote %s" % conf_hfile
 
406
 
 
407
    # Write www/php/phpBB3/config.php
 
408
 
 
409
    conf = open(phpBBconffile, "w")
 
410
    
 
411
    # php-pg work around
 
412
    if conf_options['database/host'] == 'localhost':
 
413
        forumdb_host = '127.0.0.1'
 
414
    else:
 
415
        forumdb_host = conf_options['database/host']
 
416
 
 
417
    conf.write( """<?php
 
418
// phpBB 3.0.x auto-generated configuration file
 
419
// Do not change anything in this file!
 
420
$dbms = 'postgres';
 
421
$dbhost = '""" + forumdb_host + """';
 
422
$dbport = '""" + str(conf_options['database/port']) + """';
 
423
$dbname = '""" + conf_options['plugins/forum/dbname'] + """';
 
424
$dbuser = '""" + conf_options['database/username'] + """';
 
425
$dbpasswd = '""" + conf_options['database/password'] + """';
 
426
 
 
427
$table_prefix = 'phpbb_';
 
428
$acm_type = 'file';
 
429
$load_extensions = '';
 
430
@define('PHPBB_INSTALLED', true);
 
431
// @define('DEBUG', true);
 
432
//@define('DEBUG_EXTRA', true);
 
433
 
 
434
$forum_secret = '""" + forum_secret +"""';
 
435
?>"""   )
 
436
    
 
437
    conf.close()
 
438
 
 
439
    print "Successfully wrote %s" % phpBBconffile
 
440
 
 
441
    print
 
442
    print "You may modify the configuration at any time by editing"
 
443
    print conffile
 
444
    print conf_hfile
 
445
    print phpBBconffile
 
446
    print
389
447
    
390
448
    return 0
391
 
 
392
 
def main(argv=None):
393
 
    if argv is None:
394
 
        argv = sys.argv
395
 
 
396
 
    # Print the opening spiel including the GPL notice
397
 
 
398
 
    print """IVLE - Informatics Virtual Learning Environment Setup
399
 
Copyright (C) 2007-2009 The University of Melbourne
400
 
IVLE comes with ABSOLUTELY NO WARRANTY.
401
 
This is free software, and you are welcome to redistribute it
402
 
under certain conditions. See LICENSE.txt for details.
403
 
 
404
 
IVLE Configuration
405
 
"""
406
 
 
407
 
    return configure(argv[1:])
408
 
 
409
 
if __name__ == "__main__":
410
 
    sys.exit(main())