~launchpad-pqm/launchpad/devel

7675.1120.20 by Jeroen Vermeulen
Schedule index creation during InitialiseDistroSeries.
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
4
"""Initialize a distroseries from its parent distroseries."""
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
5
6
7
__metaclass__ = type
8
__all__ = [
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
9
    'InitializationError',
10
    'InitializeDistroSeries',
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
11
    ]
12
12771.3.4 by Steve Kowalik
Use new storm, so we can use is_empty on almost everything.
13
from operator import methodcaller
13117.2.3 by Raphael Badin
Fix series initialisation to use DSP (and not previous_series).
14
12771.3.1 by Steve Kowalik
Drop initialise_distro_series, the generic run_jobs can deal with it.
15
import transaction
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
16
from zope.component import getUtility
11403.1.4 by Henning Eggers
Reformatted imports using format-imports script r32.
17
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
18
from canonical.database.sqlbase import sqlvalues
7675.166.315 by Stuart Bishop
Be more careful about casting to Unicode
19
from canonical.launchpad.helpers import ensure_unicode
13168.12.2 by Raphael Badin
Refactor ISD.
20
from canonical.launchpad.interfaces.lpstorm import IMasterStore
21
from lp.app.errors import NotFoundError
11458.1.1 by Jelmer Vernooij
Move enums of buildmaster.
22
from lp.buildmaster.enums import BuildStatus
13117.2.3 by Raphael Badin
Fix series initialisation to use DSP (and not previous_series).
23
from lp.registry.interfaces.distroseriesparent import IDistroSeriesParentSet
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
24
from lp.registry.interfaces.pocket import PackagePublishingPocket
13168.12.2 by Raphael Badin
Refactor ISD.
25
from lp.services.database import bulk
11258.1.7 by Steve Kowalik
* Remove some more now unused imports from distroseries.
26
from lp.soyuz.adapters.packagelocation import PackageLocation
11411.6.9 by Julian Edwards
Move PackageUploadStatus and PackageUploadCustomFormat
27
from lp.soyuz.enums import (
28
    ArchivePurpose,
29
    PackageUploadStatus,
30
    )
13168.12.32 by Raphael Badin
Apply MP's comments.
31
from lp.soyuz.interfaces.archive import (
32
    CannotCopy,
33
    IArchiveSet,
34
    )
13117.2.9 by Raphael Badin
Pockets and components are passed as strings.
35
from lp.soyuz.interfaces.component import IComponentSet
13457.4.3 by Raphael Badin
Intermediate step.
36
from lp.soyuz.interfaces.distributionjob import (
37
    IDistroSeriesDifferenceJobSource,
38
    )
11584.3.3 by Steve Kowalik
* Switch to IPackageCloner, rather than clone_packages.
39
from lp.soyuz.interfaces.packagecloner import IPackageCloner
13168.12.3 by Raphael Badin
Merge identical packagesets from the parents into the child.
40
from lp.soyuz.interfaces.packageset import (
41
    IPackagesetSet,
42
    NoSuchPackageSet,
43
    )
11411.7.24 by j.c.sackett
Merged from devel.
44
from lp.soyuz.model.packageset import Packageset
13168.12.23 by Raphael Badin
Refactor initialization to use the packagecopier if the destination archive is not empty.
45
from lp.soyuz.scripts.packagecopier import do_copy
11258.1.6 by Steve Kowalik
* Remove unneeded import from distroseries, and move it to IDS.
46
47
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
48
class InitializationError(Exception):
49
    """Raised when there is an exception during the initialization process."""
50
51
52
class InitializeDistroSeries:
13168.12.11 by Raphael Badin
Fix doc.
53
    """Copy in all of the parents distroseries's configuration. This
11258.1.9 by Steve Kowalik
Add a docstring to InitialiseDistroSeries, and correct slight thinko with the script's docstring
54
    includes all configuration for distroseries as well as distroarchseries,
55
    publishing and all publishing records for sources and binaries.
56
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
57
    We support 2 use cases here:
58
      #1 If the child distribution has zero initialized series:
59
        - the parent list can't be empty (otherwise we trigger an error);
13168.13.3 by Raphael Badin
Fix doc.
60
        - the series will be derived from the parents passed as argument;
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
61
        - the parents will be set to the parents passed as argument;
62
        - first_derivation = True.
63
      #2 If the child distribution has more than zero initialized series:
13168.13.3 by Raphael Badin
Fix doc.
64
        - the series will be derived from the previous_series;
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
65
        - the parents will be set to the parents passed as argument or
66
          the parents of the previous_series if the passed argument is empty;
67
        - first_derivation = False.
68
11258.1.9 by Steve Kowalik
Add a docstring to InitialiseDistroSeries, and correct slight thinko with the script's docstring
69
    Preconditions:
70
      The distroseries must exist, and be completly unused, with no source
71
      or binary packages existing, as well as no distroarchseries set up.
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
72
      Section and component selections must be empty. It must not have any
73
      parent series.
11258.1.9 by Steve Kowalik
Add a docstring to InitialiseDistroSeries, and correct slight thinko with the script's docstring
74
75
    Outcome:
76
      The distroarchseries set up in the parent series will be copied.
13168.12.11 by Raphael Badin
Fix doc.
77
      The publishing structure will be copied from the parents. All
78
      PUBLISHED and PENDING packages in the parents will be created in
7675.1010.1 by William Grant
Excise lucilleconfig from the model.
79
      this distroseries and its distroarchseriess. All component and section
80
      selections will be duplicated, as will any permission-related
81
      structures.
11258.1.9 by Steve Kowalik
Add a docstring to InitialiseDistroSeries, and correct slight thinko with the script's docstring
82
83
    Note:
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
84
      This method will raise a InitializationError when the pre-conditions
11258.1.9 by Steve Kowalik
Add a docstring to InitialiseDistroSeries, and correct slight thinko with the script's docstring
85
      are not met. After this is run, you still need to construct chroots
86
      for building, you need to add anything missing wrt. ports etc. This
13168.12.11 by Raphael Badin
Fix doc.
87
      method is only meant to give you a basic copy of parent series in
11258.1.9 by Steve Kowalik
Add a docstring to InitialiseDistroSeries, and correct slight thinko with the script's docstring
88
      order to assist you in preparing a new series of a distribution or
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
89
      in the initialization of a derivative.
11258.1.9 by Steve Kowalik
Add a docstring to InitialiseDistroSeries, and correct slight thinko with the script's docstring
90
    """
11258.1.8 by Steve Kowalik
* Fix the 3 tests that use .initialiseFromParent() to use
91
11566.1.3 by Steve Kowalik
Merge devel, fixing conflicts
92
    def __init__(
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
93
        self, distroseries, parents=(), arches=(), packagesets=(),
13117.2.13 by Raphael Badin
Use immutable objects for default parameters (MP's comments).
94
        rebuild=False, overlays=(), overlay_pockets=(),
95
        overlay_components=()):
7675.883.1 by Steve Kowalik
* First shot at the deriveDistroSeries() function, and exporting it over the
96
        # Avoid circular imports
97
        from lp.registry.model.distroseries import DistroSeries
13117.2.1 by Raphael Badin
Fix initseries to support multiple parents as an argument.
98
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
99
        self.distroseries = distroseries
13168.12.2 by Raphael Badin
Refactor ISD.
100
        self.parent_ids = [int(id) for id in parents]
101
        # Load parent objects in bulk...
102
        parents_bulk = bulk.load(DistroSeries, self.parent_ids)
103
        # ... sort the parents to match the order in the 'parents' parameter.
104
        self.parents = sorted(
105
            parents_bulk,
106
            key=lambda parent: self.parent_ids.index(parent.id))
11393.1.1 by Steve Kowalik
* Limit the number of arches we copy when so instructed.
107
        self.arches = arches
7675.166.315 by Stuart Bishop
Be more careful about casting to Unicode
108
        self.packagesets = [
109
            ensure_unicode(packageset) for packageset in packagesets]
11584.3.1 by Steve Kowalik
Add rebuild support. Drive-by a fix to _full_initialise() in the test-suite.
110
        self.rebuild = rebuild
13117.2.3 by Raphael Badin
Fix series initialisation to use DSP (and not previous_series).
111
        self.overlays = overlays
13117.2.8 by Raphael Badin
Add overlay support to initseries.
112
        self.overlay_pockets = overlay_pockets
113
        self.overlay_components = overlay_components
11470.1.1 by Steve Kowalik
* Switch IDS and its tests from IStoreSelector to I{Master,}Store.
114
        self._store = IMasterStore(DistroSeries)
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
115
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
116
        self.first_derivation = (
13168.13.2 by Raphael Badin
Use distribution.has_published_sources.
117
            not self.distroseries.distribution.has_published_sources)
13457.4.3 by Raphael Badin
Intermediate step.
118
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
119
        if self.first_derivation:
120
            # Use-case #1.
121
            self.derivation_parents = self.parents
122
            self.derivation_parent_ids = self.parent_ids
123
        else:
124
            # Use-case #2.
125
            self.derivation_parents = [self.distroseries.previous_series]
126
            self.derivation_parent_ids = [
13371.2.2 by Raphael Badin
Initialize series should make sure previous_series is not None.
127
                p.id for p in self.derivation_parents if p is not None]
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
128
            if self.parent_ids == []:
129
                self.parents = (
130
                    self.distroseries.previous_series.getParentSeries())
131
11258.1.10 by Steve Kowalik
* Undo test changes.
132
    def check(self):
13225.2.1 by Gavin Panella
Change is_derived_series from a property to an isDerivedSeries() method.
133
        if self.distroseries.isDerivedSeries():
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
134
            raise InitializationError(
13117.2.3 by Raphael Badin
Fix series initialisation to use DSP (and not previous_series).
135
                ("DistroSeries {child.name} has already been initialized"
136
                 ".").format(
12494.1.89 by Gavin Panella
The error message when parent_series was already set was ambiguous, misleading even.
137
                    child=self.distroseries))
13371.2.2 by Raphael Badin
Initialize series should make sure previous_series is not None.
138
        if (self.distroseries.distribution.has_published_sources and
139
            self.distroseries.previous_series is None):
140
            raise InitializationError(
141
                ("DistroSeries {child.name} has no previous series and "
142
                 "the distribution already has initialized series"
143
                 ".").format(
144
                    child=self.distroseries))
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
145
        self._checkParents()
146
        for parent in self.derivation_parents:
7675.1201.5 by Raphael Badin
Init multiple parents.
147
            if self.distroseries.distribution.id == parent.distribution.id:
148
                self._checkBuilds(parent)
149
            self._checkQueue(parent)
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
150
        self._checkSeries()
151
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
152
    def _checkParents(self):
13168.13.6 by Raphael Badin
Fix style.
153
        """If self.first_derivation, the parents list cannot be empty."""
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
154
        if self.first_derivation:
155
            # Use-case #1.
156
            if len(self.parent_ids) == 0:
13168.13.7 by Raphael Badin
Merge devel.
157
                raise InitializationError(
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
158
                    ("Distroseries {child.name} cannot be initialized: "
159
                     "No other series in the distribution is initialized "
160
                     "and no parent was passed to the initilization method"
161
                     ".").format(
162
                        child=self.distroseries))
163
7675.1201.5 by Raphael Badin
Init multiple parents.
164
    def _checkBuilds(self, parent):
13168.12.2 by Raphael Badin
Refactor ISD.
165
        """Assert there are no pending builds for the given parent series.
12771.3.2 by Steve Kowalik
Revert kwargs call to Store.find(), fix another mention of parent_series I
166
167
        Only cares about the RELEASE pocket, which is the only one inherited
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
168
        via initializeFromParent method.
12771.3.2 by Steve Kowalik
Revert kwargs call to Store.find(), fix another mention of parent_series I
169
        """
170
        # only the RELEASE pocket is inherited, so we only check
171
        # pending build records for it.
7675.1201.5 by Raphael Badin
Init multiple parents.
172
        pending_builds = parent.getBuildRecords(
12771.3.2 by Steve Kowalik
Revert kwargs call to Store.find(), fix another mention of parent_series I
173
            BuildStatus.NEEDSBUILD, pocket=PackagePublishingPocket.RELEASE)
174
175
        if pending_builds.any():
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
176
            raise InitializationError("Parent series has pending builds.")
12771.3.2 by Steve Kowalik
Revert kwargs call to Store.find(), fix another mention of parent_series I
177
7675.1201.5 by Raphael Badin
Init multiple parents.
178
    def _checkQueue(self, parent):
13168.12.2 by Raphael Badin
Refactor ISD.
179
        """Assert upload queue is empty on the given parent series.
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
180
181
        Only cares about the RELEASE pocket, which is the only one inherited
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
182
        via initializeFromParent method.
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
183
        """
184
        # only the RELEASE pocket is inherited, so we only check
185
        # queue items for it.
13233.3.1 by Jeroen Vermeulen
Replaced non-test use of getQueueItems with getPackageUploads.
186
        statuses = [
187
            PackageUploadStatus.NEW,
188
            PackageUploadStatus.ACCEPTED,
189
            PackageUploadStatus.UNAPPROVED,
190
            ]
13168.12.20 by Raphael Badin
Merge devel.
191
        items = parent.getPackageUploads(
13233.3.1 by Jeroen Vermeulen
Replaced non-test use of getQueueItems with getPackageUploads.
192
            status=statuses, pocket=PackagePublishingPocket.RELEASE)
193
        if not items.is_empty():
194
            raise InitializationError(
195
                "Parent series queues are not empty.")
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
196
197
    def _checkSeries(self):
11258.1.6 by Steve Kowalik
* Remove unneeded import from distroseries, and move it to IDS.
198
        error = (
199
            "Can not copy distroarchseries from parent, there are "
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
200
            "already distroarchseries(s) initialized for this series.")
12771.3.1 by Steve Kowalik
Drop initialise_distro_series, the generic run_jobs can deal with it.
201
        sources = self.distroseries.getAllPublishedSources()
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
202
        binaries = self.distroseries.getAllPublishedBinaries()
12771.3.4 by Steve Kowalik
Use new storm, so we can use is_empty on almost everything.
203
        if not all(
204
            map(methodcaller('is_empty'), (
205
                sources, binaries, self.distroseries.architectures,
206
                self.distroseries.sections))):
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
207
            raise InitializationError(error)
12771.3.4 by Steve Kowalik
Use new storm, so we can use is_empty on almost everything.
208
        if self.distroseries.components:
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
209
            raise InitializationError(error)
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
210
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
211
    def initialize(self):
13168.12.2 by Raphael Badin
Refactor ISD.
212
        self._set_parents()
213
        self._copy_configuration()
214
        self._copy_architectures()
215
        self._copy_packages()
216
        self._copy_packagesets()
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
217
        self._create_dsds()
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
218
        self._set_initialized()
13168.12.2 by Raphael Badin
Refactor ISD.
219
        transaction.commit()
7675.1201.5 by Raphael Badin
Init multiple parents.
220
13168.12.2 by Raphael Badin
Refactor ISD.
221
    def _set_parents(self):
13168.12.5 by Raphael Badin
Fix IDS._set_parents.
222
        count = 0
13168.12.2 by Raphael Badin
Refactor ISD.
223
        for parent in self.parents:
224
            dsp_set = getUtility(IDistroSeriesParentSet)
13168.12.5 by Raphael Badin
Fix IDS._set_parents.
225
            if self.overlays and self.overlays[count]:
13168.12.2 by Raphael Badin
Refactor ISD.
226
                pocket = PackagePublishingPocket.__metaclass__.getTermByToken(
13168.12.5 by Raphael Badin
Fix IDS._set_parents.
227
                    PackagePublishingPocket,
228
                    self.overlay_pockets[count]).value
13168.12.2 by Raphael Badin
Refactor ISD.
229
                component_set = getUtility(IComponentSet)
13168.12.5 by Raphael Badin
Fix IDS._set_parents.
230
                component = component_set[self.overlay_components[count]]
13168.12.2 by Raphael Badin
Refactor ISD.
231
                dsp_set.new(
232
                    self.distroseries, parent, initialized=False,
233
                    is_overlay=True, pocket=pocket, component=component,
13168.12.16 by Raphael Badin
Use 0-based ordering for parent's order.
234
                    ordering=count)
13168.12.2 by Raphael Badin
Refactor ISD.
235
            else:
236
                dsp_set.new(
237
                    self.distroseries, parent, initialized=False,
13168.12.16 by Raphael Badin
Use 0-based ordering for parent's order.
238
                    is_overlay=False, ordering=count)
13168.12.5 by Raphael Badin
Fix IDS._set_parents.
239
            count += 1
13168.12.2 by Raphael Badin
Refactor ISD.
240
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
241
    def _set_initialized(self):
13168.12.2 by Raphael Badin
Refactor ISD.
242
        dsp_set = getUtility(IDistroSeriesParentSet)
243
        distroseriesparents = dsp_set.getByDerivedSeries(
244
            self.distroseries)
245
        for distroseriesparent in distroseriesparents:
246
            distroseriesparent.initialized = True
247
13457.4.1 by Raphael Badin
Add the creation of DSDs to post-first series initialization.
248
    def _has_same_parents_as_previous_series(self):
13457.4.2 by Raphael Badin
Typo.
249
        # Does this distroseries have the same parents as its previous
13457.4.1 by Raphael Badin
Add the creation of DSDs to post-first series initialization.
250
        # series? (note that the parent's order does not matter here)
251
        dsp_set = getUtility(IDistroSeriesParentSet)
252
        previous_series_parents = [
253
            dsp.parent_series for dsp in dsp_set.getByDerivedSeries(
254
                self.distroseries.previous_series)]
255
        return set(previous_series_parents) == set(self.parents)
256
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
257
    def _create_dsds(self):
13457.4.1 by Raphael Badin
Add the creation of DSDs to post-first series initialization.
258
        if not self.first_derivation:
259
            if (self._has_same_parents_as_previous_series() and
260
                not self.packagesets):
261
                # If the parents are the same as previous_series's
262
                # parents and all the packagesets are being copied,
263
                # then we simply copy the DSDs from previous_series
264
                # for performance reasons.
265
                self._copy_dsds_from_previous_series()
266
            else:
267
                # Either the parents have changed (compared to
13457.4.3 by Raphael Badin
Intermediate step.
268
                # previous_series's parents) or a selection only of the
13457.4.1 by Raphael Badin
Add the creation of DSDs to post-first series initialization.
269
                # packagesets is being copied so we have to recompute
13457.4.3 by Raphael Badin
Intermediate step.
270
                # the DSDs by creating DSD Jobs.
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
271
                self._create_dsd_jobs()
13457.4.3 by Raphael Badin
Intermediate step.
272
        else:
273
            # If this is the first derivation, create the DSD Jobs.
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
274
            self._create_dsd_jobs()
13457.4.1 by Raphael Badin
Add the creation of DSDs to post-first series initialization.
275
276
    def _copy_dsds_from_previous_series(self):
277
        self._store.execute("""
278
            INSERT INTO DistroSeriesDifference
279
                (derived_series, source_package_name, package_diff,
280
                status, difference_type, parent_package_diff,
281
                source_version, parent_source_version,
282
                base_version, parent_series)
283
            SELECT
284
                %s AS derived_series, source_package_name,
285
                package_diff, status,
286
                difference_type, parent_package_diff, source_version,
287
                parent_source_version, base_version, parent_series
288
            FROM DistroSeriesDifference AS dsd
289
                WHERE dsd.derived_series = %s
290
            """ % sqlvalues(
291
                self.distroseries.id,
292
                self.distroseries.previous_series.id))
293
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
294
    def _create_dsd_jobs(self):
13457.4.3 by Raphael Badin
Intermediate step.
295
        job_source = getUtility(IDistroSeriesDifferenceJobSource)
13457.4.1 by Raphael Badin
Add the creation of DSDs to post-first series initialization.
296
        for parent in self.parents:
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
297
            job_source.massCreateForSeries(self.distroseries, parent)
13457.4.1 by Raphael Badin
Add the creation of DSDs to post-first series initialization.
298
13168.12.2 by Raphael Badin
Refactor ISD.
299
    def _copy_configuration(self):
300
        self.distroseries.backports_not_automatic = any(
13168.13.4 by Raphael Badin
Merge init-series-bug-789091-devel.
301
            parent.backports_not_automatic
302
                for parent in self.derivation_parents)
13168.12.2 by Raphael Badin
Refactor ISD.
303
304
    def _copy_architectures(self):
13168.12.13 by Raphael Badin
Fix style. Do not duplicate conflicting packagings.
305
        das_filter = ' AND distroseries IN %s ' % (
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
306
                sqlvalues([p.id for p in self.derivation_parents]))
11393.1.1 by Steve Kowalik
* Limit the number of arches we copy when so instructed.
307
        if self.arches:
13168.12.13 by Raphael Badin
Fix style. Do not duplicate conflicting packagings.
308
            das_filter += ' AND architecturetag IN %s ' % (
7675.1201.5 by Raphael Badin
Init multiple parents.
309
                sqlvalues(self.arches))
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
310
        self._store.execute("""
311
            INSERT INTO DistroArchSeries
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
312
            (distroseries, processorfamily, architecturetag, owner, official,
313
             supports_virtualized)
13168.12.2 by Raphael Badin
Refactor ISD.
314
            SELECT %s, processorfamily, architecturetag, %s,
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
315
                bool_and(official), bool_or(supports_virtualized)
13168.12.2 by Raphael Badin
Refactor ISD.
316
            FROM DistroArchSeries WHERE enabled = TRUE %s
317
            GROUP BY processorfamily, architecturetag
318
            """ % (sqlvalues(self.distroseries, self.distroseries.owner)
13168.12.13 by Raphael Badin
Fix style. Do not duplicate conflicting packagings.
319
            + (das_filter, )))
12771.3.1 by Steve Kowalik
Drop initialise_distro_series, the generic run_jobs can deal with it.
320
        self._store.flush()
13168.12.5 by Raphael Badin
Fix IDS._set_parents.
321
        # Take nominatedarchindep from the first parent.
13168.12.2 by Raphael Badin
Refactor ISD.
322
        self.distroseries.nominatedarchindep = self.distroseries[
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
323
            self.derivation_parents[0].nominatedarchindep.architecturetag]
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
324
13168.12.2 by Raphael Badin
Refactor ISD.
325
    def _copy_packages(self):
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
326
        # Perform the copies
13168.12.2 by Raphael Badin
Refactor ISD.
327
        self._copy_component_section_and_format_selections()
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
328
13168.12.2 by Raphael Badin
Refactor ISD.
329
        # Prepare the lists of distroarchseries for which binary packages
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
330
        # shall be copied.
13168.12.2 by Raphael Badin
Refactor ISD.
331
        distroarchseries_lists = {}
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
332
        for parent in self.derivation_parents:
13168.12.2 by Raphael Badin
Refactor ISD.
333
            distroarchseries_lists[parent] = []
334
            for arch in self.distroseries.architectures:
335
                if self.arches and (arch.architecturetag not in self.arches):
336
                    continue
337
                try:
13168.12.13 by Raphael Badin
Fix style. Do not duplicate conflicting packagings.
338
                    parent_arch = parent.getDistroArchSeries(
339
                        arch.architecturetag)
13168.12.2 by Raphael Badin
Refactor ISD.
340
                except NotFoundError:
341
                    continue
7675.1201.5 by Raphael Badin
Init multiple parents.
342
13168.12.2 by Raphael Badin
Refactor ISD.
343
                distroarchseries_lists[parent].append((parent_arch, arch))
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
344
        # Now copy source and binary packages.
13168.12.2 by Raphael Badin
Refactor ISD.
345
        self._copy_publishing_records(distroarchseries_lists)
346
        self._copy_packaging_links()
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
347
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
348
    def _use_cloner(self, target_archive, archive):
13168.12.37 by Raphael Badin
Test copying strategy.
349
        """Returns True if it's safe to use the packagecloner (as opposed
350
        to using the packagecopier).
351
        We use two different ways to copy packages:
352
         - the packagecloner: fast but not conflict safe.
353
         - the packagecopier: slow but performs lots of checks to
354
         avoid creating conflicts.
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
355
        1. We'll use the cloner:
356
        If this is not a first initialization.
357
        And If:
358
            1.a If the archives are different and the target archive is
359
                empty use the cloner.
360
            Or
361
            1.b. If the archives are the same and the target series is
362
                empty use the cloner.
13168.12.37 by Raphael Badin
Test copying strategy.
363
        2.  Otherwise use the copier.
364
        """
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
365
        if self.first_derivation:
366
            return False
367
13168.12.37 by Raphael Badin
Test copying strategy.
368
        target_archive_empty = target_archive.getPublishedSources().is_empty()
369
        case_1a = (target_archive != archive and
370
                   target_archive_empty)
371
        case_1b = (target_archive == archive and
372
                   (target_archive_empty or
373
                    target_archive.getPublishedSources(
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
374
                        distroseries=self.distroseries).is_empty()))
13168.12.37 by Raphael Badin
Test copying strategy.
375
        return case_1a or case_1b
376
13168.12.2 by Raphael Badin
Refactor ISD.
377
    def _copy_publishing_records(self, distroarchseries_lists):
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
378
        """Copy the publishing records from the parent arch series
379
        to the given arch series in ourselves.
380
381
        We copy all PENDING and PUBLISHED records as PENDING into our own
382
        publishing records.
383
11258.1.4 by Steve Kowalik
Correct docstring, and copy debug archives too
384
        We copy only the RELEASE pocket in the PRIMARY and DEBUG archives.
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
385
        """
386
        archive_set = getUtility(IArchiveSet)
387
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
388
        for parent in self.derivation_parents:
13371.1.1 by Raphael Badin
Fix initialize_distroseries to copy packagesets' contents from each parent.
389
            spns = []
390
            # The overhead from looking up each packageset is mitigated by
391
            # this usually running from a job.
392
            if self.packagesets:
393
                for pkgsetid in self.packagesets:
394
                    pkgset = self._store.get(Packageset, int(pkgsetid))
395
                    if pkgset.distroseries == parent:
396
                        spns += list(pkgset.getSourcesIncluded())
397
398
                # Some packagesets where selected but not a single
399
                # source from this parent: we skip the copy since
13371.1.3 by Raphael Badin
Make test more readable.
400
                # calling copy with spns=[] would copy all the packagesets
401
                # from this parent.
402
                if len(spns) == 0:
13371.1.1 by Raphael Badin
Fix initialize_distroseries to copy packagesets' contents from each parent.
403
                    continue
404
13168.12.2 by Raphael Badin
Refactor ISD.
405
            distroarchseries_list = distroarchseries_lists[parent]
406
            for archive in parent.distribution.all_distro_archives:
407
                if archive.purpose not in (
408
                    ArchivePurpose.PRIMARY, ArchivePurpose.DEBUG):
409
                    continue
410
411
                target_archive = archive_set.getByDistroPurpose(
412
                    self.distroseries.distribution, archive.purpose)
413
                if archive.purpose is ArchivePurpose.PRIMARY:
414
                    assert target_archive is not None, (
415
                        "Target archive doesn't exist?")
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
416
                if self._use_cloner(target_archive, archive):
13168.12.32 by Raphael Badin
Apply MP's comments.
417
                    origin = PackageLocation(
418
                        archive, parent.distribution, parent,
419
                        PackagePublishingPocket.RELEASE)
420
                    destination = PackageLocation(
421
                        target_archive, self.distroseries.distribution,
422
                        self.distroseries, PackagePublishingPocket.RELEASE)
423
                    proc_families = None
424
                    if self.rebuild:
13168.12.29 by Raphael Badin
Do not use the packagecloner.createMissingBuilds.
425
                        proc_families = [
13168.12.32 by Raphael Badin
Apply MP's comments.
426
                            das[1].processorfamily
427
                            for das in distroarchseries_list]
428
                        distroarchseries_list = ()
429
                    getUtility(IPackageCloner).clonePackages(
430
                        origin, destination, distroarchseries_list,
431
                        proc_families, spns, self.rebuild)
432
                else:
433
                    # There is only one available pocket in an unreleased
434
                    # series.
13457.5.2 by Raphael Badin
The package copier method in initialize distroseries should take packages from RELEASE, UPDATES and SECURITY.
435
                    target_pocket = PackagePublishingPocket.RELEASE
436
                    pockets_to_copy = (
437
                        PackagePublishingPocket.RELEASE,
438
                        PackagePublishingPocket.UPDATES,
439
                        PackagePublishingPocket.SECURITY)
13168.12.32 by Raphael Badin
Apply MP's comments.
440
                    sources = archive.getPublishedSources(
13457.5.2 by Raphael Badin
The package copier method in initialize distroseries should take packages from RELEASE, UPDATES and SECURITY.
441
                        distroseries=parent, pocket=pockets_to_copy,
442
                        name=spns)
13168.12.32 by Raphael Badin
Apply MP's comments.
443
                    # XXX: rvb 2011-06-23 bug=801112: do_copy is atomic (all
444
                    # or none of the sources will be copied). This might
445
                    # lead to a partially initialised series if there is a
446
                    # single conflict in the destination series.
447
                    try:
13168.12.33 by Raphael Badin
Raise InitializationError if do_copy raises CannotCopy.
448
                        sources_published = do_copy(
13168.12.32 by Raphael Badin
Apply MP's comments.
449
                            sources, target_archive, self.distroseries,
13457.5.2 by Raphael Badin
The package copier method in initialize distroseries should take packages from RELEASE, UPDATES and SECURITY.
450
                            target_pocket, include_binaries=not self.rebuild,
13403.2.1 by Raphael Badin
Add a parameter to control the creation of dsd jobs.
451
                            check_permissions=False, strict_binaries=False,
13457.4.4 by Raphael Badin
Create DSDJs outside the packages copy loop.
452
                            close_bugs=False, create_dsd_job=False)
13168.12.33 by Raphael Badin
Raise InitializationError if do_copy raises CannotCopy.
453
                        if self.rebuild:
13168.12.29 by Raphael Badin
Do not use the packagecloner.createMissingBuilds.
454
                            for pubrec in sources_published:
13457.4.10 by Raphael Badin
Use the package copier for first initializations.
455
                                pubrec.createMissingBuilds(
456
                                   list(self.distroseries.architectures))
13168.12.33 by Raphael Badin
Raise InitializationError if do_copy raises CannotCopy.
457
                    except CannotCopy, error:
458
                        raise InitializationError(error)
13168.12.2 by Raphael Badin
Refactor ISD.
459
460
    def _copy_component_section_and_format_selections(self):
461
        """Copy the section, component and format selections from the parents
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
462
        distro series into this one.
463
        """
464
        # Copy the component selections
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
465
        self._store.execute('''
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
466
            INSERT INTO ComponentSelection (distroseries, component)
13168.12.2 by Raphael Badin
Refactor ISD.
467
            SELECT DISTINCT %s AS distroseries, cs.component AS component
468
            FROM ComponentSelection AS cs WHERE cs.distroseries IN %s
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
469
            ''' % sqlvalues(self.distroseries.id,
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
470
            self.derivation_parent_ids))
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
471
        # Copy the section selections
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
472
        self._store.execute('''
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
473
            INSERT INTO SectionSelection (distroseries, section)
13168.12.2 by Raphael Badin
Refactor ISD.
474
            SELECT DISTINCT %s as distroseries, ss.section AS section
475
            FROM SectionSelection AS ss WHERE ss.distroseries IN %s
11258.1.3 by Steve Kowalik
* Clean up the test to reflect that the script will now error and then exit,
476
            ''' % sqlvalues(self.distroseries.id,
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
477
            self.derivation_parent_ids))
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
478
        # Copy the source format selections
13168.12.2 by Raphael Badin
Refactor ISD.
479
        self._store.execute('''
480
            INSERT INTO SourcePackageFormatSelection (distroseries, format)
481
            SELECT DISTINCT %s as distroseries, spfs.format AS format
482
            FROM SourcePackageFormatSelection AS spfs
483
            WHERE spfs.distroseries IN %s
484
            ''' % sqlvalues(self.distroseries.id,
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
485
            self.derivation_parent_ids))
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
486
13168.12.2 by Raphael Badin
Refactor ISD.
487
    def _copy_packaging_links(self):
11258.1.1 by Steve Kowalik
First shot of moving IDistroSeries.initialiseFromParent out into it's own
488
        """Copy the packaging links from the parent series to this one."""
13168.12.13 by Raphael Badin
Fix style. Do not duplicate conflicting packagings.
489
        # We iterate over the parents and copy into the child in
490
        # sequence to avoid creating duplicates.
13168.13.4 by Raphael Badin
Merge init-series-bug-789091-devel.
491
        for parent_id in self.derivation_parent_ids:
13168.12.13 by Raphael Badin
Fix style. Do not duplicate conflicting packagings.
492
            self._store.execute("""
493
                INSERT INTO
494
                    Packaging(
495
                        distroseries, sourcepackagename, productseries,
496
                        packaging, owner)
497
                SELECT
498
                    ChildSeries.id,
499
                    Packaging.sourcepackagename,
500
                    Packaging.productseries,
501
                    Packaging.packaging,
502
                    Packaging.owner
503
                FROM
504
                    Packaging
505
                    -- Joining the parent distroseries permits the query to
506
                    -- build the data set for the series being updated, yet
507
                    -- results are in fact the data from the original series.
508
                    JOIN Distroseries ChildSeries
509
                        ON Packaging.distroseries = %s
510
                WHERE
511
                    -- Select only the packaging links that are in the parent
512
                    -- that are not in the child.
513
                    ChildSeries.id = %s
514
                    AND Packaging.sourcepackagename in (
515
                        SELECT sourcepackagename
516
                        FROM Packaging
517
                        WHERE distroseries in (
518
                            SELECT id
519
                            FROM Distroseries
520
                            WHERE id = %s
521
                            )
522
                        EXCEPT
523
                        SELECT sourcepackagename
524
                        FROM Packaging
525
                        WHERE distroseries in (
526
                            SELECT id
527
                            FROM Distroseries
528
                            WHERE id = ChildSeries.id
529
                            )
530
                        )
531
                """ % sqlvalues(
532
                    parent_id, self.distroseries.id, parent_id))
11316.8.2 by Steve Kowalik
* Allow lucille to INSERT into packageset
533
13168.12.2 by Raphael Badin
Refactor ISD.
534
    def _copy_packagesets(self):
11316.8.2 by Steve Kowalik
* Allow lucille to INSERT into packageset
535
        """Copy packagesets from the parent distroseries."""
13168.12.2 by Raphael Badin
Refactor ISD.
536
        # Avoid circular imports.
537
        from lp.registry.model.distroseries import DistroSeries
538
539
        packagesets = self._store.find(
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
540
            Packageset, DistroSeries.id.is_in(self.derivation_parent_ids))
11411.7.24 by j.c.sackett
Merged from devel.
541
        parent_to_child = {}
13383.1.1 by Raphael Badin
Copy archive permission if we're not copying cross-distribution.
542
        # Create the packagesets and any archivepermissions if we're not
543
        # copying cross-distribution.
13168.12.3 by Raphael Badin
Merge identical packagesets from the parents into the child.
544
        parent_distro_ids = [
13168.13.1 by Raphael Badin
Support initializing from an empty parent list (Ubuntu use-case).
545
            parent.distribution.id for parent in self.derivation_parents]
11411.7.24 by j.c.sackett
Merged from devel.
546
        for parent_ps in packagesets:
13194.2.1 by Gavin Panella
Change all uses of 'initialise' to 'initialize'.
547
            # Cross-distro initializations get packagesets owned by the
12685.4.1 by William Grant
Preserve packageset ownership when initialising a new series of a distribution. It's set to the new distro owner when initialising a new distro.
548
            # distro owner, otherwise the old owner is preserved.
13117.2.6 by Raphael Badin
Parent and packageset are passed by id.
549
            if self.packagesets and str(parent_ps.id) not in self.packagesets:
11566.1.1 by Steve Kowalik
Add limited support for copying only specified packagesets
550
                continue
13168.12.3 by Raphael Badin
Merge identical packagesets from the parents into the child.
551
            packageset_set = getUtility(IPackagesetSet)
552
            # First, try to fetch an existing packageset with this name.
553
            try:
554
                child_ps = packageset_set.getByName(
555
                    parent_ps.name, self.distroseries)
556
            except NoSuchPackageSet:
557
                if self.distroseries.distribution.id in parent_distro_ids:
558
                    new_owner = parent_ps.owner
559
                else:
560
                    new_owner = self.distroseries.owner
561
                child_ps = getUtility(IPackagesetSet).new(
562
                    parent_ps.name, parent_ps.description,
563
                    new_owner, distroseries=self.distroseries,
564
                    related_set=parent_ps)
11411.7.24 by j.c.sackett
Merged from devel.
565
            parent_to_child[parent_ps] = child_ps
13383.1.1 by Raphael Badin
Copy archive permission if we're not copying cross-distribution.
566
            # Copy archivepermissions if we're not copying
567
            # cross-distribution.
568
            if (self.distroseries.distribution ==
569
                    parent_ps.distroseries.distribution):
570
                self._store.execute("""
571
                    INSERT INTO Archivepermission
572
                    (person, permission, archive, packageset, explicit)
573
                    SELECT person, permission, %s, %s, explicit
574
                    FROM Archivepermission WHERE packageset = %s
575
                    """ % sqlvalues(
576
                        self.distroseries.main_archive, child_ps.id,
577
                        parent_ps.id))
13168.12.3 by Raphael Badin
Merge identical packagesets from the parents into the child.
578
        # Copy the relations between sets, and the contents.
11411.7.24 by j.c.sackett
Merged from devel.
579
        for old_series_ps, new_series_ps in parent_to_child.items():
580
            old_series_sets = old_series_ps.setsIncluded(
581
                direct_inclusion=True)
582
            for old_series_child in old_series_sets:
583
                new_series_ps.add(parent_to_child[old_series_child])
584
            new_series_ps.add(old_series_ps.sourcesIncluded(
585
                direct_inclusion=True))