]> git.decadent.org.uk Git - dak.git/blob - heidi
Add new top level directories
[dak.git] / heidi
1 #!/usr/bin/env python
2
3 # Manipulate suite tags
4 # Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005  James Troup <james@nocrew.org>
5 # $Id: heidi,v 1.19 2005-11-15 09:50:32 ajt Exp $
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 # 8to6Guy: "Wow, Bob, You look rough!"
24 # BTAF: "Mbblpmn..."
25 # BTAF <.oO>: "You moron! This is what you get for staying up all night drinking vodka and salad dressing!"
26 # BTAF <.oO>: "This coffee I.V. drip is barely even keeping me awake! I need something with more kick! But what?"
27 # BTAF: "OMIGOD! I OVERDOSED ON HEROIN"
28 # CoWorker#n: "Give him air!!"
29 # CoWorker#n+1: "We need a syringe full of adrenaline!"
30 # CoWorker#n+2: "Stab him in the heart!"
31 # BTAF: "*YES!*"
32 # CoWorker#n+3: "Bob's been overdosing quite a bit lately..."
33 # CoWorker#n+4: "Third time this week."
34
35 # -- http://www.angryflower.com/8to6.gif
36
37 #######################################################################################
38
39 # Adds or removes packages from a suite.  Takes the list of files
40 # either from stdin or as a command line argument.  Special action
41 # "set", will reset the suite (!) and add all packages from scratch.
42
43 #######################################################################################
44
45 import pg, sys;
46 import apt_pkg;
47 import utils, db_access, logging;
48
49 #######################################################################################
50
51 Cnf = None;
52 projectB = None;
53 Logger = None;
54
55 ################################################################################
56
57 def usage (exit_code=0):
58     print """Usage: heidi [OPTIONS] [FILE]
59 Display or alter the contents of a suite using FILE(s), or stdin.
60
61   -a, --add=SUITE            add to SUITE
62   -h, --help                 show this help and exit
63   -l, --list=SUITE           list the contents of SUITE
64   -r, --remove=SUITE         remove from SUITE
65   -s, --set=SUITE            set SUITE"""
66
67     sys.exit(exit_code)
68
69 #######################################################################################
70
71 def get_id (package, version, architecture):
72     if architecture == "source":
73         q = projectB.query("SELECT id FROM source WHERE source = '%s' AND version = '%s'" % (package, version))
74     else:
75         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))
76
77     ql = q.getresult();
78     if not ql:
79         utils.warn("Couldn't find '%s~%s~%s'." % (package, version, architecture));
80         return None;
81     if len(ql) > 1:
82         utils.warn("Found more than one match for '%s~%s~%s'." % (package, version, architecture));
83         return None;
84     id = ql[0][0];
85     return id;
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             id = current[key];
122             if architecture == "source":
123                 q = projectB.query("DELETE FROM src_associations WHERE id = %s" % (id));
124             else:
125                 q = projectB.query("DELETE FROM bin_associations WHERE id = %s" % (id));
126             Logger.log(["removed",key,id]);
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             id = get_id (package, version, architecture);
133             if not id:
134                 continue;
135             if architecture == "source":
136                 q = projectB.query("INSERT INTO src_associations (suite, source) VALUES (%s, %s)" % (suite_id, id));
137             else:
138                 q = projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%s, %s)" % (suite_id, id));
139             Logger.log(["added",key,id]);
140
141     projectB.query("COMMIT WORK");
142
143 #######################################################################################
144
145 def process_file (file, suite, action):
146
147     suite_id = db_access.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         id = get_id(package, version, architecture);
166         if not id:
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, id));
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, id));
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, id));
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, id));
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 = db_access.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","Heidi::Options::Add", "HasArg"),
238                  ('h',"help","Heidi::Options::Help"),
239                  ('l',"list","Heidi::Options::List","HasArg"),
240                  ('r',"remove", "Heidi::Options::Remove", "HasArg"),
241                  ('s',"set", "Heidi::Options::Set", "HasArg")];
242
243     for i in ["add", "help", "list", "remove", "set", "version" ]:
244         if not Cnf.has_key("Heidi::Options::%s" % (i)):
245             Cnf["Heidi::Options::%s" % (i)] = "";
246
247     file_list = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv);
248     Options = Cnf.SubTree("Heidi::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     db_access.init(Cnf, projectB);
256
257     action = None;
258
259     for i in ("add", "list", "remove", "set"):
260         if Cnf["Heidi::Options::%s" % (i)] != "":
261             suite = Cnf["Heidi::Options::%s" % (i)];
262             if db_access.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 != "testing":
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, "heidi");
281         if file_list:
282             for file in file_list:
283                 process_file(utils.open_file(file), suite, action);
284         else:
285             process_file(sys.stdin, suite, action);
286         Logger.close();
287
288 #######################################################################################
289
290 if __name__ == '__main__':
291     main()
292