Package dak :: Module admin
[hide private]
[frames] | no frames]

Source Code for Module dak.admin

   1  #! /usr/bin/env python3 
   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 collections 
  23  import json 
  24  import sys 
  25  from dataclasses import dataclass 
  26  from typing import Any, Callable 
  27   
  28  import apt_pkg 
  29   
  30  import daklib.archive 
  31  import daklib.gpg 
  32   
  33  from daklib import utils 
  34  from daklib.dbconn import * 
  35  from sqlalchemy.orm.exc import NoResultFound 
  36   
  37  ################################################################################ 
  38   
  39  dispatch = {} 
  40  dryrun = False 
41 42 ################################################################################ 43 44 45 -def warn(msg):
46 print(msg, file=sys.stderr)
47
48 49 -def die(msg, exit_code=1):
50 print(msg, file=sys.stderr) 51 sys.exit(exit_code)
52
53 54 -def die_arglen(args, args_needed, msg):
55 if len(args) < args_needed: 56 die(msg)
57
58 59 -def get_suite_or_die(suite_name, session=None, error_message=None):
60 suite = get_suite(suite_name.lower(), session=session) 61 if suite is None: 62 if error_message is None: 63 error_message = "E: Invalid/unknown suite %(suite_name)s" 64 die(error_message % {'suite_name': suite_name}) 65 return suite
66
67 68 -def usage(exit_code=0):
69 """Perform administrative work on the dak database.""" 70 71 print("""Usage: dak admin COMMAND 72 Perform administrative work on the dak database. 73 74 -h, --help show this help and exit. 75 -n, --dry-run don't do anything, just show what would have been done 76 (only applies to add or rm operations). 77 78 Commands can use a long or abbreviated form: 79 80 config / c: 81 c db show db config 82 c db-shell show db config in a usable form for psql 83 c NAME show option NAME as set in configuration table 84 85 keyring / k: 86 k list-all list all keyrings 87 k list-binary list all keyrings with a NULL source acl 88 k list-source list all keyrings with a non NULL source acl 89 k add-buildd NAME ARCH... add buildd keyring with upload permission 90 for the given architectures 91 92 architecture / a: 93 a list show a list of architectures 94 a rm ARCH remove an architecture (will only work if 95 no longer linked to any suites) 96 a add ARCH DESCRIPTION [SUITELIST] 97 add architecture ARCH with DESCRIPTION. 98 If SUITELIST is given, add to each of the 99 suites at the same time 100 101 component: 102 component list show a list of components 103 component rm COMPONENT remove a component (will only work if 104 empty) 105 component add NAME DESCRIPTION ORDERING 106 add component NAME with DESCRIPTION. 107 Ordered at ORDERING. 108 109 suite / s: 110 s list [--print-archive] 111 show a list of suites 112 s show SUITE show config details for a suite 113 s add SUITE VERSION [ label=LABEL ] [ description=DESCRIPTION ] 114 [ origin=ORIGIN ] [ codename=CODENAME ] 115 [ signingkey=SIGNINGKEY ] [ archive=ARCHIVE ] 116 add suite SUITE, version VERSION. 117 label, description, origin, codename 118 and signingkey are optional. 119 s rm SUITE remove a suite (will only work if empty) 120 121 s add-all-arches SUITE VERSION... as "s add" but adds suite-architecture 122 relationships for all architectures 123 s add-build-queue SUITE BUILD-QUEUE BUILD-QUEUE-CODENAME BUILD-QUEUE-ARCHIVE 124 add a build queue for an existing suite 125 126 suite-architecture / s-a: 127 s-a list show the architectures for all suites 128 s-a list-suite ARCH show the suites an ARCH is in 129 s-a list-arch SUITE show the architectures in a SUITE 130 s-a add SUITE ARCH add ARCH to suite 131 s-a rm SUITE ARCH remove ARCH from suite (will only work if 132 no packages remain for the arch in the suite) 133 134 suite-component / s-c: 135 s-c list show the architectures for all suites 136 s-c list-suite COMPONENT 137 show the suites a COMPONENT is in 138 s-c list-component SUITE 139 show the components in a SUITE 140 s-c add SUITE COMPONENT 141 add COMPONENT to suite 142 s-c rm SUITE COMPONENT remove component from suite (will only work if 143 no packages remain for the component in the suite) 144 145 suite-config / suite-cfg / s-cfg: 146 s-cfg list show the names of the configurations 147 s-cfg list SUITE show the configuration values for SUITE 148 s-cfg list-json SUITE show the configuration values for SUITE in JSON format 149 s-cfg get SUITE NAME ... 150 show the value for NAME in SUITE (format: NAME=VALUE) 151 s-cfg get-value SUITE NAME ... 152 show the value for NAME in SUITE (format: VALUE) 153 s-cfg get-json SUITE NAME ... 154 show the value for NAME in SUITE (format: JSON object) 155 s-cfg set SUITE NAME=VALUE ... 156 set NAME to VALUE in SUITE 157 s-cfg set-json SUITE 158 s-cfg set-json SUITE FILENAME 159 parse FILENAME (if absent or "-", then stdin) as JSON 160 and update all configurations listed to match the 161 value in the JSON. 162 Uses the same format as list-json or get-json outputs. 163 164 archive: 165 archive list list all archives 166 archive add NAME ROOT DESCRIPTION [primary-mirror=MIRROR] [tainted=1] 167 add archive NAME with path ROOT, 168 primary mirror MIRROR. 169 archive rm NAME remove archive NAME (will only work if there are 170 no files and no suites in the archive) 171 archive rename OLD NEW rename archive OLD to NEW 172 173 version-check / v-c: 174 v-c list show version checks for all suites 175 v-c list-suite SUITE show version checks for suite SUITE 176 v-c add SUITE CHECK REFERENCE add a version check for suite SUITE 177 v-c rm SUITE CHECK REFERENCE remove a version check 178 where 179 CHECK is one of Enhances, MustBeNewerThan, MustBeOlderThan 180 REFERENCE is another suite name 181 182 change-component: 183 change-component SUITE COMPONENT source SOURCE... 184 change-component SUITE COMPONENT binary BINARY... 185 Move source or binary packages to a different component by copying 186 associated files and changing the overrides. 187 188 forget-signature FILE: forget that we saw FILE 189 """) 190 sys.exit(exit_code)
191
192 ################################################################################ 193 194 195 -def __architecture_list(d, args):
196 q = d.session().query(Architecture).order_by(Architecture.arch_string) 197 for j in q.all(): 198 # HACK: We should get rid of source from the arch table 199 if j.arch_string == 'source': 200 continue 201 print(j.arch_string) 202 sys.exit(0)
203
204 205 -def __architecture_add(d, args):
206 die_arglen(args, 4, "E: adding an architecture requires a name and a description") 207 print("Adding architecture %s" % args[2]) 208 suites = [str(x) for x in args[4:]] 209 if len(suites) > 0: 210 print("Adding to suites %s" % ", ".join(suites)) 211 if not dryrun: 212 try: 213 s = d.session() 214 a = Architecture() 215 a.arch_string = str(args[2]).lower() 216 a.description = str(args[3]) 217 s.add(a) 218 for sn in suites: 219 su = get_suite(sn, s) 220 if su is not None: 221 a.suites.append(su) 222 else: 223 warn("W: Cannot find suite %s" % su) 224 s.commit() 225 except IntegrityError as e: 226 die("E: Integrity error adding architecture %s (it probably already exists)" % args[2]) 227 except SQLAlchemyError as e: 228 die("E: Error adding architecture %s (%s)" % (args[2], e)) 229 print("Architecture %s added" % (args[2]))
230
231 232 -def __architecture_rm(d, args):
233 die_arglen(args, 3, "E: removing an architecture requires at least a name") 234 print("Removing architecture %s" % args[2]) 235 if not dryrun: 236 try: 237 s = d.session() 238 a = get_architecture(args[2].lower(), s) 239 if a is None: 240 die("E: Cannot find architecture %s" % args[2]) 241 s.delete(a) 242 s.commit() 243 except IntegrityError as e: 244 die("E: Integrity error removing architecture %s (suite-arch entries probably still exist)" % args[2]) 245 except SQLAlchemyError as e: 246 die("E: Error removing architecture %s (%s)" % (args[2], e)) 247 print("Architecture %s removed" % args[2])
248
249 250 -def architecture(args):
251 Cnf = utils.get_conf() 252 d = DBConn() 253 254 die_arglen(args, 2, "E: architecture needs at least a command") 255 256 mode = args[1].lower() 257 if mode == 'list': 258 __architecture_list(d, args) 259 elif mode == 'add': 260 __architecture_add(d, args) 261 elif mode == 'rm': 262 __architecture_rm(d, args) 263 else: 264 die("E: architecture command unknown")
265 266 267 dispatch['architecture'] = architecture 268 dispatch['a'] = architecture
269 270 ################################################################################ 271 272 273 -def component_list():
274 session = DBConn().session() 275 for component in session.query(Component).order_by(Component.component_name): 276 print("{0} ordering={1}".format(component.component_name, component.ordering))
277
278 279 -def component_add(args):
280 (name, description, ordering) = args[0:3] 281 282 attributes = dict( 283 component_name=name, 284 description=description, 285 ordering=ordering, 286 ) 287 288 for option in args[3:]: 289 (key, value) = option.split('=') 290 attributes[key] = value 291 292 session = DBConn().session() 293 294 component = Component() 295 for key, value in attributes.items(): 296 setattr(component, key, value) 297 298 session.add(component) 299 session.flush() 300 301 if dryrun: 302 session.rollback() 303 else: 304 session.commit()
305
306 307 -def component_rm(name):
308 session = DBConn().session() 309 component = get_component(name, session) 310 session.delete(component) 311 session.flush() 312 313 if dryrun: 314 session.rollback() 315 else: 316 session.commit()
317
318 319 -def component_rename(oldname, newname):
320 session = DBConn().session() 321 component = get_component(oldname, session) 322 component.component_name = newname 323 session.flush() 324 325 if dryrun: 326 session.rollback() 327 else: 328 session.commit()
329
330 331 -def component(command):
332 mode = command[1] 333 if mode == 'list': 334 component_list() 335 elif mode == 'rename': 336 component_rename(command[2], command[3]) 337 elif mode == 'add': 338 component_add(command[2:]) 339 elif mode == 'rm': 340 component_rm(command[2]) 341 else: 342 die("E: component command unknown")
343 344 345 dispatch['component'] = component
346 347 ################################################################################ 348 349 350 -def __suite_list(d, args):
351 s = d.session() 352 for j in s.query(Suite).join(Suite.archive).order_by(Archive.archive_name, Suite.suite_name).all(): 353 if len(args) > 2 and args[2] == "--print-archive": 354 print("{0} {1}".format(j.archive.archive_name, j.suite_name)) 355 else: 356 print("{0}".format(j.suite_name))
357
358 359 -def __suite_show(d, args):
360 if len(args) < 2: 361 die("E: showing an suite entry requires a suite") 362 363 s = d.session() 364 su = get_suite_or_die(args[2]) 365 366 print(su.details())
367
368 369 -def __suite_add(d, args, addallarches=False):
370 die_arglen(args, 4, "E: adding a suite requires at least a name and a version") 371 suite_name = args[2].lower() 372 version = args[3] 373 kvpairs = __suite_config_set_confing_args_as_dict(args[4:]) 374 375 if len(version) == 0: 376 version = None 377 378 print("Adding suite %s" % suite_name) 379 if not dryrun: 380 try: 381 s = d.session() 382 suite = Suite() 383 suite.suite_name = suite_name 384 suite.overridecodename = None 385 suite.version = version or None 386 # Most configurations will be handled by 387 # __suite_config_internal_set. However, a few are managed 388 # manually here because __suite_config_internal_set cannot 389 # handle them. Either because they are create-only or 390 # because suite-add handled them different (historically) 391 suite.codename = kvpairs.pop('codename', None) 392 signingkey = kvpairs.pop('signingkey', None) 393 if signingkey is not None: 394 suite.signingkeys = [signingkey.upper()] 395 archive_name = kvpairs.pop('archive', None) 396 if archive_name is not None: 397 suite.archive = get_archive(archive_name, s) 398 else: 399 suite.archive = s.query(Archive).filter(~Archive.archive_name.in_(['build-queues', 'new', 'policy'])).one() 400 suite.srcformats = s.query(SrcFormat).all() 401 __suite_config_internal_set(suite, suite_name, kvpairs, 402 print_config_set=False) 403 s.add(suite) 404 s.flush() 405 except IntegrityError as e: 406 die("E: Integrity error adding suite %s (it probably already exists)" % suite_name) 407 except SQLAlchemyError as e: 408 die("E: Error adding suite %s (%s)" % (suite_name, e)) 409 print("Suite %s added" % (suite_name)) 410 411 if addallarches: 412 arches = [] 413 q = s.query(Architecture).order_by(Architecture.arch_string) 414 for arch in q.all(): 415 suite.architectures.append(arch) 416 arches.append(arch.arch_string) 417 418 print("Architectures %s added to %s" % (','.join(arches), suite_name)) 419 420 s.commit()
421
422 423 -def __suite_rm(d, args):
424 die_arglen(args, 3, "E: removing a suite requires at least a name") 425 name = args[2] 426 print("Removing suite {0}".format(name)) 427 if not dryrun: 428 try: 429 s = d.session() 430 su = get_suite_or_die(name, s) 431 s.delete(su) 432 s.commit() 433 except IntegrityError as e: 434 die("E: Integrity error removing suite {0} (suite-arch entries probably still exist)".format(name)) 435 except SQLAlchemyError as e: 436 die("E: Error removing suite {0} ({1})".format(name, e)) 437 print("Suite {0} removed".format(name))
438
439 440 -def __suite_add_build_queue(d, args):
441 session = d.session() 442 443 die_arglen(args, 6, "E: Adding a build queue needs four parameters.") 444 445 suite_name = args[2] 446 build_queue_name = args[3] 447 build_queue_codename = args[4] 448 build_queue_archive_name = args[5] 449 try: 450 suite = session.query(Suite).filter_by(suite_name=suite_name).one() 451 except NoResultFound: 452 die("E: Unknown suite '{0}'".format(suite_name)) 453 try: 454 build_queue_archive = session.query(Archive).filter_by(archive_name=build_queue_archive_name).one() 455 except NoResultFound: 456 die("E: Unknown archive '{0}'".format(build_queue_archive_name)) 457 458 # Create suite 459 s = Suite() 460 s.suite_name = build_queue_name 461 s.origin = suite.origin 462 s.label = suite.label 463 s.description = "buildd {0} incoming".format(suite_name) 464 s.codename = build_queue_codename 465 s.notautomatic = suite.notautomatic 466 s.overridesuite = suite.overridesuite or suite.suite_name 467 s.butautomaticupgrades = suite.butautomaticupgrades 468 s.signingkeys = suite.signingkeys 469 s.include_long_description = False 470 471 # Do not accept direct uploads to the build queue 472 s.accept_source_uploads = False 473 s.accept_binary_uploads = False 474 475 s.archive = build_queue_archive 476 s.architectures.extend(suite.architectures) 477 s.components.extend(suite.components) 478 s.srcformats.extend(suite.srcformats) 479 480 session.add(s) 481 session.flush() 482 483 bq = BuildQueue() 484 bq.queue_name = build_queue_codename 485 bq.suite = s 486 487 session.add(bq) 488 session.flush() 489 490 suite.copy_queues.append(bq) 491 492 session.commit()
493
494 495 -def suite(args):
496 Cnf = utils.get_conf() 497 d = DBConn() 498 499 die_arglen(args, 2, "E: suite needs at least a command") 500 501 mode = args[1].lower() 502 503 if mode == 'list': 504 __suite_list(d, args) 505 elif mode == 'show': 506 __suite_show(d, args) 507 elif mode == 'rm': 508 __suite_rm(d, args) 509 elif mode == 'add': 510 __suite_add(d, args, False) 511 elif mode == 'add-all-arches': 512 __suite_add(d, args, True) 513 elif mode == 'add-build-queue': 514 __suite_add_build_queue(d, args) 515 else: 516 die("E: suite command unknown")
517 518 519 dispatch['suite'] = suite 520 dispatch['s'] = suite
521 522 ################################################################################ 523 524 525 -def __suite_architecture_list(d, args):
526 s = d.session() 527 for j in s.query(Suite).order_by(Suite.suite_name): 528 architectures = j.get_architectures(skipsrc=True, skipall=True) 529 print(j.suite_name + ': ' 530 + ', '.join([a.arch_string for a in architectures]))
531
532 533 -def __suite_architecture_listarch(d, args):
534 die_arglen(args, 3, "E: suite-architecture list-arch requires a suite") 535 suite = get_suite_or_die(args[2], d.session()) 536 a = suite.get_architectures(skipsrc=True, skipall=True) 537 for j in a: 538 print(j.arch_string)
539
540 541 -def __suite_architecture_listsuite(d, args):
542 die_arglen(args, 3, "E: suite-architecture list-suite requires an arch") 543 architecture = get_architecture(args[2].lower(), d.session()) 544 if architecture is None: 545 die("E: architecture %s is invalid" % args[2].lower()) 546 for j in architecture.suites: 547 print(j.suite_name)
548
549 550 -def __suite_architecture_add(d, args):
551 if len(args) < 3: 552 die("E: adding a suite-architecture entry requires a suite and arch") 553 554 s = d.session() 555 556 suite = get_suite_or_die(args[2], s) 557 558 for arch_name in args[3:]: 559 arch = get_architecture(arch_name.lower(), s) 560 if arch is None: 561 die("E: Can't find architecture %s" % args[3].lower()) 562 563 try: 564 suite.architectures.append(arch) 565 s.flush() 566 except IntegrityError as e: 567 die("E: Can't add suite-architecture entry (%s, %s) - probably already exists" % (args[2].lower(), arch_name)) 568 except SQLAlchemyError as e: 569 die("E: Can't add suite-architecture entry (%s, %s) - %s" % (args[2].lower(), arch_name, e)) 570 571 print("Added suite-architecture entry for %s, %s" % (args[2].lower(), arch_name)) 572 573 if not dryrun: 574 s.commit() 575 576 s.close()
577
578 579 -def __suite_architecture_rm(d, args):
580 if len(args) < 3: 581 die("E: removing an suite-architecture entry requires a suite and arch") 582 583 s = d.session() 584 if not dryrun: 585 try: 586 suite_name = args[2].lower() 587 suite = get_suite_or_die(suite_name, s) 588 arch_string = args[3].lower() 589 architecture = get_architecture(arch_string, s) 590 if architecture not in suite.architectures: 591 die("E: architecture %s not found in suite %s" % (arch_string, suite_name)) 592 suite.architectures.remove(architecture) 593 s.commit() 594 except IntegrityError as e: 595 die("E: Can't remove suite-architecture entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower())) 596 except SQLAlchemyError as e: 597 die("E: Can't remove suite-architecture entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e)) 598 599 print("Removed suite-architecture entry for %s, %s" % (args[2].lower(), args[3].lower()))
600
601 602 -def suite_architecture(args):
603 Cnf = utils.get_conf() 604 d = DBConn() 605 606 die_arglen(args, 2, "E: suite-architecture needs at least a command") 607 608 mode = args[1].lower() 609 610 if mode == 'list': 611 __suite_architecture_list(d, args) 612 elif mode == 'list-arch': 613 __suite_architecture_listarch(d, args) 614 elif mode == 'list-suite': 615 __suite_architecture_listsuite(d, args) 616 elif mode == 'add': 617 __suite_architecture_add(d, args) 618 elif mode == 'rm': 619 __suite_architecture_rm(d, args) 620 else: 621 die("E: suite-architecture command unknown")
622 623 624 dispatch['suite-architecture'] = suite_architecture 625 dispatch['s-a'] = suite_architecture
626 627 ################################################################################ 628 629 630 -def __suite_component_list(d, args):
631 s = d.session() 632 for j in s.query(Suite).order_by(Suite.suite_name): 633 components = j.components 634 print(j.suite_name + ': ' 635 + ', '.join([c.component_name for c in components]))
636
637 638 -def __suite_component_listcomponent(d, args):
639 die_arglen(args, 3, "E: suite-component list-component requires a suite") 640 suite = get_suite_or_die(args[2], d.session()) 641 for c in suite.components: 642 print(c.component_name)
643
644 645 -def __suite_component_listsuite(d, args):
646 die_arglen(args, 3, "E: suite-component list-suite requires an component") 647 component = get_component(args[2].lower(), d.session()) 648 if component is None: 649 die("E: component %s is invalid" % args[2].lower()) 650 for s in component.suites: 651 print(s.suite_name)
652
653 654 -def __suite_component_add(d, args):
655 if len(args) < 3: 656 die("E: adding a suite-component entry requires a suite and component") 657 658 s = d.session() 659 660 suite = get_suite_or_die(args[2], s) 661 662 for component_name in args[3:]: 663 component = get_component(component_name.lower(), s) 664 if component is None: 665 die("E: Can't find component %s" % args[3].lower()) 666 667 try: 668 suite.components.append(component) 669 s.flush() 670 except IntegrityError as e: 671 die("E: Can't add suite-component entry (%s, %s) - probably already exists" % (args[2].lower(), component_name)) 672 except SQLAlchemyError as e: 673 die("E: Can't add suite-component entry (%s, %s) - %s" % (args[2].lower(), component_name, e)) 674 675 print("Added suite-component entry for %s, %s" % (args[2].lower(), component_name)) 676 677 if not dryrun: 678 s.commit() 679 s.close()
680
681 682 -def __suite_component_rm(d, args):
683 if len(args) < 3: 684 die("E: removing an suite-component entry requires a suite and component") 685 686 s = d.session() 687 if not dryrun: 688 try: 689 suite_name = args[2].lower() 690 suite = get_suite_or_die(suite_name, s) 691 component_string = args[3].lower() 692 component = get_component(component_string, s) 693 if component not in suite.components: 694 die("E: component %s not found in suite %s" % (component_string, suite_name)) 695 suite.components.remove(component) 696 s.commit() 697 except IntegrityError as e: 698 die("E: Can't remove suite-component entry (%s, %s) - it's probably referenced" % (args[2].lower(), args[3].lower())) 699 except SQLAlchemyError as e: 700 die("E: Can't remove suite-component entry (%s, %s) - %s" % (args[2].lower(), args[3].lower(), e)) 701 702 print("Removed suite-component entry for %s, %s" % (args[2].lower(), args[3].lower()))
703
704 705 -def suite_component(args):
706 Cnf = utils.get_conf() 707 d = DBConn() 708 709 die_arglen(args, 2, "E: suite-component needs at least a command") 710 711 mode = args[1].lower() 712 713 if mode == 'list': 714 __suite_component_list(d, args) 715 elif mode == 'list-component': 716 __suite_component_listcomponent(d, args) 717 elif mode == 'list-suite': 718 __suite_component_listsuite(d, args) 719 elif mode == 'add': 720 __suite_component_add(d, args) 721 # elif mode == 'rm': 722 # __suite_architecture_rm(d, args) 723 else: 724 die("E: suite-component command unknown")
725 726 727 dispatch['suite-component'] = suite_component 728 dispatch['s-c'] = suite_component 729 730 731 ################################################################################ 732 733 # Sentinel for detecting read-only configurations 734 SUITE_CONFIG_READ_ONLY = object() 735 SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON = object()
736 737 738 @dataclass 739 -class SuiteConfigSerializer:
740 db_name: str 741 serialize: Callable[[Any], str] 742 deserialize: Callable[[str], Any]
743
744 745 -def _serialize_suite(x):
746 if x is None: 747 return None 748 return Suite.get(x).suite_name
749
750 751 -def _deserialize_suite(x):
752 if x is None: 753 return None 754 return get_suite_or_die(x).suite_id
755
756 757 @session_wrapper 758 -def _serialize_policy_queue(x, session=None):
759 if x is None: 760 return None 761 try: 762 policy_obj = session.query(PolicyQueue).filter_by(policy_queue_id=x).one() 763 except NoResultFound: 764 return None 765 return policy_obj.queue_name
766
767 768 -def _deserialize_policy_queue(x):
769 if x is None: 770 return None 771 policy_queue = get_policy_queue(x) 772 if policy_queue is None: 773 raise ValueError("There is no policy queue with name %s" % x) 774 return policy_queue.policy_queue_id
775
776 777 @session_wrapper 778 -def _serialize_archive(x, session=None):
779 if x is None: 780 return None 781 try: 782 archive_obj = session.query(Archive).filter_by(archive_id=x).one() 783 except NoResultFound: 784 return None 785 return archive_obj.archive_name
786 787 788 CUSTOM_SUITE_CONFIG_SERIALIZERS = { 789 'archive': SuiteConfigSerializer(db_name='archive_id', serialize=_serialize_archive, 790 deserialize=None), 791 'debugsuite': SuiteConfigSerializer(db_name='debugsuite_id', serialize=_serialize_suite, 792 deserialize=_deserialize_suite), 793 'new_queue': SuiteConfigSerializer(db_name='new_queue_id', serialize=_serialize_policy_queue, 794 deserialize=_deserialize_policy_queue), 795 'policy_queue': SuiteConfigSerializer(db_name='policy_queue_id', serialize=_serialize_policy_queue, 796 deserialize=_deserialize_policy_queue), 797 } 798 799 800 ALLOWED_SUITE_CONFIGS = { 801 'accept_binary_uploads': utils.parse_boolean_from_user, 802 'accept_source_uploads': utils.parse_boolean_from_user, 803 'allowcsset': utils.parse_boolean_from_user, 804 'announce': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 805 'archive': SUITE_CONFIG_READ_ONLY, 806 'butautomaticupgrades': utils.parse_boolean_from_user, 807 'byhash': utils.parse_boolean_from_user, 808 'changelog': str, 809 'changelog_url': str, 810 'checksums': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 811 'close_bugs': utils.parse_boolean_from_user, 812 'codename': SUITE_CONFIG_READ_ONLY, 813 'debugsuite': str, 814 'description': str, 815 'include_long_description': utils.parse_boolean_from_user, 816 'indices_compression': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 817 'label': str, 818 'mail_whitelist': str, 819 'merged_pdiffs': utils.parse_boolean_from_user, 820 'new_queue': str, 821 'notautomatic': utils.parse_boolean_from_user, 822 'origin': str, 823 'overridecodename': str, 824 'overrideorigin': str, 825 'overrideprocess': utils.parse_boolean_from_user, 826 'overridesuite': str, 827 'policy_queue': str, 828 'priority': int, 829 'separate_contents_architecture_all': utils.parse_boolean_from_user, 830 # We do not support separate Packages-all, so do not let people set it. 831 'separate_packages_architecture_all': SUITE_CONFIG_READ_ONLY, 832 'signingkeys': SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON, 833 'suite_name': SUITE_CONFIG_READ_ONLY, 834 'untouchable': utils.parse_boolean_from_user, 835 'validtime': int, 836 }
837 838 839 -def _get_suite_value(suite, conf_name):
840 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name) 841 if serial_config is None: 842 return getattr(suite, conf_name) 843 db_value = getattr(suite, serial_config.db_name) 844 return serial_config.serialize(db_value)
845
846 847 -def _set_suite_value(suite, conf_name, new_value):
848 serial_config = CUSTOM_SUITE_CONFIG_SERIALIZERS.get(conf_name) 849 db_name = conf_name 850 if serial_config is not None: 851 assert serial_config.deserialize is not None, "Changing %s is not supported!" % conf_name 852 new_value = serial_config.deserialize(new_value) 853 db_name = serial_config.db_name 854 setattr(suite, db_name, new_value)
855
856 857 -def __suite_config_get(d, args, direct_value=False, json_format=False):
858 die_arglen(args, 4, "E: suite-config get needs the name of a configuration") 859 session = d.session() 860 suite_name = args[2] 861 suite = get_suite_or_die(suite_name, session) 862 values = {} 863 for arg in args[3:]: 864 if arg not in ALLOWED_SUITE_CONFIGS: 865 die("Unknown (or unsupported) suite configuration variable") 866 value = _get_suite_value(suite, arg) 867 if json_format: 868 values[arg] = value 869 elif direct_value: 870 print(value) 871 else: 872 print("%s=%s" % (arg, value)) 873 if json_format: 874 print(json.dumps(values, indent=2, sort_keys=True))
875
876 877 -def __suite_config_set(d, args):
878 die_arglen(args, 4, "E: suite-config set needs the name of a configuration") 879 session = d.session() 880 suite_name = args[2] 881 suite = get_suite_or_die(suite_name, session) 882 args_as_kvpairs = __suite_config_set_confing_args_as_dict(args[3:]) 883 __suite_config_internal_set(suite, suite_name, args_as_kvpairs, 884 print_config_set=True 885 ) 886 if dryrun: 887 session.rollback() 888 print() 889 print("This was a dryrun; changes have been rolled back") 890 else: 891 session.commit()
892
893 894 -def __suite_config_set_confing_args_as_dict(args):
895 # Use OrderedDict to preserve order (makes "dak admin suite-config set ..." 896 # less confusing when things are processed in the input order) 897 kvpairs = collections.OrderedDict() 898 for arg in args: 899 if '=' not in arg: 900 die("Missing value for configuration %s: Use key=value format" % arg) 901 conf_name, new_value_str = arg.split('=', 1) 902 kvpairs[conf_name] = new_value_str 903 return kvpairs
904
905 906 -def __suite_config_internal_set(suite, suite_name, kvpairs, print_config_set=True):
907 for kvpair in kvpairs.items(): 908 conf_name, new_value_str = kvpair 909 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name) 910 if cli_parser is None: 911 die("Unknown (or unsupported) suite configuration variable") 912 if cli_parser in (SUITE_CONFIG_READ_ONLY, SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON): 913 if cli_parser == SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON: 914 msg = "Cannot parse value for %s" \ 915 ''' - set via echo '{"%s": <...>}' | dak suite-config set-json %s instead''' 916 warn(msg % (conf_name, conf_name, suite_name)) 917 die("Cannot change %s from the command line" % conf_name) 918 try: 919 new_value = cli_parser(new_value_str) 920 except (RuntimeError, ValueError, TypeError) as e: 921 warn("Could not parse new value for %s (given: %s)" % (conf_name, new_value_str)) 922 raise e 923 try: 924 _set_suite_value(suite, conf_name, new_value) 925 except (RuntimeError, ValueError, TypeError) as e: 926 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value)) 927 raise e 928 if print_config_set: 929 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name)))
930
931 932 -def __suite_config_set_json(d, args):
933 session = d.session() 934 suite_name = args[2] 935 suite = get_suite_or_die(suite_name, session) 936 filename = '-' 937 if len(args) > 3: 938 if len(args) > 4: 939 warn("W: Ignoring extra argument after the json file name") 940 filename = args[3] 941 if filename != '-': 942 with open(filename) as fd: 943 update_config = json.load(fd) 944 else: 945 update_config = json.load(sys.stdin) 946 if update_config is None or not isinstance(update_config, dict): 947 die("E: suite-config set-json expects a dictionary (json object), got %s" % type(update_config)) 948 949 for conf_name in sorted(update_config): 950 new_value = update_config[conf_name] 951 cli_parser = ALLOWED_SUITE_CONFIGS.get(conf_name) 952 if cli_parser is None: 953 die("Unknown (or unsupported) suite configuration variable: %s" % conf_name) 954 if cli_parser is SUITE_CONFIG_READ_ONLY: 955 die("Cannot change %s via JSON" % conf_name) 956 try: 957 _set_suite_value(suite, conf_name, new_value) 958 except (RuntimeError, ValueError, TypeError) as e: 959 warn("Could not set new value for %s (given: %s)" % (conf_name, new_value)) 960 raise e 961 print("%s=%s" % (conf_name, _get_suite_value(suite, conf_name))) 962 if dryrun: 963 session.rollback() 964 print() 965 print("This was a dryrun; changes have been rolled back") 966 else: 967 session.commit()
968
969 970 -def __suite_config_list(d, args, json_format=False):
971 suite = None 972 session = d.session() 973 if len(args) > 3: 974 warn("W: Ignoring extra argument after the suite name") 975 if len(args) == 3: 976 suite_name = args[2] 977 suite = get_suite_or_die(suite_name, session) 978 else: 979 if json_format: 980 die("E: suite-config list-json requires a suite name!") 981 print("Valid suite-config options manageable by this command:") 982 print() 983 values = {} 984 985 for arg in sorted(ALLOWED_SUITE_CONFIGS): 986 mode = 'writable' 987 if suite is not None: 988 value = _get_suite_value(suite, arg) 989 if json_format: 990 values[arg] = value 991 else: 992 print("%s=%s" % (arg, value)) 993 else: 994 converter = ALLOWED_SUITE_CONFIGS[arg] 995 if converter is SUITE_CONFIG_READ_ONLY: 996 mode = 'read-only' 997 elif converter is SUITE_CONFIG_WRITABLE_ONLY_VIA_JSON: 998 mode = 'writeable (via set-json only)' 999 print(" * %s (%s)" % (arg, mode)) 1000 if json_format: 1001 print(json.dumps(values, indent=2, sort_keys=True))
1002
1003 1004 -def suite_config(args):
1005 Cnf = utils.get_conf() 1006 d = DBConn() 1007 1008 die_arglen(args, 2, "E: suite-config needs a command") 1009 mode = args[1].lower() 1010 1011 if mode in {'get', 'get-value', 'get-json'}: 1012 direct_value = False 1013 json_format = mode == 'get-json' 1014 if mode == 'get-value': 1015 direct_value = True 1016 if len(args) > 4: 1017 die("E: get-value must receive exactly one key to lookup") 1018 __suite_config_get(d, args, direct_value=direct_value, json_format=json_format) 1019 elif mode == 'set': 1020 __suite_config_set(d, args) 1021 elif mode == 'set-json': 1022 __suite_config_set_json(d, args) 1023 elif mode in {'list', 'list-json'}: 1024 json_format = mode == 'list-json' 1025 __suite_config_list(d, args, json_format=json_format) 1026 else: 1027 suite = get_suite(mode, d.session()) 1028 if suite is not None: 1029 warn("Did you get the order of the suite and the subcommand wrong?") 1030 warn("Syntax: dak admin %s {get,set,...} <suite>" % args[0]) 1031 die("E: suite-config command unknown")
1032 1033 1034 dispatch['suite-config'] = suite_config 1035 dispatch['suite-cfg'] = suite_config 1036 dispatch['s-cfg'] = suite_config
1037 1038 1039 ################################################################################ 1040 1041 1042 -def archive_list():
1043 session = DBConn().session() 1044 for archive in session.query(Archive).order_by(Archive.archive_name): 1045 print("{0} path={1} description={2} tainted={3}".format(archive.archive_name, archive.path, archive.description, archive.tainted))
1046
1047 1048 -def archive_add(args):
1049 (name, path, description) = args[0:3] 1050 1051 attributes = dict( 1052 archive_name=name, 1053 path=path, 1054 description=description, 1055 ) 1056 1057 for option in args[3:]: 1058 (key, value) = option.split('=') 1059 attributes[key] = value 1060 1061 session = DBConn().session() 1062 1063 archive = Archive() 1064 for key, value in attributes.items(): 1065 setattr(archive, key, value) 1066 1067 session.add(archive) 1068 session.flush() 1069 1070 if dryrun: 1071 session.rollback() 1072 else: 1073 session.commit()
1074
1075 1076 -def archive_rm(name):
1077 session = DBConn().session() 1078 archive = get_archive(name, session) 1079 session.delete(archive) 1080 session.flush() 1081 1082 if dryrun: 1083 session.rollback() 1084 else: 1085 session.commit()
1086
1087 1088 -def archive_rename(oldname, newname):
1089 session = DBConn().session() 1090 archive = get_archive(oldname, session) 1091 archive.archive_name = newname 1092 session.flush() 1093 1094 if dryrun: 1095 session.rollback() 1096 else: 1097 session.commit()
1098
1099 1100 -def archive(command):
1101 mode = command[1] 1102 if mode == 'list': 1103 archive_list() 1104 elif mode == 'rename': 1105 archive_rename(command[2], command[3]) 1106 elif mode == 'add': 1107 archive_add(command[2:]) 1108 elif mode == 'rm': 1109 archive_rm(command[2]) 1110 else: 1111 die("E: archive command unknown")
1112 1113 1114 dispatch['archive'] = archive
1115 1116 ################################################################################ 1117 1118 1119 -def __version_check_list(d):
1120 session = d.session() 1121 for s in session.query(Suite).order_by(Suite.suite_name): 1122 __version_check_list_suite(d, s.suite_name)
1123
1124 1125 -def __version_check_list_suite(d, suite_name):
1126 vcs = get_version_checks(suite_name) 1127 for vc in vcs: 1128 print("%s %s %s" % (suite_name, vc.check, vc.reference.suite_name))
1129
1130 1131 -def __version_check_add(d, suite_name, check, reference_name):
1132 suite = get_suite_or_die(suite_name, 1133 error_message="E: Could not find suite %(suite_name)s") 1134 reference = get_suite_or_die(reference_name, 1135 error_message="E: Could not find reference suite %(suite_name)s") 1136 1137 session = d.session() 1138 vc = VersionCheck() 1139 vc.suite = suite 1140 vc.check = check 1141 vc.reference = reference 1142 session.add(vc) 1143 session.commit()
1144
1145 1146 -def __version_check_rm(d, suite_name, check, reference_name):
1147 suite = get_suite_or_die(suite_name, 1148 error_message="E: Could not find suite %(suite_name)s") 1149 reference = get_suite_or_die(reference_name, 1150 error_message="E: Could not find reference suite %(suite_name)s") 1151 1152 session = d.session() 1153 try: 1154 vc = session.query(VersionCheck).filter_by(suite=suite, check=check, reference=reference).one() 1155 session.delete(vc) 1156 session.commit() 1157 except NoResultFound: 1158 print("W: version-check not found.")
1159
1160 1161 -def version_check(args):
1162 Cnf = utils.get_conf() 1163 d = DBConn() 1164 1165 die_arglen(args, 2, "E: version-check needs at least a command") 1166 mode = args[1].lower() 1167 1168 if mode == 'list': 1169 __version_check_list(d) 1170 elif mode == 'list-suite': 1171 if len(args) != 3: 1172 die("E: version-check list-suite needs a single parameter") 1173 __version_check_list_suite(d, args[2]) 1174 elif mode == 'add': 1175 if len(args) != 5: 1176 die("E: version-check add needs three parameters") 1177 __version_check_add(d, args[2], args[3], args[4]) 1178 elif mode == 'rm': 1179 if len(args) != 5: 1180 die("E: version-check rm needs three parameters") 1181 __version_check_rm(d, args[2], args[3], args[4]) 1182 else: 1183 die("E: version-check command unknown")
1184 1185 1186 dispatch['version-check'] = version_check 1187 dispatch['v-c'] = version_check
1188 1189 ################################################################################ 1190 1191 1192 -def show_config(args):
1193 cnf = utils.get_conf() 1194 1195 die_arglen(args, 2, "E: config needs at least a command") 1196 1197 mode = args[1].lower() 1198 1199 if mode == 'db': 1200 connstr = "" 1201 if "DB::Service" in cnf: 1202 # Service mode 1203 connstr = "postgresql://service=%s" % cnf["DB::Service"] 1204 elif "DB::Host" in cnf: 1205 # TCP/IP 1206 connstr = "postgresql://%s" % cnf["DB::Host"] 1207 if "DB::Port" in cnf and cnf["DB::Port"] != "-1": 1208 connstr += ":%s" % cnf["DB::Port"] 1209 connstr += "/%s" % cnf["DB::Name"] 1210 else: 1211 # Unix Socket 1212 connstr = "postgresql:///%s" % cnf["DB::Name"] 1213 if cnf["DB::Port"] and cnf["DB::Port"] != "-1": 1214 connstr += "?port=%s" % cnf["DB::Port"] 1215 print(connstr) 1216 elif mode == 'db-shell': 1217 e = [] 1218 if "DB::Service" in cnf: 1219 e.append('PGSERVICE') 1220 print("PGSERVICE=%s" % cnf["DB::Service"]) 1221 if "DB::Name" in cnf: 1222 e.append('PGDATABASE') 1223 print("PGDATABASE=%s" % cnf["DB::Name"]) 1224 if "DB::Host" in cnf: 1225 print("PGHOST=%s" % cnf["DB::Host"]) 1226 e.append('PGHOST') 1227 if "DB::Port" in cnf and cnf["DB::Port"] != "-1": 1228 print("PGPORT=%s" % cnf["DB::Port"]) 1229 e.append('PGPORT') 1230 print("export " + " ".join(e)) 1231 elif mode == 'get': 1232 print(cnf.get(args[2])) 1233 else: 1234 session = DBConn().session() 1235 try: 1236 o = session.query(DBConfig).filter_by(name=mode).one() 1237 print(o.value) 1238 except NoResultFound: 1239 print("W: option '%s' not set" % mode)
1240 1241 1242 dispatch['config'] = show_config 1243 dispatch['c'] = show_config
1244 1245 ################################################################################ 1246 1247 1248 -def show_keyring(args):
1249 cnf = utils.get_conf() 1250 1251 die_arglen(args, 2, "E: keyring needs at least a command") 1252 1253 mode = args[1].lower() 1254 1255 d = DBConn() 1256 1257 q = d.session().query(Keyring).filter(Keyring.active == True) # noqa:E712 1258 1259 if mode == 'list-all': 1260 pass 1261 elif mode == 'list-binary': 1262 q = q.join(Keyring.acl).filter(ACL.allow_source == False) # noqa:E712 1263 elif mode == 'list-source': 1264 q = q.join(Keyring.acl).filter(ACL.allow_source == True) # noqa:E712 1265 else: 1266 die("E: keyring command unknown") 1267 1268 for k in q.all(): 1269 print(k.keyring_name)
1270
1271 1272 -def keyring_add_buildd(command):
1273 name = command[2] 1274 arch_names = command[3:] 1275 1276 session = DBConn().session() 1277 arches = session.query(Architecture).filter(Architecture.arch_string.in_(arch_names)) 1278 1279 acl = ACL() 1280 acl.name = 'buildd-{0}'.format('+'.join(arch_names)) 1281 acl.architectures.update(arches) 1282 acl.allow_new = True 1283 acl.allow_binary = True 1284 acl.allow_binary_only = True 1285 acl.allow_hijack = True 1286 session.add(acl) 1287 1288 k = Keyring() 1289 k.keyring_name = name 1290 k.acl = acl 1291 k.priority = 10 1292 session.add(k) 1293 1294 session.commit()
1295
1296 1297 -def keyring(command):
1298 if command[1].startswith('list-'): 1299 show_keyring(command) 1300 elif command[1] == 'add-buildd': 1301 keyring_add_buildd(command) 1302 else: 1303 die("E: keyring command unknown")
1304 1305 1306 dispatch['keyring'] = keyring 1307 dispatch['k'] = keyring
1308 1309 ################################################################################ 1310 1311 1312 -def change_component_source(transaction, suite, component, source_names):
1313 session = transaction.session 1314 1315 overrides = session.query(Override).filter(Override.package.in_(source_names)).filter_by(suite=suite).join(OverrideType).filter_by(overridetype='dsc') 1316 for override in overrides: 1317 print("Changing override for {0}".format(override.package)) 1318 override.component = component 1319 session.flush() 1320 1321 sources = session.query(DBSource).filter(DBSource.source.in_(source_names)).filter(DBSource.suites.contains(suite)) 1322 for source in sources: 1323 print("Copying {0}={1}".format(source.source, source.version)) 1324 transaction.copy_source(source, suite, component)
1325
1326 1327 -def change_component_binary(transaction, suite, component, binary_names):
1328 session = transaction.session 1329 1330 overrides = session.query(Override).filter(Override.package.in_(binary_names)).filter_by(suite=suite).join(OverrideType).filter(OverrideType.overridetype.in_(['deb', 'udeb'])) 1331 for override in overrides: 1332 print("Changing override for {0}".format(override.package)) 1333 override.component = component 1334 session.flush() 1335 1336 binaries = session.query(DBBinary).filter(DBBinary.package.in_(binary_names)).filter(DBBinary.suites.contains(suite)) 1337 for binary in binaries: 1338 print("Copying {0}={1} [{2}]".format(binary.package, binary.version, binary.architecture.arch_string)) 1339 transaction.copy_binary(binary, suite, component) 1340 pass
1341
1342 1343 -def change_component(args):
1344 with daklib.archive.ArchiveTransaction() as transaction: 1345 session = transaction.session 1346 1347 suite = session.query(Suite).filter_by(suite_name=args[1]).one() 1348 component = session.query(Component).filter_by(component_name=args[2]).one() 1349 1350 if args[3] == 'source': 1351 change_component_source(transaction, suite, component, args[4:]) 1352 elif args[3] == 'binary': 1353 change_component_binary(transaction, suite, component, args[4:]) 1354 else: 1355 raise Exception("Can only move source or binary, not {0}".format(args[3])) 1356 1357 transaction.commit()
1358 1359 1360 dispatch['change-component'] = change_component
1361 1362 ################################################################################ 1363 1364 1365 -def forget_signature(args):
1366 filename = args[1] 1367 with open(filename, 'rb') as fh: 1368 data = fh.read() 1369 1370 session = DBConn().session() 1371 keyrings = [k.keyring_name for k in session.query(Keyring).filter_by(active=True).order_by(Keyring.priority)] 1372 signed_file = daklib.gpg.SignedFile(data, keyrings) 1373 history = SignatureHistory.from_signed_file(signed_file).query(session) 1374 if history is not None: 1375 session.delete(history) 1376 session.commit() 1377 else: 1378 print("Signature was not known to dak.") 1379 session.rollback()
1380 1381 1382 dispatch['forget-signature'] = forget_signature
1383 1384 ################################################################################ 1385 1386 1387 -def main():
1388 """Perform administrative work on the dak database""" 1389 global dryrun 1390 Cnf = utils.get_conf() 1391 arguments = [('h', "help", "Admin::Options::Help"), 1392 ('n', "dry-run", "Admin::Options::Dry-Run")] 1393 for i in ["help", "dry-run"]: 1394 key = "Admin::Options::%s" % i 1395 if key not in Cnf: 1396 Cnf[key] = "" 1397 1398 arguments = apt_pkg.parse_commandline(Cnf, arguments, sys.argv) 1399 1400 options = Cnf.subtree("Admin::Options") 1401 if options["Help"] or len(arguments) < 1: 1402 usage() 1403 if options["Dry-Run"]: 1404 dryrun = True 1405 1406 subcommand = arguments[0] 1407 1408 if subcommand in dispatch: 1409 dispatch[subcommand](arguments) 1410 else: 1411 die("E: Unknown command")
1412 1413 ################################################################################ 1414 1415 1416 if __name__ == '__main__': 1417 main() 1418