]> git.decadent.org.uk Git - dak.git/blob - dak/admin.py
Merge branch 'master' into security
[dak.git] / dak / admin.py
1 #!/usr/bin/env python
2
3 """Configure dak parameters in the database"""
4 # Copyright (C) 2009  Mark Hymers <mhy@debian.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 import sys
23
24 import apt_pkg
25
26 from daklib import utils
27 from daklib.dbconn import *
28
29 ################################################################################
30
31 dispatch = {}
32 dryrun = False
33
34 ################################################################################
35 def warn(msg):
36     print >> sys.stderr, msg
37
38 def die(msg, exit_code=1):
39     print >> sys.stderr, msg
40     sys.exit(exit_code)
41
42 def die_arglen(args, args_needed, msg):
43     if len(args) < args_needed:
44         die(msg)
45
46 def usage(exit_code=0):
47     """Perform administrative work on the dak database."""
48
49     print """Usage: dak admin COMMAND
50 Perform administrative work on the dak database.
51
52   -h, --help          show this help and exit.
53   -n, --dry-run       don't do anything, just show what would have been done
54                       (only applies to add or rm operations).
55
56   Commands can use a long or abbreviated form:
57
58   config / c:
59      c db                   show db config
60      c db-shell             show db config in a usable form for psql
61
62   architecture / a:
63      a list                 show a list of architectures
64      a rm ARCH              remove an architecture (will only work if
65                             no longer linked to any suites)
66      a add ARCH DESCRIPTION [SUITELIST]
67                             add architecture ARCH with DESCRIPTION.
68                             If SUITELIST is given, add to each of the
69                             suites at the same time
70
71   suite / s:
72      s list                 show a list of suites
73      s show SUITE           show config details for a suite
74      s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ]
75                          [ origin=ORIGIN ] [ codename=CODENAME ]
76                             add suite SUITE, version VERSION. label,
77                             description, origin and codename are optional.
78
79   suite-architecture / s-a:
80      s-a list-suite ARCH    show the suites an ARCH is in
81      s-a list-arch SUITE    show the architectures in a SUITE
82      s-a add SUITE ARCH     add ARCH to suite
83      s-a rm SUITE ARCH      remove ARCH from suite (will only work if
84                             no packages remain for the arch in the suite)
85 """
86     sys.exit(exit_code)
87
88 ################################################################################
89
90 def __architecture_list(d, args):
91     q = d.session().query(Architecture).order_by('arch_string')
92     for j in q.all():
93         # HACK: We should get rid of source from the arch table
94         if j.arch_string == 'source': continue
95         print j.arch_string
96     sys.exit(0)
97
98 def __architecture_add(d, args):
99     die_arglen(args, 3, "E: adding an architecture requires a name and a description")
100     print "Adding architecture %s" % args[2]
101     suites = [str(x) for x in args[4:]]
102     print suites
103     if not dryrun:
104         try:
105             s = d.session()
106             a = Architecture()
107             a.arch_string = str(args[2]).lower()
108             a.description = str(args[3])
109             s.add(a)
110             for sn in suites:
111                 su = get_suite(sn ,s)
112                 if su is not None:
113                     archsu = SuiteArchitecture()
114                     archsu.arch_id = a.arch_id
115                     archsu.suite_id = su.suite_id
116                     s.add(archsu)
117                 else:
118                     warn("W: Cannot find suite %s" % su)
119             s.commit()
120         except IntegrityError, e:
121             die("E: Integrity error adding architecture %s (it probably already exists)" % args[2])
122         except SQLAlchemyError, e:
123             die("E: Error adding architecture %s (%s)" % (args[2], e))
124     print "Architecture %s added" % (args[2])
125
126 def __architecture_rm(d, args):
127     die_arglen(args, 3, "E: removing an architecture requires at least a name")
128     print "Removing architecture %s" % args[2]
129     if not dryrun:
130         try:
131             s = d.session()
132             a = get_architecture(args[2].lower(), s)
133             if a is None:
134                 die("E: Cannot find architecture %s" % args[2])
135             s.delete(a)
136             s.commit()
137         except IntegrityError, e:
138             die("E: Integrity error removing architecture %s (suite-arch entries probably still exist)" % args[2])
139         except SQLAlchemyError, e:
140             die("E: Error removing architecture %s (%s)" % (args[2], e))
141     print "Architecture %s removed" % args[2]
142
143 def architecture(command):
144     args = [str(x) for x in command]
145     Cnf = utils.get_conf()
146     d = DBConn()
147
148     die_arglen(args, 2, "E: architecture needs at least a command")
149
150     mode = args[1].lower()
151     if mode == 'list':
152         __architecture_list(d, args)
153     elif mode == 'add':
154         __architecture_add(d, args)
155     elif mode == 'rm':
156         __architecture_rm(d, args)
157     else:
158         die("E: architecture command unknown")
159
160 dispatch['architecture'] = architecture
161 dispatch['a'] = architecture
162
163 ################################################################################
164
165 def __suite_list(d, args):
166     s = d.session()
167     for j in s.query(Suite).order_by('suite_name').all():
168         print j.suite_name
169
170 def __suite_show(d, args):
171     if len(args) < 2:
172         die("E: showing an suite entry requires a suite")
173
174     s = d.session()
175     su = get_suite(args[2].lower())
176     if su is None:
177         die("E: can't find suite entry for %s" % (args[2].lower()))
178
179     print su.details()
180
181 def __suite_add(d, args):
182     die_arglen(args, 4, "E: adding a suite requires at least a name and a version")
183     suite_name = args[2].lower()
184     version = args[3]
185     rest = args[3:]
186
187     def get_field(field):
188         for varval in args:
189             if varval.startswith(field + '='):
190                 return varval.split('=')[1]
191         return None
192
193     print "Adding suite %s" % suite_name
194     if not dryrun:
195         try:
196             s = d.session()
197             suite = Suite()
198             suite.suite_name = suite_name
199             suite.version = version
200             suite.label = get_field('label')
201             suite.description = get_field('description')
202             suite.origin = get_field('origin')
203             suite.codename = get_field('codename')
204             s.add(suite)
205             s.commit()
206         except IntegrityError, e:
207             die("E: Integrity error adding suite %s (it probably already exists)" % suite_name)
208         except SQLAlchemyError, e:
209             die("E: Error adding suite %s (%s)" % (suite_name, e))
210     print "Suite %s added" % (suite_name)
211
212 def suite(command):
213     args = [str(x) for x in command]
214     Cnf = utils.get_conf()
215     d = DBConn()
216
217     die_arglen(args, 2, "E: suite needs at least a command")
218
219     mode = args[1].lower()
220
221     if mode == 'list':
222         __suite_list(d, args)
223     elif mode == 'show':
224         __suite_show(d, args)
225     elif mode == 'add':
226         __suite_add(d, args)
227     else:
228         die("E: suite command unknown")
229
230 dispatch['suite'] = suite
231 dispatch['s'] = suite
232
233 ################################################################################
234
235 def __suite_architecture_list(d, args):
236     s = d.session()
237     for j in s.query(Suite).order_by('suite_name').all():
238         print j.suite_name + ' ' + \
239               ','.join([a.architecture.arch_string for a in j.suitearchitectures])
240
241 def __suite_architecture_listarch(d, args):
242     die_arglen(args, 3, "E: suite-architecture list-arch requires a suite")
243     a = get_suite_architectures(args[2].lower())
244     for j in a:
245         # HACK: We should get rid of source from the arch table
246         if j.arch_string != 'source':
247             print j.arch_string
248
249
250 def __suite_architecture_listsuite(d, args):
251     die_arglen(args, 3, "E: suite-architecture list-suite requires an arch")
252     for j in get_architecture_suites(args[2].lower()):
253         print j.suite_name
254
255
256 def __suite_architecture_add(d, args):
257     if len(args) < 3:
258         die("E: adding a suite-architecture entry requires a suite and arch")
259
260     s = d.session()
261
262     suite = get_suite(args[2].lower(), s)
263     if suite is None: die("E: Can't find suite %s" % args[2].lower())
264
265     arch = get_architecture(args[3].lower(), s)
266     if arch is None: die("E: Can't find architecture %s" % args[3].lower())
267
268     if not dryrun:
269         try:
270             sa = SuiteArchitecture()
271             sa.arch_id = arch.arch_id
272             sa.suite_id = suite.suite_id
273             s.add(sa)
274             s.commit()
275         except IntegrityError, e:
276             die("E: Can't add suite-architecture entry (%s, %s) - probably already exists" % (args[2].lower(), args[3].lower()))
277         except SQLAlchemyError, e:
278             die("E: Can't add suite-architecture entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
279
280     print "Added suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower())
281
282
283 def __suite_architecture_rm(d, args):
284     if len(args) < 3:
285         die("E: removing an suite-architecture entry requires a suite and arch")
286
287     s = d.session()
288     if not dryrun:
289         try:
290             sa = get_suite_architecture(args[2].lower(), args[3].lower(), s)
291             if sa is None:
292                 die("E: can't find suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower()))
293             s.delete(sa)
294             s.commit()
295         except IntegrityError, e:
296             die("E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower()))
297         except SQLAlchemyError, e:
298             die("E: Can't remove suite-architecture entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
299
300     print "Removed suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower())
301
302
303 def suite_architecture(command):
304     args = [str(x) for x in command]
305     Cnf = utils.get_conf()
306     d = DBConn()
307
308     die_arglen(args, 2, "E: suite-architecture needs at least a command")
309
310     mode = args[1].lower()
311
312     if mode == 'list':
313         __suite_architecture_list(d, args)
314     elif mode == 'list-arch':
315         __suite_architecture_listarch(d, args)
316     elif mode == 'list-suite':
317         __suite_architecture_listsuite(d, args)
318     elif mode == 'add':
319         __suite_architecture_add(d, args)
320     elif mode == 'rm':
321         __suite_architecture_rm(d, args)
322     else:
323         die("E: suite-architecture command unknown")
324
325 dispatch['suite-architecture'] = suite_architecture
326 dispatch['s-a'] = suite_architecture
327
328 ################################################################################
329
330 def show_config(command):
331     args = [str(x) for x in command]
332     cnf = utils.get_conf()
333
334     die_arglen(args, 2, "E: config needs at least a command")
335
336     mode = args[1].lower()
337
338     if mode == 'db':
339         connstr = ""
340         if cnf["DB::Host"]:
341             # TCP/IP
342             connstr = "postgres://%s" % cnf["DB::Host"]
343             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
344                 connstr += ":%s" % cnf["DB::Port"]
345             connstr += "/%s" % cnf["DB::Name"]
346         else:
347             # Unix Socket
348             connstr = "postgres:///%s" % cnf["DB::Name"]
349             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
350                 connstr += "?port=%s" % cnf["DB::Port"]
351         print connstr
352     elif mode == 'db-shell':
353         e = ['PGDATABASE']
354         print "PGDATABASE=%s" % cnf["DB::Name"]
355         if cnf["DB::Host"]:
356             print "PGHOST=%s" % cnf["DB::Host"]
357             e.append('PGHOST')
358         if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
359             print "PGPORT=%s" % cnf["DB::Port"]
360             e.append('PGPORT')
361         print "export " + " ".join(e)
362     else:
363         die("E: config command unknown")
364
365 dispatch['config'] = show_config
366 dispatch['c'] = show_config
367
368 ################################################################################
369
370 def main():
371     """Perform administrative work on the dak database"""
372     global dryrun
373     Cnf = utils.get_conf()
374     arguments = [('h', "help", "Admin::Options::Help"),
375                  ('n', "dry-run", "Admin::Options::Dry-Run")]
376     for i in [ "help", "dry-run" ]:
377         if not Cnf.has_key("Admin::Options::%s" % (i)):
378             Cnf["Admin::Options::%s" % (i)] = ""
379
380     arguments = apt_pkg.ParseCommandLine(Cnf, arguments, sys.argv)
381
382     options = Cnf.SubTree("Admin::Options")
383     if options["Help"] or len(arguments) < 1:
384         usage()
385     if options["Dry-Run"]:
386         dryrun = True
387
388     subcommand = str(arguments[0])
389
390     if subcommand in dispatch.keys():
391         dispatch[subcommand](arguments)
392     else:
393         die("E: Unknown command")
394
395 ################################################################################
396
397 if __name__ == '__main__':
398     main()