#!/usr/bin/env python
# $Id$
# Creates a new BOINC project.
# TODO: use database.py (mainly for setup_project.py)
# TODO: don't add app / app version here.
# TODO: create 'apps' subdirectory and set it in config.xml
# TODO: use configxml module
import boinc_path_config
from Boinc.setup_project import *
from Boinc import database, db_mid, configxml, tools
import sys, os, getopt, re, socket
def gethostname():
try:
return socket.gethostbyaddr(socket.gethostname())[0]
except:
return 'localhost'
def isurl(s):
return s.startswith('http://') or s.startswith('https://')
argv0 = sys.argv[0]
HOME = os.path.expanduser('~')
USER = os.environ['USER']
NODENAME = gethostname()
HELP = """
syntax: %(argv0)s [options] project ['Project Long Name']
Creates a new project with given name with everything running on a single
server.
Misc options:
--verbose={0,1,2} default: 1
-v alias for --verbose=2
--no_query accept all directories without querying
--user_name default: $USER (%(USER)s)
--delete_prev_inst delete project-root first (from prev installation)
--drop_db_first drop database first (from prev installation)
Dir-options:
--base default: $HOME (%(HOME)s)
--key_dir default: BASE/keys
--project_root default: BASE/projects/PROJECT
--url_base default: http://$NODENAME/ (http://%(NODENAME)s/)
--html_user_url default: URL_BASE/PROJECT/
--html_ops_url default: URL_BASE/PROJECT_ops/
--cgi_url default: URL_BASE/PROJECT_cgi/
Other:
--db_name default: PROJECT
Example command line:
./make_project --base $HOME/boinc --url_base http://boink/ yah 'YETI @ Home'
Then upload_dir = $HOME/boinc/projects/yah/upload
and cgi_url = http://boink/yah_cgi/
By default, directory options will be queried if they do not exist yet.
""" %locals()
# Project-specific directory structure that you probably don't need to change:
# --bin_dir default: PROJECT_ROOT/bin
# --cgi_bin_dir default: PROJECT_ROOT/cgi-bin
# --html_user_dir default: PROJECT_ROOT/html_user
# --html_ops_dir default: PROJECT_ROOT/html_ops
# --download_dir default: PROJECT_ROOT/download
# --upload_dir default: PROJECT_ROOT/upload
# --log_dir default: PROJECT_ROOT/log
# --pid_dir default: PROJECT_ROOT/pid
def syntax_error(str):
raise SystemExit('%s; See "%s --help" for help\n' % (str,sys.argv[0]))
def usage():
print HELP
raise SystemExit
try:
opts, args = getopt.getopt(sys.argv[1:],
'hv',
[ 'help',
'verbose=',
'no_query',
'user_name=',
'drop_db_first',
'delete_prev_inst',
'base=',
'key_dir=',
'project_root=',
'url_base=',
'html_user_url=',
'html_ops_url=',
'cgi_url=',
# 'bin_dir=',
# 'cgi_bin_dir=',
# 'html_user_dir=',
# 'html_ops_dir=',
# 'download_dir=',
# 'upload_dir=',
# 'log_dir=',
# 'pid_dir='
])
except getopt.GetoptError, e:
syntax_error(e)
options.url_base = None
options.no_query = False
options.delete_prev_inst = False
for o,a in opts:
if o == '-h' or o == '--help': usage()
elif o == '-v': options.echo_verbose = 2
elif o == '--verbose': options.echo_verbose = int(a)
elif o == '--no_query': options.no_query = True
elif o == '--user_name': options.user_name = a
elif o == '--drop_db_first': options.drop_db_first = True
elif o == '--delete_prev_inst': options.delete_prev_inst = True
elif o == '--base': options.base = a
elif o == '--key_dir': options.key_dir = a
elif o == '--project_root': options.project_root = a
elif o == '--url_base': options.url_base = a
elif o == '--html_user_url': options.html_user_url = a
elif o == '--html_ops_url': options.html_ops_url = a
elif o == '--cgi_url': options.cgi_url = a
elif o == '--db_name': options.db_name = a
# elif o == '--bin_dir': options.bin_dir = a
# elif o == '--cgi_bin_dir': options.cgi_bin_dir = a
# elif o == '--html_user_dir': options.html_user_dir = a
# elif o == '--html_ops_dir': options.html_ops_dir = a
# elif o == '--download_dir': options.download_dir = a
# elif o == '--upload_dir': options.upload_dir = a
# elif o == '--log_dir': options.log_dir = a
# elif o == '--pid_dir': options.pid_dir = a
else:
raise SystemExit('internal error o=%s'%o)
if len(args) == 2:
(project_shortname, project_longname) = args
elif len(args) == 1:
(project_shortname, project_longname) = args[0], args[0].capitalize()
else:
syntax_error('Need one or two arguments')
opt_repls = {'PROJECT':project_shortname,
'PROJECT_ops':project_shortname+'_ops',
'PROJECT_cgi':project_shortname+'_cgi'}
def delete_slash(str):
return os.path.join(str,'')[:-1]
def add_slash(str, action=True):
if action:
return os.path.join(str,'')
else:
return str
def replopt(str):
for key in opt_repls:
str = str.replace(key, delete_slash(opt_repls[key]))
return str
def defopt(name, v, isdir=True):
options.__dict__[name] = opt_repls[name.upper()] = add_slash(replopt(options.__dict__.get(name) or v), isdir)
defopt('url_base' , 'http://%s/'%NODENAME)
if not isurl(options.url_base):
syntax_error('url_base needs to be an URL')
defopt('html_user_url' , 'URL_BASE/PROJECT')
defopt('html_ops_url' , 'URL_BASE/PROJECT_ops')
defopt('cgi_url' , 'URL_BASE/PROJECT_cgi')
defopt('user_name' , USER, isdir=False)
defopt('base' , HOME)
defopt('key_dir' , 'BASE/keys')
defopt('project_root' , 'BASE/projects/PROJECT')
defopt('db_name' , 'PROJECT', isdir=False)
print "Creating project '%s' (short name '%s'):" %(project_longname, project_shortname)
for k in ['base',
'key_dir',
'project_root',
'url_base',
'html_user_url',
'html_ops_url',
'cgi_url' ]:
print k.upper().rjust(15), "=", options.__dict__[k]
project_root_parent = os.path.realpath(os.path.join(options.project_root,'..'))
if not os.path.exists(project_root_parent):
os.makedirs(project_root_parent)
if os.path.exists(options.project_root):
if options.delete_prev_inst:
if not options.no_query:
if not query_noyes('Delete %s?'%options.project_root):
raise SystemExit('Aborted')
print "Deleting", options.project_root
rmtree(options.project_root)
else:
raise SystemExit('Project root already exists! Specify --delete_prev_inst --drop_db_first to clobber')
if not options.no_query:
if not query_yesno("Continue?"):
raise SystemExit('Aborted')
options.install_method = 'copy'
init()
project = Project(project_shortname, project_longname,
project_dir = options.project_root,
master_url = options.html_user_url,
cgi_url = options.cgi_url,
key_dir = options.key_dir,
db_name = options.db_name,
production = 1
)
project.install_project()
# project.sched_install('feeder')
# project.sched_install('transitioner')
# project.sched_install('validate_test')
# project.sched_install('assimilator')
# project.sched_install('file_deleter')
httpd_conf_template_filename = os.path.join(options.project_root,
project_shortname+'.httpd.conf')
proot = delete_slash(options.project_root)
html_user_url = options.html_user_url
html_ops_url = options.html_ops_url
print >>open(httpd_conf_template_filename,'w'), '''
## Settings for BOINC project %(project_longname)s
Alias /%(project_shortname)s %(proot)s/html_user
Alias /%(project_shortname)s_ops %(proot)s/html_ops
ScriptAlias /%(project_shortname)s_cgi %(proot)s/cgi-bin
# Note: projects/*/keys/ should NOT be readable!
Options Indexes FollowSymlinks MultiViews
AllowOverride None
Order allow,deny
Allow from all
Options FollowSymLinks
AllowOverride AuthConfig
Order allow,deny
Allow from all
Options ExecCGI
Order allow,deny
Allow from all
''' %locals()
print '''Done installing files.
Steps to complete installation:
1. Set permissions for Apache:
cat %(httpd_conf_template_filename)s >> /etc/apache/httpd.conf && apachectl restart
# (path to httpd.conf varies)
2. Add to crontab (as %(USER)s)
(If cron cannot run "start", try using a helper script to set PATH and
PYTHONPATH)
0,5,10,15,20,25,30,35,40,45,50,55 * * * * %(proot)s/bin/start --cron
To start, show status, stop BOINC daemons run:
%(proot)s/bin/start
%(proot)s/bin/status
%(proot)s/bin/stop
Master URL: %(html_user_url)s
Administration URL: %(html_ops_url)s
Tasks to do:
1. Add platform(s)
%(proot)s/bin/add platform --name=c64 --user_f="Commodore 64"
%(proot)s/bin/add platform --name=i686-pc-linux-gnu --user_f="Linux x86"
2. Add application(s)
%(proot)s/bin/add app --name=SpaghettiAtHome
3. Add core client and application binaries
3a. Place compiled clients in %(proot)s/apps/boinc/ and %(proot)s/apps/APP/
3b. Run %(proot)s/bin/update_versions
4. Generate work : read documentation at http://boinc.berkeley.edu/
'''%locals()