OLD | NEW |
---|---|
1 # This file is part of Buildbot. Buildbot is free software: you can | 1 # This file is part of Buildbot. Buildbot is free software: you can |
2 # redistribute it and/or modify it under the terms of the GNU General Public | 2 # redistribute it and/or modify it under the terms of the GNU General Public |
3 # License as published by the Free Software Foundation, version 2. | 3 # License as published by the Free Software Foundation, version 2. |
4 # | 4 # |
5 # This program is distributed in the hope that it will be useful, but WITHOUT | 5 # This program is distributed in the hope that it will be useful, but WITHOUT |
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more | 7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
8 # details. | 8 # details. |
9 # | 9 # |
10 # You should have received a copy of the GNU General Public License along with | 10 # You should have received a copy of the GNU General Public License along with |
11 # this program; if not, write to the Free Software Foundation, Inc., 51 | 11 # this program; if not, write to the Free Software Foundation, Inc., 51 |
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | 12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
13 # | 13 # |
14 # Copyright Buildbot Team Members | 14 # Copyright Buildbot Team Members |
15 | 15 |
16 | 16 |
17 import weakref | 17 import weakref |
18 import gc | 18 import gc |
19 import os, re, itertools | 19 import os, re, itertools |
20 import random | |
20 from cPickle import load, dump | 21 from cPickle import load, dump |
21 | 22 |
22 from zope.interface import implements | 23 from zope.interface import implements |
23 from twisted.python import log, runtime | 24 from twisted.python import log, runtime |
24 from twisted.persisted import styles | 25 from twisted.persisted import styles |
25 from buildbot.process import metrics | 26 from buildbot.process import metrics |
26 from buildbot import interfaces, util | 27 from buildbot import interfaces, util |
27 from buildbot.util.lru import SyncLRUCache | 28 from buildbot.util.lru import SyncLRUCache |
28 from buildbot.status.event import Event | 29 from buildbot.status.event import Event |
29 from buildbot.status.build import BuildStatus | 30 from buildbot.status.build import BuildStatus |
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
316 for build in self.generateFinishedBuilds(num_builds=1): | 317 for build in self.generateFinishedBuilds(num_builds=1): |
317 assert build and build.isFinished, \ | 318 assert build and build.isFinished, \ |
318 'builder %s build %s is not finished' % ( | 319 'builder %s build %s is not finished' % ( |
319 self.getName(), build) | 320 self.getName(), build) |
320 return build | 321 return build |
321 return None | 322 return None |
322 | 323 |
323 def getCategory(self): | 324 def getCategory(self): |
324 return self.category | 325 return self.category |
325 | 326 |
326 def getBuild(self, number): | 327 def _resolveBuildNumber(self, number): |
327 if number < 0: | 328 if number < 0: |
328 number = self.nextBuildNumber + number | 329 number = self.nextBuildNumber + number |
329 if number < 0 or number >= self.nextBuildNumber: | 330 if number < 0 or number >= self.nextBuildNumber: |
330 return None | 331 return None |
332 return number | |
331 | 333 |
334 def _safeGetBuild(self, number): | |
Isaac (away)
2013/07/02 02:38:25
maybe name this var 'build_number'
Mike Stip (use stip instead)
2013/07/02 05:43:53
Done.
| |
332 try: | 335 try: |
333 return self.getBuildByNumber(number) | 336 return self.getBuildByNumber(number) |
334 except IndexError: | 337 except IndexError: |
335 return None | 338 return None |
336 | 339 |
340 def getBuild(self, number): | |
341 number = self._resolveBuildNumber(number) | |
342 | |
343 if number is None: | |
344 return None | |
345 | |
346 return self._safeGetBuild(number) | |
347 | |
348 def getBuilds(self, numbers): | |
349 """Cache-aware method to get multiple builds. | |
350 | |
351 Prevents cascading evict/load when multiple builds are requested in | |
352 succession: requesting build 1 evicts build 2, requesting build 2 evicts | |
353 build 3, etc. | |
354 | |
355 We query the buildCache and load hits first, then misses. When loading, | |
356 we randomize the load order to alleviate the problem when external web | |
357 requests load builds sequentially (they don't have access to this | |
358 function). | |
359 """ | |
360 | |
361 numbers = list(enumerate(self._resolveBuildNumber(x) for x in numbers)) | |
362 random.shuffle(numbers) | |
363 | |
364 builds = [None] * len(numbers) | |
365 misses = [] | |
366 for idx, number in numbers: | |
Isaac (away)
2013/07/02 02:38:25
better variable name for number, perhaps build_num
Mike Stip (use stip instead)
2013/07/02 05:43:53
Done.
| |
367 if number is None: | |
368 continue | |
369 if number in self.buildCache.cache: | |
370 builds[idx] = self._safeGetBuild(number) | |
371 else: | |
372 misses.append((idx, number)) | |
373 | |
374 for idx, number in misses: | |
375 builds[idx] = self._safeGetBuild(number) | |
376 | |
377 return builds | |
378 | |
337 def getEvent(self, number): | 379 def getEvent(self, number): |
338 try: | 380 try: |
339 return self.events[number] | 381 return self.events[number] |
340 except IndexError: | 382 except IndexError: |
341 return None | 383 return None |
342 | 384 |
343 def generateFinishedBuilds(self, branches=[], | 385 def generateFinishedBuilds(self, branches=[], |
344 num_builds=None, | 386 num_builds=None, |
345 max_buildnum=None, | 387 max_buildnum=None, |
346 finished_before=None, | 388 finished_before=None, |
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
609 result['basedir'] = os.path.basename(self.basedir) | 651 result['basedir'] = os.path.basename(self.basedir) |
610 result['category'] = self.category | 652 result['category'] = self.category |
611 result['slaves'] = self.slavenames | 653 result['slaves'] = self.slavenames |
612 #result['url'] = self.parent.getURLForThing(self) | 654 #result['url'] = self.parent.getURLForThing(self) |
613 # TODO(maruel): Add cache settings? Do we care? | 655 # TODO(maruel): Add cache settings? Do we care? |
614 | 656 |
615 # Transient | 657 # Transient |
616 # Collect build numbers. | 658 # Collect build numbers. |
617 # Important: Only grab the *cached* builds numbers to reduce I/O. | 659 # Important: Only grab the *cached* builds numbers to reduce I/O. |
618 current_builds = [b.getNumber() for b in self.currentBuilds] | 660 current_builds = [b.getNumber() for b in self.currentBuilds] |
661 | |
662 # Populates buildCache with last N builds. | |
663 buildnums = range(-1, -(self.buildCacheSize - 1), -1) | |
664 self.getBuilds(buildnums) | |
665 | |
619 cached_builds = list(set(self.buildCache.cache.keys() + current_builds)) | 666 cached_builds = list(set(self.buildCache.cache.keys() + current_builds)) |
620 cached_builds.sort() | 667 cached_builds.sort() |
621 result['cachedBuilds'] = cached_builds | 668 result['cachedBuilds'] = cached_builds |
622 result['currentBuilds'] = current_builds | 669 result['currentBuilds'] = current_builds |
623 result['state'] = self.getState()[0] | 670 result['state'] = self.getState()[0] |
624 # lies, but we don't have synchronous access to this info; use | 671 # lies, but we don't have synchronous access to this info; use |
625 # asDict_async instead | 672 # asDict_async instead |
626 result['pendingBuilds'] = 0 | 673 result['pendingBuilds'] = 0 |
627 return result | 674 return result |
628 | 675 |
629 def asDict_async(self): | 676 def asDict_async(self): |
630 """Just like L{asDict}, but with a nonzero pendingBuilds.""" | 677 """Just like L{asDict}, but with a nonzero pendingBuilds.""" |
631 result = self.asDict() | 678 result = self.asDict() |
632 d = self.getPendingBuildRequestStatuses() | 679 d = self.getPendingBuildRequestStatuses() |
633 def combine(statuses): | 680 def combine(statuses): |
634 result['pendingBuilds'] = len(statuses) | 681 result['pendingBuilds'] = len(statuses) |
635 return result | 682 return result |
636 d.addCallback(combine) | 683 d.addCallback(combine) |
637 return d | 684 return d |
638 | 685 |
639 def getMetrics(self): | 686 def getMetrics(self): |
640 return self.botmaster.parent.metrics | 687 return self.botmaster.parent.metrics |
641 | 688 |
642 # vim: set ts=4 sts=4 sw=4 et: | 689 # vim: set ts=4 sts=4 sw=4 et: |
OLD | NEW |