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

« back to all changes in this revision

Viewing changes to setup.py

  • Committer: mattgiuca
  • Date: 2008-01-25 06:20:32 UTC
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:trunk:313
test/test_framework: Updated examples, a bit of better descriptions, sample
    partial solutions, etc.

Added all sample subject material.
This has been copied from test/test_framework and modified slightly.
There is now a subject "sample" with 2 worksheets. The 1st worksheet has 3
exercises. These work in IVLE by default.

setup.py: Added code to install subjects into the designated directory. This
means that installing IVLE will result in the sample subjects being
immediately available.

Show diffs side-by-side

added added

removed removed

Lines of Context:
68
68
import compileall
69
69
import getopt
70
70
 
 
71
# Import modules from the website is tricky since they're in the www
 
72
# directory.
 
73
sys.path.append(os.path.join(os.getcwd(), 'www'))
 
74
import conf
 
75
import common.makeuser
 
76
 
 
77
# Operating system files to copy over into the jail.
 
78
# These will be copied from the given place on the OS file system into the
 
79
# same place within the jail.
 
80
JAIL_FILES = [
 
81
    '/lib/ld-linux.so.2',
 
82
    '/lib/tls/i686/cmov/libc.so.6',
 
83
    '/lib/tls/i686/cmov/libdl.so.2',
 
84
    '/lib/tls/i686/cmov/libm.so.6',
 
85
    '/lib/tls/i686/cmov/libpthread.so.0',
 
86
    '/lib/tls/i686/cmov/libutil.so.1',
 
87
    '/etc/ld.so.conf',
 
88
    '/etc/ld.so.cache',
 
89
    # These 2 files do not exist in Ubuntu
 
90
    #'/etc/ld.so.preload',
 
91
    #'/etc/ld.so.nohwcap',
 
92
    # UNIX commands
 
93
    '/usr/bin/strace',
 
94
    '/bin/ls',
 
95
    '/bin/echo',
 
96
    # Needed by python
 
97
    '/usr/bin/python2.5',
 
98
    # Needed by matplotlib
 
99
    '/usr/lib/i686/cmov/libssl.so.0.9.8',
 
100
    '/usr/lib/i686/cmov/libcrypto.so.0.9.8',
 
101
    '/lib/tls/i686/cmov/libnsl.so.1',
 
102
    '/usr/lib/libz.so.1',
 
103
    '/usr/lib/atlas/liblapack.so.3',
 
104
    '/usr/lib/atlas/libblas.so.3',
 
105
    '/usr/lib/libg2c.so.0',
 
106
    '/usr/lib/libstdc++.so.6',
 
107
    '/usr/lib/libfreetype.so.6',
 
108
    '/usr/lib/libpng12.so.0',
 
109
    '/usr/lib/libBLT.2.4.so.8.4',
 
110
    '/usr/lib/libtk8.4.so.0',
 
111
    '/usr/lib/libtcl8.4.so.0',
 
112
    '/usr/lib/tcl8.4/init.tcl',
 
113
    '/usr/lib/libX11.so.6',
 
114
    '/usr/lib/libXau.so.6',
 
115
    '/usr/lib/libXdmcp.so.6',
 
116
    '/lib/libgcc_s.so.1',
 
117
    '/etc/matplotlibrc',
 
118
]
 
119
# Symlinks to make within the jail. Src mapped to dst.
 
120
JAIL_LINKS = {
 
121
    'python2.5': 'jail/usr/bin/python',
 
122
}
 
123
# Trees to copy. Src mapped to dst (these will be passed to action_copytree).
 
124
JAIL_COPYTREES = {
 
125
    '/usr/lib/python2.5': 'jail/usr/lib/python2.5',
 
126
    '/usr/share/matplotlib': 'jail/usr/share/matplotlib',
 
127
    '/etc/ld.so.conf.d': 'jail/etc/ld.so.conf.d',
 
128
}
 
129
 
71
130
# Try importing existing conf, but if we can't just set up defaults
72
131
# The reason for this is that these settings are used by other phases
73
132
# of setup besides conf, so we need to know them.
74
133
# Also this allows you to hit Return to accept the existing value.
75
134
try:
76
135
    confmodule = __import__("www/conf/conf")
77
 
    root_dir = confmodule.root_dir
78
 
    ivle_install_dir = confmodule.ivle_install_dir
79
 
    jail_base = confmodule.jail_base
 
136
    try:
 
137
        root_dir = confmodule.root_dir
 
138
    except:
 
139
        root_dir = "/ivle"
 
140
    try:
 
141
        ivle_install_dir = confmodule.ivle_install_dir
 
142
    except:
 
143
        ivle_install_dir = "/opt/ivle"
 
144
    try:
 
145
        public_host = confmodule.public_host
 
146
    except:
 
147
        public_host = "public.localhost"
 
148
    try:
 
149
        jail_base = confmodule.jail_base
 
150
    except:
 
151
        jail_base = "/home/informatics/jails"
 
152
    try:
 
153
        subjects_base = confmodule.subjects_base
 
154
    except:
 
155
        subjects_base = "/home/informatics/subjects"
80
156
except ImportError:
81
157
    # Just set reasonable defaults
82
158
    root_dir = "/ivle"
83
159
    ivle_install_dir = "/opt/ivle"
 
160
    public_host = "public.localhost"
84
161
    jail_base = "/home/informatics/jails"
 
162
    subjects_base = "/home/informatics/subjects"
85
163
# Always defaults
86
164
allowed_uids = "0"
87
165
 
98
176
# as necessary, and include it in the distribution.
99
177
listmake_mimetypes = ['text/x-python', 'text/html',
100
178
    'application/x-javascript', 'application/javascript',
101
 
    'text/css', 'image/png']
 
179
    'text/css', 'image/png', 'application/xml']
102
180
 
103
181
# Main function skeleton from Guido van Rossum
104
182
# http://www.artima.com/weblogs/viewpost.jsp?thread=4829
126
204
        help([])
127
205
        return 1
128
206
 
 
207
    # Disallow run as root unless installing
 
208
    if (operation != 'install' and operation != 'updatejails'
 
209
        and os.geteuid() == 0):
 
210
        print >>sys.stderr, "I do not want to run this stage as root."
 
211
        print >>sys.stderr, "Please run as a normal user."
 
212
        return 1
129
213
    # Call the requested operation's function
130
214
    try:
131
215
        oper_func = {
134
218
            'build' : build,
135
219
            'listmake' : listmake,
136
220
            'install' : install,
 
221
            'updatejails' : updatejails,
137
222
        }[operation]
138
223
    except KeyError:
139
224
        print >>sys.stderr, (
140
225
            """Invalid operation '%s'. Try python setup.py help."""
141
226
            % operation)
 
227
        return 1
142
228
    return oper_func(argv[2:])
143
229
 
144
230
# Operation functions
148
234
        print """Usage: python setup.py operation [args]
149
235
Operation (and args) can be:
150
236
    help [operation]
 
237
    listmake (developer use only)
151
238
    conf [args]
152
239
    build
153
 
    install [--nojail] [-n|--dry]
 
240
    install [--nojail] [--nosubjects] [-n|--dry]
154
241
"""
155
242
        return 1
156
243
    elif len(args) != 1:
175
262
Either prompts the administrator for these details or accepts them as
176
263
command-line args. Will be interactive only if there are no arguments given.
177
264
Takes defaults from existing conf file if it exists.
 
265
 
 
266
To run IVLE out of the source directory (allowing development without having
 
267
to rebuild/install), just provide ivle_install_dir as the IVLE trunk
 
268
directory, and run build/install one time.
 
269
 
178
270
Creates www/conf/conf.py and trampoline/conf.h.
 
271
 
179
272
Args are:
180
273
    --root_dir
181
274
    --ivle_install_dir
 
275
    --public_host
182
276
    --jail_base
 
277
    --subjects_base
183
278
    --allowed_uids
184
279
As explained in the interactive prompt or conf.py.
185
280
"""
198
293
 
199
294
--dry | -n  Print out the actions but don't do anything."""
200
295
    elif operation == 'install':
201
 
        print """sudo python setup.py install [--nojail] [--dry|-n]
 
296
        print """sudo python setup.py install [--nojail] [--nosubjects][--dry|-n]
202
297
(Requires root)
203
298
Create target install directory ($target).
204
299
Create $target/bin.
206
301
chown and chmod the installed trampoline.
207
302
Copy www/ to $target.
208
303
Copy jail/ to jails template directory (unless --nojail specified).
209
 
 
210
 
--nojail    Do not copy the jail.
 
304
Copy subjects/ to subjects directory (unless --nosubjects specified).
 
305
 
 
306
--nojail        Do not copy the jail.
 
307
--nosubjects    Do not copy the subjects.
 
308
--dry | -n  Print out the actions but don't do anything."""
 
309
    elif operation == 'updatejails':
 
310
        print """sudo python setup.py updatejails [--dry|-n]
 
311
(Requires root)
 
312
Copy jail/ to each subdirectory in jails directory.
 
313
 
211
314
--dry | -n  Print out the actions but don't do anything."""
212
315
    else:
213
316
        print >>sys.stderr, (
219
322
    # We build two separate lists, by walking www and console
220
323
    list_www = build_list_py_files('www')
221
324
    list_console = build_list_py_files('console')
 
325
    list_subjects = build_list_py_files('subjects', no_top_level=True)
222
326
    # Make sure that the files generated by conf are in the list
223
327
    # (since listmake is typically run before conf)
224
328
    if "www/conf/conf.py" not in list_www:
248
352
# List of all installable files in console directory.
249
353
list_console = """)
250
354
        writelist_pretty(file, list_console)
 
355
        file.write("""
 
356
# List of all installable files in subjects directory.
 
357
# This is to install sample subjects and material.
 
358
list_subjects = """)
 
359
        writelist_pretty(file, list_subjects)
251
360
 
252
361
        file.close()
253
362
    except IOError, (errno, strerror):
264
373
 
265
374
    return 0
266
375
 
267
 
def build_list_py_files(dir):
 
376
def build_list_py_files(dir, no_top_level=False):
268
377
    """Builds a list of all py files found in a directory and its
269
 
    subdirectories. Returns this as a list of strings."""
 
378
    subdirectories. Returns this as a list of strings.
 
379
    no_top_level=True means the file paths will not include the top-level
 
380
    directory.
 
381
    """
270
382
    pylist = []
271
383
    for (dirpath, dirnames, filenames) in os.walk(dir):
272
384
        # Exclude directories beginning with a '.' (such as '.svn')
274
386
        # All *.py files are added to the list
275
387
        pylist += [os.path.join(dirpath, item) for item in filenames
276
388
            if mimetypes.guess_type(item)[0] in listmake_mimetypes]
 
389
    if no_top_level:
 
390
        for i in range(0, len(pylist)):
 
391
            _, pylist[i] = pylist[i].split(os.sep, 1)
277
392
    return pylist
278
393
 
279
394
def writelist_pretty(file, list):
287
402
        file.write(']\n')
288
403
 
289
404
def conf(args):
290
 
    global root_dir, ivle_install_dir, jail_base, allowed_uids
 
405
    global root_dir, ivle_install_dir, jail_base, subjects_base
 
406
    global public_host, allowed_uids
291
407
    # Set up some variables
292
408
 
293
409
    cwd = os.getcwd()
295
411
    conffile = os.path.join(cwd, "www/conf/conf.py")
296
412
    conf_hfile = os.path.join(cwd, "trampoline/conf.h")
297
413
 
298
 
    # Fixed config options that we don't ask the admin
299
 
    default_app = "dummy"
300
 
 
301
414
    # Get command-line arguments to avoid asking questions.
302
415
 
303
416
    (opts, args) = getopt.gnu_getopt(args, "", ['root_dir=',
331
444
        jail_base = query_user(jail_base,
332
445
        """Root directory where the jails (containing user files) are stored
333
446
(on the local file system):""")
 
447
        subjects_base = query_user(subjects_base,
 
448
        """Root directory where the subject directories (containing worksheets
 
449
and other per-subject files) are stored (on the local file system):""")
 
450
        public_host = query_user(public_host,
 
451
        """Hostname which will cause the server to go into "public mode",
 
452
providing login-free access to student's published work:""")
334
453
        allowed_uids = query_user(allowed_uids,
335
454
        """UID of the web server process which will run IVLE.
336
455
Only this user may execute the trampoline. May specify multiple users as
346
465
            ivle_install_dir = opts['--ivle_install_dir']
347
466
        if '--jail_base' in opts:
348
467
            jail_base = opts['--jail_base']
 
468
        if '--subjects_base' in opts:
 
469
            jail_base = opts['--subjects_base']
 
470
        if '--public_host' in opts:
 
471
            public_host = opts['--public_host']
349
472
        if '--allowed_uids' in opts:
350
473
            allowed_uids = opts['--allowed_uids']
351
474
 
377
500
# This directory should contain the "www" and "bin" directories.
378
501
ivle_install_dir = "%s"
379
502
 
 
503
# The server goes into "public mode" if the browser sends a request with this
 
504
# host. This is for security reasons - we only serve public student files on a
 
505
# separate domain to the main IVLE site.
 
506
# Public mode does not use cookies, and serves only public content.
 
507
# Private mode (normal mode) requires login, and only serves files relevant to
 
508
# the logged-in user.
 
509
public_host = "%s"
 
510
 
380
511
# In the local file system, where are the student/user file spaces located.
381
512
# The user jails are expected to be located immediately in subdirectories of
382
513
# this location.
383
514
jail_base = "%s"
384
515
 
385
 
# Which application to load by default (if the user navigates to the top level
386
 
# of the site). This is the app's URL name.
387
 
# Note that if this app requires authentication, the user will first be
388
 
# presented with the login screen.
389
 
default_app = "%s"
390
 
""" % (root_dir, ivle_install_dir, jail_base, default_app))
 
516
# In the local file system, where are the per-subject file spaces located.
 
517
# The individual subject directories are expected to be located immediately
 
518
# in subdirectories of this location.
 
519
subjects_base = "%s"
 
520
""" % (root_dir, ivle_install_dir, public_host, jail_base, subjects_base))
391
521
 
392
522
        conf.close()
393
523
    except IOError, (errno, strerror):
457
587
    # Copy all console and operating system files into the jail
458
588
    action_copylist(install_list.list_console, 'jail/opt/ivle', dry)
459
589
    copy_os_files_jail(dry)
 
590
    # Chmod the python console
 
591
    action_chmod_x('jail/opt/ivle/console/python-console', dry)
 
592
    
460
593
 
461
594
    # Compile .py files into .pyc or .pyo files
462
595
    compileall.compile_dir('www', quiet=True)
468
601
    """Copies necessary Operating System files from their usual locations
469
602
    into the jail/ directory of the cwd."""
470
603
    # Currently source paths are configured for Ubuntu.
471
 
    copy_file_to_jail('/lib/ld-linux.so.2', dry)
472
 
    copy_file_to_jail('/lib/tls/i686/cmov/libc.so.6', dry)
473
 
    copy_file_to_jail('/lib/tls/i686/cmov/libdl.so.2', dry)
474
 
    copy_file_to_jail('/lib/tls/i686/cmov/libm.so.6', dry)
475
 
    copy_file_to_jail('/lib/tls/i686/cmov/libpthread.so.0', dry)
476
 
    copy_file_to_jail('/lib/tls/i686/cmov/libutil.so.1', dry)
477
 
    copy_file_to_jail('/usr/bin/python2.5', dry)
478
 
    # TODO: ln -s jail/usr/bin/python2.5 jail/usr/bin/python
479
 
    action_copytree('/usr/lib/python2.5', 'jail/usr/lib/python2.5', dry)
 
604
    for filename in JAIL_FILES:
 
605
        copy_file_to_jail(filename, dry)
 
606
    for src, dst in JAIL_LINKS.items():
 
607
        action_symlink(src, dst, dry)
 
608
    for src, dst in JAIL_COPYTREES.items():
 
609
        action_copytree(src, dst, dry)
480
610
 
481
611
def copy_file_to_jail(src, dry):
482
612
    """Copies a single file from an absolute location into the same location
486
616
 
487
617
def install(args):
488
618
    # Get "dry" and "nojail" variables from command line
489
 
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry', 'nojail'])
 
619
    (opts, args) = getopt.gnu_getopt(args, "n",
 
620
        ['dry', 'nojail', 'nosubjects'])
490
621
    opts = dict(opts)
491
622
    dry = '-n' in opts or '--dry' in opts
492
623
    nojail = '--nojail' in opts
 
624
    nosubjects = '--nosubjects' in opts
493
625
 
494
626
    if dry:
495
627
        print "Dry run (no actions will be executed\n"
517
649
        # to the jails template directory (it will be used as a template
518
650
        # for all the students' jails).
519
651
        action_copytree('jail', os.path.join(jail_base, 'template'), dry)
520
 
        # Set up symlinks inside the jail
521
 
        action_symlink(os.path.join(jail_base, 'template/usr/bin/python2.5'),
522
 
            os.path.join(jail_base, 'template/usr/bin/python'), dry)
 
652
    if not nosubjects:
 
653
        # Copy the subjects directory across
 
654
        action_copylist(install_list.list_subjects, subjects_base, dry,
 
655
            srcdir="./subjects")
 
656
 
 
657
    # Append IVLE path to ivle.pth in python site packages
 
658
    # (Unless it's already there)
 
659
    ivle_pth = os.path.join(sys.prefix,
 
660
        "lib/python2.5/site-packages/ivle.pth")
 
661
    ivle_www = os.path.join(ivle_install_dir, "www")
 
662
    write_ivle_pth = True
 
663
    try:
 
664
        file = open(ivle_pth, 'r')
 
665
        for line in file:
 
666
            if line.strip() == ivle_www:
 
667
                write_ivle_pth = False
 
668
                break
 
669
    except (IOError, OSError):
 
670
        pass
 
671
    if write_ivle_pth:
 
672
        action_append(ivle_pth, ivle_www)
 
673
 
 
674
    return 0
 
675
 
 
676
def updatejails(args):
 
677
    # Get "dry" variable from command line
 
678
    (opts, args) = getopt.gnu_getopt(args, "n", ['dry'])
 
679
    opts = dict(opts)
 
680
    dry = '-n' in opts or '--dry' in opts
 
681
 
 
682
    if dry:
 
683
        print "Dry run (no actions will be executed\n"
 
684
 
 
685
    if not dry and os.geteuid() != 0:
 
686
        print >>sys.stderr, "Must be root to run install"
 
687
        print >>sys.stderr, "(I need to chown some files)."
 
688
        return 1
 
689
 
 
690
    # Update the template jail directory in case it hasn't been installed
 
691
    # recently.
 
692
    action_copytree('jail', os.path.join(jail_base, 'template'), dry)
 
693
 
 
694
    # Re-link all the files in all students jails.
 
695
    for dir in os.listdir(jail_base):
 
696
        if dir == 'template': continue
 
697
        # First back up the student's home directory
 
698
        temp_home = os.tmpnam()
 
699
        action_rename(os.path.join(jail_base, dir, 'home'), temp_home, dry)
 
700
        # Delete the student's jail and relink the jail files
 
701
        action_linktree(os.path.join(jail_base, 'template'),
 
702
            os.path.join(jail_base, dir), dry)
 
703
        # Restore the student's home directory
 
704
        action_rename(temp_home, os.path.join(jail_base, dir, 'home'), dry)
 
705
        # Set up the user's home directory just in case they don't have a
 
706
        # directory for this yet
 
707
        action_mkdir(os.path.join(jail_base, dir, 'home', dir), dry)
523
708
 
524
709
    return 0
525
710
 
552
737
    if ret != 0:
553
738
        raise RunError(prog, ret)
554
739
 
 
740
def action_rename(src, dst, dry):
 
741
    """Calls rename. Deletes the target if it already exists."""
 
742
    if os.access(dst, os.F_OK):
 
743
        print "rm -r", dst
 
744
        if not dry:
 
745
            shutil.rmtree(dst, True)
 
746
    print "mv ", src, dst
 
747
    if dry: return
 
748
    try:
 
749
        os.rename(src, dst)
 
750
    except OSError, (err, msg):
 
751
        if err != errno.EEXIST:
 
752
            raise
 
753
 
555
754
def action_mkdir(path, dry):
556
755
    """Calls mkdir. Silently ignored if the directory already exists.
557
756
    Creates all parent directories as necessary."""
575
774
            shutil.rmtree(dst, True)
576
775
    print "cp -r", src, dst
577
776
    if dry: return
578
 
    shutil.copytree(src, dst)
579
 
 
580
 
def action_copylist(srclist, dst, dry):
 
777
    shutil.copytree(src, dst, True)
 
778
 
 
779
def action_linktree(src, dst, dry):
 
780
    """Hard-links an entire directory tree. Same as copytree but the created
 
781
    files are hard-links not actual copies. Removes the existing destination.
 
782
    """
 
783
    if os.access(dst, os.F_OK):
 
784
        print "rm -r", dst
 
785
        if not dry:
 
786
            shutil.rmtree(dst, True)
 
787
    print "<cp with hardlinks> -r", src, dst
 
788
    if dry: return
 
789
    common.makeuser.linktree(src, dst)
 
790
 
 
791
def action_copylist(srclist, dst, dry, srcdir="."):
581
792
    """Copies all files in a list to a new location. The files in the list
582
793
    are read relative to the current directory, and their destinations are the
583
794
    same paths relative to dst. Creates all parent directories as necessary.
 
795
    srcdir is "." by default, can be overridden.
584
796
    """
585
797
    for srcfile in srclist:
586
798
        dstfile = os.path.join(dst, srcfile)
 
799
        srcfile = os.path.join(srcdir, srcfile)
587
800
        dstdir = os.path.split(dstfile)[0]
588
801
        if not os.path.isdir(dstdir):
589
802
            action_mkdir(dstdir, dry)
590
803
        print "cp -f", srcfile, dstfile
591
804
        if not dry:
592
 
            shutil.copyfile(srcfile, dstfile)
 
805
            try:
 
806
                shutil.copyfile(srcfile, dstfile)
 
807
                shutil.copymode(srcfile, dstfile)
 
808
            except shutil.Error:
 
809
                pass
593
810
 
594
811
def action_copyfile(src, dst, dry):
595
812
    """Copies one file to a new location. Creates all parent directories
600
817
        action_mkdir(dstdir, dry)
601
818
    print "cp -f", src, dst
602
819
    if not dry:
603
 
        shutil.copyfile(src, dst)
 
820
        try:
 
821
            shutil.copyfile(src, dst)
 
822
            shutil.copymode(src, dst)
 
823
        except shutil.Error:
 
824
            pass
604
825
 
605
826
def action_symlink(src, dst, dry):
606
827
    """Creates a symlink in a given location. Creates all parent directories
609
830
    dstdir = os.path.split(dst)[0]
610
831
    if not os.path.isdir(dstdir):
611
832
        action_mkdir(dstdir, dry)
612
 
    print "ln -s", src, dst
 
833
    # Delete existing file
 
834
    if os.path.exists(dst):
 
835
        os.remove(dst)
 
836
    print "ln -fs", src, dst
613
837
    if not dry:
614
838
        os.symlink(src, dst)
615
839
 
 
840
def action_append(ivle_pth, ivle_www):
 
841
    file = open(ivle_pth, 'a+')
 
842
    file.write(ivle_www + '\n')
 
843
    file.close()
 
844
 
616
845
def action_chown_setuid(file, dry):
617
846
    """Chowns a file to root, and sets the setuid bit on the file.
618
847
    Calling this function requires the euid to be root.
627
856
        os.chmod(file, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
628
857
            | stat.S_ISUID | stat.S_IRUSR | stat.S_IWUSR)
629
858
 
 
859
def action_chmod_x(file, dry):
 
860
    """Chmod +xs a file (sets execute permission)."""
 
861
    print "chmod u+rwx", file
 
862
    if not dry:
 
863
        os.chmod(file, stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)
 
864
 
630
865
def query_user(default, prompt):
631
866
    """Prompts the user for a string, which is read from a line of stdin.
632
867
    Exits silently if EOF is encountered. Returns the string, with spaces