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

« back to all changes in this revision

Viewing changes to setup/configure.py

[Uber-commit of holiday work because I lacked a local copy of the branch.]

 ivle.makeuser: Don't use jailconf.py as a header for the in-jail conf.py;
     generate the whole thing using string formatting operators and include
     the template inline.

 ivle.makeuser.make_conf_py: XXX the inclusion of ivle.conf.jail_base in
     the jail. It is simply there to placate ivle.studpath, and needs
     to go before we can entirely remove the in-jail config.

 ivle-buildjail:
   - Add. Converted from setup.buildjail.
   - Build the jail in __base_build__ and rsync it to __base__ when
     done, rather than operating only in ./jail
   - Rename --rebuildjail/-j to --recreate/-r, as the whole script
     is now for jail rebuilding. Also add a warning to the usage string about
     the large volume likely to be downloaded.
   - Check existence before removing trees.
   - Don't copy jailconf.py over conf.py in the jail. Also make
     sure that we remove conf.pyc.

 setup.configure:
   - Stop generating jailconf.py at all.
   - Add a jail_system_build setting, defaulting to __base_build__ next to
     the existing __base__.
   - Don't use an OptionParser before calling the real function, as that
     adds options dynamically.

 setup.install:
   - Add an option (-R) to avoid writing out svn revision info to
     $PREFIX/share/ivle/revision.txt.
   - Remove jail-copying things.
   - Install all services to the host, rather than just usrmgt-server. We do
     this so we can build the jail from the host without the source tree.
   - Shuffle some things, and don't install phpBB3 twice.
   - Add a --root argument, to take an alternate root directory to install
     into (as given to autotools in $DESTDIR).

 setup.build:
   - Allow running as non-root.
   - Take a --no-compile option to not byte-compile Python files.

 setup.util:
   - Include usrmgt-server in the list of services.
   - Add make_install_path(), a wrapper around os.path.join() that ensures
     the second path is relative.
   - Install ivle-buildjail with the other binaries.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# IVLE - Informatics Virtual Learning Environment
 
2
# Copyright (C) 2007-2009 The University of Melbourne
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
17
 
 
18
# Module: setup/config
 
19
# Author: Matt Giuca, Refactored by David Coles
 
20
# Date:   03/07/2008
 
21
 
 
22
'''Configures IVLE with machine-specific details, most notably, various paths.
 
23
Either prompts the administrator for these details or accepts them as
 
24
command-line args.
 
25
 
 
26
Creates ivle/conf/conf.py and bin/trampoline/trampoline/conf.h.
 
27
'''
 
28
 
 
29
import optparse
 
30
import getopt
 
31
import os
 
32
import sys
 
33
import hashlib
 
34
import uuid
 
35
 
 
36
from setup.util import query_user
 
37
 
 
38
class ConfigOption:
 
39
    """A configuration option; one of the things written to conf.py."""
 
40
    def __init__(self, option_name, default, prompt, comment, ask=True):
 
41
        """Creates a configuration option.
 
42
        option_name: Name of the variable in conf.py. Also name of the
 
43
            command-line argument to setup.py conf.
 
44
        default: Default value for this variable.
 
45
        prompt: (Short) string presented during the interactive prompt in
 
46
            setup.py conf.
 
47
        comment: (Long) comment string stored in conf.py. Each line of this
 
48
            string should begin with a '#'.
 
49
        ask: Whether to ask the question in the default config run.
 
50
        """
 
51
        self.option_name = option_name
 
52
        self.default = default
 
53
        self.prompt = prompt
 
54
        self.comment = comment
 
55
        self.ask = ask
 
56
 
 
57
# Configuration options, defaults and descriptions
 
58
config_options = []
 
59
 
 
60
config_options.append(ConfigOption("root_dir", "/",
 
61
    """Root directory where IVLE is located (in URL space):""",
 
62
    """
 
63
# In URL space, where in the site is IVLE located. (All URLs will be prefixed
 
64
# with this).
 
65
# eg. "/" or "/ivle".""", ask=False))
 
66
 
 
67
config_options.append(ConfigOption("prefix", "/usr/local",
 
68
    """In the local file system, the prefix to the system directory where IVLE
 
69
is installed. (This should either be /usr or /usr/local):""",
 
70
    """
 
71
# In the local file system, the prefix to the system directory where IVLE is
 
72
# installed. This should either be '/usr' or '/usr/local'.
 
73
# ('/usr/local' for the usual install, '/usr' for distribution packages)""",
 
74
    ask=False))
 
75
 
 
76
config_options.append(ConfigOption("python_site_packages_override",
 
77
    None,
 
78
    """site-packages directory in Python, where Python libraries are to be
 
79
installed. May be left as the default, in which case the value will be
 
80
computed from prefix and the current Python version:""",
 
81
    """
 
82
# 'site-packages' directory in Python, where Python libraries are to be
 
83
# installed. May be None (recommended), in which case the value will be
 
84
# computed from prefix and the current Python version.""", ask=False))
 
85
 
 
86
config_options.append(ConfigOption("data_path",
 
87
    "/var/lib/ivle",
 
88
    "In the local file system, where user-modifiable data files should be "
 
89
    "located:",
 
90
    """
 
91
# In the local file system, where user-modifiable data files should be
 
92
# located.""", ask=False))
 
93
 
 
94
config_options.append(ConfigOption("log_path",
 
95
    "/var/log/ivle",
 
96
    """Directory where IVLE log files are stored (on the local
 
97
file system). Note - this must be writable by the user the IVLE server 
 
98
process runs as (usually www-data):""",
 
99
    """
 
100
# In the local file system, where IVLE error logs should be located.""",
 
101
    ask=False))
 
102
 
 
103
config_options.append(ConfigOption("public_host", "public.localhost",
 
104
    """Hostname which will cause the server to go into "public mode",
 
105
providing login-free access to student's published work:""",
 
106
    """
 
107
# The server goes into "public mode" if the browser sends a request with this
 
108
# host. This is for security reasons - we only serve public student files on a
 
109
# separate domain to the main IVLE site.
 
110
# Public mode does not use cookies, and serves only public content.
 
111
# Private mode (normal mode) requires login, and only serves files relevant to
 
112
# the logged-in user."""))
 
113
 
 
114
config_options.append(ConfigOption("allowed_uids", "33",
 
115
    """UID of the web server process which will run IVLE.
 
116
Only this user may execute the trampoline. May specify multiple users as
 
117
a comma-separated list.
 
118
    (eg. "1002,78")""",
 
119
    """
 
120
# The User-ID of the web server process which will run IVLE, and any other
 
121
# users who are allowed to run the trampoline. This is stores as a string of
 
122
# comma-separated integers, simply because it is not used within Python, only
 
123
# used by the setup program to write to conf.h (see setup.py config).""",
 
124
    ask=False))
 
125
 
 
126
config_options.append(ConfigOption("db_host", "localhost",
 
127
    """PostgreSQL Database config
 
128
==========================
 
129
Hostname of the DB server:""",
 
130
    """
 
131
### PostgreSQL Database config ###
 
132
# Database server hostname"""))
 
133
 
 
134
config_options.append(ConfigOption("db_port", "5432",
 
135
    """Port of the DB server:""",
 
136
    """
 
137
# Database server port"""))
 
138
 
 
139
config_options.append(ConfigOption("db_dbname", "ivle",
 
140
    """Database name:""",
 
141
    """
 
142
# Database name"""))
 
143
 
 
144
config_options.append(ConfigOption("db_forumdbname", "ivle_forum",
 
145
    """Forum Database name:""",
 
146
    """
 
147
# Forum Database name"""))
 
148
 
 
149
config_options.append(ConfigOption("db_user", "postgres",
 
150
    """Username for DB server login:""",
 
151
    """
 
152
# Database username"""))
 
153
 
 
154
config_options.append(ConfigOption("db_password", "",
 
155
    """Password for DB server login:
 
156
    (Caution: This password is stored in plaintext in ivle/conf/conf.py)""",
 
157
    """
 
158
# Database password"""))
 
159
 
 
160
config_options.append(ConfigOption("auth_modules", "",
 
161
    """Authentication config
 
162
=====================
 
163
Comma-separated list of authentication modules.""",
 
164
    """
 
165
# Comma-separated list of authentication modules.
 
166
# Note that auth is always enabled against the local database, and NO OTHER
 
167
# auth is enabled by default. This section is for specifying additional auth
 
168
# modules.
 
169
# These refer to importable Python modules in the www/auth directory.
 
170
# Modules "ldap_auth" and "guest" are available in the source tree, but
 
171
# other modules may be plugged in to auth against organisation-specific
 
172
# auth backends.""", ask=False))
 
173
 
 
174
config_options.append(ConfigOption("ldap_url", "ldaps://www.example.com",
 
175
    """(LDAP options are only relevant if "ldap" is included in the list of
 
176
auth modules).
 
177
URL for LDAP authentication server:""",
 
178
    """
 
179
# URL for LDAP authentication server""", ask=False))
 
180
 
 
181
config_options.append(ConfigOption("ldap_format_string",
 
182
    "uid=%s,ou=users,o=example",
 
183
    """Format string for LDAP auth request:
 
184
    (Must contain a single "%s" for the user's login name)""",
 
185
    """
 
186
# Format string for LDAP auth request
 
187
# (Must contain a single "%s" for the user's login name)""", ask=False))
 
188
 
 
189
config_options.append(ConfigOption("subject_pulldown_modules", "",
 
190
    """Comma-separated list of subject pulldown modules.
 
191
Add proprietary modules to automatically enrol students in subjects.""",
 
192
    """
 
193
# Comma-separated list of subject pulldown modules.
 
194
# These refer to importable Python modules in the lib/pulldown_subj directory.
 
195
# Only "dummy_subj" is available in the source tree (an example), but
 
196
# other modules may be plugged in to pulldown against organisation-specific
 
197
# pulldown backends.""", ask=False))
 
198
 
 
199
config_options.append(ConfigOption("svn_addr", "http://svn.localhost/",
 
200
    """Subversion config
 
201
=================
 
202
The base url for accessing subversion repositories:""",
 
203
    """
 
204
# The base url for accessing subversion repositories."""))
 
205
 
 
206
config_options.append(ConfigOption("usrmgt_host", "localhost",
 
207
    """User Management Server config
 
208
============================
 
209
The hostname where the usrmgt-server runs:""",
 
210
    """
 
211
# The hostname where the usrmgt-server runs."""))
 
212
 
 
213
config_options.append(ConfigOption("usrmgt_port", "2178",
 
214
    """The port where the usrmgt-server runs:""",
 
215
    """
 
216
# The port where the usrmgt-server runs.""", ask=False))
 
217
 
 
218
config_options.append(ConfigOption("usrmgt_magic", None,
 
219
    """The password for the usrmgt-server:""",
 
220
    """
 
221
# The password for the usrmgt-server.""", ask=False))
 
222
 
 
223
def configure(args):
 
224
    # Call the real function
 
225
    return __configure(args)
 
226
 
 
227
def __configure(args):
 
228
    global db_port, usrmgt_port
 
229
 
 
230
    # Try importing existing conf, but if we can't just set up defaults
 
231
    # The reason for this is that these settings are used by other phases
 
232
    # of setup besides conf, so we need to know them.
 
233
    # Also this allows you to hit Return to accept the existing value.
 
234
    try:
 
235
        confmodule = __import__("ivle/conf/conf")
 
236
        for opt in config_options:
 
237
            try:
 
238
                globals()[opt.option_name] = \
 
239
                confmodule.__dict__[opt.option_name]
 
240
            except:
 
241
                globals()[opt.option_name] = opt.default
 
242
    except ImportError:
 
243
        # Just set reasonable defaults
 
244
        for opt in config_options:
 
245
            globals()[opt.option_name] = opt.default
 
246
 
 
247
    # Set up some variables
 
248
    cwd = os.getcwd()
 
249
 
 
250
    # the files that will be created/overwritten
 
251
    conffile = os.path.join(cwd, "ivle/conf/conf.py")
 
252
    conf_hfile = os.path.join(cwd, "bin/trampoline/conf.h")
 
253
    phpBBconffile = os.path.join(cwd, "www/php/phpBB3/config.php")
 
254
 
 
255
    # Get command-line arguments to avoid asking questions.
 
256
 
 
257
    optnames = []
 
258
    for opt in config_options:
 
259
        optnames.append(opt.option_name + "=")
 
260
    (opts, args) = getopt.gnu_getopt(args, "", optnames)
 
261
 
 
262
    if args != []:
 
263
        print >>sys.stderr, "Invalid arguments:", string.join(args, ' ')
 
264
        return 2
 
265
 
 
266
    if opts == []:
 
267
        # Interactive mode. Prompt the user for all the values.
 
268
 
 
269
        print """This tool will create the following files:
 
270
    %s
 
271
    %s
 
272
    %s
 
273
prompting you for details about your configuration. The file will be
 
274
overwritten if it already exists. It will *not* install or deploy IVLE.
 
275
 
 
276
Please hit Ctrl+C now if you do not wish to do this.
 
277
""" % (conffile, conf_hfile, phpBBconffile)
 
278
 
 
279
        # Get information from the administrator
 
280
        # If EOF is encountered at any time during the questioning, just exit
 
281
        # silently
 
282
 
 
283
        for opt in config_options:
 
284
            if opt.ask:
 
285
                globals()[opt.option_name] = \
 
286
                    query_user(globals()[opt.option_name], opt.prompt)
 
287
    else:
 
288
        opts = dict(opts)
 
289
        # Non-interactive mode. Parse the options.
 
290
        for opt in config_options:
 
291
            if '--' + opt.option_name in opts:
 
292
                globals()[opt.option_name] = opts['--' + opt.option_name]
 
293
 
 
294
    # Error handling on input values
 
295
    try:
 
296
        allowed_uids_list = map(int, allowed_uids.split(','))
 
297
    except ValueError:
 
298
        print >>sys.stderr, (
 
299
        "Invalid UID list (%s).\n"
 
300
        "Must be a comma-separated list of integers." % allowed_uids)
 
301
        return 1
 
302
    try:
 
303
        db_port = int(db_port)
 
304
        if db_port < 0 or db_port >= 65536: raise ValueError()
 
305
    except ValueError:
 
306
        print >>sys.stderr, (
 
307
        "Invalid DB port (%s).\n"
 
308
        "Must be an integer between 0 and 65535." % repr(db_port))
 
309
        return 1
 
310
    try:
 
311
        usrmgt_port = int(usrmgt_port)
 
312
        if usrmgt_port < 0 or usrmgt_port >= 65536: raise ValueError()
 
313
    except ValueError:
 
314
        print >>sys.stderr, (
 
315
        "Invalid user management port (%s).\n"
 
316
        "Must be an integer between 0 and 65535." % repr(usrmgt_port))
 
317
        return 1
 
318
 
 
319
    # By default we generate the magic randomly.
 
320
    if globals()['usrmgt_magic'] is None:
 
321
        globals()['usrmgt_magic'] = hashlib.md5(uuid.uuid4().bytes).hexdigest()
 
322
 
 
323
    # Generate the forum secret
 
324
    forum_secret = hashlib.md5(uuid.uuid4().bytes).hexdigest()
 
325
 
 
326
    # Write lib/conf/conf.py
 
327
 
 
328
    try:
 
329
        conf = open(conffile, "w")
 
330
 
 
331
        conf.write("""# IVLE Configuration File
 
332
# conf.py
 
333
# Miscellaneous application settings
 
334
 
 
335
import os
 
336
import sys
 
337
""")
 
338
        for opt in config_options:
 
339
            conf.write('%s\n%s = %r\n' % (opt.comment, opt.option_name,
 
340
                globals()[opt.option_name]))
 
341
 
 
342
            # Add the forum secret to the config file (regenerated each config)
 
343
        conf.write('forum_secret = "%s"\n\n' % (forum_secret))
 
344
 
 
345
        write_conf_file_boilerplate(conf)
 
346
 
 
347
        conf.close()
 
348
    except IOError, (errno, strerror):
 
349
        print "IO error(%s): %s" % (errno, strerror)
 
350
        sys.exit(1)
 
351
 
 
352
    print "Successfully wrote %s" % conffile
 
353
 
 
354
    # Write bin/trampoline/conf.h
 
355
 
 
356
    try:
 
357
        conf = open(conf_hfile, "w")
 
358
 
 
359
        # XXX Compute jail_base, jail_src_base and jail_system. These will
 
360
        # ALSO be done by the boilerplate code, but we need them here in order
 
361
        # to write to the C file.
 
362
        jail_base = os.path.join(data_path, 'jailmounts')
 
363
        jail_src_base = os.path.join(data_path, 'jails')
 
364
        jail_system = os.path.join(jail_src_base, '__base__')
 
365
 
 
366
        conf.write("""/* IVLE Configuration File
 
367
 * conf.h
 
368
 * Administrator settings required by trampoline.
 
369
 * Note: trampoline will have to be rebuilt in order for changes to this file
 
370
 * to take effect.
 
371
 */
 
372
 
 
373
#define IVLE_AUFS_JAILS
 
374
 
 
375
/* In the local file system, where are the jails located.
 
376
 * The trampoline does not allow the creation of a jail anywhere besides
 
377
 * jail_base or a subdirectory of jail_base.
 
378
 */
 
379
static const char* jail_base = "%s";
 
380
static const char* jail_src_base = "%s";
 
381
static const char* jail_system = "%s";
 
382
 
 
383
/* Which user IDs are allowed to run the trampoline.
 
384
 * This list should be limited to the web server user.
 
385
 * (Note that root is an implicit member of this list).
 
386
 */
 
387
static const int allowed_uids[] = { %s };
 
388
""" % (repr(jail_base)[1:-1], repr(jail_src_base)[1:-1],
 
389
       repr(jail_system)[1:-1], repr(allowed_uids_list)[1:-1]))
 
390
    # Note: The above uses PYTHON reprs, not C reprs
 
391
    # However they should be the same with the exception of the outer
 
392
    # characters, which are stripped off and replaced
 
393
 
 
394
        conf.close()
 
395
    except IOError, (errno, strerror):
 
396
        print "IO error(%s): %s" % (errno, strerror)
 
397
        sys.exit(1)
 
398
 
 
399
    print "Successfully wrote %s" % conf_hfile
 
400
 
 
401
    # Write www/php/phpBB3/config.php
 
402
 
 
403
    try:
 
404
        conf = open(phpBBconffile, "w")
 
405
        
 
406
        # php-pg work around
 
407
        if db_host == 'localhost':
 
408
            forumdb_host = '127.0.0.1'
 
409
        else:
 
410
            forumdb_host = db_host
 
411
 
 
412
        conf.write( """<?php
 
413
// phpBB 3.0.x auto-generated configuration file
 
414
// Do not change anything in this file!
 
415
$dbms = 'postgres';
 
416
$dbhost = '""" + forumdb_host + """';
 
417
$dbport = '""" + str(db_port) + """';
 
418
$dbname = '""" + db_forumdbname + """';
 
419
$dbuser = '""" + db_user + """';
 
420
$dbpasswd = '""" + db_password + """';
 
421
 
 
422
$table_prefix = 'phpbb_';
 
423
$acm_type = 'file';
 
424
$load_extensions = '';
 
425
@define('PHPBB_INSTALLED', true);
 
426
// @define('DEBUG', true);
 
427
//@define('DEBUG_EXTRA', true);
 
428
 
 
429
$forum_secret = '""" + forum_secret +"""';
 
430
?>"""   )
 
431
    
 
432
        conf.close()
 
433
    except IOError, (errno, strerror):
 
434
        print "IO error(%s): %s" % (errno, strerror)
 
435
        sys.exit(1)
 
436
 
 
437
    print "Successfully wrote %s" % phpBBconffile
 
438
 
 
439
    print
 
440
    print "You may modify the configuration at any time by editing"
 
441
    print conffile
 
442
    print conf_hfile
 
443
    print phpBBconffile
 
444
    print
 
445
    
 
446
    return 0
 
447
 
 
448
def write_conf_file_boilerplate(conf_file):
 
449
    conf_file.write("""\
 
450
### Below is boilerplate code, appended by ./setup.py config ###
 
451
 
 
452
# Path where architecture-dependent data (including non-user-executable
 
453
# binaries) is installed.
 
454
lib_path = os.path.join(prefix, 'lib/ivle')
 
455
 
 
456
# Path where arch-independent data is installed.
 
457
share_path = os.path.join(prefix, 'share/ivle')
 
458
 
 
459
# Path where user-executable binaries are installed.
 
460
bin_path = os.path.join(prefix, 'bin')
 
461
 
 
462
# 'site-packages' directory in Python, where Python libraries are to be
 
463
# installed.
 
464
if python_site_packages_override is None:
 
465
    PYTHON_VERSION = sys.version[0:3]   # eg. "2.5"
 
466
    python_site_packages = os.path.join(prefix,
 
467
                               'lib/python%s/site-packages' % PYTHON_VERSION)
 
468
else:
 
469
    python_site_packages = python_site_packages_override
 
470
 
 
471
# In the local file system, where the student/user jails will be mounted.
 
472
# Only a single copy of the jail's system components will be stored here -
 
473
# all user jails will be virtually mounted here.
 
474
jail_base = os.path.join(data_path, 'jailmounts')
 
475
 
 
476
# In the local file system, where are the student/user file spaces located.
 
477
# The user jails are expected to be located immediately in subdirectories of
 
478
# this location. Note that no complete jails reside here - only user
 
479
# modifications.
 
480
jail_src_base = os.path.join(data_path, 'jails')
 
481
 
 
482
# In the local file system, where the template system jail will be stored.
 
483
jail_system = os.path.join(jail_src_base, '__base__')
 
484
 
 
485
# In the local file system, where the template system jail will be stored.
 
486
jail_system_build = os.path.join(jail_src_base, '__base_build__')
 
487
 
 
488
# In the local file system, where the subject content files are located.
 
489
# (The 'subjects' and 'exercises' directories).
 
490
content_path = os.path.join(data_path, 'content')
 
491
 
 
492
# In the local file system, where are the per-subject file spaces located.
 
493
# The individual subject directories are expected to be located immediately
 
494
# in subdirectories of this location.
 
495
subjects_base = os.path.join(content_path, 'subjects')
 
496
 
 
497
# In the local file system, where are the subject-independent exercise sheet
 
498
# file spaces located.
 
499
exercises_base = os.path.join(content_path, 'exercises')
 
500
 
 
501
# In the local file system, where the system notices are stored (such as terms
 
502
# of service and MOTD).
 
503
notices_path = os.path.join(data_path, 'notices')
 
504
 
 
505
# In the local file system, where is the Terms of Service document located.
 
506
tos_path = os.path.join(notices_path, 'tos.html')
 
507
 
 
508
# In the local file system, where is the Message of the Day document
 
509
# located. This is an HTML file (just the body fragment), which will
 
510
# be displayed on the login page. It is optional.
 
511
motd_path = os.path.join(notices_path, 'motd.html')
 
512
 
 
513
# The location of all the subversion config and repositories.
 
514
svn_path = os.path.join(data_path, 'svn')
 
515
 
 
516
# The location of the subversion configuration file used by
 
517
# apache to host the user repositories.
 
518
svn_conf = os.path.join(svn_path, 'svn.conf')
 
519
 
 
520
# The location of the subversion configuration file used by
 
521
# apache to host the user repositories.
 
522
svn_group_conf = os.path.join(svn_path, 'svn-group.conf')
 
523
 
 
524
# The root directory for the subversion repositories.
 
525
svn_repo_path = os.path.join(svn_path, 'repositories')
 
526
 
 
527
# The location of the password file used to authenticate users
 
528
# of the subversion repository from the ivle server.
 
529
svn_auth_ivle = os.path.join(svn_path, 'ivle.auth')
 
530
""")