]> git.decadent.org.uk Git - dak.git/blob - dak/control_suite.py
Merge commit 'sec-merge' into security
[dak.git] / dak / control_suite.py
1 #!/usr/bin/env python
2
3 # Manipulate suite tags
4 # Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006  James Troup <james@nocrew.org>
5
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.
10
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.
15
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
19
20 #######################################################################################
21
22 # 8to6Guy: "Wow, Bob, You look rough!"
23 # BTAF: "Mbblpmn..."
24 # BTAF <.oO>: "You moron! This is what you get for staying up all night drinking vodka and salad dressing!"
25 # BTAF <.oO>: "This coffee I.V. drip is barely even keeping me awake! I need something with more kick! But what?"
26 # BTAF: "OMIGOD! I OVERDOSED ON HEROIN"
27 # CoWorker#n: "Give him air!!"
28 # CoWorker#n+1: "We need a syringe full of adrenaline!"
29 # CoWorker#n+2: "Stab him in the heart!"
30 # BTAF: "*YES!*"
31 # CoWorker#n+3: "Bob's been overdosing quite a bit lately..."
32 # CoWorker#n+4: "Third time this week."
33
34 # -- http://www.angryflower.com/8to6.gif
35
36 #######################################################################################
37
38 # Adds or removes packages from a suite.  Takes the list of files
39 # either from stdin or as a command line argument.  Special action
40 # "set", will reset the suite (!) and add all packages from scratch.
41
42 #######################################################################################
43
44 import pg, sys
45 import apt_pkg
46 import daklib.database as database
47 import daklib.logging as logging
48 import daklib.utils as utils
49
50 #######################################################################################
51
52 Cnf = None
53 projectB = None
54 Logger = None
55
56 ################################################################################
57
58 def usage (exit_code=0):
59     print """Usage: dak control-suite [OPTIONS] [FILE]
60 Display or alter the contents of a suite using FILE(s), or stdin.
61
62   -a, --add=SUITE            add to SUITE
63   -h, --help                 show this help and exit
64   -l, --list=SUITE           list the contents of SUITE
65   -r, --remove=SUITE         remove from SUITE
66   -s, --set=SUITE            set SUITE"""
67
68     sys.exit(exit_code)
69
70 #######################################################################################
71
72 def get_id (package, version, architecture):
73     if architecture == "source":
74         q = projectB.query("SELECT id FROM source WHERE source = '%s' AND version = '%s'" % (package, version))
75     else:
76         q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all') AND b.architecture = a.id" % (package, version, architecture))
77
78     ql = q.getresult()
79     if not ql:
80         utils.warn("Couldn't find '%s_%s_%s'." % (package, version, architecture))
81         return None
82     if len(ql) > 1:
83         utils.warn("Found more than one match for '%s_%s_%s'." % (package, version, architecture))
84         return None
85     return ql[0][0]
86
87 #######################################################################################
88
89 def set_suite (file, suite_id):
90     lines = file.readlines()
91
92     projectB.query("BEGIN WORK")
93
94     # Build up a dictionary of what is currently in the suite
95     current = {}
96     q = projectB.query("SELECT b.package, b.version, a.arch_string, ba.id FROM binaries b, bin_associations ba, architecture a WHERE ba.suite = %s AND ba.bin = b.id AND b.architecture = a.id" % (suite_id))
97     ql = q.getresult()
98     for i in ql:
99         key = " ".join(i[:3])
100         current[key] = i[3]
101     q = projectB.query("SELECT s.source, s.version, sa.id FROM source s, src_associations sa WHERE sa.suite = %s AND sa.source = s.id" % (suite_id))
102     ql = q.getresult()
103     for i in ql:
104         key = " ".join(i[:2]) + " source"
105         current[key] = i[2]
106
107     # Build up a dictionary of what should be in the suite
108     desired = {}
109     for line in lines:
110         split_line = line.strip().split()
111         if len(split_line) != 3:
112             utils.warn("'%s' does not break into 'package version architecture'." % (line[:-1]))
113             continue
114         key = " ".join(split_line)
115         desired[key] = ""
116
117     # Check to see which packages need removed and remove them
118     for key in current.keys():
119         if not desired.has_key(key):
120             (package, version, architecture) = key.split()
121             pkid = current[key]
122             if architecture == "source":
123                 q = projectB.query("DELETE FROM src_associations WHERE id = %s" % (pkid))
124             else:
125                 q = projectB.query("DELETE FROM bin_associations WHERE id = %s" % (pkid))
126             Logger.log(["removed", key, pkid])
127
128     # Check to see which packages need added and add them
129     for key in desired.keys():
130         if not current.has_key(key):
131             (package, version, architecture) = key.split()
132             pkid = get_id (package, version, architecture)
133             if not pkid:
134                 continue
135             if architecture == "source":
136                 q = projectB.query("INSERT INTO src_associations (suite, source) VALUES (%s, %s)" % (suite_id, pkid))
137             else:
138                 q = projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%s, %s)" % (suite_id, pkid))
139             Logger.log(["added", key, pkid])
140
141     projectB.query("COMMIT WORK")
142
143 #######################################################################################
144
145 def process_file (file, suite, action):
146
147     suite_id = database.get_suite_id(suite)
148
149     if action == "set":
150         set_suite (file, suite_id)
151         return
152
153     lines = file.readlines()
154
155     projectB.query("BEGIN WORK")
156
157     for line in lines:
158         split_line = line.strip().split()
159         if len(split_line) != 3:
160             utils.warn("'%s' does not break into 'package version architecture'." % (line[:-1]))
161             continue
162
163         (package, version, architecture) = split_line
164
165         pkid = get_id(package, version, architecture)
166         if not pkid:
167             continue
168
169         if architecture == "source":
170             # Find the existing assoications ID, if any
171             q = projectB.query("SELECT id FROM src_associations WHERE suite = %s and source = %s" % (suite_id, pkid))
172             ql = q.getresult()
173             if not ql:
174                 assoication_id = None
175             else:
176                 assoication_id = ql[0][0]
177             # Take action
178             if action == "add":
179                 if assoication_id:
180                     utils.warn("'%s_%s_%s' already exists in suite %s." % (package, version, architecture, suite))
181                     continue
182                 else:
183                     q = projectB.query("INSERT INTO src_associations (suite, source) VALUES (%s, %s)" % (suite_id, pkid))
184             elif action == "remove":
185                 if assoication_id == None:
186                     utils.warn("'%s_%s_%s' doesn't exist in suite %s." % (package, version, architecture, suite))
187                     continue
188                 else:
189                     q = projectB.query("DELETE FROM src_associations WHERE id = %s" % (assoication_id))
190         else:
191             # Find the existing assoications ID, if any
192             q = projectB.query("SELECT id FROM bin_associations WHERE suite = %s and bin = %s" % (suite_id, pkid))
193             ql = q.getresult()
194             if not ql:
195                 assoication_id = None
196             else:
197                 assoication_id = ql[0][0]
198             # Take action
199             if action == "add":
200                 if assoication_id:
201                     utils.warn("'%s_%s_%s' already exists in suite %s." % (package, version, architecture, suite))
202                     continue
203                 else:
204                     q = projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%s, %s)" % (suite_id, pkid))
205             elif action == "remove":
206                 if assoication_id == None:
207                     utils.warn("'%s_%s_%s' doesn't exist in suite %s." % (package, version, architecture, suite))
208                     continue
209                 else:
210                     q = projectB.query("DELETE FROM bin_associations WHERE id = %s" % (assoication_id))
211
212     projectB.query("COMMIT WORK")
213
214 #######################################################################################
215
216 def get_list (suite):
217     suite_id = database.get_suite_id(suite)
218     # List binaries
219     q = projectB.query("SELECT b.package, b.version, a.arch_string FROM binaries b, bin_associations ba, architecture a WHERE ba.suite = %s AND ba.bin = b.id AND b.architecture = a.id" % (suite_id))
220     ql = q.getresult()
221     for i in ql:
222         print " ".join(i)
223
224     # List source
225     q = projectB.query("SELECT s.source, s.version FROM source s, src_associations sa WHERE sa.suite = %s AND sa.source = s.id" % (suite_id))
226     ql = q.getresult()
227     for i in ql:
228         print " ".join(i) + " source"
229
230 #######################################################################################
231
232 def main ():
233     global Cnf, projectB, Logger
234
235     Cnf = utils.get_conf()
236
237     Arguments = [('a',"add","Control-Suite::Options::Add", "HasArg"),
238                  ('h',"help","Control-Suite::Options::Help"),
239                  ('l',"list","Control-Suite::Options::List","HasArg"),
240                  ('r',"remove", "Control-Suite::Options::Remove", "HasArg"),
241                  ('s',"set", "Control-Suite::Options::Set", "HasArg")]
242
243     for i in ["add", "help", "list", "remove", "set", "version" ]:
244         if not Cnf.has_key("Control-Suite::Options::%s" % (i)):
245             Cnf["Control-Suite::Options::%s" % (i)] = ""
246
247     file_list = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv)
248     Options = Cnf.SubTree("Control-Suite::Options")
249
250     if Options["Help"]:
251         usage()
252
253     projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"],int(Cnf["DB::Port"]))
254
255     database.init(Cnf, projectB)
256
257     action = None
258
259     for i in ("add", "list", "remove", "set"):
260         if Cnf["Control-Suite::Options::%s" % (i)] != "":
261             suite = Cnf["Control-Suite::Options::%s" % (i)]
262             if database.get_suite_id(suite) == -1:
263                 utils.fubar("Unknown suite '%s'." %(suite))
264             else:
265                 if action:
266                     utils.fubar("Can only perform one action at a time.")
267                 action = i
268
269     # Need an action...
270     if action == None:
271         utils.fubar("No action specified.")
272
273     # Safety/Sanity check
274     if action == "set" and suite not in ["testing", "etch-m68k"]:
275         utils.fubar("Will not reset a suite other than testing.")
276
277     if action == "list":
278         get_list(suite)
279     else:
280         Logger = logging.Logger(Cnf, "control-suite")
281         if file_list:
282             for f in file_list:
283                 process_file(utils.open_file(f), suite, action)
284         else:
285             process_file(sys.stdin, suite, action)
286         Logger.close()
287
288 #######################################################################################
289
290 if __name__ == '__main__':
291     main()