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
54import apt_pkg
56from daklib.dbconn import *
57from daklib.config import Config
58from daklib import utils
59from daklib import daklog
60from daklib.regexes import re_comments
62################################################################################
64Logger = None
66################################################################################
69def usage(exit_code=0):
70 print("""Usage: dak control-overrides [OPTIONS]
71 -h, --help print this help and exit
73 -c, --component=CMPT list/set overrides by component
74 (contrib,*main,non-free)
75 -s, --suite=SUITE list/set overrides by suite
76 (experimental,stable,testing,*unstable)
77 -t, --type=TYPE list/set overrides by type
78 (*deb,dsc,udeb)
80 -a, --add add overrides (changes and deletions are ignored)
81 -S, --set set overrides
82 -C, --change change overrides (additions and deletions are ignored)
83 -l, --list list overrides
85 -q, --quiet be less verbose
86 -n, --no-action only list the action that would have been done
88 starred (*) values are default""")
89 sys.exit(exit_code)
91################################################################################
94def process_file(file, suite, component, otype, mode, action, session):
95 cnf = Config()
97 s = get_suite(suite, session=session)
98 if s is None: 98 ↛ 99line 98 didn't jump to line 99, because the condition on line 98 was never true
99 utils.fubar("Suite '%s' not recognised." % (suite))
100 suite_id = s.suite_id
102 c = get_component(component, session=session)
103 if c is None: 103 ↛ 104line 103 didn't jump to line 104, because the condition on line 103 was never true
104 utils.fubar("Component '%s' not recognised." % (component))
105 component_id = c.component_id
107 o = get_override_type(otype)
108 if o is None: 108 ↛ 109line 108 didn't jump to line 109, because the condition on line 108 was never true
109 utils.fubar("Type '%s' not recognised. (Valid types are deb, udeb and dsc.)" % (otype))
110 type_id = o.overridetype_id
112 # --set is done mostly internal for performance reasons; most
113 # invocations of --set will be updates and making people wait 2-3
114 # minutes while 6000 select+inserts are run needlessly isn't cool.
116 original = {}
117 new = {}
118 c_skipped = 0
119 c_added = 0
120 c_updated = 0
121 c_removed = 0
122 c_error = 0
124 q = session.execute("""SELECT o.package, o.priority, o.section, o.maintainer, p.priority, s.section
125 FROM override o, priority p, section s
126 WHERE o.suite = :suiteid AND o.component = :componentid AND o.type = :typeid
127 and o.priority = p.id and o.section = s.id""",
128 {'suiteid': suite_id, 'componentid': component_id, 'typeid': type_id})
129 for i in q.fetchall():
130 original[i[0]] = i[1:]
132 start_time = time.time()
134 section_cache = get_sections(session)
135 priority_cache = get_priorities(session)
137 # Our session is already in a transaction
139 for line in file.readlines():
140 line = re_comments.sub('', line).strip()
141 if line == "": 141 ↛ 142line 141 didn't jump to line 142, because the condition on line 141 was never true
142 continue
144 maintainer_override = None
145 if otype == "dsc":
146 split_line = line.split(None, 2)
147 if len(split_line) == 2: 147 ↛ 149line 147 didn't jump to line 149, because the condition on line 147 was never false
148 (package, section) = split_line
149 elif len(split_line) == 3:
150 (package, section, maintainer_override) = split_line
151 else:
152 utils.warn("'%s' does not break into 'package section [maintainer-override]'." % (line))
153 c_error += 1
154 continue
155 priority = "optional"
156 else: # binary or udeb
157 split_line = line.split(None, 3)
158 if len(split_line) == 3: 158 ↛ 160line 158 didn't jump to line 160, because the condition on line 158 was never false
159 (package, priority, section) = split_line
160 elif len(split_line) == 4:
161 (package, priority, section, maintainer_override) = split_line
162 else:
163 utils.warn("'%s' does not break into 'package priority section [maintainer-override]'." % (line))
164 c_error += 1
165 continue
167 if section not in section_cache: 167 ↛ 168line 167 didn't jump to line 168, because the condition on line 167 was never true
168 utils.warn("'%s' is not a valid section. ['%s' in suite %s, component %s]." % (section, package, suite, component))
169 c_error += 1
170 continue
172 section_id = section_cache[section]
174 if priority not in priority_cache: 174 ↛ 175line 174 didn't jump to line 175, because the condition on line 174 was never true
175 utils.warn("'%s' is not a valid priority. ['%s' in suite %s, component %s]." % (priority, package, suite, component))
176 c_error += 1
177 continue
179 priority_id = priority_cache[priority]
181 if package in new: 181 ↛ 182line 181 didn't jump to line 182, because the condition on line 181 was never true
182 utils.warn("Can't insert duplicate entry for '%s'; ignoring all but the first. [suite %s, component %s]" % (package, suite, component))
183 c_error += 1
184 continue
185 new[package] = ""
187 if package in original: 187 ↛ 188line 187 didn't jump to line 188, because the condition on line 187 was never true
188 (old_priority_id, old_section_id, old_maintainer_override, old_priority, old_section) = original[package]
189 if mode == "add" or old_priority_id == priority_id and \
190 old_section_id == section_id and \
191 old_maintainer_override == maintainer_override:
192 # If it's unchanged or we're in 'add only' mode, ignore it
193 c_skipped += 1
194 continue
195 else:
196 # If it's changed, delete the old one so we can
197 # reinsert it with the new information
198 c_updated += 1
199 if action:
200 session.execute("""DELETE FROM override WHERE suite = :suite AND component = :component
201 AND package = :package AND type = :typeid""",
202 {'suite': suite_id, 'component': component_id,
203 'package': package, 'typeid': type_id})
205 # Log changes
206 if old_priority_id != priority_id:
207 Logger.log(["changed priority", package, old_priority, priority])
208 if old_section_id != section_id:
209 Logger.log(["changed section", package, old_section, section])
210 if old_maintainer_override != maintainer_override:
211 Logger.log(["changed maintainer override", package, old_maintainer_override, maintainer_override])
212 update_p = 1
213 elif mode == "change": 213 ↛ 215line 213 didn't jump to line 215, because the condition on line 213 was never true
214 # Ignore additions in 'change only' mode
215 c_skipped += 1
216 continue
217 else:
218 c_added += 1
219 update_p = 0
221 if action: 221 ↛ 236line 221 didn't jump to line 236, because the condition on line 221 was never false
222 if not maintainer_override: 222 ↛ 225line 222 didn't jump to line 225, because the condition on line 222 was never false
223 m_o = None
224 else:
225 m_o = maintainer_override
226 session.execute("""INSERT INTO override (suite, component, type, package,
227 priority, section, maintainer)
228 VALUES (:suiteid, :componentid, :typeid,
229 :package, :priorityid, :sectionid,
230 :maintainer)""",
231 {'suiteid': suite_id, 'componentid': component_id,
232 'typeid': type_id, 'package': package,
233 'priorityid': priority_id, 'sectionid': section_id,
234 'maintainer': m_o})
236 if not update_p: 236 ↛ 139line 236 didn't jump to line 139, because the condition on line 236 was never false
237 Logger.log(["new override", suite, component, otype, package, priority, section, maintainer_override])
239 if mode == "set": 239 ↛ 241line 239 didn't jump to line 241, because the condition on line 239 was never true
240 # Delete any packages which were removed
241 for package in original.keys():
242 if package not in new:
243 if action:
244 session.execute("""DELETE FROM override
245 WHERE suite = :suiteid AND component = :componentid
246 AND package = :package AND type = :typeid""",
247 {'suiteid': suite_id, 'componentid': component_id,
248 'package': package, 'typeid': type_id})
249 c_removed += 1
250 Logger.log(["removed override", suite, component, otype, package])
252 if action: 252 ↛ 255line 252 didn't jump to line 255, because the condition on line 252 was never false
253 session.commit()
255 if not cnf["Control-Overrides::Options::Quiet"]: 255 ↛ 258line 255 didn't jump to line 258, because the condition on line 255 was never false
256 print("Done in %d seconds. [Updated = %d, Added = %d, Removed = %d, Skipped = %d, Errors = %d]" % (int(time.time() - start_time), c_updated, c_added, c_removed, c_skipped, c_error))
258 Logger.log(["set complete", c_updated, c_added, c_removed, c_skipped, c_error])
260################################################################################
263def list_overrides(suite, component, otype, session):
264 dat = {}
265 s = get_suite(suite, session)
266 if s is None: 266 ↛ 267line 266 didn't jump to line 267, because the condition on line 266 was never true
267 utils.fubar("Suite '%s' not recognised." % (suite))
269 dat['suiteid'] = s.suite_id
271 c = get_component(component, session)
272 if c is None: 272 ↛ 273line 272 didn't jump to line 273, because the condition on line 272 was never true
273 utils.fubar("Component '%s' not recognised." % (component))
275 dat['componentid'] = c.component_id
277 o = get_override_type(otype)
278 if o is None: 278 ↛ 279line 278 didn't jump to line 279, because the condition on line 278 was never true
279 utils.fubar("Type '%s' not recognised. (Valid types are deb, udeb and dsc)" % (otype))
281 dat['typeid'] = o.overridetype_id
283 if otype == "dsc": 283 ↛ 284line 283 didn't jump to line 284, because the condition on line 283 was never true
284 q = session.execute("""SELECT o.package, s.section, o.maintainer FROM override o, section s
285 WHERE o.suite = :suiteid AND o.component = :componentid
286 AND o.type = :typeid AND o.section = s.id
287 ORDER BY s.section, o.package""", dat)
288 for i in q.fetchall():
289 print(utils.result_join(i))
290 else:
291 q = session.execute("""SELECT o.package, p.priority, s.section, o.maintainer, p.level
292 FROM override o, priority p, section s
293 WHERE o.suite = :suiteid AND o.component = :componentid
294 AND o.type = :typeid AND o.priority = p.id AND o.section = s.id
295 ORDER BY s.section, p.level, o.package""", dat)
296 for i in q.fetchall(): 296 ↛ 297line 296 didn't jump to line 297, because the loop on line 296 never started
297 print(utils.result_join(i[:-1]))
299################################################################################
302def main():
303 global Logger
305 cnf = Config()
306 Arguments = [('a', "add", "Control-Overrides::Options::Add"),
307 ('c', "component", "Control-Overrides::Options::Component", "HasArg"),
308 ('h', "help", "Control-Overrides::Options::Help"),
309 ('l', "list", "Control-Overrides::Options::List"),
310 ('q', "quiet", "Control-Overrides::Options::Quiet"),
311 ('s', "suite", "Control-Overrides::Options::Suite", "HasArg"),
312 ('S', "set", "Control-Overrides::Options::Set"),
313 ('C', "change", "Control-Overrides::Options::Change"),
314 ('n', "no-action", "Control-Overrides::Options::No-Action"),
315 ('t', "type", "Control-Overrides::Options::Type", "HasArg")]
317 # Default arguments
318 for i in ["add", "help", "list", "quiet", "set", "change", "no-action"]:
319 key = "Control-Overrides::Options::%s" % i
320 if key not in cnf: 320 ↛ 318line 320 didn't jump to line 318, because the condition on line 320 was never false
321 cnf[key] = ""
322 if "Control-Overrides::Options::Component" not in cnf: 322 ↛ 324line 322 didn't jump to line 324, because the condition on line 322 was never false
323 cnf["Control-Overrides::Options::Component"] = "main"
324 if "Control-Overrides::Options::Suite" not in cnf: 324 ↛ 326line 324 didn't jump to line 326, because the condition on line 324 was never false
325 cnf["Control-Overrides::Options::Suite"] = "unstable"
326 if "Control-Overrides::Options::Type" not in cnf: 326 ↛ 329line 326 didn't jump to line 329, because the condition on line 326 was never false
327 cnf["Control-Overrides::Options::Type"] = "deb"
329 file_list = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv)
331 if cnf["Control-Overrides::Options::Help"]:
332 usage()
334 session = DBConn().session()
336 mode = None
337 for i in ["add", "list", "set", "change"]:
338 if cnf["Control-Overrides::Options::%s" % (i)]:
339 if mode: 339 ↛ 340line 339 didn't jump to line 340, because the condition on line 339 was never true
340 utils.fubar("Can not perform more than one action at once.")
341 mode = i
343 # Need an action...
344 if mode is None: 344 ↛ 345line 344 didn't jump to line 345, because the condition on line 344 was never true
345 utils.fubar("No action specified.")
347 (suite, component, otype) = (cnf["Control-Overrides::Options::Suite"],
348 cnf["Control-Overrides::Options::Component"],
349 cnf["Control-Overrides::Options::Type"])
351 if mode == "list":
352 list_overrides(suite, component, otype, session)
353 else:
354 if get_suite(suite).untouchable: 354 ↛ 355line 354 didn't jump to line 355, because the condition on line 354 was never true
355 utils.fubar("%s: suite is untouchable" % suite)
357 action = True
358 if cnf["Control-Overrides::Options::No-Action"]: 358 ↛ 359line 358 didn't jump to line 359, because the condition on line 358 was never true
359 utils.warn("In No-Action Mode")
360 action = False
362 Logger = daklog.Logger("control-overrides", mode)
363 if file_list: 363 ↛ 364line 363 didn't jump to line 364, because the condition on line 363 was never true
364 for f in file_list:
365 process_file(open(f), suite, component, otype, mode, action, session)
366 else:
367 process_file(sys.stdin, suite, component, otype, mode, action, session)
368 Logger.close()
370#######################################################################################
373if __name__ == '__main__':
374 main()