45
50
class InitializationError(Exception):
46
51
"""Raised when there is an exception during the initialization process."""
53
# Pockets to consider when initializing the derived series from its parent(s).
55
PackagePublishingPocket.RELEASE,
56
PackagePublishingPocket.SECURITY,
57
PackagePublishingPocket.UPDATES,
49
61
class InitializeDistroSeries:
50
62
"""Copy in all of the parents distroseries's configuration. This
160
177
def _checkBuilds(self, parent):
161
178
"""Assert there are no pending builds for the given parent series.
163
Only cares about the RELEASE pocket, which is the only one inherited
164
via initializeFromParent method.
180
Only cares about the RELEASE, SECURITY and UPDATES pockets, which are
181
the only ones inherited via initializeFromParent method.
182
Restrict the check to the select architectures (if applicable).
183
Restrict the check to the selected packages if a limited set of
184
packagesets is used by the initialization.
166
# only the RELEASE pocket is inherited, so we only check
167
# pending build records for it.
186
spns = self.source_names_by_parent.get(parent, None)
187
if spns is not None and len(spns) == 0:
188
# If no sources are selected in this parent, skip the check.
190
# spns=None means no packagesets selected so we need to consider
193
arch_tags = self.arches if self.arches is not () else None
168
194
pending_builds = parent.getBuildRecords(
169
BuildStatus.NEEDSBUILD, pocket=PackagePublishingPocket.RELEASE)
195
BuildStatus.NEEDSBUILD, pocket=INIT_POCKETS,
196
arch_tag=arch_tags, name=spns)
171
if pending_builds.any():
172
raise InitializationError("Parent series has pending builds.")
198
if not pending_builds.is_empty():
199
raise InitializationError(
200
"Parent series has pending builds for selected sources, "
201
"see help text for more information.")
174
203
def _checkQueue(self, parent):
175
204
"""Assert upload queue is empty on the given parent series.
177
Only cares about the RELEASE pocket, which is the only one inherited
178
via initializeFromParent method.
180
# only the RELEASE pocket is inherited, so we only check
181
# queue items for it.
206
Only cares about the RELEASE, SECURITY and UPDATES pockets, which are
207
the only ones inherited via initializeFromParent method.
208
Restrict the check to the selected packages if a limited set of
209
packagesets is used by the initialization.
183
212
PackageUploadStatus.NEW,
184
213
PackageUploadStatus.ACCEPTED,
185
214
PackageUploadStatus.UNAPPROVED,
187
items = parent.getPackageUploads(
188
status=statuses, pocket=PackagePublishingPocket.RELEASE)
216
spns = self.source_names_by_parent.get(parent, None)
217
if spns is not None and len(spns) == 0:
218
# If no sources are selected in this parent, skip the check.
220
# spns=None means no packagesets selected so we need to consider
223
items = getUtility(IPackageUploadSet).getBuildsForSources(
224
parent, statuses, INIT_POCKETS, spns)
189
225
if not items.is_empty():
190
226
raise InitializationError(
191
"Parent series queues are not empty.")
227
"Parent series has sources waiting in its upload queues "
228
"that match your selection, see help text for more "
193
231
def _checkSeries(self):
240
279
for distroseriesparent in distroseriesparents:
241
280
distroseriesparent.initialized = True
282
def _has_same_parents_as_previous_series(self):
283
# Does this distroseries have the same parents as its previous
284
# series? (note that the parent's order does not matter here)
285
dsp_set = getUtility(IDistroSeriesParentSet)
286
previous_series_parents = [
287
dsp.parent_series for dsp in dsp_set.getByDerivedSeries(
288
self.distroseries.previous_series)]
289
return set(previous_series_parents) == set(self.parents)
291
def _create_dsds(self):
292
if not self.first_derivation:
293
if (self._has_same_parents_as_previous_series() and
294
not self.packagesets_ids):
295
# If the parents are the same as previous_series's
296
# parents and all the packagesets are being copied,
297
# then we simply copy the DSDs from previous_series
298
# for performance reasons.
299
self._copy_dsds_from_previous_series()
301
# Either the parents have changed (compared to
302
# previous_series's parents) or a selection only of the
303
# packagesets is being copied so we have to recompute
304
# the DSDs by creating DSD Jobs.
305
self._create_dsd_jobs()
307
# If this is the first derivation, create the DSD Jobs.
308
self._create_dsd_jobs()
310
def _copy_dsds_from_previous_series(self):
311
self._store.execute("""
312
INSERT INTO DistroSeriesDifference
313
(derived_series, source_package_name, package_diff,
314
status, difference_type, parent_package_diff,
315
source_version, parent_source_version,
316
base_version, parent_series)
318
%s AS derived_series, source_package_name,
319
package_diff, status,
320
difference_type, parent_package_diff, source_version,
321
parent_source_version, base_version, parent_series
322
FROM DistroSeriesDifference AS dsd
323
WHERE dsd.derived_series = %s
325
self.distroseries.id,
326
self.distroseries.previous_series.id))
328
def _create_dsd_jobs(self):
329
job_source = getUtility(IDistroSeriesDifferenceJobSource)
330
job_source.massCreateForSeries(self.distroseries)
243
332
def _copy_configuration(self):
244
333
self.distroseries.backports_not_automatic = any(
245
334
parent.backports_not_automatic
256
345
sqlvalues(self.arches))
257
346
self._store.execute("""
258
347
INSERT INTO DistroArchSeries
259
(distroseries, processorfamily, architecturetag, owner, official)
348
(distroseries, processorfamily, architecturetag, owner, official,
349
supports_virtualized)
260
350
SELECT %s, processorfamily, architecturetag, %s,
351
bool_and(official), bool_or(supports_virtualized)
262
352
FROM DistroArchSeries WHERE enabled = TRUE %s
263
353
GROUP BY processorfamily, architecturetag
264
354
""" % (sqlvalues(self.distroseries, self.distroseries.owner)
291
381
self._copy_publishing_records(distroarchseries_lists)
292
382
self._copy_packaging_links()
295
def _use_cloner(cls, target_archive, archive, distroseries):
384
def _use_cloner(self, target_archive, archive):
296
385
"""Returns True if it's safe to use the packagecloner (as opposed
297
386
to using the packagecopier).
298
387
We use two different ways to copy packages:
299
388
- the packagecloner: fast but not conflict safe.
300
389
- the packagecopier: slow but performs lots of checks to
301
390
avoid creating conflicts.
302
1a. If the archives are different and the target archive is
303
empty use the cloner.
304
1b. If the archives are the same and the target series is
305
empty use the cloner.
391
1. We'll use the cloner:
392
If this is not a first initialization.
394
1.a If the archives are different and the target archive is
395
empty use the cloner.
397
1.b. If the archives are the same and the target series is
398
empty use the cloner.
306
399
2. Otherwise use the copier.
401
if self.first_derivation:
308
404
target_archive_empty = target_archive.getPublishedSources().is_empty()
309
405
case_1a = (target_archive != archive and
310
406
target_archive_empty)
311
407
case_1b = (target_archive == archive and
312
408
(target_archive_empty or
313
409
target_archive.getPublishedSources(
314
distroseries=distroseries).is_empty()))
410
distroseries=self.distroseries).is_empty()))
315
411
return case_1a or case_1b
413
def _create_source_names_by_parent(self):
414
"""If only a subset of the packagesets was selected to be copied,
415
create a dict with the list of source names to be copied for each
418
source_names_by_parent.get(parent) can be 3 different things:
419
- None: this means that no specific packagesets where selected
420
for the initialization. In this case we need to consider *all*
421
the packages in this parent.
422
- []: this means that some specific packagesets where selected
423
for the initialization but none in this parent. We can skip
424
this parent for all the copy/check operations.
425
- [name1, ...]: this means that some specific packagesets
426
were selected for the initialization and some are in this
427
parent so the list of packages to consider in not empty.
429
source_names_by_parent = {}
430
if self.packagesets_ids:
431
for parent in self.derivation_parents:
433
for pkgset in self.packagesets:
434
if pkgset.distroseries == parent:
435
spns += list(pkgset.getSourcesIncluded())
436
source_names_by_parent[parent] = spns
437
self.source_names_by_parent = source_names_by_parent
317
439
def _copy_publishing_records(self, distroarchseries_lists):
318
440
"""Copy the publishing records from the parent arch series
319
441
to the given arch series in ourselves.
326
448
archive_set = getUtility(IArchiveSet)
328
450
for parent in self.derivation_parents:
330
# The overhead from looking up each packageset is mitigated by
331
# this usually running from a job.
333
for pkgsetid in self.packagesets:
334
pkgset = self._store.get(Packageset, int(pkgsetid))
335
if pkgset.distroseries == parent:
336
spns += list(pkgset.getSourcesIncluded())
451
spns = self.source_names_by_parent.get(parent, None)
452
if spns is not None and len(spns) == 0:
338
453
# Some packagesets where selected but not a single
339
454
# source from this parent: we skip the copy since
340
455
# calling copy with spns=[] would copy all the packagesets
341
456
# from this parent.
458
# spns=None means no packagesets selected so we need to consider
345
461
distroarchseries_list = distroarchseries_lists[parent]
346
462
for archive in parent.distribution.all_distro_archives:
353
469
if archive.purpose is ArchivePurpose.PRIMARY:
354
470
assert target_archive is not None, (
355
471
"Target archive doesn't exist?")
358
target_archive, archive, self.distroseries):
472
if self._use_cloner(target_archive, archive):
359
473
origin = PackageLocation(
360
474
archive, parent.distribution, parent,
361
475
PackagePublishingPocket.RELEASE)
375
489
# There is only one available pocket in an unreleased
377
pocket = PackagePublishingPocket.RELEASE
491
target_pocket = PackagePublishingPocket.RELEASE
378
492
sources = archive.getPublishedSources(
379
distroseries=parent, pocket=pocket, name=spns)
493
distroseries=parent, pocket=INIT_POCKETS,
494
status=(PackagePublishingStatus.PENDING,
495
PackagePublishingStatus.PUBLISHED),
380
497
# XXX: rvb 2011-06-23 bug=801112: do_copy is atomic (all
381
498
# or none of the sources will be copied). This might
382
499
# lead to a partially initialised series if there is a
385
502
sources_published = do_copy(
386
503
sources, target_archive, self.distroseries,
387
pocket, include_binaries=not self.rebuild,
388
check_permissions=False, strict_binaries=False)
504
target_pocket, include_binaries=not self.rebuild,
505
check_permissions=False, strict_binaries=False,
506
close_bugs=False, create_dsd_job=False)
390
508
for pubrec in sources_published:
391
pubrec.createMissingBuilds()
509
pubrec.createMissingBuilds(
510
list(self.distroseries.architectures))
393
511
except CannotCopy, error:
394
512
raise InitializationError(error)
475
593
packagesets = self._store.find(
476
594
Packageset, DistroSeries.id.is_in(self.derivation_parent_ids))
477
595
parent_to_child = {}
478
# Create the packagesets.
596
# Create the packagesets and any archivepermissions if we're not
597
# copying cross-distribution.
479
598
parent_distro_ids = [
480
599
parent.distribution.id for parent in self.derivation_parents]
481
600
for parent_ps in packagesets:
482
601
# Cross-distro initializations get packagesets owned by the
483
602
# distro owner, otherwise the old owner is preserved.
484
if self.packagesets and str(parent_ps.id) not in self.packagesets:
603
if (self.packagesets_ids and
604
str(parent_ps.id) not in self.packagesets_ids):
486
606
packageset_set = getUtility(IPackagesetSet)
487
607
# First, try to fetch an existing packageset with this name.
497
617
parent_ps.name, parent_ps.description,
498
618
new_owner, distroseries=self.distroseries,
499
619
related_set=parent_ps)
501
620
parent_to_child[parent_ps] = child_ps
621
# Copy archivepermissions if we're not copying
622
# cross-distribution.
623
if (self.distroseries.distribution ==
624
parent_ps.distroseries.distribution):
625
self._store.execute("""
626
INSERT INTO Archivepermission
627
(person, permission, archive, packageset, explicit)
628
SELECT person, permission, %s, %s, explicit
629
FROM Archivepermission WHERE packageset = %s
631
self.distroseries.main_archive, child_ps.id,
502
633
# Copy the relations between sets, and the contents.
503
634
for old_series_ps, new_series_ps in parent_to_child.items():
504
635
old_series_sets = old_series_ps.setsIncluded(