check_overrides.py 19.2 KB
Newer Older
J
sync  
James Troup 已提交
1 2
#!/usr/bin/env python

3 4 5 6 7 8 9 10 11
""" Cruft checker and hole filler for overrides

@contact: Debian FTPMaster <ftpmaster@debian.org>
@copyright: 2000, 2001, 2002, 2004, 2006  James Troup <james@nocrew.org>
@opyright: 2005  Jeroen van Wolffelaar <jeroen@wolffelaar.nl>
@copyright: 2011  Joerg Jaspert <joerg@debian.org>
@license: GNU General Public License version 2 or later

"""
J
sync  
James Troup 已提交
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

27
################################################################################
J
sync  
James Troup 已提交
28

J
sync  
James Troup 已提交
29
######################################################################
30 31 32 33 34 35
# NB: dak check-overrides is not a good idea with New Incoming as it #
# doesn't take into account accepted.  You can minimize the impact   #
# of this by running it immediately after dak process-accepted but   #
# that's still racy because 'dak process-new' doesn't lock with 'dak #
# process-accepted'.  A better long term fix is the evil plan for    #
# accepted to be in the DB.                                          #
J
sync  
James Troup 已提交
36
######################################################################
J
sync  
James Troup 已提交
37

38 39 40 41 42
# dak check-overrides should now work fine being done during
# cron.daily, for example just before 'dak make-overrides' (after 'dak
# process-accepted' and 'dak make-suite-file-list'). At that point,
# queue/accepted should be empty and installed, so... dak
# check-overrides does now take into account suites sharing overrides
43 44 45 46 47 48 49 50 51 52 53

# TODO:
# * Only update out-of-sync overrides when corresponding versions are equal to
#   some degree
# * consistency checks like:
#   - section=debian-installer only for udeb and # dsc
#   - priority=source iff dsc
#   - (suite, package, 'dsc') is unique,
#   - just as (suite, package, (u)deb) (yes, across components!)
#   - sections match their component (each component has an own set of sections,
#     could probably be reduced...)
J
sync  
James Troup 已提交
54

J
sync  
James Troup 已提交
55 56
################################################################################

B
Bastian Blank 已提交
57 58
from __future__ import print_function

59
import sys
60
import apt_pkg
61 62 63

from daklib.config import Config
from daklib.dbconn import *
64
from daklib import daklog
65
from daklib import utils
J
sync  
James Troup 已提交
66 67 68

################################################################################

69 70
Options = None                 #: Commandline arguments parsed into this
Logger = None                  #: Our logging object
71 72 73
sections = {}
priorities = {}
blacklist = {}
J
sync  
James Troup 已提交
74 75 76

################################################################################

77

B
Bastian Blank 已提交
78
def usage(exit_code=0):
B
Bastian Blank 已提交
79
    print("""Usage: dak check-overrides
J
James Troup 已提交
80 81
Check for cruft in overrides.

82
  -n, --no-action            don't do anything
B
Bastian Blank 已提交
83
  -h, --help                 show this help and exit""")
J
James Troup 已提交
84 85 86 87 88

    sys.exit(exit_code)

################################################################################

89

90 91
def process(osuite, affected_suites, originosuite, component, otype, session):
    global Logger, Options, sections, priorities
92

93 94
    o = get_suite(osuite, session)
    if o is None:
M
Mark Hymers 已提交
95
        utils.fubar("Suite '%s' not recognised." % (osuite))
96 97
    osuite_id = o.suite_id

98 99
    originosuite_id = None
    if originosuite:
100 101
        oo = get_suite(originosuite, session)
        if oo is None:
M
Mark Hymers 已提交
102
            utils.fubar("Suite '%s' not recognised." % (originosuite))
103
        originosuite_id = oo.suite_id
J
sync  
James Troup 已提交
104

105 106
    c = get_component(component, session)
    if c is None:
M
Mark Hymers 已提交
107
        utils.fubar("Component '%s' not recognised." % (component))
108
    component_id = c.component_id
J
sync  
James Troup 已提交
109

110 111 112 113 114
    ot = get_override_type(otype, session)
    if ot is None:
        utils.fubar("Type '%s' not recognised. (Valid types are deb, udeb and dsc)" % (otype))
    type_id = ot.overridetype_id
    dsc_type_id = get_override_type("dsc", session).overridetype_id
115

116
    source_priority_id = get_priority("source", session).priority_id
J
sync  
James Troup 已提交
117

118
    if otype == "deb" or otype == "udeb":
119
        packages = {}
120 121
        # TODO: Fix to use placeholders (check how to with arrays)
        q = session.execute("""
122 123 124 125 126 127
SELECT b.package
  FROM binaries b
  JOIN bin_associations ba ON b.id = ba.bin
  JOIN suite ON ba.suite = suite.id
  JOIN files_archive_map af ON b.file = af.file_id AND suite.archive_id = af.archive_id
 WHERE b.type = :otype AND ba.suite IN (%s) AND af.component_id = :component_id
B
Bastian Blank 已提交
128
""" % (",".join([str(i) for i in affected_suites])), {'otype': otype, 'component_id': component_id})
129
        for i in q.fetchall():
130
            packages[i[0]] = 0
J
sync  
James Troup 已提交
131

132
    src_packages = {}
133
    q = session.execute("""
134 135 136 137 138
SELECT s.source FROM source s
  JOIN src_associations sa ON s.id = sa.source
  JOIN suite ON sa.suite = suite.id
  JOIN files_archive_map af ON s.file = af.file_id AND suite.archive_id = af.archive_id
 WHERE sa.suite IN (%s) AND af.component_id = :component_id
B
Bastian Blank 已提交
139
""" % (",".join([str(i) for i in affected_suites])), {'component_id': component_id})
140
    for i in q.fetchall():
141
        src_packages[i[0]] = 0
J
sync  
James Troup 已提交
142

143 144 145
    # -----------
    # Drop unused overrides

146 147 148 149 150 151 152 153
    q = session.execute("""SELECT package, priority, section, maintainer
                             FROM override WHERE suite = :suite_id
                              AND component = :component_id AND type = :type_id""",
                        {'suite_id': osuite_id, 'component_id': component_id,
                         'type_id': type_id})
    # We're already within a transaction
    if otype == "dsc":
        for i in q.fetchall():
154
            package = i[0]
155
            if package in src_packages:
156 157
                src_packages[package] = 1
            else:
158
                if package in blacklist:
M
Mark Hymers 已提交
159
                    utils.warn("%s in incoming, not touching" % package)
160 161
                    continue
                Logger.log(["removing unused override", osuite, component,
162
                    otype, package, priorities[i[1]], sections[i[2]], i[3]])
163
                if not Options["No-Action"]:
164 165
                    session.execute("""DELETE FROM override WHERE package = :package
                                          AND suite = :suite_id AND component = :component_id
166 167
                                          AND type = :type_id
                                          AND created < now() - interval '14 days'""",
168 169
                                    {'package': package, 'suite_id': osuite_id,
                                     'component_id': component_id, 'type_id': type_id})
170 171
        # create source overrides based on binary overrides, as source
        # overrides not always get created
172 173 174 175
        q = session.execute("""SELECT package, priority, section, maintainer
                                 FROM override WHERE suite = :suite_id AND component = :component_id""",
                            {'suite_id': osuite_id, 'component_id': component_id})
        for i in q.fetchall():
176
            package = i[0]
177
            if package not in src_packages or src_packages[package]:
178 179
                continue
            src_packages[package] = 1
180

181
            Logger.log(["add missing override", osuite, component,
182
                otype, package, "source", sections[i[2]], i[3]])
183
            if not Options["No-Action"]:
184 185 186 187 188 189 190
                session.execute("""INSERT INTO override (package, suite, component,
                                                        priority, section, type, maintainer)
                                         VALUES (:package, :suite_id, :component_id,
                                                 :priority_id, :section_id, :type_id, :maintainer)""",
                               {'package': package, 'suite_id': osuite_id,
                                'component_id': component_id, 'priority_id': source_priority_id,
                                'section_id': i[2], 'type_id': dsc_type_id, 'maintainer': i[3]})
191 192 193
        # Check whether originosuite has an override for us we can
        # copy
        if originosuite:
194 195 196 197 198 199 200 201 202 203 204 205 206 207
            q = session.execute("""SELECT origin.package, origin.priority, origin.section,
                                         origin.maintainer, target.priority, target.section,
                                         target.maintainer
                                    FROM override origin
                               LEFT JOIN override target ON (origin.package = target.package
                                                             AND target.suite = :suite_id
                                                             AND origin.component = target.component
                                                             AND origin.type = target.type)
                                   WHERE origin.suite = :originsuite_id
                                     AND origin.component = :component_id
                                     AND origin.type = :type_id""",
                                {'suite_id': osuite_id, 'originsuite_id': originosuite_id,
                                 'component_id': component_id, 'type_id': type_id})
            for i in q.fetchall():
208
                package = i[0]
209
                if package not in src_packages or src_packages[package]:
210 211
                    if i[4] and (i[1] != i[4] or i[2] != i[5] or i[3] != i[6]):
                        Logger.log(["syncing override", osuite, component,
212
                            otype, package, "source", sections[i[5]], i[6], "source", sections[i[2]], i[3]])
213
                        if not Options["No-Action"]:
214
                            session.execute("""UPDATE override
215 216
                                                 SET priority = :priority,
                                                     section = :section,
217 218 219
                                                     maintainer = :maintainer
                                               WHERE package = :package AND suite = :suite_id
                                                 AND component = :component_id AND type = :type_id""",
220 221
                                            {'priority': i[1],
                                             'section': i[2], 'maintainer': i[3],
222 223
                                             'package': package, 'suite_id': osuite_id,
                                             'component_id': component_id, 'type_id': dsc_type_id})
224
                    continue
225

226 227 228
                # we can copy
                src_packages[package] = 1
                Logger.log(["copying missing override", osuite, component,
229
                    otype, package, "source", sections[i[2]], i[3]])
230
                if not Options["No-Action"]:
231 232 233 234 235 236 237 238
                    session.execute("""INSERT INTO override (package, suite, component,
                                                             priority, section, type, maintainer)
                                            VALUES (:package, :suite_id, :component_id,
                                                    :priority_id, :section_id, :type_id,
                                                    :maintainer)""",
                                    {'package': package, 'suite_id': osuite_id,
                                     'component_id': component_id, 'priority_id': source_priority_id,
                                     'section_id': i[2], 'type_id': dsc_type_id, 'maintainer': i[3]})
239 240 241

        for package, hasoverride in src_packages.items():
            if not hasoverride:
M
Mark Hymers 已提交
242
                utils.warn("%s has no override!" % package)
243 244

    else: # binary override
245
        for i in q.fetchall():
246
            package = i[0]
247
            if package in packages:
248 249
                packages[package] = 1
            else:
250
                if package in blacklist:
M
Mark Hymers 已提交
251
                    utils.warn("%s in incoming, not touching" % package)
252 253
                    continue
                Logger.log(["removing unused override", osuite, component,
254
                    otype, package, priorities[i[1]], sections[i[2]], i[3]])
255
                if not Options["No-Action"]:
256 257
                    session.execute("""DELETE FROM override
                                        WHERE package = :package AND suite = :suite_id
258 259
                                          AND component = :component_id AND type = :type_id
                                          AND created < now() - interval '14 days'""",
260 261
                                    {'package': package, 'suite_id': osuite_id,
                                     'component_id': component_id, 'type_id': type_id})
262 263 264 265

        # Check whether originosuite has an override for us we can
        # copy
        if originosuite:
266 267 268 269 270 271 272 273 274 275 276 277 278 279
            q = session.execute("""SELECT origin.package, origin.priority, origin.section,
                                          origin.maintainer, target.priority, target.section,
                                          target.maintainer
                                     FROM override origin LEFT JOIN override target
                                                          ON (origin.package = target.package
                                                              AND target.suite = :suite_id
                                                              AND origin.component = target.component
                                                              AND origin.type = target.type)
                                    WHERE origin.suite = :originsuite_id
                                      AND origin.component = :component_id
                                      AND origin.type = :type_id""",
                                 {'suite_id': osuite_id, 'originsuite_id': originosuite_id,
                                  'component_id': component_id, 'type_id': type_id})
            for i in q.fetchall():
280
                package = i[0]
281
                if package not in packages or packages[package]:
282 283
                    if i[4] and (i[1] != i[4] or i[2] != i[5] or i[3] != i[6]):
                        Logger.log(["syncing override", osuite, component,
284
                            otype, package, priorities[i[4]], sections[i[5]],
285 286
                            i[6], priorities[i[1]], sections[i[2]], i[3]])
                        if not Options["No-Action"]:
287 288 289 290 291 292 293 294 295 296 297 298
                            session.execute("""UPDATE override
                                                  SET priority = :priority_id,
                                                      section = :section_id,
                                                      maintainer = :maintainer
                                                WHERE package = :package
                                                  AND suite = :suite_id
                                                  AND component = :component_id
                                                  AND type = :type_id""",
                                            {'priority_id': i[1], 'section_id': i[2],
                                             'maintainer': i[3], 'package': package,
                                             'suite_id': osuite_id, 'component_id': component_id,
                                             'type_id': type_id})
299 300 301 302
                    continue
                # we can copy
                packages[package] = 1
                Logger.log(["copying missing override", osuite, component,
C
Chris Lamb 已提交
303
                    otype, package, priorities[i[1]], sections[i[2]], i[3]])
304
                if not Options["No-Action"]:
305 306 307 308 309 310 311
                    session.execute("""INSERT INTO override (package, suite, component,
                                                             priority, section, type, maintainer)
                                            VALUES (:package, :suite_id, :component_id,
                                                    :priority_id, :section_id, :type_id, :maintainer)""",
                                    {'package': package, 'suite_id': osuite_id,
                                     'component_id': component_id, 'priority_id': i[1],
                                     'section_id': i[2], 'type_id': type_id, 'maintainer': i[3]})
312 313 314

        for package, hasoverride in packages.items():
            if not hasoverride:
M
Mark Hymers 已提交
315
                utils.warn("%s has no override!" % package)
316

317
    session.commit()
318
    sys.stdout.flush()
319

J
sync  
James Troup 已提交
320 321 322

################################################################################

B
Bastian Blank 已提交
323
def main():
324
    global Logger, Options, sections, priorities
J
sync  
James Troup 已提交
325

326
    cnf = Config()
J
James Troup 已提交
327

328 329
    Arguments = [('h', "help", "Check-Overrides::Options::Help"),
                 ('n', "no-action", "Check-Overrides::Options::No-Action")]
B
Bastian Blank 已提交
330
    for i in ["help", "no-action"]:
331 332 333
        key = "Check-Overrides::Options::%s" % i
        if key not in cnf:
            cnf[key] = ""
334 335
    apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
    Options = cnf.subtree("Check-Overrides::Options")
J
James Troup 已提交
336 337

    if Options["Help"]:
338
        usage()
J
sync  
James Troup 已提交
339

340
    session = DBConn().session()
J
sync  
James Troup 已提交
341

342
    # init sections, priorities:
343 344 345 346 347 348 349 350 351

    # We need forward and reverse
    sections = get_sections(session)
    for name, entry in sections.items():
        sections[entry] = name

    priorities = get_priorities(session)
    for name, entry in priorities.items():
        priorities[entry] = name
352 353

    if not Options["No-Action"]:
354
        Logger = daklog.Logger("check-overrides")
355
    else:
356
        Logger = daklog.Logger("check-overrides", 1)
357

358
    for suite in session.query(Suite).filter(Suite.overrideprocess == True):  # noqa:E712
359
        originosuite = None
360 361 362 363 364 365 366
        originremark = ''

        if suite.overrideorigin is not None:
            originosuite = get_suite(suite.overrideorigin, session)
            if originosuite is None:
                utils.fubar("%s has an override origin suite of %s but it doesn't exist!" % (suite.suite_name, suite.overrideorigin))
            originosuite = originosuite.suite_name
367 368
            originremark = " taking missing from %s" % originosuite

B
Bastian Blank 已提交
369
        print("Processing %s%s..." % (suite.suite_name, originremark))
370 371 372 373

        # Get a list of all suites that use the override file of 'suite.suite_name' as
        # well as the suite
        ocodename = suite.codename
J
Joerg Jaspert 已提交
374
        suiteids = [x.suite_id for x in session.query(Suite).filter(Suite.overridecodename == ocodename).all()]
375 376
        if suite.suite_id not in suiteids:
            suiteids.append(suite.suite_id)
J
Joerg Jaspert 已提交
377 378 379

        if len(suiteids) < 1:
            utils.fubar("Couldn't find id's of all suites: %s" % suiteids)
380

381
        for component in session.query(Component).all():
382 383
            # It is crucial for the dsc override creation based on binary
            # overrides that 'dsc' goes first
384
            component_name = component.component_name
385 386 387 388 389 390
            otypes = ['dsc']
            for ot in session.query(OverrideType):
                if ot.overridetype == 'dsc':
                    continue
                otypes.append(ot.overridetype)

391
            for otype in otypes:
B
Bastian Blank 已提交
392 393
                print("Processing %s [%s - %s]"
                    % (suite.suite_name, component_name, otype))
394
                sys.stdout.flush()
395
                process(suite.suite_name, suiteids, originosuite, component_name, otype, session)
396 397

    Logger.close()
J
sync  
James Troup 已提交
398

J
James Troup 已提交
399
################################################################################
J
sync  
James Troup 已提交
400 401 402

if __name__ == '__main__':
    main()