1138
by Colin Watson
copy-build-scheduler: switch to Python 3 |
1 |
#! /usr/bin/python3
|
355
by Colin Watson
Apply GPLv3 to anything not already licensed; ok slangasek, broder, laney, kitterman, geser |
2 |
|
3 |
# Copyright (C) 2011, 2012 Canonical Ltd.
|
|
4 |
||
5 |
# This program is free software: you can redistribute it and/or modify
|
|
6 |
# it under the terms of the GNU General Public License as published by
|
|
7 |
# the Free Software Foundation; version 3 of the License.
|
|
8 |
#
|
|
9 |
# This program is distributed in the hope that it will be useful,
|
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 |
# GNU General Public License for more details.
|
|
13 |
#
|
|
14 |
# You should have received a copy of the GNU General Public License
|
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16 |
||
276.1.1
by Francis J. Lacoste
Initial version. |
17 |
# This script can be used to reschedule some of the copy archives
|
18 |
# builds so that they are processed like regular PPA builds.
|
|
19 |
#
|
|
20 |
# Copy archives builds have a huge penalty applied to them which means
|
|
21 |
# that they are only processed when there is nothing else being processed
|
|
22 |
# by the build farm. That's usually fine, but for some rebuilds, we want
|
|
339
by Colin Watson
remove trailing whitespace |
23 |
# more timely processing, while at the same time, we do want to continue to
|
276.1.1
by Francis J. Lacoste
Initial version. |
24 |
# service regular PPA builds.
|
25 |
#
|
|
26 |
# This script will try to have a portion of the build farm processing copy
|
|
27 |
# builds. It does that by rescoring builds to the normal build priority
|
|
28 |
# range. But will only rescore a few builds at a time, so as not to take ove
|
|
29 |
# the build pool. By default, it won't rescore more than 1/4 the number of
|
|
339
by Colin Watson
remove trailing whitespace |
30 |
# available builders. So for example, if there are 12 i386 builders, only
|
276.1.1
by Francis J. Lacoste
Initial version. |
31 |
# 3 builds at a time will have a "normal priority".
|
355
by Colin Watson
Apply GPLv3 to anything not already licensed; ok slangasek, broder, laney, kitterman, geser |
32 |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
33 |
import argparse |
555
by Colin Watson
Simplify using collections.defaultdict. |
34 |
from collections import defaultdict |
276.1.1
by Francis J. Lacoste
Initial version. |
35 |
import logging |
36 |
import time |
|
37 |
||
38 |
from launchpadlib.launchpad import Launchpad |
|
39 |
||
40 |
||
41 |
API_NAME = 'copy-build-scheduler' |
|
42 |
||
43 |
NEEDS_BUILDING = 'Needs building' |
|
44 |
BUILDING = 'Currently building' |
|
45 |
COPY_ARCHIVE_SCORE_PENALTY = 2600 |
|
276.1.4
by Francis J. Lacoste
Increase time between schedule run, and recompute capacity every 5 schedule iteration. |
46 |
# Number of minutes to wait between schedule run.
|
47 |
SCHEDULE_PERIOD = 5 |
|
276.1.1
by Francis J. Lacoste
Initial version. |
48 |
|
545
by Colin Watson
Make all scripts pass pep8(1). |
49 |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
50 |
def determine_builder_capacity(lp, args): |
276.1.1
by Francis J. Lacoste
Initial version. |
51 |
"""Find how many builders to use for copy builds by processor."""
|
52 |
capacity = {} |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
53 |
for processor in args.processors: |
1139
by Colin Watson
copy-build-scheduler: skip inactive builders |
54 |
queue = [ |
55 |
builder for builder in lp.builders.getBuildersForQueue( |
|
56 |
processor='/+processors/%s' % processor, virtualized=True) |
|
57 |
if builder.active] |
|
276.1.1
by Francis J. Lacoste
Initial version. |
58 |
max_capacity = len(queue) |
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
59 |
capacity[processor] = round(max_capacity * args.builder_ratio) |
276.1.1
by Francis J. Lacoste
Initial version. |
60 |
# Make sure at least 1 builders is associated
|
61 |
if capacity[processor] == 0: |
|
62 |
capacity[processor] = 1 |
|
63 |
logging.info( |
|
64 |
'Will use %d out of %d %s builders', capacity[processor], |
|
65 |
max_capacity, processor) |
|
66 |
return capacity |
|
67 |
||
276.1.4
by Francis J. Lacoste
Increase time between schedule run, and recompute capacity every 5 schedule iteration. |
68 |
|
276.1.1
by Francis J. Lacoste
Initial version. |
69 |
def get_archive_used_builders_capacity(archive): |
70 |
"""Return the number of builds currently being done for the archive."""
|
|
555
by Colin Watson
Simplify using collections.defaultdict. |
71 |
capacity = defaultdict(int) |
276.1.1
by Francis J. Lacoste
Initial version. |
72 |
building = archive.getBuildRecords(build_state=BUILDING) |
73 |
for build in building: |
|
74 |
capacity[build.arch_tag] += 1 |
|
75 |
return capacity |
|
76 |
||
77 |
||
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
78 |
def main(): |
79 |
parser = argparse.ArgumentParser() |
|
80 |
parser.add_argument( |
|
276.1.1
by Francis J. Lacoste
Initial version. |
81 |
'--lp-instance', default='production', dest='lp_instance', |
530
by Colin Watson
copy-build-scheduler: typo |
82 |
help="Select the Launchpad instance to run against. Defaults to " |
276.1.1
by Francis J. Lacoste
Initial version. |
83 |
"'production'") |
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
84 |
parser.add_argument( |
276.1.1
by Francis J. Lacoste
Initial version. |
85 |
'-v', '--verbose', default=0, action='count', dest='verbose', |
86 |
help="Increase verbosity of the script. -v prints info messages" |
|
87 |
"-vv will print debug messages.") |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
88 |
parser.add_argument( |
276.1.1
by Francis J. Lacoste
Initial version. |
89 |
'-c', '--credentials', default=None, action='store', |
90 |
dest='credentials', |
|
91 |
help="Use the OAuth credentials in FILE instead of the desktop " |
|
92 |
"one.", metavar='FILE') |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
93 |
parser.add_argument( |
276.1.1
by Francis J. Lacoste
Initial version. |
94 |
'-d', '--distribution', default='ubuntu', action='store', |
95 |
dest='distribution', |
|
96 |
help="The archive distribution. Defaults to 'ubuntu'.") |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
97 |
parser.add_argument( |
98 |
'-p', '--processor', action='append', dest='processors', |
|
276.1.1
by Francis J. Lacoste
Initial version. |
99 |
help="The processor for which to schedule builds. " |
100 |
"Default to i386 and amd64.") |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
101 |
parser.add_argument( |
102 |
'-r', '--ratio', default=0.25, action='store', type=float, |
|
276.1.1
by Francis J. Lacoste
Initial version. |
103 |
dest='builder_ratio', |
104 |
help="The ratio of builders that you want to use for the copy " |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
105 |
"builds. Default to 25%% of the available builders.") |
106 |
parser.add_argument('copy_archive_name', help='Name of copy archive') |
|
107 |
args = parser.parse_args() |
|
276.1.1
by Francis J. Lacoste
Initial version. |
108 |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
109 |
if args.verbose >= 2: |
276.1.1
by Francis J. Lacoste
Initial version. |
110 |
log_level = logging.DEBUG |
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
111 |
elif args.verbose == 1: |
276.1.1
by Francis J. Lacoste
Initial version. |
112 |
log_level = logging.INFO |
113 |
else: |
|
114 |
log_level = logging.WARNING |
|
115 |
logging.basicConfig(level=log_level) |
|
116 |
||
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
117 |
if args.builder_ratio >= 1 or args.builder_ratio < 0: |
276.1.1
by Francis J. Lacoste
Initial version. |
118 |
parser.error( |
119 |
'ratio should be a float between 0 and 1: %s' % |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
120 |
args.builder_ratio) |
121 |
||
122 |
if not args.processors: |
|
123 |
args.processors = ['amd64', 'i386'] |
|
276.1.1
by Francis J. Lacoste
Initial version. |
124 |
|
125 |
lp = Launchpad.login_with( |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
126 |
API_NAME, args.lp_instance, |
127 |
credentials_file=args.credentials, |
|
276.1.1
by Francis J. Lacoste
Initial version. |
128 |
version='devel') |
129 |
||
130 |
try: |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
131 |
distribution = lp.distributions[args.distribution] |
276.1.1
by Francis J. Lacoste
Initial version. |
132 |
except KeyError: |
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
133 |
parser.error('unknown distribution: %s' % args.distribution) |
276.1.1
by Francis J. Lacoste
Initial version. |
134 |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
135 |
archive = distribution.getArchive(name=args.copy_archive_name) |
276.1.1
by Francis J. Lacoste
Initial version. |
136 |
if archive is None: |
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
137 |
parser.error('unknown archive: %s' % args.copy_archive_name) |
276.1.1
by Francis J. Lacoste
Initial version. |
138 |
|
276.1.4
by Francis J. Lacoste
Increase time between schedule run, and recompute capacity every 5 schedule iteration. |
139 |
iteration = 0 |
276.1.1
by Francis J. Lacoste
Initial version. |
140 |
while True: |
276.1.5
by Francis J. Lacoste
Typo. |
141 |
# Every 5 schedules run - and on the first - compute available
|
142 |
# capacity.
|
|
276.1.4
by Francis J. Lacoste
Increase time between schedule run, and recompute capacity every 5 schedule iteration. |
143 |
if (iteration % 5) == 0: |
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
144 |
capacity = determine_builder_capacity(lp, args) |
276.1.4
by Francis J. Lacoste
Increase time between schedule run, and recompute capacity every 5 schedule iteration. |
145 |
iteration += 1 |
146 |
||
276.1.1
by Francis J. Lacoste
Initial version. |
147 |
pending_builds = archive.getBuildRecords(build_state=NEEDS_BUILDING) |
148 |
logging.debug('Found %d pending builds.' % len(pending_builds)) |
|
149 |
if len(pending_builds) == 0: |
|
150 |
logging.info('No more builds pending. We are done.') |
|
151 |
break
|
|
152 |
||
153 |
used_capacity = get_archive_used_builders_capacity(archive) |
|
154 |
||
155 |
# For each processor, rescore up as many builds as we have
|
|
156 |
# capacity for.
|
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
157 |
for processor in args.processors: |
276.1.1
by Francis J. Lacoste
Initial version. |
158 |
builds_to_rescore = ( |
159 |
capacity[processor] - used_capacity.get(processor, 0)) |
|
160 |
logging.debug( |
|
161 |
'Will try to rescore %d %s builds', builds_to_rescore, |
|
162 |
processor) |
|
163 |
for build in pending_builds: |
|
164 |
if builds_to_rescore <= 0: |
|
165 |
break
|
|
166 |
||
167 |
if build.arch_tag != processor: |
|
168 |
continue
|
|
169 |
||
170 |
if build.score < 0: |
|
171 |
# Only rescore builds that look like the negative
|
|
172 |
# copy archive modified have been applied.
|
|
173 |
logging.info('Rescoring %s' % build.title) |
|
174 |
# This should make them considered like a regular build.
|
|
175 |
build.rescore( |
|
176 |
score=build.score + COPY_ARCHIVE_SCORE_PENALTY) |
|
276.1.3
by Francis J. Lacoste
Better logging. |
177 |
else: |
178 |
logging.debug('%s already rescored', build.title) |
|
276.1.1
by Francis J. Lacoste
Initial version. |
179 |
|
180 |
# If the score was already above 0, it was probably
|
|
181 |
# rescored already, count it against our limit anyway.
|
|
182 |
builds_to_rescore -= 1 |
|
183 |
||
276.1.4
by Francis J. Lacoste
Increase time between schedule run, and recompute capacity every 5 schedule iteration. |
184 |
# Reschedule in a while.
|
185 |
logging.debug('Sleeping for %d minutes.', SCHEDULE_PERIOD) |
|
186 |
time.sleep(SCHEDULE_PERIOD * 60) |
|
276.1.1
by Francis J. Lacoste
Initial version. |
187 |
|
188 |
||
189 |
if __name__ == '__main__': |
|
1137
by Colin Watson
copy-build-scheduler: convert to argparse |
190 |
main() |