#!/usr/bin/env python
# $Id$
# Creates a new BOINC project.
import boinc_path_config
from Boinc.setup_project import *
from Boinc import database, db_mid, configxml, tools
import sys, os, getopt, re, socket
def getfqdn():
try:
return socket.getfqdn()
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 = getfqdn()
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:
--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)
--test_app install example application
--web_only install web files, no executables (for Bossa, Bolt)
--no_db don't create the database
--srcdir where to find the source files (default: current directory)
Dir-options:
--project_root default: HOME/projects/PROJECT
--key_dir default: PROJECT_ROOT/keys
--project_host default: $HOSTNAME (%(NODENAME)s)
--url_base default: http://PROJECT_HOST/
--html_user_url default: URL_BASE/PROJECT/
--html_ops_url default: URL_BASE/PROJECT_ops/
Other:
--db_name default: PROJECT
--db_user default: USER_NAME
--db_passwd default: None
--db_host default: None
Example command line:
./make_project --project_root $HOME/boinc/projects --url_base http://tah.org/ tah 'Test@home'
Then upload_dir = $HOME/boinc/projects/tah/upload
and cgi_url = http://tah.org/tah_cgi/
By default, directory options will be queried if they do not exist yet.
""" %locals()
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', [
'base=',
'db_host=',
'db_name=',
'db_passwd=',
'db_user=',
'delete_prev_inst',
'drop_db_first',
'help',
'html_ops_url=',
'html_user_url=',
'key_dir=',
'no_db',
'no_query',
'project_host=',
'project_root=',
'srcdir=',
'test_app',
'url_base=',
'user_name=',
'verbose=',
'web_only',
]
)
except getopt.GetoptError, e:
syntax_error(e)
options.url_base = None
options.no_query = False
options.test_app = False
options.web_only = False
options.no_db = False
options.delete_prev_inst = False
options.srcdir = None
for o,a in opts:
if o == '-h' or o == '--help': usage()
elif o == '--db_host': options.db_host = a
elif o == '--db_name': options.db_name = a
elif o == '--db_passwd': options.db_passwd = a
elif o == '--db_user': options.db_user = a
elif o == '--delete_prev_inst': options.delete_prev_inst = True
elif o == '--drop_db_first': options.drop_db_first = True
elif o == '--html_ops_url': options.html_ops_url = a
elif o == '--html_user_url': options.html_user_url = a
elif o == '--key_dir': options.key_dir = a
elif o == '--no_db': options.no_db = True
elif o == '--no_query': options.no_query = True
elif o == '--project_host': options.project_host = a
elif o == '--project_root': options.project_root = a
elif o == '--srcdir': options.srcdir = a
elif o == '--test_app': options.test_app = True
elif o == '--url_base': options.url_base = a
elif o == '--user_name': options.user_name = a
elif o == '--verbose': options.echo_verbose = int(a)
elif o == '--web_only': options.web_only = True
elif o == '-v': options.echo_verbose = 2
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]
else:
syntax_error('Need one or two arguments')
if not options.srcdir:
for dir in ('.', '..'):
if os.path.exists(os.path.join(dir, 'html', 'project.sample', 'project.inc')):
options.srcdir = dir
if not options.srcdir:
syntax_error('Not running in the source directory and --srcdir was not specified')
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('project_host' , NODENAME, isdir=False)
defopt('url_base' , 'http://%s/'%options.project_host)
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('user_name' , USER, isdir=False)
defopt('project_root' , HOME+'/projects/PROJECT')
defopt('key_dir' , options.project_root+'keys')
defopt('db_name' , 'PROJECT', isdir=False)
defopt('db_user' , 'USER_NAME', isdir=False)
defopt('db_passwd' , '', isdir=False)
defopt('db_host' , '', isdir=False)
print "Creating project '%s' (short name '%s'):" %(project_longname, project_shortname)
for k in [
'project_root',
'project_host',
'url_base',
'html_user_url',
'html_ops_url',
'key_dir',
'db_name',
'db_host',
]:
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,
options.url_base + project_shortname + '_cgi',
project_dir = options.project_root,
master_url = options.html_user_url,
key_dir = options.key_dir,
db_name = options.db_name,
host = options.project_host,
web_only = options.web_only,
no_db = options.no_db,
production = True
)
project.install_project()
proot = delete_slash(options.project_root)
# set up default daemons
project.sched_install('feeder')
project.sched_install('transitioner')
project.sched_install('file_deleter')
if options.test_app:
project.config.daemons.make_node_and_append("daemon").cmd = 'sample_work_generator -d 3'
project.config.daemons.make_node_and_append("daemon").cmd = 'sample_bitwise_validator -d 3 --app example_app'
project.config.daemons.make_node_and_append("daemon").cmd = 'sample_assimilator -d 3 --app example_app'
# set up default tasks
t = project.config.tasks.make_node_and_append("task")
t.period = '24 hours'
t.output = 'antique_file_deleter.out'
t.cmd = 'antique_file_deleter -d 2'
t.disabled = 0
t = project.config.tasks.make_node_and_append("task")
t.period = '24 hours'
t.output = 'db_dump.out'
t.cmd = 'db_dump -d 2 --dump_spec ../db_dump_spec.xml'
t.disabled = 1
t = project.config.tasks.make_node_and_append("task")
t.period = '1 days'
t.output = 'update_uotd.out'
t.cmd = 'run_in_ops ./update_uotd.php'
t.disabled = 0
t = project.config.tasks.make_node_and_append("task")
t.period = '1 hour'
t.output = 'update_forum_activities.out'
t.cmd = 'run_in_ops ./update_forum_activities.php'
t.disabled = 0
t = project.config.tasks.make_node_and_append("task")
t.period = '1 days'
t.output = 'update_stats.out'
t.cmd = 'update_stats'
t.disabled = 0
t = project.config.tasks.make_node_and_append("task")
t.period = '24 hours'
t.output = 'update_profile_pages.out'
t.cmd = 'run_in_ops ./update_profile_pages.php'
t.disabled = 0
t = project.config.tasks.make_node_and_append("task")
t.period = '24 hours'
t.output = 'team_import.out'
t.cmd = 'run_in_ops ./team_import.php'
t.disabled = 1
t = project.config.tasks.make_node_and_append("task")
t.period = '24 hours'
t.output = 'notify.out'
t.cmd = 'run_in_ops ./notify.php'
t.disabled = 0
t = project.config.tasks.make_node_and_append("task")
t.period = '24 hours'
t.output = 'badge_assign.out'
t.cmd = 'run_in_ops ./badge_assign.php'
t.disabled = 0
project.config.write()
if not options.no_db:
try:
os.system('cd '+proot+'/html/ops; ./db_schemaversion.php > '+proot+'/db_revision')
except:
print '''Couldn't set db schema version number'''
try:
os.system('cd '+proot+'/html/ops; ./update_translations.php -d 1')
except:
print '''Couldn't install translation files'''
print '''Done installing default daemons.'''
# copy the test app if needed
if options.test_app:
app_dir = proot+'/apps/example_app/'
os.mkdir(app_dir)
os.system('cp -r ../samples/example_app/bin/* ' + app_dir);
shutil.copy('example_app_in', proot+'/templates/')
shutil.copy('example_app_out', proot+'/templates/')
shutil.copy('../tools/create_work_example', proot+'/bin/')
httpd_conf_template_filename = os.path.join(
options.project_root,
project_shortname+'.httpd.conf'
)
print >>open(httpd_conf_template_filename,'w'), '''
## Settings for BOINC project %(project_longname)s
Alias /%(project_shortname)s/download %(proot)s/download
Alias /%(project_shortname)s/stats %(proot)s/html/stats
Alias /%(project_shortname)s/user_profile %(proot)s/html/user_profile
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
# in the following, the "Order" and "Allow" lines are for Apache 2.2;
# for Apache 2.4, replace them with the single line
# Require all granted
# or
# Require all denied
# NOTE: Turn off access to certain default directories
Order deny,allow
Deny from all
Order deny,allow
Deny from all
# NOTE: Allow access but disable PHP script execution
RemoveType .php .phtml
Order allow,deny
Allow from all
RemoveType .php .phtml
Order allow,deny
Allow from all
RemoveType .php .phtml
Order allow,deny
Allow from all
# NOTE: Allow access and allow PHP script execution
Options Indexes MultiViews
AllowOverride AuthConfig
Order allow,deny
Allow from all
# NOTE: Allow access and allow CGI execution
Options ExecCGI
AllowOverride AuthConfig
Order allow,deny
Allow from all
''' %locals()
htaccess_filename = options.project_root+'/html/ops/.htaccess'
htpasswd_filename = options.project_root+'/html/ops/.htpasswd'
print >>open(htaccess_filename, 'w'), '''
AuthName "%(project_shortname)s"
AuthType Basic
AuthUserFile %(htpasswd_filename)s
require valid-user
'''%locals()
cronjob_filename = os.path.join(
options.project_root,
project_shortname+'.cronjob'
)
print >>open(cronjob_filename,'w'), '''0,5,10,15,20,25,30,35,40,45,50,55 * * * * cd %(proot)s ; %(proot)s/bin/start --cron
'''%locals()
readme_filename = os.path.join(
options.project_root,
project_shortname+'.readme'
)
test_app_msg = ''
if options.test_app:
test_app_msg = '''
- The project is configured with a test application.
To install this application (recommended) run:
cd %(proot)s
bin/update_versions
'''%locals()
xadd_msg = ''
if not options.web_only:
xadd_msg = '''
- In the project home directory (%(proot)s) run
bin/xadd
'''%locals()
compute_msg = ''
if not options.web_only:
compute_msg = '''
----------------------------
To use this project for your own computations, you'll need to
- Port your application(s) and add them
- Develop programs to submit and handle jobs
See the online documentation at http://boinc.berkeley.edu/
'''
html_user_url = options.html_user_url
html_ops_url = options.html_ops_url
print >>open(readme_filename,'w'), '''Steps to complete installation:
- Change Apache configuration (as root):
cat %(httpd_conf_template_filename)s >> /etc/apache/httpd.conf
(path to httpd.conf varies; try /etc/httpd/ or /etc/apache2)
Then restart the web server:
/usr/sbin/apache2ctl restart
(or /usr/sbin/apachectl restart)
- Add to crontab (as %(USER)s)
crontab -e
and add the line:
0,5,10,15,20,25,30,35,40,45,50,55 * * * * cd %(proot)s ; %(proot)s/bin/start --cron
%(xadd_msg)s%(test_app_msg)s
- Add a .htpasswd file in the project's html/ops directory:
(You'll need this to access the project's Administrative page)
htpasswd -c .htpasswd username
----------------------------
To start, show status, and stop the project, run:
bin/start
bin/status
bin/stop
The project's URLs are:
Home page (and master URL): %(html_user_url)s
Administrative page: %(html_ops_url)s
%(compute_msg)s
'''%locals()
print '''
Done creating project. Please view
%(readme_filename)s
for important additional instructions.
'''%locals()