summaryrefslogtreecommitdiff
path: root/scripts/distbuild-cluster.py
blob: 4c2151aaf32b049dfcf7b05103100530049deaae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# Copyright (C) 2014  Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import os
import subprocess
import sys
import time
import yaml
import argparse

import morphlib


''' distbuild-cluster: Build all systems in a cluster using distbuild.

This script should be removed once Morph has grown the capability to
build an entire cluster itself. This will require changes either to the
distbuild component (so that a single controller can build for multiple
architectures) or to the way Morph talks to distbuild (so that it can
handle multiple controllers).

'''


def read_morph(morph_name, kind=None):
    with open(morph_name + '.morph') as f:
        morph = yaml.load(f)
    if kind is not None:
        assert morph['kind'] == kind
    return morph

class Context:
    '''Holds the script's general context stuff'''

    def __init__(self):
        # Handle the command line parameters and set up help/usage
        purpose = 'Build all systems in a cluster using distbuild.'
        parser = argparse.ArgumentParser(description=purpose)
        parser.add_argument('cluster', nargs=1, help='Cluster to build')
        parser.add_argument('ref', nargs=1, help='Reference to build')
        parser.add_argument('controllers', nargs='*',
                            help='List of controllers [id:host] [id:host]...')
        args = parser.parse_args()

        # Build controller dictionary from supplied list of controllers
        self.controllers = {}
        for controller in args.controllers:
            self.controllers.update([controller.split(':', 1)])

        # Get cluster and ref to build from supplied arguments
        self.ref_to_build = args.ref[0]
        self.cluster_to_build = args.cluster[0]

    def show(self):
        # Print out the context
        key_width = max(len(key) for key in self.controllers)

        print "-"*80
        print "  Performing distbuild of: '" + self.cluster_to_build + "'"
        print "           with reference: '" + self.ref_to_build + "'"
        print "  Using controllers:"
        for key, host in self.controllers.iteritems():
            print "    " + key.rjust(key_width) + ": " + host
        print "-"*80


class Build(object):
    '''A single distbuild instance.'''

    def __init__(self, ctx, system_name, arch):
        self.system_name = system_name
        self.distbuild_controller = ctx.controllers[system['arch']]

        self.command = [
            'morph', 'distbuild-morphology',
            '--controller-initiator-address=%s' % self.distbuild_controller,
            'baserock:baserock/definitions', ctx.ref_to_build, system_name]

    def start(self):
        self.process = subprocess.Popen(self.command)

    def completed(self):
        return (self.process.poll() is not None)


if __name__ == '__main__':
    ctx = Context()
    ctx.show()

    cluster_name = morphlib.util.strip_morph_extension(ctx.cluster_to_build)

    cluster = read_morph(cluster_name, kind='cluster')
    system_list = [system['morph'] for system in cluster['systems']]

    builds = []
    for system_name in system_list:
        system = read_morph(system_name)
        builds.append(Build(ctx, system_name, system['arch']))

    # Morph dumps many log files to the current directory, which I don't
    # want to be in the root of 'definitions'.
    if not os.path.exists('builds'):
        os.mkdir('builds')
    os.chdir('builds')

    for build in builds:
        build.start()

    while not all(build.completed() for build in builds):
        time.sleep(1)

    for build in builds:
        if build.process.returncode != 0:
            sys.stderr.write("Building failed for %s\n" % build.system_name)