3 """Configure dak parameters in the database"""
4 # Copyright (C) 2009 Mark Hymers <mhy@debian.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 ################################################################################
26 from daklib import utils
27 from daklib.dbconn import *
28 from sqlalchemy.orm.exc import NoResultFound
30 ################################################################################
35 ################################################################################
37 print >> sys.stderr, msg
39 def die(msg, exit_code=1):
40 print >> sys.stderr, msg
43 def die_arglen(args, args_needed, msg):
44 if len(args) < args_needed:
47 def usage(exit_code=0):
48 """Perform administrative work on the dak database."""
50 print """Usage: dak admin COMMAND
51 Perform administrative work on the dak database.
53 -h, --help show this help and exit.
54 -n, --dry-run don't do anything, just show what would have been done
55 (only applies to add or rm operations).
57 Commands can use a long or abbreviated form:
61 c db-shell show db config in a usable form for psql
62 c NAME show option NAME as set in configuration table
65 k list-all list all keyrings
66 k list-binary list all keyrings with a NULL source acl
67 k list-source list all keyrings with a non NULL source acl
68 k add-buildd NAME ARCH... add buildd keyring with upload permission
69 for the given architectures
72 a list show a list of architectures
73 a rm ARCH remove an architecture (will only work if
74 no longer linked to any suites)
75 a add ARCH DESCRIPTION [SUITELIST]
76 add architecture ARCH with DESCRIPTION.
77 If SUITELIST is given, add to each of the
78 suites at the same time
81 component list show a list of components
82 component rm COMPONENT remove a component (will only work if
84 component add NAME DESCRIPTION ORDERING
85 add component NAME with DESCRIPTION.
89 s list [--print-archive]
91 s show SUITE show config details for a suite
92 s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ]
93 [ origin=ORIGIN ] [ codename=CODENAME ]
94 [ signingkey=SIGNINGKEY ] [ archive=ARCHIVE ]
95 add suite SUITE, version VERSION.
96 label, description, origin, codename
97 and signingkey are optional.
99 s add-all-arches SUITE VERSION... as "s add" but adds suite-architecture
100 relationships for all architectures
102 suite-architecture / s-a:
103 s-a list show the architectures for all suites
104 s-a list-suite ARCH show the suites an ARCH is in
105 s-a list-arch SUITE show the architectures in a SUITE
106 s-a add SUITE ARCH add ARCH to suite
107 s-a rm SUITE ARCH remove ARCH from suite (will only work if
108 no packages remain for the arch in the suite)
111 archive list list all archives
112 archive add NAME ROOT DESCRIPTION [primary-mirror=MIRROR] [tainted=1]
113 add archive NAME with path ROOT,
114 primary mirror MIRROR.
115 archive rm NAME remove archive NAME (will only work if there are
116 no files and no suites in the archive)
117 archive rename OLD NEW rename archive OLD to NEW
120 v-c list show version checks for all suites
121 v-c list-suite SUITE show version checks for suite SUITE
122 v-c add SUITE CHECK REFERENCE add a version check for suite SUITE
123 v-c rm SUITE CHECK REFERENCE remove a version check
125 CHECK is one of Enhances, MustBeNewerThan, MustBeOlderThan
126 REFERENCE is another suite name
130 ################################################################################
132 def __architecture_list(d, args):
133 q = d.session().query(Architecture).order_by(Architecture.arch_string)
135 # HACK: We should get rid of source from the arch table
136 if j.arch_string == 'source': continue
140 def __architecture_add(d, args):
141 die_arglen(args, 4, "E: adding an architecture requires a name and a description")
142 print "Adding architecture %s" % args[2]
143 suites = [str(x) for x in args[4:]]
145 print "Adding to suites %s" % ", ".join(suites)
150 a.arch_string = str(args[2]).lower()
151 a.description = str(args[3])
154 su = get_suite(sn, s)
158 warn("W: Cannot find suite %s" % su)
160 except IntegrityError as e:
161 die("E: Integrity error adding architecture %s (it probably already exists)" % args[2])
162 except SQLAlchemyError as e:
163 die("E: Error adding architecture %s (%s)" % (args[2], e))
164 print "Architecture %s added" % (args[2])
166 def __architecture_rm(d, args):
167 die_arglen(args, 3, "E: removing an architecture requires at least a name")
168 print "Removing architecture %s" % args[2]
172 a = get_architecture(args[2].lower(), s)
174 die("E: Cannot find architecture %s" % args[2])
177 except IntegrityError as e:
178 die("E: Integrity error removing architecture %s (suite-arch entries probably still exist)" % args[2])
179 except SQLAlchemyError as e:
180 die("E: Error removing architecture %s (%s)" % (args[2], e))
181 print "Architecture %s removed" % args[2]
183 def architecture(command):
184 args = [str(x) for x in command]
185 Cnf = utils.get_conf()
188 die_arglen(args, 2, "E: architecture needs at least a command")
190 mode = args[1].lower()
192 __architecture_list(d, args)
194 __architecture_add(d, args)
196 __architecture_rm(d, args)
198 die("E: architecture command unknown")
200 dispatch['architecture'] = architecture
201 dispatch['a'] = architecture
203 ################################################################################
205 def component_list():
206 session = DBConn().session()
207 for component in session.query(Component).order_by(Component.component_name):
208 print "{0} ordering={1}".format(component.component_name, component.ordering)
210 def component_add(args):
211 (name, description, ordering) = args[0:3]
215 description=description,
219 for option in args[3:]:
220 (key, value) = option.split('=')
221 attributes[key] = value
223 session = DBConn().session()
225 component = Component()
226 for key, value in attributes.iteritems():
227 setattr(component, key, value)
229 session.add(component)
237 def component_rm(name):
238 session = DBConn().session()
239 component = get_component(name, session)
240 session.delete(component)
248 def component_rename(oldname, newname):
249 session = DBConn().session()
250 component = get_component(oldname, session)
251 component.component_name = newname
259 def component(command):
263 elif mode == 'rename':
264 component_rename(command[2], command[3])
266 component_add(command[2:])
268 component_rm(command[2])
270 die("E: component command unknown")
272 dispatch['component'] = component
274 ################################################################################
276 def __suite_list(d, args):
278 for j in s.query(Suite).join(Suite.archive).order_by(Archive.archive_name, Suite.suite_name).all():
279 if len(args) > 2 and args[2] == "--print-archive":
280 print "{0} {1}".format(j.archive.archive_name, j.suite_name)
282 print "{0}".format(j.suite_name)
284 def __suite_show(d, args):
286 die("E: showing an suite entry requires a suite")
289 su = get_suite(args[2].lower())
291 die("E: can't find suite entry for %s" % (args[2].lower()))
295 def __suite_add(d, args, addallarches=False):
296 die_arglen(args, 4, "E: adding a suite requires at least a name and a version")
297 suite_name = args[2].lower()
301 def get_field(field):
303 if varval.startswith(field + '='):
304 return varval.split('=')[1]
307 print "Adding suite %s" % suite_name
312 suite.suite_name = suite_name
313 suite.overridecodename = suite_name
314 suite.version = version
315 suite.label = get_field('label')
316 suite.description = get_field('description')
317 suite.origin = get_field('origin')
318 suite.codename = get_field('codename')
319 signingkey = get_field('signingkey')
320 if signingkey is not None:
321 suite.signingkeys = [signingkey.upper()]
322 archive_name = get_field('archive')
323 if archive_name is not None:
324 suite.archive = get_archive(archive_name, s)
326 suite.archive = s.query(Archive).filter(~Archive.archive_name.in_(['build-queues', 'new', 'policy'])).one()
327 suite.srcformats = s.query(SrcFormat).all()
330 except IntegrityError as e:
331 die("E: Integrity error adding suite %s (it probably already exists)" % suite_name)
332 except SQLAlchemyError as e:
333 die("E: Error adding suite %s (%s)" % (suite_name, e))
334 print "Suite %s added" % (suite_name)
338 q = s.query(Architecture).order_by(Architecture.arch_string)
340 suite.architectures.append(arch)
341 arches.append(arch.arch_string)
343 print "Architectures %s added to %s" % (','.join(arches), suite_name)
347 def __suite_rm(d, args):
348 die_arglen(args, 3, "E: removing a suite requires at least a name")
350 print "Removing suite {0}".format(name)
354 su = get_suite(name.lower())
356 die("E: Cannot find suite {0}".format(name))
359 except IntegrityError as e:
360 die("E: Integrity error removing suite {0} (suite-arch entries probably still exist)".format(name))
361 except SQLAlchemyError as e:
362 die("E: Error removing suite {0} ({1})".format(name, e))
363 print "Suite {0} removed".format(name)
366 args = [str(x) for x in command]
367 Cnf = utils.get_conf()
370 die_arglen(args, 2, "E: suite needs at least a command")
372 mode = args[1].lower()
375 __suite_list(d, args)
377 __suite_show(d, args)
381 __suite_add(d, args, False)
382 elif mode == 'add-all-arches':
383 __suite_add(d, args, True)
385 die("E: suite command unknown")
387 dispatch['suite'] = suite
388 dispatch['s'] = suite
390 ################################################################################
392 def __suite_architecture_list(d, args):
394 for j in s.query(Suite).order_by(Suite.suite_name):
395 architectures = j.get_architectures(skipsrc = True, skipall = True)
396 print j.suite_name + ': ' + \
397 ', '.join([a.arch_string for a in architectures])
399 def __suite_architecture_listarch(d, args):
400 die_arglen(args, 3, "E: suite-architecture list-arch requires a suite")
401 suite = get_suite(args[2].lower(), d.session())
403 die('E: suite %s is invalid' % args[2].lower())
404 a = suite.get_architectures(skipsrc = True, skipall = True)
409 def __suite_architecture_listsuite(d, args):
410 die_arglen(args, 3, "E: suite-architecture list-suite requires an arch")
411 architecture = get_architecture(args[2].lower(), d.session())
412 if architecture is None:
413 die("E: architecture %s is invalid" % args[2].lower())
414 for j in architecture.suites:
418 def __suite_architecture_add(d, args):
420 die("E: adding a suite-architecture entry requires a suite and arch")
424 suite = get_suite(args[2].lower(), s)
425 if suite is None: die("E: Can't find suite %s" % args[2].lower())
427 arch = get_architecture(args[3].lower(), s)
428 if arch is None: die("E: Can't find architecture %s" % args[3].lower())
432 suite.architectures.append(arch)
434 except IntegrityError as e:
435 die("E: Can't add suite-architecture entry (%s, %s) - probably already exists" % (args[2].lower(), args[3].lower()))
436 except SQLAlchemyError as e:
437 die("E: Can't add suite-architecture entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
439 print "Added suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower())
442 def __suite_architecture_rm(d, args):
444 die("E: removing an suite-architecture entry requires a suite and arch")
449 suite_name = args[2].lower()
450 suite = get_suite(suite_name, s)
452 die('E: no such suite %s' % suite_name)
453 arch_string = args[3].lower()
454 architecture = get_architecture(arch_string, s)
455 if architecture not in suite.architectures:
456 die("E: architecture %s not found in suite %s" % (arch_string, suite_name))
457 suite.architectures.remove(architecture)
459 except IntegrityError as e:
460 die("E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower()))
461 except SQLAlchemyError as e:
462 die("E: Can't remove suite-architecture entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e))
464 print "Removed suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower())
467 def suite_architecture(command):
468 args = [str(x) for x in command]
469 Cnf = utils.get_conf()
472 die_arglen(args, 2, "E: suite-architecture needs at least a command")
474 mode = args[1].lower()
477 __suite_architecture_list(d, args)
478 elif mode == 'list-arch':
479 __suite_architecture_listarch(d, args)
480 elif mode == 'list-suite':
481 __suite_architecture_listsuite(d, args)
483 __suite_architecture_add(d, args)
485 __suite_architecture_rm(d, args)
487 die("E: suite-architecture command unknown")
489 dispatch['suite-architecture'] = suite_architecture
490 dispatch['s-a'] = suite_architecture
492 ################################################################################
495 session = DBConn().session()
496 for archive in session.query(Archive).order_by(Archive.archive_name):
497 print "{0} path={1} description={2} tainted={3}".format(archive.archive_name, archive.path, archive.description, archive.tainted)
499 def archive_add(args):
500 (name, path, description) = args[0:3]
505 description=description,
508 for option in args[3:]:
509 (key, value) = option.split('=')
510 attributes[key] = value
512 session = DBConn().session()
515 for key, value in attributes.iteritems():
516 setattr(archive, key, value)
526 def archive_rm(name):
527 session = DBConn().session()
528 archive = get_archive(name, session)
529 session.delete(archive)
537 def archive_rename(oldname, newname):
538 session = DBConn().session()
539 archive = get_archive(oldname, session)
540 archive.archive_name = newname
548 def archive(command):
552 elif mode == 'rename':
553 archive_rename(command[2], command[3])
555 archive_add(command[2:])
557 archive_rm(command[2])
559 die("E: archive command unknown")
561 dispatch['archive'] = archive
563 ################################################################################
565 def __version_check_list(d):
566 session = d.session()
567 for s in session.query(Suite).order_by(Suite.suite_name):
568 __version_check_list_suite(d, s.suite_name)
570 def __version_check_list_suite(d, suite_name):
571 vcs = get_version_checks(suite_name)
573 print "%s %s %s" % (suite_name, vc.check, vc.reference.suite_name)
575 def __version_check_add(d, suite_name, check, reference_name):
576 suite = get_suite(suite_name)
578 die("E: Could not find suite %s." % (suite_name))
579 reference = get_suite(reference_name)
581 die("E: Could not find reference suite %s." % (reference_name))
583 session = d.session()
587 vc.reference = reference
591 def __version_check_rm(d, suite_name, check, reference_name):
592 suite = get_suite(suite_name)
594 die("E: Could not find suite %s." % (suite_name))
595 reference = get_suite(reference_name)
597 die("E: Could not find reference suite %s." % (reference_name))
599 session = d.session()
601 vc = session.query(VersionCheck).filter_by(suite=suite, check=check, reference=reference).one()
604 except NoResultFound:
605 print "W: version-check not found."
607 def version_check(command):
608 args = [str(x) for x in command]
609 Cnf = utils.get_conf()
612 die_arglen(args, 2, "E: version-check needs at least a command")
613 mode = args[1].lower()
616 __version_check_list(d)
617 elif mode == 'list-suite':
619 die("E: version-check list-suite needs a single parameter")
620 __version_check_list_suite(d, args[2])
623 die("E: version-check add needs three parameters")
624 __version_check_add(d, args[2], args[3], args[4])
627 die("E: version-check rm needs three parameters")
628 __version_check_rm(d, args[2], args[3], args[4])
630 die("E: version-check command unknown")
632 dispatch['version-check'] = version_check
633 dispatch['v-c'] = version_check
635 ################################################################################
637 def show_config(command):
638 args = [str(x) for x in command]
639 cnf = utils.get_conf()
641 die_arglen(args, 2, "E: config needs at least a command")
643 mode = args[1].lower()
647 if cnf.has_key("DB::Service"):
649 connstr = "postgresql://service=%s" % cnf["DB::Service"]
650 elif cnf.has_key("DB::Host"):
652 connstr = "postgres://%s" % cnf["DB::Host"]
653 if cnf.has_key("DB::Port") and cnf["DB::Port"] != "-1":
654 connstr += ":%s" % cnf["DB::Port"]
655 connstr += "/%s" % cnf["DB::Name"]
658 connstr = "postgres:///%s" % cnf["DB::Name"]
659 if cnf["DB::Port"] and cnf["DB::Port"] != "-1":
660 connstr += "?port=%s" % cnf["DB::Port"]
662 elif mode == 'db-shell':
664 if cnf.has_key("DB::Service"):
665 e.append('PGSERVICE')
666 print "PGSERVICE=%s" % cnf["DB::Service"]
667 if cnf.has_key("DB::Name"):
668 e.append('PGDATABASE')
669 print "PGDATABASE=%s" % cnf["DB::Name"]
670 if cnf.has_key("DB::Host"):
671 print "PGHOST=%s" % cnf["DB::Host"]
673 if cnf.has_key("DB::Port") and cnf["DB::Port"] != "-1":
674 print "PGPORT=%s" % cnf["DB::Port"]
676 print "export " + " ".join(e)
678 print cnf.get(args[2])
680 session = DBConn().session()
682 o = session.query(DBConfig).filter_by(name = mode).one()
684 except NoResultFound:
685 print "W: option '%s' not set" % mode
687 dispatch['config'] = show_config
688 dispatch['c'] = show_config
690 ################################################################################
692 def show_keyring(command):
693 args = [str(x) for x in command]
694 cnf = utils.get_conf()
696 die_arglen(args, 2, "E: keyring needs at least a command")
698 mode = args[1].lower()
702 q = d.session().query(Keyring).filter(Keyring.active == True)
704 if mode == 'list-all':
706 elif mode == 'list-binary':
707 q = q.join(Keyring.acl).filter(ACL.allow_source == False)
708 elif mode == 'list-source':
709 q = q.join(Keyring.acl).filter(ACL.allow_source == True)
711 die("E: keyring command unknown")
716 def keyring_add_buildd(command):
718 arch_names = command[3:]
720 session = DBConn().session()
721 arches = session.query(Architecture).filter(Architecture.arch_string.in_(arch_names))
724 acl.name = 'buildd-{0}'.format('+'.join(arch_names))
725 acl.architectures.update(arches)
727 acl.allow_binary = True
728 acl.allow_binary_only = True
729 acl.allow_hijack = True
733 k.keyring_name = name
740 def keyring(command):
741 if command[1].startswith('list-'):
742 show_keyring(command)
743 elif command[1] == 'add-buildd':
744 keyring_add_buildd(command)
746 die("E: keyring command unknown")
748 dispatch['keyring'] = keyring
749 dispatch['k'] = keyring
751 ################################################################################
754 """Perform administrative work on the dak database"""
756 Cnf = utils.get_conf()
757 arguments = [('h', "help", "Admin::Options::Help"),
758 ('n', "dry-run", "Admin::Options::Dry-Run")]
759 for i in [ "help", "dry-run" ]:
760 if not Cnf.has_key("Admin::Options::%s" % (i)):
761 Cnf["Admin::Options::%s" % (i)] = ""
763 arguments = apt_pkg.parse_commandline(Cnf, arguments, sys.argv)
765 options = Cnf.subtree("Admin::Options")
766 if options["Help"] or len(arguments) < 1:
768 if options["Dry-Run"]:
771 subcommand = str(arguments[0])
773 if subcommand in dispatch.keys():
774 dispatch[subcommand](arguments)
776 die("E: Unknown command")
778 ################################################################################
780 if __name__ == '__main__':