blob: 18d9788f140500283f59365362c6a65f06b8c0a0 [file] [log] [blame]
"""Implements the command line 'llvmlab' tool."""
import hashlib
import os
import random
import shutil
import sys
import flask
import llvmlab.data
import llvmlab.user
import llvmlab.ci.status
import llvmlab.ui.app
def note(message):
print >>sys.stderr,"note: %s" % message
def warning(message):
print >>sys.stderr,"warning: %s" % message
def sorted(items):
items = list(items)
items.sort()
return items
def split_name_and_email(str):
if (str.count('<') != 1 or
str.count('>') != 1 or
not str.endswith('>')):
raise ValueError,"Don't know how to parse: %r" % (str,)
lhs,rhs = str[:-1].split("<")
return lhs.strip(), rhs.strip()
def action_create(name, args):
"""create a llvmlab installation"""
import llvmlab
from optparse import OptionParser, OptionGroup
parser = OptionParser("%%prog %s [options] <path>" % name)
group = OptionGroup(parser, "CONFIG OPTIONS")
group.add_option("", "--admin-login", dest="admin_login",
help="administrator login [%default]", default='admin')
group.add_option("", "--admin-name", dest="admin_name",
help="administrator name [%default]",
default='Administrator')
group.add_option("", "--admin-password", dest="admin_password",
help="administrator password [%default]", default='admin')
group.add_option("", "--admin-email", dest="admin_email",
help="administrator email [%default]",
default='admin@example.com')
group.add_option("", "--master-url", dest="master_url",
help="URL for the buildbot master [%default]",
default='http://lab.llvm.org:8013')
group.add_option("", "--plugin-module", dest="plugin_module",
help="name of the dashboard plugin to load [%default]",
default=None)
group.add_option("", "--debug-server", dest="debug_server",
help="run server in debug mode [%default]",
action="store_true", default=False)
parser.add_option_group(group)
(opts, args) = parser.parse_args(args)
if len(args) != 1:
parser.error("invalid number of arguments")
install_path, = args
install_path = os.path.abspath(install_path)
cfg_path = os.path.join(install_path, 'lab.cfg')
app_path = os.path.join(install_path, 'app.wsgi')
# Create the install directory.
if os.path.exists(install_path):
parser.error("refusing to install: %r exists" % install_path)
try:
os.mkdir(install_path)
except:
parser.error("unable to create directory: %r" % install_path)
# Construct the config file.
sample_cfg_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
"lab.cfg.sample")
sample_cfg_file = open(sample_cfg_path, "rb")
sample_cfg_data = sample_cfg_file.read()
sample_cfg_file.close()
# Fill in the sample config.
secret_key = hashlib.sha1(str(random.getrandbits(256))).hexdigest()
cfg_options = dict(opts.__dict__)
cfg_options['admin_passhash'] = hashlib.sha256(
opts.admin_password + secret_key).hexdigest()
cfg_options['secret_key'] = secret_key
cfg_options['install_path'] = install_path
cfg_options['plugin_module'] = opts.plugin_module
cfg_data = sample_cfg_data % cfg_options
# Write the initial config file.
cfg_file = open(cfg_path, 'w')
cfg_file.write(cfg_data)
cfg_file.close()
# Construct the WSGI app file.
app_wsgi_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
"app.wsgi.sample")
app_wsgi_file = open(app_wsgi_path, "rb")
app_wsgi_data = app_wsgi_file.read()
app_wsgi_file.close()
# Fill in the sample WSGI app.
virtual_env = os.environ.get('VIRTUAL_ENV')
if virtual_env:
site_import_string = """
import site
site.addsitedir(%r)\n""" % virtual_env
else:
site_import_string = ""
app_data = app_wsgi_data % { 'site_import_string' : site_import_string,
'lab_config_path' : cfg_path }
app_file = open(app_path, 'w')
app_file.write(app_data)
app_file.close()
# Construct the initial database and status files.
data = llvmlab.data.Data(users = [], machines = [])
status = llvmlab.ci.status.Status(opts.master_url, {})
# Construct an app instance, and save the data.
instance = llvmlab.ui.app.App.create_standalone(data = data,
status = status,
config_path = cfg_path)
instance.save_data()
instance.save_status()
def action_runserver(name, args):
"""run a llvmlab instance"""
import llvmlab
from optparse import OptionParser, OptionGroup
parser = OptionParser("%%prog %s [options]" % name)
parser.add_option("", "--reloader", dest="reloader", default=False,
action="store_true", help="use WSGI reload monitor")
parser.add_option("", "--debugger", dest="debugger", default=False,
action="store_true", help="use WSGI debugger")
parser.add_option("", "--profiler", dest="profiler", default=False,
action="store_true", help="enable WSGI profiler")
(opts, args) = parser.parse_args(args)
if len(args) != 0:
parser.error("invalid number of arguments")
app = llvmlab.ui.app.App.create_standalone()
if opts.debugger:
app.debug = True
if opts.profiler:
app.wsgi_app = werkzeug.contrib.profiler.ProfilerMiddleware(
app.wsgi_app, stream = open('profiler.log', 'w'))
app.run(use_reloader = opts.reloader,
use_debugger = opts.debugger)
def action_import_users(name, args):
"""import users from SVN information"""
import llvmlab
import ConfigParser
from optparse import OptionParser, OptionGroup
parser = OptionParser("""\
%%prog %s [options] <lab config path> <svn mailer config> <svn htpasswd path>
This command imports user information from the llvm.org SVN information. It will
add any users who are not present in the lab.llvm.org database, and import their
name, email, and SVN login information.\
""" % name)
(opts, args) = parser.parse_args(args)
if len(args) != 3:
parser.error("invalid number of arguments")
config_path, svn_mailer_path, svn_htpasswd_path = args
# Load the app object.
instance = llvmlab.ui.app.App.create_standalone(config_path = config_path)
data = instance.config.data
# Load the SVN mailer config.
parser = ConfigParser.RawConfigParser()
parser.read(svn_mailer_path)
# Load the SVN htpasswd file.
file = open(svn_htpasswd_path)
svn_htpasswd = {}
for ln in file:
if ln.strip():
user,htpasswd,module = ln.split(":")
svn_htpasswd[user] = (htpasswd, module)
file.close()
# Validate that the authors list and the htpasswd list coincide.
svn_authors = dict((author, parser.get("authors", author))
for author in parser.options("authors"))
for id in set(svn_authors) - set(svn_htpasswd):
warning("svn mailer authors contains user without htpasswd: %r " % id)
for id in set(svn_htpasswd) - set(svn_authors):
warning("svn contains passwd but no mailer entry: %r " % id)
# Add user entries for any missing users.
for id in sorted(set(svn_authors) & set(svn_htpasswd)):
name,email = split_name_and_email(svn_authors[id])
htpasswd = svn_htpasswd[id][0]
passhash = hashlib.sha256(
htpasswd + instance.config['SECRET_KEY']).hexdigest()
# Lookup the user entry.
user = data.users.get(id)
# Never allow modifying the admin user.
if user is data.admin_user:
warning("ignore %r, is the admin user!" % id)
continue
# Create the user if missing.
if user is None:
# Use the users htpasswd (itself) as the initial password.
user = data.users[id] = llvmlab.user.User(id, passhash, name,
email, htpasswd)
note("added user %r" % id)
continue
# Otherwise, update the users info if necessary.
for kind,new,old in (('name', name, user.name),
('email', email, user.email),
('htpasswd', htpasswd, user.htpasswd)):
if new != old:
note("changed %r %s from %r to %r" % (
id, kind, old, new))
setattr(user, kind, new)
# Save the instance data.
instance.save_data()
###
commands = dict((name[7:].replace("_","-"), f)
for name,f in locals().items()
if name.startswith('action_'))
def usage():
print >>sys.stderr, "Usage: %s command [options]" % (
os.path.basename(sys.argv[0]))
print >>sys.stderr
print >>sys.stderr, "Available commands:"
cmds_width = max(map(len, commands))
for name,func in sorted(commands.items()):
print >>sys.stderr, " %-*s - %s" % (cmds_width, name, func.__doc__)
sys.exit(1)
def main():
import sys
if len(sys.argv) < 2 or sys.argv[1] not in commands:
usage()
cmd = sys.argv[1]
commands[cmd](cmd, sys.argv[2:])