Chromium Code Reviews| 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 |