]> git.decadent.org.uk Git - dak.git/blob - dak/check_overrides.py
bin_assoc
[dak.git] / dak / check_overrides.py
1 #!/usr/bin/env python
2
3 """ Cruft checker and hole filler for overrides """
4 # Copyright (C) 2000, 2001, 2002, 2004, 2006  James Troup <james@nocrew.org>
5 # Copyright (C) 2005  Jeroen van Wolffelaar <jeroen@wolffelaar.nl>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU General Public License for more details.
16
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 ################################################################################
22
23 ######################################################################
24 # NB: dak check-overrides is not a good idea with New Incoming as it #
25 # doesn't take into account accepted.  You can minimize the impact   #
26 # of this by running it immediately after dak process-accepted but   #
27 # that's still racy because 'dak process-new' doesn't lock with 'dak #
28 # process-accepted'.  A better long term fix is the evil plan for    #
29 # accepted to be in the DB.                                          #
30 ######################################################################
31
32 # dak check-overrides should now work fine being done during
33 # cron.daily, for example just before 'dak make-overrides' (after 'dak
34 # process-accepted' and 'dak make-suite-file-list'). At that point,
35 # queue/accepted should be empty and installed, so... dak
36 # check-overrides does now take into account suites sharing overrides
37
38 # TODO:
39 # * Only update out-of-sync overrides when corresponding versions are equal to
40 #   some degree
41 # * consistency checks like:
42 #   - section=debian-installer only for udeb and # dsc
43 #   - priority=source iff dsc
44 #   - (suite, package, 'dsc') is unique,
45 #   - just as (suite, package, (u)deb) (yes, across components!)
46 #   - sections match their component (each component has an own set of sections,
47 #     could probably be reduced...)
48
49 ################################################################################
50
51 import pg, sys, os
52 import apt_pkg
53 from daklib import database
54 from daklib import logging
55 from daklib import utils
56
57 ################################################################################
58
59 Options = None
60 projectB = None
61 Logger = None
62 sections = {}
63 priorities = {}
64 blacklist = {}
65
66 ################################################################################
67
68 def usage (exit_code=0):
69     print """Usage: dak check-overrides
70 Check for cruft in overrides.
71
72   -n, --no-action            don't do anything
73   -h, --help                 show this help and exit"""
74
75     sys.exit(exit_code)
76
77 ################################################################################
78
79 def gen_blacklist(dir):
80     for entry in os.listdir(dir):
81         entry = entry.split('_')[0]
82         blacklist[entry] = 1
83
84 def process(osuite, affected_suites, originosuite, component, type):
85     global Logger, Options, projectB, sections, priorities
86
87     osuite_id = database.get_suite_id(osuite)
88     if osuite_id == -1:
89         utils.fubar("Suite '%s' not recognised." % (osuite))
90     originosuite_id = None
91     if originosuite:
92         originosuite_id = database.get_suite_id(originosuite)
93         if originosuite_id == -1:
94             utils.fubar("Suite '%s' not recognised." % (originosuite))
95
96     component_id = database.get_component_id(component)
97     if component_id == -1:
98         utils.fubar("Component '%s' not recognised." % (component))
99
100     type_id = database.get_override_type_id(type)
101     if type_id == -1:
102         utils.fubar("Type '%s' not recognised. (Valid types are deb, udeb and dsc)" % (type))
103     dsc_type_id = database.get_override_type_id("dsc")
104
105     source_priority_id = database.get_priority_id("source")
106
107     if type == "deb" or type == "udeb":
108         packages = {}
109         q = projectB.query("""
110 SELECT b.package FROM binaries b, bin_associations ba, files f,
111                               location l, component c
112  WHERE b.type = '%s' AND b.id = ba.bin AND f.id = b.file AND l.id = f.location
113    AND c.id = l.component AND ba.suite IN (%s) AND c.id = %s
114 """ % (type, ",".join([ str(i) for i in affected_suites ]), component_id))
115         for i in q.getresult():
116             packages[i[0]] = 0
117
118     src_packages = {}
119     q = projectB.query("""
120 SELECT s.source FROM source s, src_associations sa, files f, location l,
121                      component c
122  WHERE s.id = sa.source AND f.id = s.file AND l.id = f.location
123    AND c.id = l.component AND sa.suite IN (%s) AND c.id = %s
124 """ % (",".join([ str(i) for i in affected_suites]), component_id))
125     for i in q.getresult():
126         src_packages[i[0]] = 0
127
128     # -----------
129     # Drop unused overrides
130
131     q = projectB.query("SELECT package, priority, section, maintainer FROM override WHERE suite = %s AND component = %s AND type = %s" % (osuite_id, component_id, type_id))
132     projectB.query("BEGIN WORK")
133     if type == "dsc":
134         for i in q.getresult():
135             package = i[0]
136             if src_packages.has_key(package):
137                 src_packages[package] = 1
138             else:
139                 if blacklist.has_key(package):
140                     utils.warn("%s in incoming, not touching" % package)
141                     continue
142                 Logger.log(["removing unused override", osuite, component,
143                     type, package, priorities[i[1]], sections[i[2]], i[3]])
144                 if not Options["No-Action"]:
145                     projectB.query("""DELETE FROM override WHERE package =
146                         '%s' AND suite = %s AND component = %s AND type =
147                         %s""" % (package, osuite_id, component_id, type_id))
148         # create source overrides based on binary overrides, as source
149         # overrides not always get created
150         q = projectB.query(""" SELECT package, priority, section,
151             maintainer FROM override WHERE suite = %s AND component = %s
152             """ % (osuite_id, component_id))
153         for i in q.getresult():
154             package = i[0]
155             if not src_packages.has_key(package) or src_packages[package]:
156                 continue
157             src_packages[package] = 1
158
159             Logger.log(["add missing override", osuite, component,
160                 type, package, "source", sections[i[2]], i[3]])
161             if not Options["No-Action"]:
162                 projectB.query("""INSERT INTO override (package, suite,
163                     component, priority, section, type, maintainer) VALUES
164                     ('%s', %s, %s, %s, %s, %s, '%s')""" % (package,
165                     osuite_id, component_id, source_priority_id, i[2],
166                     dsc_type_id, i[3]))
167         # Check whether originosuite has an override for us we can
168         # copy
169         if originosuite:
170             q = projectB.query("""SELECT origin.package, origin.priority,
171                 origin.section, origin.maintainer, target.priority,
172                 target.section, target.maintainer FROM override origin LEFT
173                 JOIN override target ON (origin.package = target.package AND
174                 target.suite=%s AND origin.component = target.component AND origin.type =
175                 target.type) WHERE origin.suite = %s AND origin.component = %s
176                 AND origin.type = %s""" %
177                 (osuite_id, originosuite_id, component_id, type_id))
178             for i in q.getresult():
179                 package = i[0]
180                 if not src_packages.has_key(package) or src_packages[package]:
181                     if i[4] and (i[1] != i[4] or i[2] != i[5] or i[3] != i[6]):
182                         Logger.log(["syncing override", osuite, component,
183                             type, package, "source", sections[i[5]], i[6], "source", sections[i[2]], i[3]])
184                         if not Options["No-Action"]:
185                             projectB.query("""UPDATE override SET section=%s,
186                                 maintainer='%s' WHERE package='%s' AND
187                                 suite=%s AND component=%s AND type=%s""" %
188                                 (i[2], i[3], package, osuite_id, component_id,
189                                 dsc_type_id))
190                     continue
191                 # we can copy
192                 src_packages[package] = 1
193                 Logger.log(["copying missing override", osuite, component,
194                     type, package, "source", sections[i[2]], i[3]])
195                 if not Options["No-Action"]:
196                     projectB.query("""INSERT INTO override (package, suite,
197                         component, priority, section, type, maintainer) VALUES
198                         ('%s', %s, %s, %s, %s, %s, '%s')""" % (package,
199                         osuite_id, component_id, source_priority_id, i[2],
200                         dsc_type_id, i[3]))
201
202         for package, hasoverride in src_packages.items():
203             if not hasoverride:
204                 utils.warn("%s has no override!" % package)
205
206     else: # binary override
207         for i in q.getresult():
208             package = i[0]
209             if packages.has_key(package):
210                 packages[package] = 1
211             else:
212                 if blacklist.has_key(package):
213                     utils.warn("%s in incoming, not touching" % package)
214                     continue
215                 Logger.log(["removing unused override", osuite, component,
216                     type, package, priorities[i[1]], sections[i[2]], i[3]])
217                 if not Options["No-Action"]:
218                     projectB.query("""DELETE FROM override WHERE package =
219                         '%s' AND suite = %s AND component = %s AND type =
220                         %s""" % (package, osuite_id, component_id, type_id))
221
222         # Check whether originosuite has an override for us we can
223         # copy
224         if originosuite:
225             q = projectB.query("""SELECT origin.package, origin.priority,
226                 origin.section, origin.maintainer, target.priority,
227                 target.section, target.maintainer FROM override origin LEFT
228                 JOIN override target ON (origin.package = target.package AND
229                 target.suite=%s AND origin.component = target.component AND
230                 origin.type = target.type) WHERE origin.suite = %s AND
231                 origin.component = %s AND origin.type = %s""" % (osuite_id,
232                 originosuite_id, component_id, type_id))
233             for i in q.getresult():
234                 package = i[0]
235                 if not packages.has_key(package) or packages[package]:
236                     if i[4] and (i[1] != i[4] or i[2] != i[5] or i[3] != i[6]):
237                         Logger.log(["syncing override", osuite, component,
238                             type, package, priorities[i[4]], sections[i[5]],
239                             i[6], priorities[i[1]], sections[i[2]], i[3]])
240                         if not Options["No-Action"]:
241                             projectB.query("""UPDATE override SET priority=%s, section=%s,
242                                 maintainer='%s' WHERE package='%s' AND
243                                 suite=%s AND component=%s AND type=%s""" %
244                                 (i[1], i[2], i[3], package, osuite_id,
245                                 component_id, type_id))
246                     continue
247                 # we can copy
248                 packages[package] = 1
249                 Logger.log(["copying missing override", osuite, component,
250                     type, package, priorities[i[1]], sections[i[2]], i[3]])
251                 if not Options["No-Action"]:
252                     projectB.query("""INSERT INTO override (package, suite,
253                         component, priority, section, type, maintainer) VALUES
254                         ('%s', %s, %s, %s, %s, %s, '%s')""" % (package, osuite_id, component_id, i[1], i[2], type_id, i[3]))
255
256         for package, hasoverride in packages.items():
257             if not hasoverride:
258                 utils.warn("%s has no override!" % package)
259
260     projectB.query("COMMIT WORK")
261     sys.stdout.flush()
262
263
264 ################################################################################
265
266 def main ():
267     global Logger, Options, projectB, sections, priorities
268
269     Cnf = utils.get_conf()
270
271     Arguments = [('h',"help","Check-Overrides::Options::Help"),
272                  ('n',"no-action", "Check-Overrides::Options::No-Action")]
273     for i in [ "help", "no-action" ]:
274         if not Cnf.has_key("Check-Overrides::Options::%s" % (i)):
275             Cnf["Check-Overrides::Options::%s" % (i)] = ""
276     apt_pkg.ParseCommandLine(Cnf, Arguments, sys.argv)
277     Options = Cnf.SubTree("Check-Overrides::Options")
278
279     if Options["Help"]:
280         usage()
281
282     projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"], int(Cnf["DB::Port"]))
283     database.init(Cnf, projectB)
284
285     # init sections, priorities:
286     q = projectB.query("SELECT id, section FROM section")
287     for i in q.getresult():
288         sections[i[0]] = i[1]
289     q = projectB.query("SELECT id, priority FROM priority")
290     for i in q.getresult():
291         priorities[i[0]] = i[1]
292
293     if not Options["No-Action"]:
294         Logger = logging.Logger(Cnf, "check-overrides")
295     else:
296         Logger = logging.Logger(Cnf, "check-overrides", 1)
297
298     gen_blacklist(Cnf["Dir::Queue::Accepted"])
299
300     for osuite in Cnf.SubTree("Check-Overrides::OverrideSuites").List():
301         if "1" != Cnf["Check-Overrides::OverrideSuites::%s::Process" % osuite]:
302             continue
303
304         osuite = osuite.lower()
305
306         originosuite = None
307         originremark = ""
308         try:
309             originosuite = Cnf["Check-Overrides::OverrideSuites::%s::OriginSuite" % osuite]
310             originosuite = originosuite.lower()
311             originremark = " taking missing from %s" % originosuite
312         except KeyError:
313             pass
314
315         print "Processing %s%s..." % (osuite, originremark)
316         # Get a list of all suites that use the override file of 'osuite'
317         ocodename = Cnf["Suite::%s::codename" % osuite].lower()
318         suites = []
319         for suite in Cnf.SubTree("Suite").List():
320             if ocodename == Cnf["Suite::%s::OverrideCodeName" % suite].lower():
321                 suites.append(suite)
322
323         q = projectB.query("SELECT id FROM suite WHERE suite_name in (%s)" \
324             % ", ".join([ repr(i) for i in suites ]).lower())
325
326         suiteids = []
327         for i in q.getresult():
328             suiteids.append(i[0])
329
330         if len(suiteids) != len(suites) or len(suiteids) < 1:
331             utils.fubar("Couldn't find id's of all suites: %s" % suites)
332
333         for component in Cnf.SubTree("Component").List():
334             if component == "mixed":
335                 continue; # Ick
336             # It is crucial for the dsc override creation based on binary
337             # overrides that 'dsc' goes first
338             otypes = Cnf.ValueList("OverrideType")
339             otypes.remove("dsc")
340             otypes = ["dsc"] + otypes
341             for otype in otypes:
342                 print "Processing %s [%s - %s] using %s..." \
343                     % (osuite, component, otype, suites)
344                 sys.stdout.flush()
345                 process(osuite, suiteids, originosuite, component, otype)
346
347     Logger.close()
348
349 ################################################################################
350
351 if __name__ == '__main__':
352     main()