~abentley/juju-ci-tools/client-from-config-4

« back to all changes in this revision

Viewing changes to concurrently.py

  • Committer: Curtis Hovey
  • Date: 2015-12-20 15:14:05 UTC
  • Revision ID: curtis@canonical.com-20151220151405-pm3dauunjr2978gz
skip any client-server that starts with 1.26.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
 
3
 
from __future__ import print_function
4
 
 
5
 
from argparse import ArgumentParser
6
 
from contextlib import contextmanager
7
 
import logging
8
 
import os
9
 
import subprocess
10
 
import sys
11
 
import traceback
12
 
 
13
 
from utility import configure_logging
14
 
 
15
 
 
16
 
__metaclass__ = type
17
 
 
18
 
 
19
 
log = logging.getLogger("concurrently")
20
 
 
21
 
 
22
 
class Task:
23
 
 
24
 
    def __init__(self, name_commdline, log_dir='.'):
25
 
        self.name, self.commandline = name_commdline.split('=', 1)
26
 
        self.command = self.commandline.split()
27
 
        self.out_log_name = os.path.join(
28
 
            log_dir, '{}-out.log'.format(self.name))
29
 
        self.err_log_name = os.path.join(
30
 
            log_dir, '{}-err.log'.format(self.name))
31
 
        self.returncode = None
32
 
        self.proc = None
33
 
 
34
 
    def __eq__(self, other):
35
 
        if type(self) != type(other):
36
 
            return False
37
 
        return (self.name == other.name and
38
 
                self.command == other.command and
39
 
                self.out_log_name == other.out_log_name and
40
 
                self.err_log_name == other.err_log_name)
41
 
 
42
 
    @contextmanager
43
 
    def start(self):
44
 
        """Yield the running proc, then wait to set the returncode."""
45
 
        with open(self.out_log_name, 'ab') as out_log:
46
 
            with open(self.err_log_name, 'ab') as err_log:
47
 
                self.proc = subprocess.Popen(
48
 
                    self.command, stdout=out_log, stderr=err_log)
49
 
                log.debug('Started {}'.format(self.name))
50
 
                yield self.proc
51
 
 
52
 
    def finish(self):
53
 
        log.debug('Waiting for {} to finish'.format(self.name))
54
 
        self.returncode = self.proc.wait()
55
 
        log.debug('{} finished'.format(self.name))
56
 
 
57
 
 
58
 
def run_all(tasks):
59
 
    """Run all tasks in the list.
60
 
 
61
 
    The list is a queue that will be emptied.
62
 
    """
63
 
    try:
64
 
        task = tasks.pop()
65
 
    except IndexError:
66
 
        return
67
 
    with task.start():
68
 
        run_all(tasks)
69
 
        task.finish()
70
 
 
71
 
 
72
 
def summarise_tasks(tasks):
73
 
    """Return of sum of tasks returncodes."""
74
 
    returncode = sum([t.returncode for t in tasks])
75
 
    if returncode == 0:
76
 
        log.debug('SUCCESS')
77
 
    else:
78
 
        log.debug('FAIL')
79
 
        for task in tasks:
80
 
            if task.returncode != 0:
81
 
                log.error('{} failed with {}\nSee {}'.format(
82
 
                          task.name, task.returncode, task.err_log_name))
83
 
    return returncode
84
 
 
85
 
 
86
 
def parse_args(argv=None):
87
 
    """Return the parsed args for this program."""
88
 
    parser = ArgumentParser(
89
 
        description="Run many tasks concurrently.")
90
 
    parser.add_argument(
91
 
        '-v', '--verbose', action='store_const',
92
 
        default=logging.INFO, const=logging.DEBUG,
93
 
        help='Increase verbosity.')
94
 
    parser.add_argument(
95
 
        '-l', '--log_dir', default='.', type=os.path.expanduser,
96
 
        help='The path to store the logs for each task.')
97
 
    parser.add_argument(
98
 
        'tasks', nargs='+', default=[],
99
 
        help="one or more tasks to run in the form of name='cmc -opt arg'.")
100
 
    return parser.parse_args(argv)
101
 
 
102
 
 
103
 
def main(argv=None):
104
 
    """Run many tasks concurrently."""
105
 
    returncode = 254
106
 
    args = parse_args(argv)
107
 
    configure_logging(args.verbose)
108
 
    tasks = [Task(t, args.log_dir) for t in args.tasks]
109
 
    try:
110
 
        names = [t.name for t in tasks]
111
 
        log.debug('Running these tasks {}'.format(names))
112
 
        run_all(list(tasks))
113
 
        returncode = summarise_tasks(tasks)
114
 
    except Exception as e:
115
 
        log.error(str(e))
116
 
        log.error(traceback.print_exc())
117
 
        return 253
118
 
    return returncode
119
 
 
120
 
 
121
 
if __name__ == '__main__':
122
 
    sys.exit(main())