14291.1.2
by Jeroen Vermeulen
Lint. |
1 |
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
|
8687.15.17
by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
3 |
||
4983.1.2
by Curtis Hovey
Added pylint exceptions to database classes. |
4 |
# pylint: disable-msg=E0611,W0212
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
5 |
|
6 |
__metaclass__ = type |
|
7 |
||
8 |
__all__ = [ |
|
9 |
'BuildQueue', |
|
7675.452.1
by Muharem Hrnjadovic
imported old development branch |
10 |
'BuildQueueSet', |
7675.452.10
by Muharem Hrnjadovic
jtv's review comments |
11 |
'specific_job_classes', |
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
12 |
]
|
13 |
||
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
14 |
from collections import defaultdict |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
15 |
from datetime import ( |
16 |
datetime, |
|
17 |
timedelta, |
|
18 |
)
|
|
14513.2.1
by Raphael Badin
Preload build data prior to displaying the builders homepage. |
19 |
from itertools import groupby |
4674.2.8
by Celso Providelo
applying review comments, take 3, [r=kiko]. |
20 |
import logging |
14513.2.1
by Raphael Badin
Preload build data prior to displaying the builders homepage. |
21 |
from operator import attrgetter |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
22 |
|
10466.9.14
by Jeroen Vermeulen
Review changes. |
23 |
import pytz |
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
24 |
from sqlobject import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
25 |
BoolCol, |
26 |
ForeignKey, |
|
27 |
IntCol, |
|
28 |
IntervalCol, |
|
29 |
SQLObjectNotFound, |
|
30 |
StringCol, |
|
31 |
)
|
|
32 |
from zope.component import ( |
|
33 |
getSiteManager, |
|
34 |
getUtility, |
|
35 |
)
|
|
7675.575.1
by William Grant
Move lp.soyuz.interfaces.build.BuildStatus to lp.buildmaster.interfaces.buildbase. Sort various imports along the way. |
36 |
from zope.interface import implements |
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
37 |
|
11270.1.3
by Tim Penhey
Changed NotFoundError imports - gee there were a lot of them. |
38 |
from lp.app.errors import NotFoundError |
11458.1.2
by Jelmer Vernooij
Move BuildFarmJobType to lp.buildmaster.enums. |
39 |
from lp.buildmaster.enums import BuildFarmJobType |
14291.1.2
by Jeroen Vermeulen
Lint. |
40 |
from lp.buildmaster.interfaces.buildfarmjob import IBuildFarmJob |
7675.420.12
by Michael Nelson
Made the BuildQueue item responsible for providing the required behavior. |
41 |
from lp.buildmaster.interfaces.buildfarmjobbehavior import ( |
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
42 |
IBuildFarmJobBehavior, |
43 |
)
|
|
44 |
from lp.buildmaster.interfaces.buildqueue import ( |
|
45 |
IBuildQueue, |
|
46 |
IBuildQueueSet, |
|
47 |
)
|
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
48 |
from lp.services.database.constants import DEFAULT |
49 |
from lp.services.database.enumcol import EnumCol |
|
50 |
from lp.services.database.sqlbase import ( |
|
51 |
SQLBase, |
|
52 |
sqlvalues, |
|
53 |
)
|
|
7675.391.14
by Muharem Hrnjadovic
Fixing code to make tests pass. |
54 |
from lp.services.job.interfaces.job import JobStatus |
55 |
from lp.services.job.model.job import Job |
|
14606.3.1
by William Grant
Merge canonical.database into lp.services.database. |
56 |
from lp.services.webapp.interfaces import ( |
57 |
DEFAULT_FLAVOR, |
|
58 |
IStoreSelector, |
|
59 |
MAIN_STORE, |
|
60 |
)
|
|
4674.2.3
by Celso Providelo
Integrating 'score' implementation in IBuildQueue. |
61 |
|
62 |
||
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
63 |
def normalize_virtualization(virtualized): |
64 |
"""Jobs with NULL virtualization settings should be treated the
|
|
65 |
same way as virtualized jobs."""
|
|
66 |
return virtualized is None or virtualized |
|
67 |
||
68 |
||
7675.452.1
by Muharem Hrnjadovic
imported old development branch |
69 |
def specific_job_classes(): |
70 |
"""Job classes that may run on the build farm."""
|
|
71 |
job_classes = dict() |
|
72 |
# Get all components that implement the `IBuildFarmJob` interface.
|
|
73 |
components = getSiteManager() |
|
74 |
implementations = sorted(components.getUtilitiesFor(IBuildFarmJob)) |
|
75 |
# The above yields a collection of 2-tuples where the first element
|
|
76 |
# is the name of the `BuildFarmJobType` enum and the second element
|
|
77 |
# is the implementing class respectively.
|
|
78 |
for job_enum_name, job_class in implementations: |
|
79 |
job_enum = getattr(BuildFarmJobType, job_enum_name) |
|
80 |
job_classes[job_enum] = job_class |
|
81 |
||
82 |
return job_classes |
|
83 |
||
84 |
||
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
85 |
def get_builder_data(): |
86 |
"""How many working builders are there, how are they configured?"""
|
|
87 |
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
|
88 |
builder_data = """ |
|
89 |
SELECT processor, virtualized, COUNT(id) FROM builder
|
|
90 |
WHERE builderok = TRUE AND manual = FALSE
|
|
91 |
GROUP BY processor, virtualized;
|
|
92 |
"""
|
|
93 |
results = store.execute(builder_data).get_all() |
|
7675.509.141
by William Grant
Fix lint. |
94 |
builders_in_total = virtualized_total = 0 |
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
95 |
|
96 |
builder_stats = defaultdict(int) |
|
97 |
for processor, virtualized, count in results: |
|
98 |
builders_in_total += count |
|
99 |
if virtualized: |
|
100 |
virtualized_total += count |
|
101 |
builder_stats[(processor, virtualized)] = count |
|
102 |
||
103 |
builder_stats[(None, True)] = virtualized_total |
|
104 |
# Jobs with a NULL virtualized flag should be treated the same as
|
|
105 |
# jobs where virtualized=TRUE.
|
|
106 |
builder_stats[(None, None)] = virtualized_total |
|
107 |
builder_stats[(None, False)] = builders_in_total - virtualized_total |
|
108 |
return builder_stats |
|
109 |
||
10234.1.5
by Muharem Hrnjadovic
Cleaned up more test code. |
110 |
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
111 |
class BuildQueue(SQLBase): |
112 |
implements(IBuildQueue) |
|
113 |
_table = "BuildQueue" |
|
114 |
_defaultOrder = "id" |
|
115 |
||
10899.2.13
by Aaron Bentley
Fix more test failures. |
116 |
def __init__(self, job, job_type=DEFAULT, estimated_duration=DEFAULT, |
117 |
virtualized=DEFAULT, processor=DEFAULT, lastscore=None): |
|
10899.2.10
by Aaron Bentley
Make scoring automatic and give manual builds a higher score. |
118 |
super(BuildQueue, self).__init__(job_type=job_type, job=job, |
119 |
virtualized=virtualized, processor=processor, |
|
10899.2.12
by Aaron Bentley
Fix failing tests. |
120 |
estimated_duration=estimated_duration, lastscore=lastscore) |
10899.2.13
by Aaron Bentley
Fix more test failures. |
121 |
if lastscore is None and self.specific_job is not None: |
10899.2.12
by Aaron Bentley
Fix failing tests. |
122 |
self.score() |
10899.2.10
by Aaron Bentley
Make scoring automatic and give manual builds a higher score. |
123 |
|
7675.391.4
by Muharem Hrnjadovic
Removed unused IBuildQueue.buildduration property. |
124 |
job = ForeignKey(dbName='job', foreignKey='Job', notNull=True) |
7675.391.14
by Muharem Hrnjadovic
Fixing code to make tests pass. |
125 |
job_type = EnumCol( |
7675.390.7
by Muharem Hrnjadovic
Review comments, round 3. |
126 |
enum=BuildFarmJobType, notNull=True, |
127 |
default=BuildFarmJobType.PACKAGEBUILD, dbName='job_type') |
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
128 |
builder = ForeignKey(dbName='builder', foreignKey='Builder', default=None) |
129 |
logtail = StringCol(dbName='logtail', default=None) |
|
130 |
lastscore = IntCol(dbName='lastscore', default=0) |
|
131 |
manual = BoolCol(dbName='manual', default=False) |
|
7675.403.2
by Muharem Hrnjadovic
Merged in code/test fixes. |
132 |
estimated_duration = IntervalCol() |
10130.8.6
by William Grant
BuildQueue.processor can be NULL. |
133 |
processor = ForeignKey(dbName='processor', foreignKey='Processor') |
7675.447.4
by Muharem Hrnjadovic
model class adjustments |
134 |
virtualized = BoolCol(dbName='virtualized') |
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
135 |
|
7675.391.28
by Muharem Hrnjadovic
More improvements. |
136 |
@property
|
7675.420.12
by Michael Nelson
Made the BuildQueue item responsible for providing the required behavior. |
137 |
def required_build_behavior(self): |
138 |
"""See `IBuildQueue`."""
|
|
7675.420.16
by Michael Nelson
Refactored to adapt on the job rather than the job type and added tests. |
139 |
return IBuildFarmJobBehavior(self.specific_job) |
7675.420.12
by Michael Nelson
Made the BuildQueue item responsible for providing the required behavior. |
140 |
|
14513.2.3
by Raphael Badin
Cachedproperty back to property. |
141 |
@property
|
7675.391.28
by Muharem Hrnjadovic
More improvements. |
142 |
def specific_job(self): |
143 |
"""See `IBuildQueue`."""
|
|
7675.452.1
by Muharem Hrnjadovic
imported old development branch |
144 |
specific_class = specific_job_classes()[self.job_type] |
10137.5.8
by Jeroen Vermeulen
Delegated the BuildQueue.specific_job query to BuildFarmJob, and specialized for my build-farm job class; added interface. |
145 |
return specific_class.getByJob(self.job) |
7675.391.5
by Muharem Hrnjadovic
work in progress. |
146 |
|
14513.2.1
by Raphael Badin
Preload build data prior to displaying the builders homepage. |
147 |
@staticmethod
|
148 |
def preloadSpecificJobData(queues): |
|
149 |
key = attrgetter('job_type') |
|
150 |
for job_type, grouped_queues in groupby(queues, key=key): |
|
151 |
specific_class = specific_job_classes()[job_type] |
|
152 |
queue_subset = list(grouped_queues) |
|
153 |
# We need to preload the build farm jobs early to avoid
|
|
154 |
# the call to _set_build_farm_job to look up BuildFarmBuildJobs
|
|
155 |
# one by one.
|
|
156 |
specific_class.preloadBuildFarmJobs(queue_subset) |
|
157 |
specific_jobs = specific_class.getByJobs(queue_subset) |
|
158 |
if len(list(specific_jobs)) == 0: |
|
14513.2.2
by Raphael Badin
Fix shortcut. |
159 |
continue
|
14513.2.1
by Raphael Badin
Preload build data prior to displaying the builders homepage. |
160 |
specific_class.preloadJobsData(specific_jobs) |
161 |
||
7675.390.5
by Muharem Hrnjadovic
Review changes, round 1. |
162 |
@property
|
163 |
def date_started(self): |
|
164 |
"""See `IBuildQueue`."""
|
|
165 |
return self.job.date_started |
|
166 |
||
10466.9.5
by Jeroen Vermeulen
Prototype with weird timezone-related test failure in test_min_time_to_next_builder. |
167 |
@property
|
168 |
def current_build_duration(self): |
|
169 |
"""See `IBuildQueue`."""
|
|
170 |
date_started = self.date_started |
|
171 |
if date_started is None: |
|
172 |
return None |
|
173 |
else: |
|
10466.9.14
by Jeroen Vermeulen
Review changes. |
174 |
return self._now() - date_started |
10466.9.5
by Jeroen Vermeulen
Prototype with weird timezone-related test failure in test_min_time_to_next_builder. |
175 |
|
10011.1.3
by Julian Edwards
Add fix and spruce up test |
176 |
def destroySelf(self): |
177 |
"""Remove this record and associated job/specific_job."""
|
|
178 |
job = self.job |
|
179 |
specific_job = self.specific_job |
|
180 |
SQLBase.destroySelf(self) |
|
10793.1.1
by Jeroen Vermeulen
Delegate deletion of build-farm jobs to IBuildFarmJobDerived. |
181 |
specific_job.cleanUp() |
10011.1.3
by Julian Edwards
Add fix and spruce up test |
182 |
job.destroySelf() |
183 |
||
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
184 |
def manualScore(self, value): |
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
185 |
"""See `IBuildQueue`."""
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
186 |
self.lastscore = value |
187 |
self.manual = True |
|
188 |
||
4674.2.8
by Celso Providelo
applying review comments, take 3, [r=kiko]. |
189 |
def score(self): |
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
190 |
"""See `IBuildQueue`."""
|
4674.2.8
by Celso Providelo
applying review comments, take 3, [r=kiko]. |
191 |
# Grab any logger instance available.
|
192 |
logger = logging.getLogger() |
|
7675.391.28
by Muharem Hrnjadovic
More improvements. |
193 |
name = self.specific_job.getName() |
5089.2.2
by Celso Providelo
sorting slave-scanner slowness. |
194 |
|
4674.2.3
by Celso Providelo
Integrating 'score' implementation in IBuildQueue. |
195 |
if self.manual: |
4674.2.6
by Celso Providelo
applying review comments, [r=kiko]. |
196 |
logger.debug( |
7675.391.23
by Muharem Hrnjadovic
Fixed more tests. |
197 |
"%s (%d) MANUALLY RESCORED" % (name, self.lastscore)) |
4674.2.6
by Celso Providelo
applying review comments, [r=kiko]. |
198 |
return
|
4674.2.3
by Celso Providelo
Integrating 'score' implementation in IBuildQueue. |
199 |
|
7675.390.7
by Muharem Hrnjadovic
Review comments, round 3. |
200 |
# Allow the `IBuildFarmJob` instance with the data/logic specific to
|
7675.390.6
by Muharem Hrnjadovic
Review comments, round 2. |
201 |
# the job at hand to calculate the score as appropriate.
|
7675.391.29
by Muharem Hrnjadovic
Simplified code. |
202 |
self.lastscore = self.specific_job.score() |
4674.2.3
by Celso Providelo
Integrating 'score' implementation in IBuildQueue. |
203 |
|
3691.454.27
by Robert Collins
Delegate log file naming in the build slave scanner to the BuildQueue |
204 |
def getLogFileName(self): |
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
205 |
"""See `IBuildQueue`."""
|
7675.390.7
by Muharem Hrnjadovic
Review comments, round 3. |
206 |
# Allow the `IBuildFarmJob` instance with the data/logic specific to
|
7675.390.6
by Muharem Hrnjadovic
Review comments, round 2. |
207 |
# the job at hand to calculate the log file name as appropriate.
|
7675.391.29
by Muharem Hrnjadovic
Simplified code. |
208 |
return self.specific_job.getLogFileName() |
3691.454.27
by Robert Collins
Delegate log file naming in the build slave scanner to the BuildQueue |
209 |
|
5008.3.5
by Julian Edwards
Clean up code and tests. |
210 |
def markAsBuilding(self, builder): |
211 |
"""See `IBuildQueue`."""
|
|
212 |
self.builder = builder |
|
7675.391.27
by Muharem Hrnjadovic
More fixes. |
213 |
if self.job.status != JobStatus.RUNNING: |
214 |
self.job.start() |
|
7675.391.29
by Muharem Hrnjadovic
Simplified code. |
215 |
self.specific_job.jobStarted() |
5008.3.5
by Julian Edwards
Clean up code and tests. |
216 |
|
8137.17.24
by Barry Warsaw
thread merge |
217 |
def reset(self): |
218 |
"""See `IBuildQueue`."""
|
|
219 |
self.builder = None |
|
7675.391.22
by Muharem Hrnjadovic
Fixed more breakage. |
220 |
if self.job.status != JobStatus.WAITING: |
221 |
self.job.queue() |
|
7675.391.5
by Muharem Hrnjadovic
work in progress. |
222 |
self.job.date_started = None |
7675.391.19
by Muharem Hrnjadovic
Refactored code related to build dispatching. |
223 |
self.job.date_finished = None |
8137.17.24
by Barry Warsaw
thread merge |
224 |
self.logtail = None |
7675.391.29
by Muharem Hrnjadovic
Simplified code. |
225 |
self.specific_job.jobReset() |
8137.17.24
by Barry Warsaw
thread merge |
226 |
|
14206.1.2
by Julian Edwards
merge a chunk of the backed-out change from r14192 |
227 |
def cancel(self): |
228 |
"""See `IBuildQueue`."""
|
|
229 |
self.specific_job.jobCancel() |
|
230 |
self.destroySelf() |
|
231 |
||
7675.391.38
by Muharem Hrnjadovic
Work in progress. |
232 |
def setDateStarted(self, timestamp): |
233 |
"""See `IBuildQueue`."""
|
|
234 |
self.job.date_started = timestamp |
|
235 |
||
10234.1.20
by Muharem Hrnjadovic
jtv's review comments, round #3 |
236 |
def _getFreeBuildersCount(self, processor, virtualized): |
10096.1.1
by Muharem Hrnjadovic
imported builder data functions and tests |
237 |
"""How many builders capable of running jobs for the given processor
|
238 |
and virtualization combination are idle/free at present?"""
|
|
239 |
query = """ |
|
240 |
SELECT COUNT(id) FROM builder
|
|
241 |
WHERE
|
|
242 |
builderok = TRUE AND manual = FALSE
|
|
243 |
AND id NOT IN (
|
|
244 |
SELECT builder FROM BuildQueue WHERE builder IS NOT NULL)
|
|
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
245 |
AND virtualized = %s |
246 |
""" % sqlvalues(normalize_virtualization(virtualized)) |
|
10096.1.1
by Muharem Hrnjadovic
imported builder data functions and tests |
247 |
if processor is not None: |
248 |
query += """ |
|
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
249 |
AND processor = %s |
250 |
""" % sqlvalues(processor) |
|
10096.1.1
by Muharem Hrnjadovic
imported builder data functions and tests |
251 |
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
252 |
result_set = store.execute(query) |
|
253 |
free_builders = result_set.get_one()[0] |
|
254 |
return free_builders |
|
255 |
||
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
256 |
def _estimateTimeToNextBuilder(self): |
10122.1.3
by Muharem Hrnjadovic
Rectified the _estimateTimeToNextBuilder() method and the tests for it. |
257 |
"""Estimate time until next builder becomes available.
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
258 |
|
10122.1.3
by Muharem Hrnjadovic
Rectified the _estimateTimeToNextBuilder() method and the tests for it. |
259 |
For the purpose of estimating the dispatch time of the job of interest
|
260 |
(JOI) we need to know how long it will take until the job at the head
|
|
261 |
of JOI's queue is dispatched.
|
|
262 |
||
263 |
There are two cases to consider here: the head job is
|
|
264 |
||
265 |
- processor dependent: only builders with the matching
|
|
266 |
processor/virtualization combination should be considered.
|
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
267 |
- *not* processor dependent: all builders with the matching
|
268 |
virtualization setting should be considered.
|
|
10122.1.3
by Muharem Hrnjadovic
Rectified the _estimateTimeToNextBuilder() method and the tests for it. |
269 |
|
270 |
:return: The estimated number of seconds untils a builder capable of
|
|
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
271 |
running the head job becomes available.
|
10122.1.3
by Muharem Hrnjadovic
Rectified the _estimateTimeToNextBuilder() method and the tests for it. |
272 |
"""
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
273 |
head_job_platform = self._getHeadJobPlatform() |
274 |
||
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
275 |
# Return a zero delay if we still have free builders available for the
|
276 |
# given platform/virtualization combination.
|
|
10234.1.20
by Muharem Hrnjadovic
jtv's review comments, round #3 |
277 |
free_builders = self._getFreeBuildersCount(*head_job_platform) |
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
278 |
if free_builders > 0: |
279 |
return 0 |
|
280 |
||
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
281 |
head_job_processor, head_job_virtualized = head_job_platform |
10122.1.1
by Muharem Hrnjadovic
merged development branch |
282 |
|
10409.4.2
by Muharem Hrnjadovic
Monkey-patched BuildQueue._now() in unit tests to return a constant time stamp. |
283 |
now = self._now() |
10122.1.1
by Muharem Hrnjadovic
merged development branch |
284 |
delay_query = """ |
285 |
SELECT MIN(
|
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
286 |
CASE WHEN
|
10122.1.1
by Muharem Hrnjadovic
merged development branch |
287 |
EXTRACT(EPOCH FROM
|
288 |
(BuildQueue.estimated_duration -
|
|
10409.4.2
by Muharem Hrnjadovic
Monkey-patched BuildQueue._now() in unit tests to return a constant time stamp. |
289 |
(((%s AT TIME ZONE 'UTC') - Job.date_started)))) >= 0 |
10122.1.1
by Muharem Hrnjadovic
merged development branch |
290 |
THEN
|
291 |
EXTRACT(EPOCH FROM
|
|
292 |
(BuildQueue.estimated_duration -
|
|
10409.4.2
by Muharem Hrnjadovic
Monkey-patched BuildQueue._now() in unit tests to return a constant time stamp. |
293 |
(((%s AT TIME ZONE 'UTC') - Job.date_started)))) |
10122.1.1
by Muharem Hrnjadovic
merged development branch |
294 |
ELSE
|
295 |
-- Assume that jobs that have overdrawn their estimated
|
|
296 |
-- duration time budget will complete within 2 minutes.
|
|
297 |
-- This is a wild guess but has worked well so far.
|
|
10122.1.4
by Muharem Hrnjadovic
Graham B.'s review comments |
298 |
--
|
299 |
-- Please note that this is entirely innocuous i.e. if our
|
|
300 |
-- guess is off nothing bad will happen but our estimate will
|
|
301 |
-- not be as good as it could be.
|
|
10122.1.1
by Muharem Hrnjadovic
merged development branch |
302 |
120
|
303 |
END)
|
|
304 |
FROM
|
|
305 |
BuildQueue, Job, Builder
|
|
306 |
WHERE
|
|
307 |
BuildQueue.job = Job.id
|
|
308 |
AND BuildQueue.builder = Builder.id
|
|
309 |
AND Builder.manual = False
|
|
310 |
AND Builder.builderok = True
|
|
311 |
AND Job.status = %s |
|
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
312 |
AND Builder.virtualized = %s |
313 |
""" % sqlvalues( |
|
10409.4.2
by Muharem Hrnjadovic
Monkey-patched BuildQueue._now() in unit tests to return a constant time stamp. |
314 |
now, now, JobStatus.RUNNING, |
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
315 |
normalize_virtualization(head_job_virtualized)) |
316 |
||
317 |
if head_job_processor is not None: |
|
318 |
# Only look at builders with specific processor types.
|
|
319 |
delay_query += """ |
|
320 |
AND Builder.processor = %s |
|
321 |
""" % sqlvalues(head_job_processor) |
|
10122.1.1
by Muharem Hrnjadovic
merged development branch |
322 |
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
323 |
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
10122.1.1
by Muharem Hrnjadovic
merged development branch |
324 |
result_set = store.execute(delay_query) |
10122.1.3
by Muharem Hrnjadovic
Rectified the _estimateTimeToNextBuilder() method and the tests for it. |
325 |
head_job_delay = result_set.get_one()[0] |
10234.1.3
by Muharem Hrnjadovic
Streamlined code and tests, _estimateTimeToNextBuilder() is now only concerned with estimating the time to the next builder. The calling function needs to check whether there are available builders in first place. |
326 |
return (0 if head_job_delay is None else int(head_job_delay)) |
10122.1.1
by Muharem Hrnjadovic
merged development branch |
327 |
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
328 |
def _getPendingJobsClauses(self): |
329 |
"""WHERE clauses for pending job queries, used for dipatch time
|
|
330 |
estimation."""
|
|
331 |
virtualized = normalize_virtualization(self.virtualized) |
|
332 |
clauses = """ |
|
10234.1.21
by Muharem Hrnjadovic
jtv's review comments, round #4 |
333 |
BuildQueue.job = Job.id
|
334 |
AND Job.status = %s |
|
335 |
AND (
|
|
336 |
-- The score must be either above my score or the
|
|
337 |
-- job must be older than me in cases where the
|
|
338 |
-- score is equal.
|
|
339 |
BuildQueue.lastscore > %s OR |
|
340 |
(BuildQueue.lastscore = %s AND Job.id < %s)) |
|
10234.1.23
by Muharem Hrnjadovic
jtv's review comments, round #6 |
341 |
-- The virtualized values either match or the job
|
342 |
-- does not care about virtualization and the job
|
|
343 |
-- of interest (JOI) is to be run on a virtual builder
|
|
344 |
-- (we want to prevent the execution of untrusted code
|
|
345 |
-- on native builders).
|
|
346 |
AND COALESCE(buildqueue.virtualized, TRUE) = %s |
|
10234.1.21
by Muharem Hrnjadovic
jtv's review comments, round #4 |
347 |
""" % sqlvalues( |
348 |
JobStatus.WAITING, self.lastscore, self.lastscore, self.job, |
|
10234.1.23
by Muharem Hrnjadovic
jtv's review comments, round #6 |
349 |
virtualized) |
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
350 |
processor_clause = """ |
10234.1.21
by Muharem Hrnjadovic
jtv's review comments, round #4 |
351 |
AND (
|
352 |
-- The processor values either match or the candidate
|
|
353 |
-- job is processor-independent.
|
|
354 |
buildqueue.processor = %s OR |
|
355 |
buildqueue.processor IS NULL)
|
|
356 |
""" % sqlvalues(self.processor) |
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
357 |
# We don't care about processors if the estimation is for a
|
358 |
# processor-independent job.
|
|
359 |
if self.processor is not None: |
|
360 |
clauses += processor_clause |
|
361 |
return clauses |
|
362 |
||
363 |
def _getHeadJobPlatform(self): |
|
364 |
"""Find the processor and virtualization setting for the head job.
|
|
365 |
||
366 |
Among the jobs that compete with the job of interest (JOI) for
|
|
367 |
builders and are queued ahead of it the head job is the one in pole
|
|
368 |
position i.e. the one to be dispatched to a builder next.
|
|
369 |
||
370 |
:return: A (processor, virtualized) tuple which is the head job's
|
|
371 |
platform or None if the JOI is the head job.
|
|
372 |
"""
|
|
373 |
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
|
374 |
my_platform = ( |
|
375 |
getattr(self.processor, 'id', None), |
|
376 |
normalize_virtualization(self.virtualized)) |
|
377 |
query = """ |
|
378 |
SELECT
|
|
379 |
processor,
|
|
380 |
virtualized
|
|
381 |
FROM
|
|
382 |
BuildQueue, Job
|
|
383 |
WHERE
|
|
384 |
"""
|
|
385 |
query += self._getPendingJobsClauses() |
|
386 |
query += """ |
|
387 |
ORDER BY lastscore DESC, job LIMIT 1
|
|
388 |
"""
|
|
10234.1.19
by Muharem Hrnjadovic
jtv's review comments, round #2 |
389 |
result = store.execute(query).get_one() |
390 |
return (my_platform if result is None else result) |
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
391 |
|
7675.503.18
by Muharem Hrnjadovic
Removed superfluous parameter |
392 |
def _estimateJobDelay(self, builder_stats): |
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
393 |
"""Sum of estimated durations for *pending* jobs ahead in queue.
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
394 |
|
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
395 |
For the purpose of estimating the dispatch time of the job of
|
396 |
interest (JOI) we need to know the delay caused by all the pending
|
|
397 |
jobs that are ahead of the JOI in the queue and that compete with it
|
|
398 |
for builders.
|
|
399 |
||
400 |
:param builder_stats: A dictionary with builder counts where the
|
|
401 |
key is a (processor, virtualized) combination (aka "platform") and
|
|
402 |
the value is the number of builders that can take on jobs
|
|
403 |
requiring that combination.
|
|
7675.503.29
by Muharem Hrnjadovic
allenap's review comments, round 3 |
404 |
:return: An integer value holding the sum of delays (in seconds)
|
405 |
caused by the jobs that are ahead of and competing with the JOI.
|
|
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
406 |
"""
|
7675.503.13
by Muharem Hrnjadovic
Juhu! First dispatch estimation for a recipe build job works! |
407 |
def jobs_compete_for_builders(a, b): |
408 |
"""True if the two jobs compete for builders."""
|
|
409 |
a_processor, a_virtualized = a |
|
410 |
b_processor, b_virtualized = b |
|
411 |
if a_processor is None or b_processor is None: |
|
412 |
# If either of the jobs is platform-independent then the two
|
|
413 |
# jobs compete for the same builders if the virtualization
|
|
414 |
# settings match.
|
|
415 |
if a_virtualized == b_virtualized: |
|
416 |
return True |
|
417 |
else: |
|
418 |
# Neither job is platform-independent, match processor and
|
|
419 |
# virtualization settings.
|
|
420 |
return a == b |
|
421 |
||
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
422 |
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
7675.503.13
by Muharem Hrnjadovic
Juhu! First dispatch estimation for a recipe build job works! |
423 |
my_platform = ( |
424 |
getattr(self.processor, 'id', None), |
|
425 |
normalize_virtualization(self.virtualized)) |
|
7675.503.5
by Muharem Hrnjadovic
simplified code and tests, next step: removal of the IBuildFarmJobDispatchEstimation interface |
426 |
query = """ |
427 |
SELECT
|
|
428 |
BuildQueue.processor,
|
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
429 |
BuildQueue.virtualized,
|
430 |
COUNT(BuildQueue.job),
|
|
431 |
CAST(EXTRACT(
|
|
432 |
EPOCH FROM
|
|
433 |
SUM(BuildQueue.estimated_duration)) AS INTEGER)
|
|
7675.503.5
by Muharem Hrnjadovic
simplified code and tests, next step: removal of the IBuildFarmJobDispatchEstimation interface |
434 |
FROM
|
435 |
BuildQueue, Job
|
|
436 |
WHERE
|
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
437 |
"""
|
438 |
query += self._getPendingJobsClauses() |
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
439 |
query += """ |
440 |
GROUP BY BuildQueue.processor, BuildQueue.virtualized
|
|
441 |
"""
|
|
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
442 |
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
443 |
delays_by_platform = store.execute(query).get_all() |
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
444 |
|
445 |
# This will be used to capture per-platform delay totals.
|
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
446 |
delays = defaultdict(int) |
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
447 |
# This will be used to capture per-platform job counts.
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
448 |
job_counts = defaultdict(int) |
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
449 |
|
10234.1.22
by Muharem Hrnjadovic
jtv's review comments, round #5 |
450 |
# Divide the estimated duration of the jobs as follows:
|
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
451 |
# - if a job is tied to a processor TP then divide the estimated
|
452 |
# duration of that job by the number of builders that target TP
|
|
453 |
# since only these can build the job.
|
|
454 |
# - if the job is processor-independent then divide its estimated
|
|
7675.503.19
by Muharem Hrnjadovic
corrected comment |
455 |
# duration by the total number of builders with the same
|
456 |
# virtualization setting because any one of them may run it.
|
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
457 |
for processor, virtualized, job_count, delay in delays_by_platform: |
7675.503.13
by Muharem Hrnjadovic
Juhu! First dispatch estimation for a recipe build job works! |
458 |
virtualized = normalize_virtualization(virtualized) |
459 |
platform = (processor, virtualized) |
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
460 |
builder_count = builder_stats.get(platform, 0) |
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
461 |
if builder_count == 0: |
462 |
# There is no builder that can run this job, ignore it
|
|
463 |
# for the purpose of dispatch time estimation.
|
|
464 |
continue
|
|
465 |
||
7675.503.13
by Muharem Hrnjadovic
Juhu! First dispatch estimation for a recipe build job works! |
466 |
if jobs_compete_for_builders(my_platform, platform): |
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
467 |
# The jobs that target the platform at hand compete with
|
468 |
# the JOI for builders, add their delays.
|
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
469 |
delays[platform] += delay |
470 |
job_counts[platform] += job_count |
|
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
471 |
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
472 |
sum_of_delays = 0 |
10234.1.22
by Muharem Hrnjadovic
jtv's review comments, round #5 |
473 |
# Now devide the delays based on a jobs/builders comparison.
|
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
474 |
for platform, duration in delays.iteritems(): |
475 |
jobs = job_counts[platform] |
|
476 |
builders = builder_stats[platform] |
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
477 |
# If there are less jobs than builders that can take them on,
|
478 |
# the delays should be averaged/divided by the number of jobs.
|
|
479 |
denominator = (jobs if jobs < builders else builders) |
|
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
480 |
if denominator > 1: |
14291.1.2
by Jeroen Vermeulen
Lint. |
481 |
duration = int(duration / float(denominator)) |
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
482 |
|
483 |
sum_of_delays += duration |
|
7675.503.23
by Muharem Hrnjadovic
allenap's review comments, round 2 |
484 |
|
7675.503.26
by Muharem Hrnjadovic
The python-domain iteration were replaced by aggregate functions in the db. |
485 |
return sum_of_delays |
7675.503.1
by Muharem Hrnjadovic
Imported code from mega-branch and some initial tests. |
486 |
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
487 |
def getEstimatedJobStartTime(self): |
488 |
"""See `IBuildQueue`.
|
|
489 |
||
490 |
The estimated dispatch time for the build farm job at hand is
|
|
491 |
calculated from the following ingredients:
|
|
492 |
* the start time for the head job (job at the
|
|
493 |
head of the respective build queue)
|
|
494 |
* the estimated build durations of all jobs that
|
|
495 |
precede the job of interest (JOI) in the build queue
|
|
10234.1.22
by Muharem Hrnjadovic
jtv's review comments, round #5 |
496 |
(divided by the number of machines in the respective
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
497 |
build pool)
|
498 |
"""
|
|
499 |
# This method may only be invoked for pending jobs.
|
|
500 |
if self.job.status != JobStatus.WAITING: |
|
501 |
raise AssertionError( |
|
502 |
"The start time is only estimated for pending jobs.") |
|
503 |
||
10234.1.5
by Muharem Hrnjadovic
Cleaned up more test code. |
504 |
builder_stats = get_builder_data() |
10234.1.9
by Muharem Hrnjadovic
added test for a (None,False) job |
505 |
platform = (getattr(self.processor, 'id', None), self.virtualized) |
506 |
if builder_stats[platform] == 0: |
|
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
507 |
# No builders that can run the job at hand
|
508 |
# -> no dispatch time estimation available.
|
|
10234.1.5
by Muharem Hrnjadovic
Cleaned up more test code. |
509 |
return None |
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
510 |
|
511 |
# Get the sum of the estimated run times for *pending* jobs that are
|
|
512 |
# ahead of us in the queue.
|
|
513 |
sum_of_delays = self._estimateJobDelay(builder_stats) |
|
514 |
||
515 |
# Get the minimum time duration until the next builder becomes
|
|
516 |
# available.
|
|
517 |
min_wait_time = self._estimateTimeToNextBuilder() |
|
518 |
||
10234.1.12
by Muharem Hrnjadovic
added boundary tests |
519 |
# A job will not get dispatched in less than 5 seconds no matter what.
|
520 |
start_time = max(5, min_wait_time + sum_of_delays) |
|
10409.4.2
by Muharem Hrnjadovic
Monkey-patched BuildQueue._now() in unit tests to return a constant time stamp. |
521 |
result = self._now() + timedelta(seconds=start_time) |
10234.1.1
by Muharem Hrnjadovic
imported work in progress |
522 |
|
523 |
return result |
|
524 |
||
10409.4.2
by Muharem Hrnjadovic
Monkey-patched BuildQueue._now() in unit tests to return a constant time stamp. |
525 |
@staticmethod
|
526 |
def _now(): |
|
10466.9.7
by Jeroen Vermeulen
Cleaned up some uses of utcnow(), which isn't timezone-aware. |
527 |
"""Return current time (UTC). Overridable for test purposes."""
|
10466.9.14
by Jeroen Vermeulen
Review changes. |
528 |
return datetime.now(pytz.UTC) |
10409.4.2
by Muharem Hrnjadovic
Monkey-patched BuildQueue._now() in unit tests to return a constant time stamp. |
529 |
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
530 |
|
531 |
class BuildQueueSet(object): |
|
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
532 |
"""Utility to deal with BuildQueue content class."""
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
533 |
implements(IBuildQueueSet) |
534 |
||
535 |
def __init__(self): |
|
536 |
self.title = "The Launchpad build queue" |
|
537 |
||
538 |
def __iter__(self): |
|
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
539 |
"""See `IBuildQueueSet`."""
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
540 |
return iter(BuildQueue.select()) |
541 |
||
10137.5.1
by Jeroen Vermeulen
Added BuildQueueSet.getByJob. |
542 |
def __getitem__(self, buildqueue_id): |
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
543 |
"""See `IBuildQueueSet`."""
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
544 |
try: |
10137.5.1
by Jeroen Vermeulen
Added BuildQueueSet.getByJob. |
545 |
return BuildQueue.get(buildqueue_id) |
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
546 |
except SQLObjectNotFound: |
10137.5.1
by Jeroen Vermeulen
Added BuildQueueSet.getByJob. |
547 |
raise NotFoundError(buildqueue_id) |
548 |
||
549 |
def get(self, buildqueue_id): |
|
550 |
"""See `IBuildQueueSet`."""
|
|
551 |
return BuildQueue.get(buildqueue_id) |
|
552 |
||
553 |
def getByJob(self, job): |
|
554 |
"""See `IBuildQueueSet`."""
|
|
555 |
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
|
556 |
return store.find(BuildQueue, BuildQueue.job == job).one() |
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
557 |
|
558 |
def count(self): |
|
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
559 |
"""See `IBuildQueueSet`."""
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
560 |
return BuildQueue.select().count() |
561 |
||
562 |
def getByBuilder(self, builder): |
|
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
563 |
"""See `IBuildQueueSet`."""
|
3147.5.54
by Celso Providelo
Builder & BuildQueue content objects in separated files, general code cleanup, established IBuilder.trusted behaviour (trusted -> builds ubuntu pkgs & untrusted-> ppa/grumpy pkgs) |
564 |
return BuildQueue.selectOneBy(builder=builder) |
565 |
||
566 |
def getActiveBuildJobs(self): |
|
5089.2.9
by Celso Providelo
applying review comments, r=intellectronica. |
567 |
"""See `IBuildQueueSet`."""
|
7675.391.12
by Muharem Hrnjadovic
Tons of fixes. |
568 |
store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
569 |
result_set = store.find( |
|
570 |
BuildQueue, |
|
571 |
BuildQueue.job == Job.id, |
|
10360.4.1
by Michael Nelson
Ensure that active build jobs returned by getActiveBuildJobs() have an associated builder. |
572 |
# XXX Michael Nelson 2010-02-22 bug=499421
|
573 |
# Avoid corrupt build jobs where the builder is None.
|
|
574 |
BuildQueue.builder != None, |
|
10130.2.16
by William Grant
BuildQueueSet.getActiveBuildJobs only returns RUNNING jobs. |
575 |
# status is a property. Let's use _status.
|
576 |
Job._status == JobStatus.RUNNING, |
|
7675.392.5
by Julian Edwards
Fix buildqueue.txt test |
577 |
Job.date_started != None) |
7675.391.12
by Muharem Hrnjadovic
Tons of fixes. |
578 |
return result_set |