~laney/ubuntu-archive-tools/cm-show-fix-released

516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
1
#! /usr/bin/python
2
3
# Copyright (C) 2009, 2010, 2011, 2012 Canonical Ltd.
4
5
# This program is free software: you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; version 3 of the License.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17
"""Apply suitable overrides to new kernel binaries, matching previous ones."""
18
19
from __future__ import print_function
20
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
21
import atexit
555 by Colin Watson
Simplify using collections.defaultdict.
22
from collections import defaultdict
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
23
from contextlib import closing
1011 by Colin Watson
auto-sync, kernel-overrides, orphaned-sources: stop relying on {Packages,Sources}.bz2; use .gz for now because I'm lazy
24
import gzip
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
25
from optparse import OptionParser, Values
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
26
import os
27
import shutil
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
28
import sys
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
29
import tempfile
30
try:
31
    from urllib.request import urlopen
32
except ImportError:
33
    from urllib2 import urlopen
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
34
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
35
import apt_pkg
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
36
from launchpadlib.launchpad import Launchpad
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
37
from lazr.restfulclient.errors import ServerError
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
38
from ubuntutools.question import YesNoQuestion
39
40
import lputils
41
42
43
CONSUMER_KEY = "kernel-overrides"
44
45
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
46
tempdir = None
47
48
49
def ensure_tempdir():
50
    global tempdir
51
    if not tempdir:
52
        tempdir = tempfile.mkdtemp(prefix='kernel-overrides')
53
        atexit.register(shutil.rmtree, tempdir)
54
55
56
class FakeBPPH:
57
    def __init__(self, pkg, component, das):
58
        self.binary_package_name = pkg
59
        self.component_name = component
60
        self.distro_arch_series = das
61
62
63
def get_published_binaries(options, source):
64
    """If getPublishedBinaries times out, fall back to doing it by hand."""
65
    try:
66
        for binary in source.getPublishedBinaries():
719.1.1 by William Grant
Teach overrides and deletions to skip debug publications. They follow the corresponding non-debug publication and cannot be modified independently.
67
            if not binary.is_debug:
68
                yield binary
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
69
    except ServerError as e:
70
        if e.response.status != 503:
71
            raise
72
        print("getPublishedBinaries timed out; fetching Packages instead ...")
73
        ensure_tempdir()
74
        for section_name in ("", "debian-installer"):
75
            for component in ("main", "restricted", "universe", "multiverse"):
76
                for das in options.old.series.architectures:
77
                    arch = das.architecture_tag
78
                    if arch in ("amd64", "i386"):
79
                        base = "http://archive.ubuntu.com/ubuntu"
80
                    else:
81
                        base = "http://ports.ubuntu.com/ubuntu-ports"
1011 by Colin Watson
auto-sync, kernel-overrides, orphaned-sources: stop relying on {Packages,Sources}.bz2; use .gz for now because I'm lazy
82
                    url = ("%s/dists/%s/%s%s/binary-%s/Packages.gz" %
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
83
                           (base, options.old.suite, component,
84
                            "/%s" % section_name if section_name else "",
85
                            arch))
86
                    path = os.path.join(
87
                        tempdir, "Ubuntu_%s_%s%s_Packages_%s" %
88
                        (options.old.suite, component,
89
                         "_%s" % section_name if section_name else "", arch))
90
                    with closing(urlopen(url)) as url_file:
1011 by Colin Watson
auto-sync, kernel-overrides, orphaned-sources: stop relying on {Packages,Sources}.bz2; use .gz for now because I'm lazy
91
                        with open("%s.gz" % path, "wb") as comp_file:
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
92
                            comp_file.write(url_file.read())
1011 by Colin Watson
auto-sync, kernel-overrides, orphaned-sources: stop relying on {Packages,Sources}.bz2; use .gz for now because I'm lazy
93
                    with closing(gzip.GzipFile("%s.gz" % path)) as gz_file:
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
94
                        with open(path, "wb") as out_file:
1011 by Colin Watson
auto-sync, kernel-overrides, orphaned-sources: stop relying on {Packages,Sources}.bz2; use .gz for now because I'm lazy
95
                            out_file.write(gz_file.read())
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
96
                    with open(path) as packages_file:
97
                        apt_packages = apt_pkg.TagFile(packages_file)
98
                        for section in apt_packages:
99
                            pkg = section["Package"]
928 by Colin Watson
component-mismatches, kernel-overrides: strip any version from Source fields
100
                            src = section.get("Source", pkg).split(" ", 1)[0]
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
101
                            if src != options.source:
102
                                continue
103
                            yield FakeBPPH(pkg, component, das)
104
105
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
106
def find_current_binaries(options):
107
    print("Checking existing binaries in %s ..." % options.old.suite,
108
          file=sys.stderr)
109
    sources = options.old.archive.getPublishedSources(
110
        source_name=options.source, distro_series=options.old.series,
111
        pocket=options.old.pocket, exact_match=True, status="Published")
112
    for source in sources:
555 by Colin Watson
Simplify using collections.defaultdict.
113
        binaries = defaultdict(dict)
662 by Colin Watson
kernel-overrides: fall back to fetching Packages if SPPH.getPublishedBinaries() times out
114
        for binary in get_published_binaries(options, source):
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
115
            print(".", end="")
116
            sys.stdout.flush()
117
            arch = binary.distro_arch_series.architecture_tag
118
            name = binary.binary_package_name
119
            component = binary.component_name
120
            if name not in binaries[arch]:
121
                binaries[arch][name] = component
556 by Colin Watson
Rely on default iterator for mappings iterating over keys.
122
        if binaries:
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
123
            print()
124
            return binaries
125
    print()
621 by Colin Watson
kernel-overrides: fix crash if there are no current binary publications
126
    return []
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
127
128
129
def find_matching_uploads(options, newabi):
130
    print("Checking %s uploads to %s ..." %
131
          (options.queue.lower(), options.suite), file=sys.stderr)
132
    uploads = options.series.getPackageUploads(
680 by Colin Watson
kernel-overrides: simplify find_matching_uploads now that LP #33700 is fixed
133
        name=options.source, exact_match=True, archive=options.archive,
578 by Colin Watson
kernel-overrides, new-binary-debian-universe: pass archive to DistroSeries.getPackageUploads
134
        pocket=options.pocket, status=options.queue)
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
135
    for upload in uploads:
136
        if upload.contains_build:
137
            # display_name is inaccurate for the theoretical case of an
138
            # upload containing multiple builds, but in practice it's close
139
            # enough.
140
            source = upload.display_name.split(",")[0]
141
            if source == options.source:
142
                binaries = upload.getBinaryProperties()
143
                binaries = [b for b in binaries if "customformat" not in b]
144
                if [b for b in binaries if newabi in b["version"]]:
145
                    yield upload, binaries
146
147
148
def equal_except_abi(old, new, abi):
149
    """Are OLD and NEW the same package name aside from ABI?"""
150
    # Make sure new always contains the ABI.
151
    if abi in old:
152
        old, new = new, old
153
    if abi not in new:
154
        return False
155
156
    left, _, right = new.partition(abi)
157
    if not old.startswith(left) or not old.endswith(right):
158
        return False
159
    old_abi = old[len(left):]
160
    if right:
161
        old_abi = old_abi[:-len(right)]
162
    return old_abi[0].isdigit() and old_abi[-1].isdigit()
163
164
165
def apply_kernel_overrides(options, newabi):
166
    current_binaries = find_current_binaries(options)
167
    all_changes = []
168
169
    for upload, binaries in find_matching_uploads(options, newabi):
170
        print("%s/%s (%s):" %
171
              (upload.package_name, upload.package_version,
172
               upload.display_arches.split(",")[0]))
173
        changes = []
174
        for binary in binaries:
175
            if binary["architecture"] not in current_binaries:
176
                continue
177
            current_binaries_arch = current_binaries[binary["architecture"]]
178
            for name, component in current_binaries_arch.items():
179
                if (binary["component"] != component and
677 by Colin Watson
make all scripts pass current stricter pep8(1) in raring
180
                        equal_except_abi(name, binary["name"], newabi)):
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
181
                    print("\t%s: %s -> %s" %
182
                          (binary["name"], binary["component"], component))
183
                    changes.append(
184
                        {"name": binary["name"], "component": component})
185
        if changes:
186
            all_changes.append((upload, changes))
187
188
    if all_changes:
189
        if options.dry_run:
190
            print("Dry run; no changes made.")
191
        else:
192
            if not options.confirm_all:
620 by Colin Watson
kernel-overrides: fix --confirm-all handling
193
                if YesNoQuestion().ask("Override", "no") == "no":
194
                    return
195
            for upload, changes in all_changes:
196
                upload.overrideBinaries(changes=changes)
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
197
198
199
def main():
200
    parser = OptionParser(usage="usage: %prog [options] NEW-ABI")
201
    parser.add_option(
202
        "-l", "--launchpad", dest="launchpad_instance", default="production")
203
    parser.add_option(
204
        "-d", "--distribution", metavar="DISTRO", default="ubuntu",
205
        help="look in distribution DISTRO")
206
    parser.add_option(
591 by Colin Watson
kernel-overrides: improve --suite/--old-suite defaults so that the standard case of development kernel uploads can be handled without options
207
        "-S", "--suite", metavar="SUITE",
208
        help="look in suite SUITE (default: <current series>-proposed)")
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
209
    parser.add_option(
210
        "--old-suite", metavar="SUITE",
591 by Colin Watson
kernel-overrides: improve --suite/--old-suite defaults so that the standard case of development kernel uploads can be handled without options
211
        help="look for previous binaries in suite SUITE "
212
             "(default: value of --suite without -proposed)")
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
213
    parser.add_option(
214
        "-s", "--source", metavar="SOURCE", default="linux",
215
        help="operate on source package SOURCE")
216
    parser.add_option(
217
        "-Q", "--queue", metavar="QUEUE", default="new",
218
        help="consider packages in QUEUE")
219
    parser.add_option(
220
        "-n", "--dry-run", default=False, action="store_true",
221
        help="don't make any modifications")
222
    parser.add_option(
223
        "-y", "--confirm-all", default=False, action="store_true",
224
        help="do not ask for confirmation")
225
    options, args = parser.parse_args()
226
    if len(args) != 1:
227
        parser.error("must supply NEW-ABI")
228
    newabi = args[0]
229
230
    options.launchpad = Launchpad.login_with(
231
        CONSUMER_KEY, options.launchpad_instance, version="devel")
232
591 by Colin Watson
kernel-overrides: improve --suite/--old-suite defaults so that the standard case of development kernel uploads can be handled without options
233
    if options.suite is None:
234
        distribution = options.launchpad.distributions[options.distribution]
235
        options.suite = "%s-proposed" % distribution.current_series.name
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
236
    if options.old_suite is None:
237
        options.old_suite = options.suite
591 by Colin Watson
kernel-overrides: improve --suite/--old-suite defaults so that the standard case of development kernel uploads can be handled without options
238
        if options.old_suite.endswith("-proposed"):
239
            options.old_suite = options.old_suite[:-9]
516 by Colin Watson
kernel-overrides: new script, based on a local script on ftpmaster but converted to use the API
240
    options.queue = options.queue.title()
241
    options.version = None
242
    lputils.setup_location(options)
243
    options.old = Values()
244
    options.old.launchpad = options.launchpad
245
    options.old.distribution = options.distribution
246
    options.old.suite = options.old_suite
247
    lputils.setup_location(options.old)
248
249
    apply_kernel_overrides(options, newabi)
250
251
252
if __name__ == '__main__':
253
    main()