1
# Copyright 2009 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
#Authors: Daniel Silverstone <daniel.silverstone@canonical.com>
5
# Celso Providelo <celso.providelo@canonical.com>
7
"""Common code for Buildd scripts
9
Module used by buildd-queue-builder.py and buildd-slave-scanner.py
18
from zope.component import getUtility
20
from canonical.buildd.utils import notes
21
from canonical.config import config
22
from canonical.librarian.interfaces import ILibrarianClient
23
from lp.archivepublisher.utils import process_in_batches
24
from lp.buildmaster.interfaces.buildbase import BuildStatus
25
from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
26
from lp.buildmaster.buildergroup import BuilderGroup
27
from lp.soyuz.interfaces.build import IBuildSet
28
from lp.soyuz.pas import BuildDaemonPackagesArchSpecific
32
"""Canonical autobuilder master, toolkit and algorithms.
34
This class is in the process of being deprecated in favour of the regular
37
# XXX cprov 2007-06-15: Please do not extend this class except as
38
# required to move more logic into the content classes. A new feature
39
# should be modeled directly in IBuilder.
41
def __init__(self, logger, tm):
44
self.librarian = getUtility(ILibrarianClient)
46
self._logger.debug("Buildd Master has been initialised")
51
def addDistroArchSeries(self, distroarchseries):
52
"""Setting up a workable DistroArchSeries for this session."""
53
self._logger.debug("Adding DistroArchSeries %s/%s/%s"
54
% (distroarchseries.distroseries.distribution.name,
55
distroarchseries.distroseries.name,
56
distroarchseries.architecturetag))
58
# Is there a chroot for this archseries?
59
if distroarchseries.getChroot():
60
# Fill out the contents.
61
self._archseries.setdefault(distroarchseries, {})
63
def setupBuilders(self, archseries):
64
"""Setting up a group of builder slaves for a given DistroArchSeries.
66
Use the annotation utility to store a BuilderGroup instance
67
keyed by the the DistroArchSeries.processorfamily in the
68
global registry 'notes' and refer to this 'note' in the private
69
attribute '_archseries' keyed by the given DistroArchSeries
70
and the label 'builders'. This complicated arrangement enables us
71
to share builder slaves between different DistroArchRelases since
72
their processorfamily values are the same (compatible processors).
74
# Determine the builders for this distroarchseries...
75
if archseries not in self._archseries:
76
# Avoid entering in the huge loop if we don't find at least
77
# one architecture for which we can build on.
79
"Chroot missing for %s/%s/%s, skipping"
80
% (archseries.distroseries.distribution.name,
81
archseries.distroseries.name,
82
archseries.architecturetag))
85
# query the global annotation registry and verify if
86
# we have already done the builder checks for the
87
# processor family in question. if it's already done
88
# simply refer to that information in the _archseries
90
if 'builders' not in notes[archseries.processorfamily]:
92
# setup a BuilderGroup object
93
info = "builders.%s" % archseries.processorfamily.name
94
builderGroup = BuilderGroup(self.getLogger(info), self._tm)
96
# check the available slaves for this archseries
97
builderGroup.checkAvailableSlaves(archseries)
99
# annotate the group of builders for the
100
# DistroArchSeries.processorfamily in question and the
102
notes[archseries.processorfamily]["builders"] = builderGroup
104
# consolidate the annotation for the architecture release
105
# in the private attribute _archreleases
106
self._archseries[archseries]["builders"] = \
107
notes[archseries.processorfamily]["builders"]
109
def createMissingBuilds(self, distroseries):
110
"""Ensure that each published package is completly built."""
111
self._logger.info("Processing %s" % distroseries.name)
112
# Do not create builds for distroseries with no nominatedarchindep
113
# they can't build architecture independent packages properly.
114
if not distroseries.nominatedarchindep:
116
"No nominatedarchindep for %s, skipping" % distroseries.name)
119
# Listify the architectures to avoid hitting this MultipleJoin
121
distroseries_architectures = list(distroseries.architectures)
122
if not distroseries_architectures:
124
"No architectures defined for %s, skipping"
128
architectures_available = list(distroseries.enabled_architectures)
129
if not architectures_available:
131
"Chroots missing for %s, skipping" % distroseries.name)
135
"Supported architectures: %s" %
136
" ".join(arch_series.architecturetag
137
for arch_series in architectures_available))
139
pas_verify = BuildDaemonPackagesArchSpecific(
140
config.builddmaster.root, distroseries)
142
sources_published = distroseries.getSourcesPublishedForAllArchives()
144
"Found %d source(s) published." % sources_published.count())
146
def process_source(pubrec):
147
builds = pubrec.createMissingBuilds(
148
architectures_available=architectures_available,
149
pas_verify=pas_verify, logger=self._logger)
154
sources_published, process_source, self._logger,
155
minimum_chunk_size=1000)
157
def addMissingBuildQueueEntries(self):
158
"""Create missing Buildd Jobs. """
159
self._logger.info("Scanning for build queue entries that are missing")
161
buildset = getUtility(IBuildSet)
162
builds = buildset.getPendingBuildsForArchSet(self._archseries)
168
if not build.buildqueue_record:
169
name = build.sourcepackagerelease.name
170
version = build.sourcepackagerelease.version
171
tag = build.distroarchseries.architecturetag
173
"Creating buildqueue record for %s (%s) on %s"
174
% (name, version, tag))
179
def scanActiveBuilders(self):
180
"""Collect informations/results of current build jobs."""
182
queueItems = getUtility(IBuildQueueSet).getActiveBuildJobs()
185
"scanActiveBuilders() found %d active build(s) to check"
186
% queueItems.count())
188
for job in queueItems:
189
job.builder.updateBuild(job)
192
def getLogger(self, subname=None):
193
"""Return the logger instance with specific prefix"""
196
return logging.getLogger("%s.%s" % (self._logger.name, subname))
198
def scoreCandidates(self):
199
"""Iterate over the pending buildqueue entries and re-score them."""
200
if not self._archseries:
201
self._logger.info("No architecture found to rescore.")
204
# Get the current build job candidates.
205
archseries = self._archseries.keys()
206
bqset = getUtility(IBuildQueueSet)
207
candidates = bqset.calculateCandidates(archseries)
209
self._logger.info("Found %d build in NEEDSBUILD state. Rescoring"
210
% candidates.count())
212
for job in candidates:
213
uptodate_build = getUtility(IBuildSet).getByQueueEntry(job)
214
if uptodate_build.buildstate != BuildStatus.NEEDSBUILD: