1#! /usr/bin/env python3
3"""Bulk manipulation of the overrides"""
4# Copyright (C) 2000, 2001, 2002, 2003, 2006 James Troup <james@nocrew.org>
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20################################################################################
22# On 30 Nov 1998, James Troup wrote:
23#
24# > James Troup<2> <troup2@debian.org>
25# >
26# > James is a clone of James; he's going to take over the world.
27# > After he gets some sleep.
28#
29# Could you clone other things too? Sheep? Llamas? Giant mutant turnips?
30#
31# Your clone will need some help to take over the world, maybe clone up an
32# army of penguins and threaten to unleash them on the world, forcing
33# governments to sway to the new James' will!
34#
35# Yes, I can envision a day when James' duplicate decides to take a horrific
36# vengance on the James that spawned him and unleashes his fury in the form
37# of thousands upon thousands of chickens that look just like Captin Blue
38# Eye! Oh the horror.
39#
40# Now you'll have to were name tags to people can tell you apart, unless of
41# course the new clone is truely evil in which case he should be easy to
42# identify!
43#
44# Jason
45# Chicken. Black. Helicopters.
46# Be afraid.
48# <Pine.LNX.3.96.981130011300.30365Z-100000@wakko>
50################################################################################
52import sys
53import time
55import apt_pkg
57from daklib import daklog, utils
58from daklib.config import Config
59from daklib.dbconn import (
60 DBConn,
61 get_component,
62 get_override_type,
63 get_priorities,
64 get_sections,
65 get_suite,
66)
67from daklib.regexes import re_comments
69################################################################################
71Logger = None
73################################################################################
76def usage(exit_code=0):
77 print(
78 """Usage: dak control-overrides [OPTIONS]
79 -h, --help print this help and exit
81 -c, --component=CMPT list/set overrides by component
82 (contrib,*main,non-free)
83 -s, --suite=SUITE list/set overrides by suite
84 (experimental,stable,testing,*unstable)
85 -t, --type=TYPE list/set overrides by type
86 (*deb,dsc,udeb)
88 -a, --add add overrides (changes and deletions are ignored)
89 -S, --set set overrides
90 -C, --change change overrides (additions and deletions are ignored)
91 -l, --list list overrides
93 -q, --quiet be less verbose
94 -n, --no-action only list the action that would have been done
96 starred (*) values are default"""
97 )
98 sys.exit(exit_code)
101################################################################################
104def process_file(file, suite, component, otype, mode, action, session):
105 cnf = Config()
107 s = get_suite(suite, session=session)
108 if s is None: 108 ↛ 109line 108 didn't jump to line 109, because the condition on line 108 was never true
109 utils.fubar("Suite '%s' not recognised." % (suite))
110 suite_id = s.suite_id
112 c = get_component(component, session=session)
113 if c is None: 113 ↛ 114line 113 didn't jump to line 114, because the condition on line 113 was never true
114 utils.fubar("Component '%s' not recognised." % (component))
115 component_id = c.component_id
117 o = get_override_type(otype)
118 if o is None: 118 ↛ 119line 118 didn't jump to line 119, because the condition on line 118 was never true
119 utils.fubar(
120 "Type '%s' not recognised. (Valid types are deb, udeb and dsc.)" % (otype)
121 )
122 type_id = o.overridetype_id
124 # --set is done mostly internal for performance reasons; most
125 # invocations of --set will be updates and making people wait 2-3
126 # minutes while 6000 select+inserts are run needlessly isn't cool.
128 original = {}
129 new = {}
130 c_skipped = 0
131 c_added = 0
132 c_updated = 0
133 c_removed = 0
134 c_error = 0
136 q = session.execute(
137 """SELECT o.package, o.priority, o.section, o.maintainer, p.priority, s.section
138 FROM override o, priority p, section s
139 WHERE o.suite = :suiteid AND o.component = :componentid AND o.type = :typeid
140 and o.priority = p.id and o.section = s.id""",
141 {"suiteid": suite_id, "componentid": component_id, "typeid": type_id},
142 )
143 for i in q.fetchall():
144 original[i[0]] = i[1:]
146 start_time = time.time()
148 section_cache = get_sections(session)
149 priority_cache = get_priorities(session)
151 # Our session is already in a transaction
153 for line in file.readlines():
154 line = re_comments.sub("", line).strip()
155 if line == "": 155 ↛ 156line 155 didn't jump to line 156, because the condition on line 155 was never true
156 continue
158 maintainer_override = None
159 if otype == "dsc":
160 split_line = line.split(None, 2)
161 if len(split_line) == 2: 161 ↛ 163line 161 didn't jump to line 163, because the condition on line 161 was never false
162 (package, section) = split_line
163 elif len(split_line) == 3:
164 (package, section, maintainer_override) = split_line
165 else:
166 utils.warn(
167 "'%s' does not break into 'package section [maintainer-override]'."
168 % (line)
169 )
170 c_error += 1
171 continue
172 priority = "optional"
173 else: # binary or udeb
174 split_line = line.split(None, 3)
175 if len(split_line) == 3: 175 ↛ 177line 175 didn't jump to line 177, because the condition on line 175 was never false
176 (package, priority, section) = split_line
177 elif len(split_line) == 4:
178 (package, priority, section, maintainer_override) = split_line
179 else:
180 utils.warn(
181 "'%s' does not break into 'package priority section [maintainer-override]'."
182 % (line)
183 )
184 c_error += 1
185 continue
187 if section not in section_cache: 187 ↛ 188line 187 didn't jump to line 188, because the condition on line 187 was never true
188 utils.warn(
189 "'%s' is not a valid section. ['%s' in suite %s, component %s]."
190 % (section, package, suite, component)
191 )
192 c_error += 1
193 continue
195 section_id = section_cache[section]
197 if priority not in priority_cache: 197 ↛ 198line 197 didn't jump to line 198, because the condition on line 197 was never true
198 utils.warn(
199 "'%s' is not a valid priority. ['%s' in suite %s, component %s]."
200 % (priority, package, suite, component)
201 )
202 c_error += 1
203 continue
205 priority_id = priority_cache[priority]
207 if package in new: 207 ↛ 208line 207 didn't jump to line 208, because the condition on line 207 was never true
208 utils.warn(
209 "Can't insert duplicate entry for '%s'; ignoring all but the first. [suite %s, component %s]"
210 % (package, suite, component)
211 )
212 c_error += 1
213 continue
214 new[package] = ""
216 if package in original: 216 ↛ 217line 216 didn't jump to line 217
217 (
218 old_priority_id,
219 old_section_id,
220 old_maintainer_override,
221 old_priority,
222 old_section,
223 ) = original[package]
224 if (
225 mode == "add"
226 or old_priority_id == priority_id
227 and old_section_id == section_id
228 and old_maintainer_override == maintainer_override
229 ):
230 # If it's unchanged or we're in 'add only' mode, ignore it
231 c_skipped += 1
232 continue
233 else:
234 # If it's changed, delete the old one so we can
235 # reinsert it with the new information
236 c_updated += 1
237 if action:
238 session.execute(
239 """DELETE FROM override WHERE suite = :suite AND component = :component
240 AND package = :package AND type = :typeid""",
241 {
242 "suite": suite_id,
243 "component": component_id,
244 "package": package,
245 "typeid": type_id,
246 },
247 )
249 # Log changes
250 if old_priority_id != priority_id:
251 Logger.log(["changed priority", package, old_priority, priority])
252 if old_section_id != section_id:
253 Logger.log(["changed section", package, old_section, section])
254 if old_maintainer_override != maintainer_override:
255 Logger.log(
256 [
257 "changed maintainer override",
258 package,
259 old_maintainer_override,
260 maintainer_override,
261 ]
262 )
263 update_p = 1
264 elif mode == "change": 264 ↛ 266line 264 didn't jump to line 266, because the condition on line 264 was never true
265 # Ignore additions in 'change only' mode
266 c_skipped += 1
267 continue
268 else:
269 c_added += 1
270 update_p = 0
272 if action: 272 ↛ 294line 272 didn't jump to line 294, because the condition on line 272 was never false
273 if not maintainer_override: 273 ↛ 276line 273 didn't jump to line 276, because the condition on line 273 was never false
274 m_o = None
275 else:
276 m_o = maintainer_override
277 session.execute(
278 """INSERT INTO override (suite, component, type, package,
279 priority, section, maintainer)
280 VALUES (:suiteid, :componentid, :typeid,
281 :package, :priorityid, :sectionid,
282 :maintainer)""",
283 {
284 "suiteid": suite_id,
285 "componentid": component_id,
286 "typeid": type_id,
287 "package": package,
288 "priorityid": priority_id,
289 "sectionid": section_id,
290 "maintainer": m_o,
291 },
292 )
294 if not update_p: 294 ↛ 153line 294 didn't jump to line 153, because the condition on line 294 was never false
295 Logger.log(
296 [
297 "new override",
298 suite,
299 component,
300 otype,
301 package,
302 priority,
303 section,
304 maintainer_override,
305 ]
306 )
308 if mode == "set": 308 ↛ 310line 308 didn't jump to line 310, because the condition on line 308 was never true
309 # Delete any packages which were removed
310 for package in original.keys():
311 if package not in new:
312 if action:
313 session.execute(
314 """DELETE FROM override
315 WHERE suite = :suiteid AND component = :componentid
316 AND package = :package AND type = :typeid""",
317 {
318 "suiteid": suite_id,
319 "componentid": component_id,
320 "package": package,
321 "typeid": type_id,
322 },
323 )
324 c_removed += 1
325 Logger.log(["removed override", suite, component, otype, package])
327 if action: 327 ↛ 330line 327 didn't jump to line 330, because the condition on line 327 was never false
328 session.commit()
330 if not cnf["Control-Overrides::Options::Quiet"]: 330 ↛ 343line 330 didn't jump to line 343, because the condition on line 330 was never false
331 print(
332 "Done in %d seconds. [Updated = %d, Added = %d, Removed = %d, Skipped = %d, Errors = %d]"
333 % (
334 int(time.time() - start_time),
335 c_updated,
336 c_added,
337 c_removed,
338 c_skipped,
339 c_error,
340 )
341 )
343 Logger.log(["set complete", c_updated, c_added, c_removed, c_skipped, c_error])
346################################################################################
349def list_overrides(suite, component, otype, session):
350 dat = {}
351 s = get_suite(suite, session)
352 if s is None: 352 ↛ 353line 352 didn't jump to line 353, because the condition on line 352 was never true
353 utils.fubar("Suite '%s' not recognised." % (suite))
355 dat["suiteid"] = s.suite_id
357 c = get_component(component, session)
358 if c is None: 358 ↛ 359line 358 didn't jump to line 359, because the condition on line 358 was never true
359 utils.fubar("Component '%s' not recognised." % (component))
361 dat["componentid"] = c.component_id
363 o = get_override_type(otype)
364 if o is None: 364 ↛ 365line 364 didn't jump to line 365, because the condition on line 364 was never true
365 utils.fubar(
366 "Type '%s' not recognised. (Valid types are deb, udeb and dsc)" % (otype)
367 )
369 dat["typeid"] = o.overridetype_id
371 if otype == "dsc": 371 ↛ 372line 371 didn't jump to line 372, because the condition on line 371 was never true
372 q = session.execute(
373 """SELECT o.package, s.section, o.maintainer FROM override o, section s
374 WHERE o.suite = :suiteid AND o.component = :componentid
375 AND o.type = :typeid AND o.section = s.id
376 ORDER BY s.section, o.package""",
377 dat,
378 )
379 for i in q.fetchall():
380 print(utils.result_join(i))
381 else:
382 q = session.execute(
383 """SELECT o.package, p.priority, s.section, o.maintainer, p.level
384 FROM override o, priority p, section s
385 WHERE o.suite = :suiteid AND o.component = :componentid
386 AND o.type = :typeid AND o.priority = p.id AND o.section = s.id
387 ORDER BY s.section, p.level, o.package""",
388 dat,
389 )
390 for i in q.fetchall(): 390 ↛ 391line 390 didn't jump to line 391, because the loop on line 390 never started
391 print(utils.result_join(i[:-1]))
394################################################################################
397def main():
398 global Logger
400 cnf = Config()
401 Arguments = [
402 ("a", "add", "Control-Overrides::Options::Add"),
403 ("c", "component", "Control-Overrides::Options::Component", "HasArg"),
404 ("h", "help", "Control-Overrides::Options::Help"),
405 ("l", "list", "Control-Overrides::Options::List"),
406 ("q", "quiet", "Control-Overrides::Options::Quiet"),
407 ("s", "suite", "Control-Overrides::Options::Suite", "HasArg"),
408 ("S", "set", "Control-Overrides::Options::Set"),
409 ("C", "change", "Control-Overrides::Options::Change"),
410 ("n", "no-action", "Control-Overrides::Options::No-Action"),
411 ("t", "type", "Control-Overrides::Options::Type", "HasArg"),
412 ]
414 # Default arguments
415 for i in ["add", "help", "list", "quiet", "set", "change", "no-action"]:
416 key = "Control-Overrides::Options::%s" % i
417 if key not in cnf: 417 ↛ 415line 417 didn't jump to line 415, because the condition on line 417 was never false
418 cnf[key] = ""
419 if "Control-Overrides::Options::Component" not in cnf: 419 ↛ 421line 419 didn't jump to line 421, because the condition on line 419 was never false
420 cnf["Control-Overrides::Options::Component"] = "main"
421 if "Control-Overrides::Options::Suite" not in cnf: 421 ↛ 423line 421 didn't jump to line 423, because the condition on line 421 was never false
422 cnf["Control-Overrides::Options::Suite"] = "unstable"
423 if "Control-Overrides::Options::Type" not in cnf: 423 ↛ 426line 423 didn't jump to line 426, because the condition on line 423 was never false
424 cnf["Control-Overrides::Options::Type"] = "deb"
426 file_list = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
428 if cnf["Control-Overrides::Options::Help"]:
429 usage()
431 session = DBConn().session()
433 mode = None
434 for i in ["add", "list", "set", "change"]:
435 if cnf["Control-Overrides::Options::%s" % (i)]:
436 if mode: 436 ↛ 437line 436 didn't jump to line 437, because the condition on line 436 was never true
437 utils.fubar("Can not perform more than one action at once.")
438 mode = i
440 # Need an action...
441 if mode is None: 441 ↛ 442line 441 didn't jump to line 442, because the condition on line 441 was never true
442 utils.fubar("No action specified.")
444 (suite, component, otype) = (
445 cnf["Control-Overrides::Options::Suite"],
446 cnf["Control-Overrides::Options::Component"],
447 cnf["Control-Overrides::Options::Type"],
448 )
450 if mode == "list":
451 list_overrides(suite, component, otype, session)
452 else:
453 if get_suite(suite).untouchable: 453 ↛ 454line 453 didn't jump to line 454, because the condition on line 453 was never true
454 utils.fubar("%s: suite is untouchable" % suite)
456 action = True
457 if cnf["Control-Overrides::Options::No-Action"]: 457 ↛ 458line 457 didn't jump to line 458, because the condition on line 457 was never true
458 utils.warn("In No-Action Mode")
459 action = False
461 Logger = daklog.Logger("control-overrides", mode)
462 if file_list: 462 ↛ 463line 462 didn't jump to line 463, because the condition on line 462 was never true
463 for f in file_list:
464 process_file(open(f), suite, component, otype, mode, action, session)
465 else:
466 process_file(sys.stdin, suite, component, otype, mode, action, session)
467 Logger.close()
470#######################################################################################
473if __name__ == "__main__":
474 main()