2011-06-03 21:47:44 +00:00
|
|
|
import argparse
|
2011-06-15 19:10:27 +00:00
|
|
|
import os
|
2011-06-03 21:47:44 +00:00
|
|
|
import yaml
|
2013-02-21 22:51:54 +00:00
|
|
|
import StringIO
|
|
|
|
import contextlib
|
2011-06-03 21:47:44 +00:00
|
|
|
|
|
|
|
def config_file(string):
|
|
|
|
config = {}
|
|
|
|
try:
|
|
|
|
with file(string) as f:
|
|
|
|
g = yaml.safe_load_all(f)
|
|
|
|
for new in g:
|
|
|
|
config.update(new)
|
|
|
|
except IOError, e:
|
|
|
|
raise argparse.ArgumentTypeError(str(e))
|
|
|
|
return config
|
|
|
|
|
|
|
|
class MergeConfig(argparse.Action):
|
|
|
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
|
|
config = getattr(namespace, self.dest)
|
2011-11-17 21:06:36 +00:00
|
|
|
from teuthology.misc import deep_merge
|
2011-06-03 21:47:44 +00:00
|
|
|
for new in values:
|
2011-11-17 21:06:36 +00:00
|
|
|
deep_merge(config, new)
|
2011-06-03 21:47:44 +00:00
|
|
|
|
|
|
|
def parse_args():
|
|
|
|
parser = argparse.ArgumentParser(description='Run ceph integration tests')
|
|
|
|
parser.add_argument(
|
|
|
|
'-v', '--verbose',
|
|
|
|
action='store_true', default=None,
|
|
|
|
help='be more verbose',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'config',
|
|
|
|
metavar='CONFFILE',
|
|
|
|
nargs='+',
|
|
|
|
type=config_file,
|
|
|
|
action=MergeConfig,
|
|
|
|
default={},
|
|
|
|
help='config file to read',
|
|
|
|
)
|
2011-06-07 21:47:30 +00:00
|
|
|
parser.add_argument(
|
2012-07-05 20:43:19 +00:00
|
|
|
'-a', '--archive',
|
2011-06-07 21:47:30 +00:00
|
|
|
metavar='DIR',
|
|
|
|
help='path to archive results in',
|
|
|
|
)
|
2011-06-28 21:15:19 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'--description',
|
2011-07-12 01:00:03 +00:00
|
|
|
help='job description',
|
2011-06-28 21:15:19 +00:00
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--owner',
|
2011-07-12 01:00:03 +00:00
|
|
|
help='job owner',
|
2011-06-28 21:15:19 +00:00
|
|
|
)
|
2011-07-06 21:22:43 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'--lock',
|
|
|
|
action='store_true',
|
|
|
|
default=False,
|
|
|
|
help='lock machines for the duration of the run',
|
|
|
|
)
|
2013-02-05 20:53:08 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'--machine-type',
|
|
|
|
default=None,
|
|
|
|
help='Type of machine to lock/run tests on.',
|
|
|
|
)
|
2011-07-07 23:15:18 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'--block',
|
|
|
|
action='store_true',
|
|
|
|
default=False,
|
|
|
|
help='block until locking machines succeeds (use with --lock)',
|
|
|
|
)
|
2013-01-23 03:53:14 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'--name',
|
2013-02-01 13:46:04 +00:00
|
|
|
metavar='NAME',
|
2013-01-23 03:53:14 +00:00
|
|
|
help='name for this teuthology run',
|
|
|
|
)
|
2011-06-03 21:47:44 +00:00
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
return args
|
|
|
|
|
|
|
|
def main():
|
2012-08-13 23:10:05 +00:00
|
|
|
from gevent import monkey; monkey.patch_all(dns=False)
|
2011-09-13 21:53:02 +00:00
|
|
|
from .orchestra import monkey; monkey.patch_all()
|
2011-06-03 21:47:44 +00:00
|
|
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
ctx = parse_args()
|
|
|
|
|
|
|
|
loglevel = logging.INFO
|
|
|
|
if ctx.verbose:
|
|
|
|
loglevel = logging.DEBUG
|
|
|
|
|
|
|
|
logging.basicConfig(
|
|
|
|
level=loglevel,
|
|
|
|
)
|
|
|
|
|
2012-04-03 21:53:17 +00:00
|
|
|
|
|
|
|
if 'targets' in ctx.config and 'roles' in ctx.config:
|
|
|
|
targets = len(ctx.config['targets'])
|
|
|
|
roles = len(ctx.config['roles'])
|
|
|
|
assert targets >= roles, \
|
|
|
|
'%d targets are needed for all roles but found %d listed.' % (roles, targets)
|
2013-02-05 20:53:08 +00:00
|
|
|
|
|
|
|
machine_type = ctx.machine_type
|
|
|
|
if machine_type is None:
|
2013-03-29 19:16:39 +00:00
|
|
|
fallback_default = ctx.config.get('machine_type', 'plana')
|
|
|
|
machine_type = ctx.config.get('machine-type', fallback_default)
|
2013-02-05 20:53:08 +00:00
|
|
|
|
2011-07-07 23:15:18 +00:00
|
|
|
if ctx.block:
|
|
|
|
assert ctx.lock, \
|
|
|
|
'the --block option is only supported with the --lock option'
|
|
|
|
|
2011-07-07 18:43:35 +00:00
|
|
|
from teuthology.misc import read_config
|
|
|
|
read_config(ctx)
|
|
|
|
|
2012-06-20 18:35:43 +00:00
|
|
|
log.debug('\n '.join(['Config:', ] + yaml.safe_dump(ctx.config, default_flow_style=False).splitlines()))
|
|
|
|
|
|
|
|
ctx.summary = dict(success=True)
|
|
|
|
|
|
|
|
if ctx.owner is None:
|
|
|
|
from teuthology.misc import get_user
|
|
|
|
ctx.owner = get_user()
|
|
|
|
ctx.summary['owner'] = ctx.owner
|
|
|
|
|
|
|
|
if ctx.description is not None:
|
|
|
|
ctx.summary['description'] = ctx.description
|
|
|
|
|
2011-06-15 19:10:27 +00:00
|
|
|
if ctx.archive is not None:
|
|
|
|
os.mkdir(ctx.archive)
|
|
|
|
|
2011-06-15 19:32:22 +00:00
|
|
|
handler = logging.FileHandler(
|
|
|
|
filename=os.path.join(ctx.archive, 'teuthology.log'),
|
|
|
|
)
|
|
|
|
formatter = logging.Formatter(
|
|
|
|
fmt='%(asctime)s.%(msecs)03d %(levelname)s:%(name)s:%(message)s',
|
|
|
|
datefmt='%Y-%m-%dT%H:%M:%S',
|
|
|
|
)
|
|
|
|
handler.setFormatter(formatter)
|
|
|
|
logging.getLogger().addHandler(handler)
|
|
|
|
|
2012-01-08 22:39:30 +00:00
|
|
|
with file(os.path.join(ctx.archive, 'pid'), 'w') as f:
|
|
|
|
f.write('%d' % os.getpid())
|
|
|
|
|
2012-06-20 18:35:43 +00:00
|
|
|
with file(os.path.join(ctx.archive, 'owner'), 'w') as f:
|
|
|
|
f.write(ctx.owner + '\n')
|
2011-06-28 21:15:19 +00:00
|
|
|
|
2012-11-25 16:37:06 +00:00
|
|
|
with file(os.path.join(ctx.archive, 'orig.config.yaml'), 'w') as f:
|
|
|
|
yaml.safe_dump(ctx.config, f, default_flow_style=False)
|
|
|
|
|
2011-06-30 22:53:42 +00:00
|
|
|
for task in ctx.config['tasks']:
|
|
|
|
assert 'kernel' not in task, \
|
|
|
|
'kernel installation shouldn be a base-level item, not part of the tasks list'
|
|
|
|
|
2011-07-06 21:22:43 +00:00
|
|
|
init_tasks = []
|
|
|
|
if ctx.lock:
|
|
|
|
assert 'targets' not in ctx.config, \
|
|
|
|
'You cannot specify targets in a config file when using the --lock option'
|
2013-02-05 20:53:08 +00:00
|
|
|
init_tasks.append({'internal.lock_machines': (len(ctx.config['roles']), machine_type)})
|
2011-07-06 21:22:43 +00:00
|
|
|
|
|
|
|
init_tasks.extend([
|
2011-11-17 19:57:07 +00:00
|
|
|
{'internal.save_config': None},
|
2011-07-06 21:22:43 +00:00
|
|
|
{'internal.check_lock': None},
|
|
|
|
{'internal.connect': None},
|
|
|
|
{'internal.check_conflict': None},
|
2013-03-24 03:58:46 +00:00
|
|
|
{'internal.check_ceph_data': None},
|
2013-04-03 01:27:38 +00:00
|
|
|
{'internal.vm_setup': None},
|
2011-07-06 21:22:43 +00:00
|
|
|
])
|
2011-06-30 22:53:42 +00:00
|
|
|
if 'kernel' in ctx.config:
|
2013-07-13 03:20:45 +00:00
|
|
|
try:
|
|
|
|
distro = ctx.config['downburst'].get('distro')
|
|
|
|
if distro is None:
|
|
|
|
distro = 'ubuntu'
|
|
|
|
except:
|
|
|
|
distro = 'ubuntu'
|
|
|
|
if distro == 'ubuntu':
|
|
|
|
init_tasks.append({'kernel': ctx.config['kernel']})
|
2011-06-30 22:53:42 +00:00
|
|
|
init_tasks.extend([
|
|
|
|
{'internal.base': None},
|
|
|
|
{'internal.archive': None},
|
|
|
|
{'internal.coredump': None},
|
|
|
|
{'internal.syslog': None},
|
2012-02-21 23:01:45 +00:00
|
|
|
{'internal.timer': None},
|
2011-06-30 22:53:42 +00:00
|
|
|
])
|
|
|
|
|
|
|
|
ctx.config['tasks'][:0] = init_tasks
|
2011-06-16 21:17:14 +00:00
|
|
|
|
2011-06-16 20:01:09 +00:00
|
|
|
from teuthology.run_tasks import run_tasks
|
2011-06-15 22:52:30 +00:00
|
|
|
try:
|
2011-06-16 20:01:09 +00:00
|
|
|
run_tasks(tasks=ctx.config['tasks'], ctx=ctx)
|
2011-06-15 22:52:30 +00:00
|
|
|
finally:
|
2011-11-09 00:01:39 +00:00
|
|
|
if not ctx.summary.get('success') and ctx.config.get('nuke-on-error'):
|
2012-04-25 00:51:16 +00:00
|
|
|
from teuthology.nuke import nuke
|
|
|
|
# only unlock if we locked them in the first place
|
|
|
|
nuke(ctx, log, ctx.lock)
|
2011-11-18 21:53:51 +00:00
|
|
|
if ctx.archive is not None:
|
|
|
|
with file(os.path.join(ctx.archive, 'summary.yaml'), 'w') as f:
|
|
|
|
yaml.safe_dump(ctx.summary, f, default_flow_style=False)
|
2013-02-21 22:51:54 +00:00
|
|
|
with contextlib.closing(StringIO.StringIO()) as f:
|
|
|
|
yaml.safe_dump(ctx.summary, f)
|
|
|
|
log.info('Summary data:\n%s' % f.getvalue())
|
2013-02-27 19:32:37 +00:00
|
|
|
with contextlib.closing(StringIO.StringIO()) as f:
|
|
|
|
if 'email-on-error' in ctx.config and not ctx.summary.get('success', False):
|
|
|
|
yaml.safe_dump(ctx.summary, f)
|
|
|
|
yaml.safe_dump(ctx.config, f)
|
|
|
|
emsg = f.getvalue()
|
|
|
|
subject = "Teuthology error -- %s" % ctx.summary['failure_reason']
|
|
|
|
from teuthology.suite import email_results
|
|
|
|
email_results(subject,"Teuthology",ctx.config['email-on-error'],emsg)
|
2013-02-27 19:35:55 +00:00
|
|
|
if ctx.summary.get('success', True):
|
|
|
|
log.info('pass')
|
|
|
|
else:
|
|
|
|
log.info('FAIL')
|
|
|
|
import sys
|
|
|
|
sys.exit(1)
|
2012-03-05 18:28:35 +00:00
|
|
|
|
2011-07-08 18:37:20 +00:00
|
|
|
def schedule():
|
|
|
|
parser = argparse.ArgumentParser(description='Schedule ceph integration tests')
|
|
|
|
parser.add_argument(
|
|
|
|
'config',
|
|
|
|
metavar='CONFFILE',
|
2011-08-26 00:11:33 +00:00
|
|
|
nargs='*',
|
2011-07-08 18:37:20 +00:00
|
|
|
type=config_file,
|
|
|
|
action=MergeConfig,
|
|
|
|
default={},
|
|
|
|
help='config file to read',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--name',
|
2011-08-26 00:11:33 +00:00
|
|
|
help='name of suite run the job is part of',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--last-in-suite',
|
|
|
|
action='store_true',
|
|
|
|
default=False,
|
|
|
|
help='mark the last job in a suite so suite post-processing can be run',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--email',
|
|
|
|
help='where to send the results of a suite (only applies to the last job in a suite)',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--timeout',
|
|
|
|
help='how many seconds to wait for jobs to finish before emailing results (only applies to the last job in a suite',
|
|
|
|
type=int,
|
2011-07-08 18:37:20 +00:00
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--description',
|
|
|
|
help='job description',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--owner',
|
|
|
|
help='job owner',
|
|
|
|
)
|
2011-09-01 00:43:14 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'--delete',
|
|
|
|
metavar='JOBID',
|
|
|
|
type=int,
|
|
|
|
nargs='*',
|
|
|
|
help='list of jobs to remove from the queue',
|
|
|
|
)
|
2012-07-14 20:02:04 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'-n', '--num',
|
|
|
|
default=1,
|
|
|
|
type=int,
|
|
|
|
help='number of times to run/queue the job'
|
|
|
|
)
|
2011-07-08 18:37:20 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'-v', '--verbose',
|
|
|
|
action='store_true',
|
|
|
|
default=False,
|
|
|
|
help='be more verbose',
|
|
|
|
)
|
2012-09-21 21:54:19 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'-b', '--branch',
|
|
|
|
default='master',
|
|
|
|
help='which branch of teuthology to use',
|
|
|
|
)
|
2012-11-02 18:08:25 +00:00
|
|
|
parser.add_argument(
|
|
|
|
'-s', '--show',
|
|
|
|
metavar='JOBID',
|
|
|
|
type=int,
|
|
|
|
nargs='*',
|
|
|
|
help='output the contents of specified jobs in the queue',
|
|
|
|
)
|
2011-07-08 18:37:20 +00:00
|
|
|
|
|
|
|
ctx = parser.parse_args()
|
2011-08-26 00:11:33 +00:00
|
|
|
if not ctx.last_in_suite:
|
|
|
|
assert not ctx.email, '--email is only applicable to the last job in a suite'
|
|
|
|
assert not ctx.timeout, '--timeout is only applicable to the last job in a suite'
|
2011-07-08 18:37:20 +00:00
|
|
|
|
|
|
|
from teuthology.misc import read_config, get_user
|
|
|
|
if ctx.owner is None:
|
2011-07-20 00:24:49 +00:00
|
|
|
ctx.owner = 'scheduled_{user}'.format(user=get_user())
|
2011-07-08 18:37:20 +00:00
|
|
|
read_config(ctx)
|
|
|
|
|
|
|
|
import teuthology.queue
|
|
|
|
beanstalk = teuthology.queue.connect(ctx)
|
|
|
|
|
2012-09-21 21:54:19 +00:00
|
|
|
tube = 'teuthology'
|
|
|
|
if ctx.branch != 'master':
|
|
|
|
tube += '-' + ctx.branch
|
|
|
|
beanstalk.use(tube)
|
2011-09-01 00:43:14 +00:00
|
|
|
|
2012-11-02 18:33:46 +00:00
|
|
|
if ctx.show:
|
|
|
|
for jobid in ctx.show:
|
2012-11-02 18:08:25 +00:00
|
|
|
job = beanstalk.peek(jobid)
|
|
|
|
if job is None and ctx.verbose:
|
|
|
|
print 'job {jid} is not in the queue'.format(jid=jobid)
|
|
|
|
else:
|
|
|
|
print 'job {jid} contains: '.format(jid=jobid), job.body
|
|
|
|
return
|
|
|
|
|
2011-09-01 00:43:14 +00:00
|
|
|
if ctx.delete:
|
|
|
|
for jobid in ctx.delete:
|
|
|
|
job = beanstalk.peek(jobid)
|
|
|
|
if job is None:
|
|
|
|
print 'job {jid} is not in the queue'.format(jid=jobid)
|
|
|
|
else:
|
|
|
|
job.delete()
|
|
|
|
return
|
|
|
|
|
2011-09-21 18:05:18 +00:00
|
|
|
job_config = dict(
|
2011-07-08 18:37:20 +00:00
|
|
|
config=ctx.config,
|
|
|
|
name=ctx.name,
|
2011-08-26 00:11:33 +00:00
|
|
|
last_in_suite=ctx.last_in_suite,
|
|
|
|
email=ctx.email,
|
2011-07-08 18:37:20 +00:00
|
|
|
description=ctx.description,
|
|
|
|
owner=ctx.owner,
|
|
|
|
verbose=ctx.verbose,
|
2011-09-21 18:05:18 +00:00
|
|
|
)
|
|
|
|
if ctx.timeout is not None:
|
|
|
|
job_config['results_timeout'] = ctx.timeout
|
|
|
|
|
|
|
|
job = yaml.safe_dump(job_config)
|
2012-07-14 20:02:04 +00:00
|
|
|
num = ctx.num
|
|
|
|
while num > 0:
|
|
|
|
jid = beanstalk.put(job, ttr=60*60*24)
|
|
|
|
print 'Job scheduled with ID {jid}'.format(jid=jid)
|
|
|
|
num -= 1
|