2013-10-16 19:31:26 +00:00
|
|
|
#!/usr/bin/python
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import beanstalkc
|
|
|
|
import yaml
|
|
|
|
import psutil
|
|
|
|
import subprocess
|
|
|
|
import tempfile
|
2013-10-17 14:42:02 +00:00
|
|
|
import logging
|
2013-10-16 19:31:26 +00:00
|
|
|
|
|
|
|
from .config import config
|
|
|
|
|
2013-10-17 14:42:02 +00:00
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
2013-10-16 19:31:26 +00:00
|
|
|
|
|
|
|
def main(args):
|
2013-12-19 21:39:15 +00:00
|
|
|
run_name = args['--run']
|
2013-12-19 22:43:11 +00:00
|
|
|
job = args['--job']
|
2013-10-16 19:31:26 +00:00
|
|
|
archive_base = args['--archive']
|
|
|
|
owner = args['--owner']
|
|
|
|
machine_type = args['--machine_type']
|
|
|
|
|
2013-12-19 22:43:11 +00:00
|
|
|
if job:
|
|
|
|
for job_id in job:
|
|
|
|
kill_job(run_name, job_id, archive_base, owner, machine_type)
|
2013-12-19 22:12:56 +00:00
|
|
|
else:
|
|
|
|
kill_run(run_name, archive_base, owner, machine_type)
|
2013-10-16 19:31:26 +00:00
|
|
|
|
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
def kill_run(run_name, archive_base=None, owner=None, machine_type=None):
|
|
|
|
run_info = {}
|
2013-10-16 19:31:26 +00:00
|
|
|
if archive_base:
|
2013-12-19 21:39:15 +00:00
|
|
|
run_archive_dir = os.path.join(archive_base, run_name)
|
2014-02-05 19:31:10 +00:00
|
|
|
if os.path.isdir(run_archive_dir):
|
|
|
|
run_info = find_run_info(run_archive_dir)
|
|
|
|
machine_type = run_info['machine_type']
|
|
|
|
owner = run_info['owner']
|
|
|
|
elif machine_type is None:
|
|
|
|
raise RuntimeError("The run is still entirely enqueued; " +
|
|
|
|
"you must also pass --machine-type")
|
2013-10-16 19:31:26 +00:00
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
remove_beanstalk_jobs(run_name, machine_type)
|
|
|
|
kill_processes(run_name, run_info.get('pids'))
|
2014-02-05 19:31:10 +00:00
|
|
|
if owner is not None:
|
|
|
|
targets = find_targets(run_name, owner)
|
|
|
|
nuke_targets(targets, owner)
|
2013-12-19 22:12:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
def kill_job(run_name, job_id, archive_base=None, owner=None,
|
|
|
|
machine_type=None):
|
|
|
|
job_archive_dir = os.path.join(archive_base, run_name, job_id)
|
|
|
|
job_info = find_job_info(job_archive_dir)
|
|
|
|
owner = job_info['owner']
|
|
|
|
kill_processes(run_name, [job_info.get('pid')])
|
|
|
|
targets = dict(targets=job_info['targets'])
|
|
|
|
nuke_targets(targets, owner)
|
2013-10-16 19:31:26 +00:00
|
|
|
|
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
def find_run_info(run_archive_dir):
|
|
|
|
run_info_fields = [
|
2013-10-17 18:02:30 +00:00
|
|
|
'machine_type',
|
|
|
|
'owner',
|
|
|
|
]
|
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
run_info = dict(pids=[])
|
2013-10-16 19:31:26 +00:00
|
|
|
job_info = {}
|
2013-12-19 21:39:15 +00:00
|
|
|
for job_id in os.listdir(run_archive_dir):
|
|
|
|
job_dir = os.path.join(run_archive_dir, job_id)
|
2013-10-17 18:02:30 +00:00
|
|
|
if not os.path.isdir(job_dir):
|
2013-12-10 22:47:35 +00:00
|
|
|
continue
|
2013-10-17 18:02:30 +00:00
|
|
|
job_info = find_job_info(job_dir)
|
|
|
|
for key in job_info.keys():
|
2013-12-19 21:39:15 +00:00
|
|
|
if key in run_info_fields and key not in run_info:
|
|
|
|
run_info[key] = job_info[key]
|
2013-12-10 22:25:28 +00:00
|
|
|
if 'pid' in job_info:
|
2013-12-19 21:39:15 +00:00
|
|
|
run_info['pids'].append(job_info['pid'])
|
|
|
|
return run_info
|
2013-10-16 19:31:26 +00:00
|
|
|
|
|
|
|
|
|
|
|
def find_job_info(job_archive_dir):
|
2013-10-17 18:02:30 +00:00
|
|
|
job_info = {}
|
|
|
|
|
2013-10-16 19:31:26 +00:00
|
|
|
info_file = os.path.join(job_archive_dir, 'info.yaml')
|
|
|
|
if os.path.isfile(info_file):
|
2013-10-17 18:02:30 +00:00
|
|
|
job_info.update(yaml.safe_load(open(info_file, 'r')))
|
|
|
|
|
|
|
|
conf_file = os.path.join(job_archive_dir, 'config.yaml')
|
|
|
|
if os.path.isfile(conf_file):
|
|
|
|
job_info.update(yaml.safe_load(open(conf_file, 'r')))
|
2014-02-26 19:13:41 +00:00
|
|
|
else:
|
|
|
|
conf_file = os.path.join(job_archive_dir, 'orig.config.yaml')
|
|
|
|
if os.path.isfile(conf_file):
|
|
|
|
log.debug("config.yaml not found but orig.config.yaml found")
|
|
|
|
job_info.update(yaml.safe_load(open(conf_file, 'r')))
|
2013-10-17 18:02:30 +00:00
|
|
|
|
|
|
|
return job_info
|
2013-10-16 19:31:26 +00:00
|
|
|
|
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
def remove_beanstalk_jobs(run_name, tube_name):
|
2013-10-16 19:31:26 +00:00
|
|
|
qhost = config.queue_host
|
|
|
|
qport = config.queue_port
|
|
|
|
if qhost is None or qport is None:
|
|
|
|
raise RuntimeError(
|
|
|
|
'Beanstalk queue information not found in {conf_path}'.format(
|
|
|
|
conf_path=config.teuthology_yaml))
|
2013-10-17 14:42:02 +00:00
|
|
|
log.info("Checking Beanstalk Queue...")
|
2013-10-16 19:31:26 +00:00
|
|
|
beanstalk = beanstalkc.Connection(host=qhost, port=qport)
|
|
|
|
beanstalk.watch(tube_name)
|
|
|
|
beanstalk.ignore('default')
|
|
|
|
|
|
|
|
curjobs = beanstalk.stats_tube(tube_name)['current-jobs-ready']
|
|
|
|
if curjobs != 0:
|
|
|
|
x = 1
|
|
|
|
while x != curjobs:
|
|
|
|
x += 1
|
|
|
|
job = beanstalk.reserve(timeout=20)
|
|
|
|
job_config = yaml.safe_load(job.body)
|
2013-12-19 21:39:15 +00:00
|
|
|
if run_name == job_config['name']:
|
2013-10-16 19:31:26 +00:00
|
|
|
job_id = job.stats()['id']
|
|
|
|
msg = "Deleting job from queue. ID: " + \
|
|
|
|
"{id} Name: {name} Desc: {desc}".format(
|
|
|
|
id=str(job_id),
|
|
|
|
name=job_config['name'],
|
|
|
|
desc=job_config['description'],
|
|
|
|
)
|
2013-10-17 14:42:02 +00:00
|
|
|
log.info(msg)
|
2013-10-16 19:31:26 +00:00
|
|
|
job.delete()
|
|
|
|
else:
|
|
|
|
print "No jobs in Beanstalk Queue"
|
|
|
|
beanstalk.close()
|
|
|
|
|
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
def kill_processes(run_name, pids=None):
|
2013-10-17 18:02:30 +00:00
|
|
|
if pids:
|
|
|
|
to_kill = set(pids).intersection(psutil.get_pid_list())
|
|
|
|
else:
|
2013-12-19 21:39:15 +00:00
|
|
|
to_kill = find_pids(run_name)
|
2013-10-17 18:02:30 +00:00
|
|
|
|
|
|
|
if len(to_kill) == 0:
|
|
|
|
log.info("No teuthology processes running")
|
|
|
|
else:
|
|
|
|
log.info("Killing Pids: " + str(to_kill))
|
|
|
|
for pid in to_kill:
|
|
|
|
subprocess.call(['sudo', 'kill', str(pid)])
|
|
|
|
|
|
|
|
|
2013-12-19 22:12:56 +00:00
|
|
|
def process_matches_run(pid, run_name):
|
|
|
|
try:
|
|
|
|
p = psutil.Process(pid)
|
|
|
|
if run_name in p.cmdline and sys.argv[0] not in p.cmdline:
|
|
|
|
return True
|
|
|
|
except psutil.NoSuchProcess:
|
|
|
|
pass
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
def find_pids(run_name):
|
|
|
|
run_pids = []
|
2013-10-16 19:31:26 +00:00
|
|
|
for pid in psutil.get_pid_list():
|
2013-12-19 22:12:56 +00:00
|
|
|
if process_matches_run(pid, run_name):
|
|
|
|
run_pids.append(pid)
|
2013-12-19 21:39:15 +00:00
|
|
|
return run_pids
|
2013-10-16 19:31:26 +00:00
|
|
|
|
|
|
|
|
2013-12-19 21:39:15 +00:00
|
|
|
def find_targets(run_name, owner):
|
2013-10-17 14:42:02 +00:00
|
|
|
lock_args = [
|
|
|
|
'teuthology-lock',
|
|
|
|
'--list-targets',
|
|
|
|
'--desc-pattern',
|
2013-12-19 21:39:15 +00:00
|
|
|
'/' + run_name + '/',
|
2013-10-17 14:42:02 +00:00
|
|
|
'--status',
|
|
|
|
'up',
|
|
|
|
'--owner',
|
|
|
|
owner
|
|
|
|
]
|
|
|
|
proc = subprocess.Popen(lock_args, stdout=subprocess.PIPE)
|
|
|
|
stdout, stderr = proc.communicate()
|
|
|
|
out_obj = yaml.safe_load(stdout)
|
|
|
|
if not out_obj or 'targets' not in out_obj:
|
|
|
|
return {}
|
|
|
|
|
|
|
|
return out_obj
|
|
|
|
|
|
|
|
|
2013-12-19 22:12:56 +00:00
|
|
|
def nuke_targets(targets_dict, owner):
|
2013-10-17 14:42:02 +00:00
|
|
|
targets = targets_dict.get('targets')
|
|
|
|
if not targets:
|
|
|
|
log.info("No locked machines. Not nuking anything")
|
|
|
|
|
|
|
|
to_nuke = []
|
2013-10-16 19:31:26 +00:00
|
|
|
for target in targets:
|
2013-10-17 14:42:02 +00:00
|
|
|
to_nuke.append(target.split('@')[1].split('.')[0])
|
|
|
|
|
|
|
|
target_file = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
target_file.write(yaml.safe_dump(targets_dict))
|
|
|
|
target_file.close()
|
|
|
|
|
|
|
|
log.info("Nuking machines: " + str(to_nuke))
|
|
|
|
nuke_args = [
|
|
|
|
'teuthology-nuke',
|
|
|
|
'-t',
|
|
|
|
target_file.name,
|
|
|
|
'--unlock',
|
|
|
|
'-r',
|
|
|
|
'--owner',
|
|
|
|
owner
|
|
|
|
]
|
|
|
|
proc = subprocess.Popen(
|
|
|
|
nuke_args,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT)
|
|
|
|
for line in iter(proc.stdout.readline, ''):
|
|
|
|
line = line.replace('\r', '').replace('\n', '')
|
|
|
|
log.info(line)
|
|
|
|
sys.stdout.flush()
|
|
|
|
|
|
|
|
os.unlink(target_file.name)
|