]> git.decadent.org.uk Git - dak.git/blob - dak/admin.py
Add "add-build-queue" to quickly setup build queues.
[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 import daklib.archive
27 import daklib.gpg
28
29 from daklib import utils
30 from daklib.dbconn import *
31 from sqlalchemy.orm.exc import NoResultFound
32
33 ################################################################################
34
35 dispatch = {}
36 dryrun = False
37
38 ################################################################################
39 def warn(msg):
40     print >> sys.stderr, msg
41
42 def die(msg, exit_code=1):
43     print >> sys.stderr, msg
44     sys.exit(exit_code)
45
46 def die_arglen(args, args_needed, msg):
47     if len(args) < args_needed:
48         die(msg)
49
50 def usage(exit_code=0):
51     """Perform administrative work on the dak database."""
52
53     print """Usage: dak admin COMMAND
54 Perform administrative work on the dak database.
55
56   -h, --help          show this help and exit.
57   -n, --dry-run       don't do anything, just show what would have been done
58                       (only applies to add or rm operations).
59
60   Commands can use a long or abbreviated form:
61
62   config / c:
63      c db                   show db config
64      c db-shell             show db config in a usable form for psql
65      c NAME                 show option NAME as set in configuration table
66
67   keyring / k:
68      k list-all             list all keyrings
69      k list-binary          list all keyrings with a NULL source acl
70      k list-source          list all keyrings with a non NULL source acl
71      k add-buildd NAME ARCH...   add buildd keyring with upload permission
72                                  for the given architectures
73
74   architecture / a:
75      a list                 show a list of architectures
76      a rm ARCH              remove an architecture (will only work if
77                             no longer linked to any suites)
78      a add ARCH DESCRIPTION [SUITELIST]
79                             add architecture ARCH with DESCRIPTION.
80                             If SUITELIST is given, add to each of the
81                             suites at the same time
82
83   component:
84      component list         show a list of components
85      component rm COMPONENT remove a component (will only work if
86                             empty)
87      component add NAME DESCRIPTION ORDERING
88                             add component NAME with DESCRIPTION.
89                             Ordered at ORDERING.
90
91   suite / s:
92      s list [--print-archive]
93                             show a list of suites
94      s show SUITE           show config details for a suite
95      s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ]
96                          [ origin=ORIGIN ] [ codename=CODENAME ]
97                          [ signingkey=SIGNINGKEY ] [ archive=ARCHIVE ]
98                             add suite SUITE, version VERSION.
99                             label, description, origin, codename
100                             and signingkey are optional.
101
102      s add-all-arches SUITE VERSION... as "s add" but adds suite-architecture
103                             relationships for all architectures
104      s add-build-queue SUITE BUILD-QUEUE BUILD-QUEUE-CODENAME BUILD-QUEUE-ARCHIVE
105                             add a build queue for an existing suite
106
107   suite-architecture / s-a:
108      s-a list               show the architectures for all suites
109      s-a list-suite ARCH    show the suites an ARCH is in
110      s-a list-arch SUITE    show the architectures in a SUITE
111      s-a add SUITE ARCH     add ARCH to suite
112      s-a rm SUITE ARCH      remove ARCH from suite (will only work if
113                             no packages remain for the arch in the suite)
114
115   suite-component / s-c:
116      s-c list               show the architectures for all suites
117      s-c list-suite COMPONENT
118                             show the suites a COMPONENT is in
119      s-c list-component SUITE
120                             show the components in a SUITE
121      s-c add SUITE COMPONENT
122                             add COMPONENT to suite
123      s-c rm SUITE COMPONENT remove component from suite (will only work if
124                             no packages remain for the component in the suite)
125
126   archive:
127      archive list           list all archives
128      archive add NAME ROOT DESCRIPTION [primary-mirror=MIRROR] [tainted=1]
129                             add archive NAME with path ROOT,
130                             primary mirror MIRROR.
131      archive rm NAME        remove archive NAME (will only work if there are
132                             no files and no suites in the archive)
133      archive rename OLD NEW rename archive OLD to NEW
134
135   version-check / v-c:
136      v-c list                        show version checks for all suites
137      v-c list-suite SUITE            show version checks for suite SUITE
138      v-c add SUITE CHECK REFERENCE   add a version check for suite SUITE
139      v-c rm SUITE CHECK REFERENCE    remove a version check
140        where
141          CHECK     is one of Enhances, MustBeNewerThan, MustBeOlderThan
142          REFERENCE is another suite name
143
144   change-component:
145      change-component SUITE COMPONENT source SOURCE...
146      change-component SUITE COMPONENT binary BINARY...
147          Move source or binary packages to a different component by copying
148          associated files and changing the overrides.
149
150   forget-signature FILE:    forget that we saw FILE
151 """
152     sys.exit(exit_code)
153
154 ################################################################################
155
156 def __architecture_list(d, args):
157     q = d.session().query(Architecture).order_by(Architecture.arch_string)
158     for j in q.all():
159         # HACK: We should get rid of source from the arch table
160         if j.arch_string == 'source': continue
161         print j.arch_string
162     sys.exit(0)
163
164 def __architecture_add(d, args):
165     die_arglen(args, 4, "E: adding an architecture requires a name and a description")
166     print "Adding architecture %s" % args[2]
167     suites = [str(x) for x in args[4:]]
168     if len(suites) > 0:
169         print "Adding to suites %s" % ", ".join(suites)
170     if not dryrun:
171         try:
172             s = d.session()
173             a = Architecture()
174             a.arch_string = str(args[2]).lower()
175             a.description = str(args[3])
176             s.add(a)
177             for sn in suites:
178                 su = get_suite(sn, s)
179                 if su is not None:
180                     a.suites.append(su)
181                 else:
182                     warn("W: Cannot find suite %s" % su)
183             s.commit()
184         except IntegrityError as e:
185             die("E: Integrity error adding architecture %s (it probably already exists)" % args[2])
186         except SQLAlchemyError as e:
187             die("E: Error adding architecture %s (%s)" % (args[2], e))
188     print "Architecture %s added" % (args[2])
189
190 def __architecture_rm(d, args):
191     die_arglen(args, 3, "E: removing an architecture requires at least a name")
192     print "Removing architecture %s" % args[2]
193     if not dryrun:
194         try:
195             s = d.session()
196             a = get_architecture(args[2].lower(), s)
197             if a is None:
198                 die("E: Cannot find architecture %s" % args[2])
199             s.delete(a)
200             s.commit()
201         except IntegrityError as e:
202             die("E: Integrity error removing architecture %s (suite-arch entries probably still exist)" % args[2])
203         except SQLAlchemyError as e:
204             die("E: Error removing architecture %s (%s)" % (args[2], e))
205     print "Architecture %s removed" % args[2]
206
207 def architecture(command):
208     args = [str(x) for x in command]
209     Cnf = utils.get_conf()
210     d = DBConn()
211
212     die_arglen(args, 2, "E: architecture needs at least a command")
213
214     mode = args[1].lower()
215     if mode == 'list':
216         __architecture_list(d, args)
217     elif mode == 'add':
218         __architecture_add(d, args)
219     elif mode == 'rm':
220         __architecture_rm(d, args)
221     else:
222         die("E: architecture command unknown")
223
224 dispatch['architecture'] = architecture
225 dispatch['a'] = architecture
226
227 ################################################################################
228
229 def component_list():
230     session = DBConn().session()
231     for component in session.query(Component).order_by(Component.component_name):
232         print "{0} ordering={1}".format(component.component_name, component.ordering)
233
234 def component_add(args):
235     (name, description, ordering) = args[0:3]
236
237     attributes = dict(
238         component_name=name,
239         description=description,
240         ordering=ordering,
241         )
242
243     for option in args[3:]:
244         (key, value) = option.split('=')
245         attributes[key] = value
246
247     session = DBConn().session()
248
249     component = Component()
250     for key, value in attributes.iteritems():
251         setattr(component, key, value)
252
253     session.add(component)
254     session.flush()
255
256     if dryrun:
257         session.rollback()
258     else:
259         session.commit()
260
261 def component_rm(name):
262     session = DBConn().session()
263     component = get_component(name, session)
264     session.delete(component)
265     session.flush()
266
267     if dryrun:
268         session.rollback()
269     else:
270         session.commit()
271
272 def component_rename(oldname, newname):
273     session = DBConn().session()
274     component = get_component(oldname, session)
275     component.component_name = newname
276     session.flush()
277
278     if dryrun:
279         session.rollback()
280     else:
281         session.commit()
282
283 def component(command):
284     mode = command[1]
285     if mode == 'list':
286         component_list()
287     elif mode == 'rename':
288         component_rename(command[2], command[3])
289     elif mode == 'add':
290         component_add(command[2:])
291     elif mode == 'rm':
292         component_rm(command[2])
293     else:
294         die("E: component command unknown")
295
296 dispatch['component'] = component
297
298 ################################################################################
299
300 def __suite_list(d, args):
301     s = d.session()
302     for j in s.query(Suite).join(Suite.archive).order_by(Archive.archive_name, Suite.suite_name).all():
303         if len(args) > 2 and args[2] == "--print-archive":
304             print "{0} {1}".format(j.archive.archive_name, j.suite_name)
305         else:
306             print "{0}".format(j.suite_name)
307
308 def __suite_show(d, args):
309     if len(args) < 2:
310         die("E: showing an suite entry requires a suite")
311
312     s = d.session()
313     su = get_suite(args[2].lower())
314     if su is None:
315         die("E: can't find suite entry for %s" % (args[2].lower()))
316
317     print su.details()
318
319 def __suite_add(d, args, addallarches=False):
320     die_arglen(args, 4, "E: adding a suite requires at least a name and a version")
321     suite_name = args[2].lower()
322     version = args[3]
323     rest = args[3:]
324
325     def get_field(field):
326         for varval in args:
327             if varval.startswith(field + '='):
328                 return varval.split('=')[1]
329         return None
330
331     print "Adding suite %s" % suite_name
332     if not dryrun:
333         try:
334             s = d.session()
335             suite = Suite()
336             suite.suite_name = suite_name
337             suite.overridecodename = suite_name
338             suite.version = version
339             suite.label = get_field('label')
340             suite.description = get_field('description')
341             suite.origin = get_field('origin')
342             suite.codename = get_field('codename')
343             signingkey = get_field('signingkey')
344             if signingkey is not None:
345                 suite.signingkeys = [signingkey.upper()]
346             archive_name = get_field('archive')
347             if archive_name is not None:
348                 suite.archive = get_archive(archive_name, s)
349             else:
350                 suite.archive = s.query(Archive).filter(~Archive.archive_name.in_(['build-queues', 'new', 'policy'])).one()
351             suite.srcformats = s.query(SrcFormat).all()
352             s.add(suite)
353             s.flush()
354         except IntegrityError as e:
355             die("E: Integrity error adding suite %s (it probably already exists)" % suite_name)
356         except SQLAlchemyError as e:
357             die("E: Error adding suite %s (%s)" % (suite_name, e))
358     print "Suite %s added" % (suite_name)
359
360     if addallarches:
361         arches = []
362         q = s.query(Architecture).order_by(Architecture.arch_string)
363         for arch in q.all():
364             suite.architectures.append(arch)
365             arches.append(arch.arch_string)
366
367         print "Architectures %s added to %s" % (','.join(arches), suite_name)
368
369     s.commit()
370
371 def __suite_rm(d, args):
372     die_arglen(args, 3, "E: removing a suite requires at least a name")
373     name = args[2]
374     print "Removing suite {0}".format(name)
375     if not dryrun:
376         try:
377             s = d.session()
378             su = get_suite(name.lower())
379             if su is None:
380                 die("E: Cannot find suite {0}".format(name))
381             s.delete(su)
382             s.commit()
383         except IntegrityError as e:
384             die("E: Integrity error removing suite {0} (suite-arch entries probably still exist)".format(name))
385         except SQLAlchemyError as e:
386             die("E: Error removing suite {0} ({1})".format(name, e))
387     print "Suite {0} removed".format(name)
388
389 def __suite_add_build_queue(d, args):
390     session = d.session()
391
392     die_arglen(args, 6, "E: Adding a build queue needs four parameters.")
393
394     suite_name = args[2]
395     build_queue_name = args[3]
396     build_queue_codename = args[4]
397     build_queue_archive_name = args[5]
398     try:
399         suite = session.query(Suite).filter_by(suite_name=suite_name).one()
400     except NoResultFound:
401         die("E: Unknown suite '{0}'".format(suite_name))
402     try:
403         build_queue_archive = session.query(Archive).filter_by(archive_name=build_queue_archive_name).one()
404     except NoResultFound:
405         die("E: Unknown archive '{1}'".format(build_queue_archive_name))
406
407     # Create suite
408     s = Suite()
409     s.suite_name = build_queue_name
410     s.origin = suite.origin
411     s.label = suite.label
412     s.description = "buildd {0} incoming".format(suite_name)
413     s.codename = build_queue_codename
414     s.notautomatic = suite.notautomatic
415     s.overridesuite = suite.overridesuite
416     s.butautomaticupgrades = suite.butautomaticupgrades
417     s.signingkeys = suite.signingkeys
418     s.include_long_description = False
419
420     s.archive = build_queue_archive
421     s.architectures.extend(suite.architectures)
422     s.components.extend(suite.components)
423     s.srcformats.extend(suite.srcformats)
424
425     session.add(s)
426     session.flush()
427
428     bq = BuildQueue()
429     bq.queue_name = build_queue_codename
430     bq.suite = s
431
432     session.add(bq)
433     session.flush()
434
435     suite.copy_queues.append(bq)
436
437     session.commit()
438
439 def suite(command):
440     args = [str(x) for x in command]
441     Cnf = utils.get_conf()
442     d = DBConn()
443
444     die_arglen(args, 2, "E: suite needs at least a command")
445
446     mode = args[1].lower()
447
448     if mode == 'list':
449         __suite_list(d, args)
450     elif mode == 'show':
451         __suite_show(d, args)
452     elif mode == 'rm':
453         __suite_rm(d, args)
454     elif mode == 'add':
455         __suite_add(d, args, False)
456     elif mode == 'add-all-arches':
457         __suite_add(d, args, True)
458     elif mode == 'add-build-queue':
459         __suite_add_build_queue(d, args)
460     else:
461         die("E: suite command unknown")
462
463 dispatch['suite'] = suite
464 dispatch['s'] = suite
465
466 ################################################################################
467
468 def __suite_architecture_list(d, args):
469     s = d.session()
470     for j in s.query(Suite).order_by(Suite.suite_name):
471         architectures = j.get_architectures(skipsrc = True, skipall = True)
472         print j.suite_name + ': ' + \
473               ', '.join([a.arch_string for a in architectures])
474
475 def __suite_architecture_listarch(d, args):
476     die_arglen(args, 3, "E: suite-architecture list-arch requires a suite")
477     suite = get_suite(args[2].lower(), d.session())
478     if suite is None:
479         die('E: suite %s is invalid' % args[2].lower())
480     a = suite.get_architectures(skipsrc = True, skipall = True)
481     for j in a:
482         print j.arch_string
483
484
485 def __suite_architecture_listsuite(d, args):
486     die_arglen(args, 3, "E: suite-architecture list-suite requires an arch")
487     architecture = get_architecture(args[2].lower(), d.session())
488     if architecture is None:
489         die("E: architecture %s is invalid" % args[2].lower())
490     for j in architecture.suites:
491         print j.suite_name
492
493
494 def __suite_architecture_add(d, args):
495     if len(args) < 3:
496         die("E: adding a suite-architecture entry requires a suite and arch")
497
498     s = d.session()
499
500     suite = get_suite(args[2].lower(), s)
501     if suite is None: die("E: Can't find suite %s" % args[2].lower())
502
503     for arch_name in args[3:]:
504         arch = get_architecture(arch_name.lower(), s)
505         if arch is None: die("E: Can't find architecture %s" % args[3].lower())
506
507         try:
508             suite.architectures.append(arch)
509             s.flush()
510         except IntegrityError as e:
511             die("E: Can't add suite-architecture entry (%s, %s) - probably already exists" % (args[2].lower(), arch_name))
512         except SQLAlchemyError as e:
513             die("E: Can't add suite-architecture entry (%s, %s) - %s" % (args[2].lower(), arch_name, e))
514
515         print "Added suite-architecture entry for %s, %s" % (args[2].lower(), arch_name)
516
517     if not dryrun:
518         s.commit()
519
520     s.close()
521
522 def __suite_architecture_rm(d, args):
523     if len(args) < 3:
524         die("E: removing an suite-architecture entry requires a suite and arch")
525
526     s = d.session()
527     if not dryrun:
528         try:
529             suite_name = args[2].lower()
530             suite = get_suite(suite_name, s)
531             if suite is None:
532                 die('E: no such suite %s' % suite_name)
533             arch_string = args[3].lower()
534             architecture = get_architecture(arch_string, s)
535             if architecture not in suite.architectures:
536                 die("E: architecture %s not found in suite %s" % (arch_string, suite_name))
537             suite.architectures.remove(architecture)
538             s.commit()
539         except IntegrityError as e:
540             die("E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower()))
541         except SQLAlchemyError as e:
542             die("E: Can't remove suite-architecture entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
543
544     print "Removed suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower())
545
546
547 def suite_architecture(command):
548     args = [str(x) for x in command]
549     Cnf = utils.get_conf()
550     d = DBConn()
551
552     die_arglen(args, 2, "E: suite-architecture needs at least a command")
553
554     mode = args[1].lower()
555
556     if mode == 'list':
557         __suite_architecture_list(d, args)
558     elif mode == 'list-arch':
559         __suite_architecture_listarch(d, args)
560     elif mode == 'list-suite':
561         __suite_architecture_listsuite(d, args)
562     elif mode == 'add':
563         __suite_architecture_add(d, args)
564     elif mode == 'rm':
565         __suite_architecture_rm(d, args)
566     else:
567         die("E: suite-architecture command unknown")
568
569 dispatch['suite-architecture'] = suite_architecture
570 dispatch['s-a'] = suite_architecture
571
572 ################################################################################
573
574 def __suite_component_list(d, args):
575     s = d.session()
576     for j in s.query(Suite).order_by(Suite.suite_name):
577         components = j.components
578         print j.suite_name + ': ' + \
579               ', '.join([c.component_name for c in components])
580
581
582 def __suite_component_listcomponent(d, args):
583      die_arglen(args, 3, "E: suite-component list-component requires a suite")
584      suite = get_suite(args[2].lower(), d.session())
585      if suite is None:
586          die('E: suite %s is invalid' % args[2].lower())
587      for c in suite.components:
588          print c.component_name
589
590
591 def __suite_component_listsuite(d, args):
592      die_arglen(args, 3, "E: suite-component list-suite requires an component")
593      component = get_component(args[2].lower(), d.session())
594      if component is None:
595          die("E: component %s is invalid" % args[2].lower())
596      for s in component.suites:
597          print s.suite_name
598
599
600 def __suite_component_add(d, args):
601      if len(args) < 3:
602          die("E: adding a suite-component entry requires a suite and component")
603
604      s = d.session()
605
606      suite = get_suite(args[2].lower(), s)
607      if suite is None: die("E: Can't find suite %s" % args[2].lower())
608
609      for component_name in args[3:]:
610          component = get_component(component_name.lower(), s)
611          if component is None: die("E: Can't find component %s" % args[3].lower())
612
613          try:
614              suite.components.append(component)
615              s.flush()
616          except IntegrityError as e:
617              die("E: Can't add suite-component entry (%s, %s) - probably already exists" % (args[2].lower(), component_name))
618          except SQLAlchemyError as e:
619              die("E: Can't add suite-component entry (%s, %s) - %s" % (args[2].lower(), component_name, e))
620
621          print "Added suite-component entry for %s, %s" % (args[2].lower(), component_name)
622
623      if not dryrun:
624          s.commit()
625      s.close()
626
627 def __suite_component_rm(d, args):
628      if len(args) < 3:
629          die("E: removing an suite-component entry requires a suite and component")
630
631      s = d.session()
632      if not dryrun:
633          try:
634              suite_name = args[2].lower()
635              suite = get_suite(suite_name, s)
636              if suite is None:
637                  die('E: no such suite %s' % suite_name)
638              component_string = args[3].lower()
639              component = get_component(arch_string, s)
640              if component not in suite.components:
641                  die("E: component %s not found in suite %s" % (component_string, suite_name))
642              suite.components.remove(component)
643              s.commit()
644          except IntegrityError as e:
645              die("E: Can't remove suite-component entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower()))
646          except SQLAlchemyError as e:
647              die("E: Can't remove suite-component entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
648
649      print "Removed suite-component entry for %s, %s" % (args[2].lower(), args[3].lower())
650
651
652 def suite_component(command):
653     args = [str(x) for x in command]
654     Cnf = utils.get_conf()
655     d = DBConn()
656
657     die_arglen(args, 2, "E: suite-component needs at least a command")
658
659     mode = args[1].lower()
660
661     if mode == 'list':
662         __suite_component_list(d, args)
663     elif mode == 'list-component':
664         __suite_component_listcomponent(d, args)
665     elif mode == 'list-suite':
666         __suite_component_listsuite(d, args)
667     elif mode == 'add':
668         __suite_component_add(d, args)
669     # elif mode == 'rm':
670     #     __suite_architecture_rm(d, args)
671     else:
672         die("E: suite-component command unknown")
673
674 dispatch['suite-component'] = suite_component
675 dispatch['s-c'] = suite_component
676
677 ################################################################################
678
679 def archive_list():
680     session = DBConn().session()
681     for archive in session.query(Archive).order_by(Archive.archive_name):
682         print "{0} path={1} description={2} tainted={3}".format(archive.archive_name, archive.path, archive.description, archive.tainted)
683
684 def archive_add(args):
685     (name, path, description) = args[0:3]
686
687     attributes = dict(
688         archive_name=name,
689         path=path,
690         description=description,
691         )
692
693     for option in args[3:]:
694         (key, value) = option.split('=')
695         attributes[key] = value
696
697     session = DBConn().session()
698
699     archive = Archive()
700     for key, value in attributes.iteritems():
701         setattr(archive, key, value)
702
703     session.add(archive)
704     session.flush()
705
706     if dryrun:
707         session.rollback()
708     else:
709         session.commit()
710
711 def archive_rm(name):
712     session = DBConn().session()
713     archive = get_archive(name, session)
714     session.delete(archive)
715     session.flush()
716
717     if dryrun:
718         session.rollback()
719     else:
720         session.commit()
721
722 def archive_rename(oldname, newname):
723     session = DBConn().session()
724     archive = get_archive(oldname, session)
725     archive.archive_name = newname
726     session.flush()
727
728     if dryrun:
729         session.rollback()
730     else:
731         session.commit()
732
733 def archive(command):
734     mode = command[1]
735     if mode == 'list':
736         archive_list()
737     elif mode == 'rename':
738         archive_rename(command[2], command[3])
739     elif mode == 'add':
740         archive_add(command[2:])
741     elif mode == 'rm':
742         archive_rm(command[2])
743     else:
744         die("E: archive command unknown")
745
746 dispatch['archive'] = archive
747
748 ################################################################################
749
750 def __version_check_list(d):
751     session = d.session()
752     for s in session.query(Suite).order_by(Suite.suite_name):
753         __version_check_list_suite(d, s.suite_name)
754
755 def __version_check_list_suite(d, suite_name):
756     vcs = get_version_checks(suite_name)
757     for vc in vcs:
758         print "%s %s %s" % (suite_name, vc.check, vc.reference.suite_name)
759
760 def __version_check_add(d, suite_name, check, reference_name):
761     suite = get_suite(suite_name)
762     if not suite:
763         die("E: Could not find suite %s." % (suite_name))
764     reference = get_suite(reference_name)
765     if not reference:
766         die("E: Could not find reference suite %s." % (reference_name))
767
768     session = d.session()
769     vc = VersionCheck()
770     vc.suite = suite
771     vc.check = check
772     vc.reference = reference
773     session.add(vc)
774     session.commit()
775
776 def __version_check_rm(d, suite_name, check, reference_name):
777     suite = get_suite(suite_name)
778     if not suite:
779         die("E: Could not find suite %s." % (suite_name))
780     reference = get_suite(reference_name)
781     if not reference:
782         die("E: Could not find reference suite %s." % (reference_name))
783
784     session = d.session()
785     try:
786       vc = session.query(VersionCheck).filter_by(suite=suite, check=check, reference=reference).one()
787       session.delete(vc)
788       session.commit()
789     except NoResultFound:
790       print "W: version-check not found."
791
792 def version_check(command):
793     args = [str(x) for x in command]
794     Cnf = utils.get_conf()
795     d = DBConn()
796
797     die_arglen(args, 2, "E: version-check needs at least a command")
798     mode = args[1].lower()
799
800     if mode == 'list':
801         __version_check_list(d)
802     elif mode == 'list-suite':
803         if len(args) != 3:
804             die("E: version-check list-suite needs a single parameter")
805         __version_check_list_suite(d, args[2])
806     elif mode == 'add':
807         if len(args) != 5:
808             die("E: version-check add needs three parameters")
809         __version_check_add(d, args[2], args[3], args[4])
810     elif mode == 'rm':
811         if len(args) != 5:
812             die("E: version-check rm needs three parameters")
813         __version_check_rm(d, args[2], args[3], args[4])
814     else:
815         die("E: version-check command unknown")
816
817 dispatch['version-check'] = version_check
818 dispatch['v-c'] = version_check
819
820 ################################################################################
821
822 def show_config(command):
823     args = [str(x) for x in command]
824     cnf = utils.get_conf()
825
826     die_arglen(args, 2, "E: config needs at least a command")
827
828     mode = args[1].lower()
829
830     if mode == 'db':
831         connstr = ""
832         if cnf.has_key("DB::Service"):
833             # Service mode
834             connstr = "postgresql://service=%s" % cnf["DB::Service"]
835         elif cnf.has_key("DB::Host"):
836             # TCP/IP
837             connstr = "postgres://%s" % cnf["DB::Host"]
838             if cnf.has_key("DB::Port") and cnf["DB::Port"] != "-1":
839                 connstr += ":%s" % cnf["DB::Port"]
840             connstr += "/%s" % cnf["DB::Name"]
841         else:
842             # Unix Socket
843             connstr = "postgres:///%s" % cnf["DB::Name"]
844             if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
845                 connstr += "?port=%s" % cnf["DB::Port"]
846         print connstr
847     elif mode == 'db-shell':
848         e = []
849         if cnf.has_key("DB::Service"):
850             e.append('PGSERVICE')
851             print "PGSERVICE=%s" % cnf["DB::Service"]
852         if cnf.has_key("DB::Name"):
853             e.append('PGDATABASE')
854             print "PGDATABASE=%s" % cnf["DB::Name"]
855         if cnf.has_key("DB::Host"):
856             print "PGHOST=%s" % cnf["DB::Host"]
857             e.append('PGHOST')
858         if cnf.has_key("DB::Port") and cnf["DB::Port"] != "-1":
859             print "PGPORT=%s" % cnf["DB::Port"]
860             e.append('PGPORT')
861         print "export " + " ".join(e)
862     elif mode == 'get':
863         print cnf.get(args[2])
864     else:
865         session = DBConn().session()
866         try:
867             o = session.query(DBConfig).filter_by(name = mode).one()
868             print o.value
869         except NoResultFound:
870             print "W: option '%s' not set" % mode
871
872 dispatch['config'] = show_config
873 dispatch['c'] = show_config
874
875 ################################################################################
876
877 def show_keyring(command):
878     args = [str(x) for x in command]
879     cnf = utils.get_conf()
880
881     die_arglen(args, 2, "E: keyring needs at least a command")
882
883     mode = args[1].lower()
884
885     d = DBConn()
886
887     q = d.session().query(Keyring).filter(Keyring.active == True)
888
889     if mode == 'list-all':
890         pass
891     elif mode == 'list-binary':
892         q = q.join(Keyring.acl).filter(ACL.allow_source == False)
893     elif mode == 'list-source':
894         q = q.join(Keyring.acl).filter(ACL.allow_source == True)
895     else:
896         die("E: keyring command unknown")
897
898     for k in q.all():
899         print k.keyring_name
900
901 def keyring_add_buildd(command):
902     name = command[2]
903     arch_names = command[3:]
904
905     session = DBConn().session()
906     arches = session.query(Architecture).filter(Architecture.arch_string.in_(arch_names))
907
908     acl = ACL()
909     acl.name = 'buildd-{0}'.format('+'.join(arch_names))
910     acl.architectures.update(arches)
911     acl.allow_new = True
912     acl.allow_binary = True
913     acl.allow_binary_only = True
914     acl.allow_hijack = True
915     session.add(acl)
916
917     k = Keyring()
918     k.keyring_name = name
919     k.acl = acl
920     k.priority = 10
921     session.add(k)
922
923     session.commit()
924
925 def keyring(command):
926     if command[1].startswith('list-'):
927         show_keyring(command)
928     elif command[1] == 'add-buildd':
929         keyring_add_buildd(command)
930     else:
931         die("E: keyring command unknown")
932
933 dispatch['keyring'] = keyring
934 dispatch['k'] = keyring
935
936 ################################################################################
937
938 def change_component_source(transaction, suite, component, source_names):
939     session = transaction.session
940
941     overrides = session.query(Override).filter(Override.package.in_(source_names)).filter_by(suite=suite).join(OverrideType).filter_by(overridetype='dsc')
942     for override in overrides:
943         print "Changing override for {0}".format(override.package)
944         override.component = component
945     session.flush()
946
947     sources = session.query(DBSource).filter(DBSource.source.in_(source_names)).filter(DBSource.suites.contains(suite))
948     for source in sources:
949         print "Copying {0}={1}".format(source.source, source.version)
950         transaction.copy_source(source, suite, component)
951
952 def change_component_binary(transaction, suite, component, binary_names):
953     session = transaction.session
954
955     overrides = session.query(Override).filter(Override.package.in_(binary_names)).filter_by(suite=suite).join(OverrideType).filter(OverrideType.overridetype.in_(['deb', 'udeb']))
956     for override in overrides:
957         print "Changing override for {0}".format(override.package)
958         override.component = component
959     session.flush()
960
961     binaries = session.query(DBBinary).filter(DBBinary.package.in_(binary_names)).filter(DBBinary.suites.contains(suite))
962     for binary in binaries:
963         print "Copying {0}={1} [{2}]".format(binary.package, binary.version, binary.architecture.arch_string)
964         transaction.copy_binary(binary, suite, component)
965     pass
966
967 def change_component(args):
968     with daklib.archive.ArchiveTransaction() as transaction:
969         session = transaction.session
970
971         suite = session.query(Suite).filter_by(suite_name=args[1]).one()
972         component = session.query(Component).filter_by(component_name=args[2]).one()
973
974         if args[3] == 'source':
975             change_component_source(transaction, suite, component, args[4:])
976         elif args[3] == 'binary':
977             change_component_binary(transaction, suite, component, args[4:])
978         else:
979             raise Exception("Can only move source or binary, not {0}".format(args[3]))
980
981         transaction.commit()
982
983 dispatch['change-component'] = change_component
984
985 ################################################################################
986
987 def forget_signature(args):
988     filename = args[1]
989     with open(filename, 'r') as fh:
990         data = fh.read()
991
992     session = DBConn().session()
993     keyrings = [ k.keyring_name for k in session.query(Keyring).filter_by(active=True).order_by(Keyring.priority) ]
994     signed_file = daklib.gpg.SignedFile(data, keyrings)
995     history = SignatureHistory.from_signed_file(signed_file).query(session)
996     if history is not None:
997         session.delete(history)
998         session.commit()
999     else:
1000         print "Signature was not known to dak."
1001     session.rollback()
1002
1003 dispatch['forget-signature'] = forget_signature
1004
1005 ################################################################################
1006
1007 def main():
1008     """Perform administrative work on the dak database"""
1009     global dryrun
1010     Cnf = utils.get_conf()
1011     arguments = [('h', "help", "Admin::Options::Help"),
1012                  ('n', "dry-run", "Admin::Options::Dry-Run")]
1013     for i in [ "help", "dry-run" ]:
1014         if not Cnf.has_key("Admin::Options::%s" % (i)):
1015             Cnf["Admin::Options::%s" % (i)] = ""
1016
1017     arguments = apt_pkg.parse_commandline(Cnf, arguments, sys.argv)
1018
1019     options = Cnf.subtree("Admin::Options")
1020     if options["Help"] or len(arguments) < 1:
1021         usage()
1022     if options["Dry-Run"]:
1023         dryrun = True
1024
1025     subcommand = str(arguments[0])
1026
1027     if subcommand in dispatch.keys():
1028         dispatch[subcommand](arguments)
1029     else:
1030         die("E: Unknown command")
1031
1032 ################################################################################
1033
1034 if __name__ == '__main__':
1035     main()