X-Git-Url: https://git.decadent.org.uk/gitweb/?p=dak.git;a=blobdiff_plain;f=dak%2Fcontrol_suite.py;h=fecd76405148e1c22a2beded10d1f1d267c52bbc;hp=aac6d717691dc9ea4c3940b5bfe1724cde158841;hb=03a86547e5d9b209016cc0b23f825d3baea92f8c;hpb=e746dde082b10ed638c61a6f1f806efbce959361 diff --git a/dak/control_suite.py b/dak/control_suite.py index aac6d717..fecd7640 100755 --- a/dak/control_suite.py +++ b/dak/control_suite.py @@ -43,11 +43,14 @@ import sys import apt_pkg +import os +from daklib.archive import ArchiveTransaction from daklib.config import Config from daklib.dbconn import * from daklib import daklog from daklib import utils +from daklib.queue import get_suite_version_by_package, get_suite_version_by_source ####################################################################################### @@ -70,27 +73,19 @@ Display or alter the contents of a suite using FILE(s), or stdin. ####################################################################################### -def get_id(package, version, architecture, session): - if architecture == "source": - q = session.execute("SELECT id FROM source WHERE source = :package AND version = :version", - {'package': package, 'version': version}) +def get_pkg(package, version, architecture, session): + if architecture == 'source': + q = session.query(DBSource).filter_by(source=package, version=version) \ + .join(DBSource.poolfile) else: - q = session.execute("""SELECT b.id FROM binaries b, architecture a - WHERE b.package = :package AND b.version = :version - AND (a.arch_string = :arch OR a.arch_string = 'all') - AND b.architecture = a.id""", - {'package': package, 'version': version, 'arch': architecture}) - - ql = q.fetchall() - if len(ql) < 1: - utils.warn("Couldn't find '%s_%s_%s'." % (package, version, architecture)) - return None + q = session.query(DBBinary).filter_by(package=package, version=version) \ + .join(DBBinary.architecture).filter(Architecture.arch_string.in_([architecture, 'all'])) \ + .join(DBBinary.poolfile) - if len(ql) > 1: - utils.warn("Found more than one match for '%s_%s_%s'." % (package, version, architecture)) - return None - - return ql[0][0] + pkg = q.first() + if pkg is None: + utils.warn("Could not find {0}_{1}_{2}.".format(package, version, architecture)) + return pkg ####################################################################################### @@ -98,6 +93,19 @@ def britney_changelog(packages, suite, session): old = {} current = {} + Cnf = utils.get_conf() + + try: + q = session.execute("SELECT changelog FROM suite WHERE id = :suiteid", \ + {'suiteid': suite.suite_id}) + brit_file = q.fetchone()[0] + except: + brit_file = None + + if brit_file: + brit_file = os.path.join(Cnf['Dir::Root'], brit_file) + else: + return q = session.execute("""SELECT s.source, s.version, sa.id FROM source s, src_associations sa @@ -107,14 +115,13 @@ def britney_changelog(packages, suite, session): for p in q.fetchall(): current[p[0]] = p[1] for p in packages.keys(): - p = p.split() if p[2] == "source": old[p[0]] = p[1] new = {} for p in current.keys(): if p in old.keys(): - if apt_pkg.VersionCompare(current[p], old[p]) > 0: + if apt_pkg.version_compare(current[p], old[p]) > 0: new[p] = [current[p], old[p]] else: new[p] = [current[p], 0] @@ -129,14 +136,14 @@ def britney_changelog(packages, suite, session): q = session.execute(query) pu = None - brit = utils.open_file(Config()["Changelogs::Britney"], 'w') + brit = utils.open_file(brit_file, 'w') for u in q: if pu and pu != u[0]: brit.write("\n") brit.write("%s\n" % u[1]) pu = u[0] - if len(u): brit.write("\n\n\n") + if q.rowcount: brit.write("\n\n\n") for p in list(set(old.keys()).difference(current.keys())): brit.write("REMOVED: %s %s\n" % (p, old[p])) @@ -146,7 +153,57 @@ def britney_changelog(packages, suite, session): ####################################################################################### -def set_suite(file, suite, session, britney=False): +def version_checks(package, architecture, target_suite, new_version, session, force = False): + if architecture == "source": + suite_version_list = get_suite_version_by_source(package, session) + else: + suite_version_list = get_suite_version_by_package(package, architecture, session) + + must_be_newer_than = [ vc.reference.suite_name for vc in get_version_checks(target_suite, "MustBeNewerThan") ] + must_be_older_than = [ vc.reference.suite_name for vc in get_version_checks(target_suite, "MustBeOlderThan") ] + + # Must be newer than an existing version in target_suite + if target_suite not in must_be_newer_than: + must_be_newer_than.append(target_suite) + + violations = False + + for suite, version in suite_version_list: + cmp = apt_pkg.version_compare(new_version, version) + if suite in must_be_newer_than and cmp < 1: + utils.warn("%s (%s): version check violated: %s targeted at %s is *not* newer than %s in %s" % (package, architecture, new_version, target_suite, version, suite)) + violations = True + if suite in must_be_older_than and cmp > 1: + utils.warn("%s (%s): version check violated: %s targeted at %s is *not* older than %s in %s" % (package, architecture, new_version, target_suite, version, suite)) + violations = True + + if violations: + if force: + utils.warn("Continuing anyway (forced)...") + else: + utils.fubar("Aborting. Version checks violated and not forced.") + +####################################################################################### + +def cmp_package_version(a, b): + """ + comparison function for tuples of the form (package-name, version, arch, ...) + """ + res = 0 + if a[2] == 'source' and b[2] != 'source': + res = -1 + elif a[2] != 'source' and b[2] == 'source': + res = 1 + if res == 0: + res = cmp(a[0], b[0]) + if res == 0: + res = apt_pkg.version_compare(a[1], b[1]) + return res + +####################################################################################### + +def set_suite(file, suite, transaction, britney=False, force=False): + session = transaction.session suite_id = suite.suite_id lines = file.readlines() @@ -158,53 +215,53 @@ def set_suite(file, suite, session, britney=False): FROM binaries b, bin_associations ba, architecture a WHERE ba.suite = :suiteid AND ba.bin = b.id AND b.architecture = a.id""", {'suiteid': suite_id}) - for i in q.fetchall(): - key = " ".join(i[:3]) + for i in q: + key = i[:3] current[key] = i[3] - q = session.execute("""SELECT s.source, s.version, sa.id + q = session.execute("""SELECT s.source, s.version, 'source', sa.id FROM source s, src_associations sa WHERE sa.suite = :suiteid AND sa.source = s.id""", {'suiteid': suite_id}) - for i in q.fetchall(): - key = " ".join(i[:2]) + " source" - current[key] = i[2] + for i in q: + key = i[:3] + current[key] = i[3] # Build up a dictionary of what should be in the suite - desired = {} + desired = set() for line in lines: split_line = line.strip().split() if len(split_line) != 3: utils.warn("'%s' does not break into 'package version architecture'." % (line[:-1])) continue - key = " ".join(split_line) - desired[key] = "" + desired.add(tuple(split_line)) - # Check to see which packages need removed and remove them - for key in current.keys(): - if not desired.has_key(key): - (package, version, architecture) = key.split() - pkid = current[key] + # Check to see which packages need added and add them + for key in sorted(desired, cmp=cmp_package_version): + if key not in current: + (package, version, architecture) = key + version_checks(package, architecture, suite.suite_name, version, session, force) + pkg = get_pkg(package, version, architecture, session) + if pkg is None: + continue + + component = pkg.poolfile.component if architecture == "source": - session.execute("""DELETE FROM src_associations WHERE id = :pkid""", {'pkid': pkid}) + transaction.copy_source(pkg, suite, component) else: - session.execute("""DELETE FROM bin_associations WHERE id = :pkid""", {'pkid': pkid}) - Logger.log(["removed", key, pkid]) + transaction.copy_binary(pkg, suite, component) - # Check to see which packages need added and add them - for key in desired.keys(): - if not current.has_key(key): - (package, version, architecture) = key.split() - pkid = get_id (package, version, architecture, session) - if not pkid: - continue + Logger.log(["added", " ".join(key)]) + + # Check to see which packages need removed and remove them + for key, pkid in current.iteritems(): + if key not in desired: + (package, version, architecture) = key if architecture == "source": - session.execute("""INSERT INTO src_associations (suite, source) - VALUES (:suiteid, :pkid)""", {'suiteid': suite_id, 'pkid': pkid}) + session.execute("""DELETE FROM src_associations WHERE id = :pkid""", {'pkid': pkid}) else: - session.execute("""INSERT INTO bin_associations (suite, bin) - VALUES (:suiteid, :pkid)""", {'suiteid': suite_id, 'pkid': pkid}) - Logger.log(["added", key, pkid]) + session.execute("""DELETE FROM bin_associations WHERE id = :pkid""", {'pkid': pkid}) + Logger.log(["removed", " ".join(key), pkid]) session.commit() @@ -213,27 +270,41 @@ def set_suite(file, suite, session, britney=False): ####################################################################################### -def process_file(file, suite, action, session, britney=False): +def process_file(file, suite, action, transaction, britney=False, force=False): + session = transaction.session + if action == "set": - set_suite(file, suite, session, britney) + set_suite(file, suite, transaction, britney, force) return suite_id = suite.suite_id - lines = file.readlines() + request = [] # Our session is already in a transaction - for line in lines: + for line in file: split_line = line.strip().split() if len(split_line) != 3: utils.warn("'%s' does not break into 'package version architecture'." % (line[:-1])) continue + request.append(split_line) - (package, version, architecture) = split_line + request.sort(cmp=cmp_package_version) - pkid = get_id(package, version, architecture, session) - if not pkid: + for package, version, architecture in request: + pkg = get_pkg(package, version, architecture, session) + if pkg is None: continue + if architecture == 'source': + pkid = pkg.source_id + else: + pkid = pkg.binary_id + + component = pkg.poolfile.component + + # Do version checks when adding packages + if action == "add": + version_checks(package, architecture, suite.suite_name, version, session, force) if architecture == "source": # Find the existing association ID, if any @@ -252,15 +323,16 @@ def process_file(file, suite, action, session, britney=False): utils.warn("'%s_%s_%s' already exists in suite %s." % (package, version, architecture, suite)) continue else: - session.execute("""INSERT INTO src_associations (suite, source) - VALUES (:suiteid, :pkid)""", - {'suiteid': suite_id, 'pkid': pkid}) + transaction.copy_source(pkg, suite, component) + Logger.log(["added", package, version, architecture, suite.suite_name, pkid]) + elif action == "remove": if association_id == None: utils.warn("'%s_%s_%s' doesn't exist in suite %s." % (package, version, architecture, suite)) continue else: session.execute("""DELETE FROM src_associations WHERE id = :pkid""", {'pkid': association_id}) + Logger.log(["removed", package, version, architecture, suite.suite_name, pkid]) else: # Find the existing associations ID, if any q = session.execute("""SELECT id FROM bin_associations @@ -278,15 +350,15 @@ def process_file(file, suite, action, session, britney=False): utils.warn("'%s_%s_%s' already exists in suite %s." % (package, version, architecture, suite)) continue else: - session.execute("""INSERT INTO bin_associations (suite, bin) - VALUES (:suiteid, :pkid)""", - {'suiteid': suite_id, 'pkid': pkid}) + transaction.copy_binary(pkg, suite, component) + Logger.log(["added", package, version, architecture, suite.suite_name, pkid]) elif action == "remove": if association_id == None: utils.warn("'%s_%s_%s' doesn't exist in suite %s." % (package, version, architecture, suite)) continue else: session.execute("""DELETE FROM bin_associations WHERE id = :pkid""", {'pkid': association_id}) + Logger.log(["removed", package, version, architecture, suite.suite_name, pkid]) session.commit() @@ -319,6 +391,7 @@ def main (): Arguments = [('a',"add","Control-Suite::Options::Add", "HasArg"), ('b',"britney","Control-Suite::Options::Britney"), + ('f','force','Control-Suite::Options::Force'), ('h',"help","Control-Suite::Options::Help"), ('l',"list","Control-Suite::Options::List","HasArg"), ('r',"remove", "Control-Suite::Options::Remove", "HasArg"), @@ -329,52 +402,59 @@ def main (): cnf["Control-Suite::Options::%s" % (i)] = "" try: - file_list = apt_pkg.ParseCommandLine(cnf.Cnf, Arguments, sys.argv); - except SystemError, e: + file_list = apt_pkg.parse_commandline(cnf.Cnf, Arguments, sys.argv); + except SystemError as e: print "%s\n" % e usage(1) - Options = cnf.SubTree("Control-Suite::Options") + Options = cnf.subtree("Control-Suite::Options") if Options["Help"]: usage() - session = DBConn().session() + force = Options.has_key("Force") and Options["Force"] action = None for i in ("add", "list", "remove", "set"): if cnf["Control-Suite::Options::%s" % (i)] != "": suite_name = cnf["Control-Suite::Options::%s" % (i)] - suite = get_suite(suite_name, session=session) - if suite is None: - utils.fubar("Unknown suite '%s'." % (suite_name)) - else: - if action: - utils.fubar("Can only perform one action at a time.") - action = i + + if action: + utils.fubar("Can only perform one action at a time.") + + action = i # Need an action... - if action == None: + if action is None: utils.fubar("No action specified.") - # Safety/Sanity check - # XXX: This should be stored in the database - if action == "set" and suite_name not in ["testing"]: - utils.fubar("Will not reset suite %s" % (suite_name)) - britney = False if action == "set" and cnf["Control-Suite::Options::Britney"]: britney = True if action == "list": + session = DBConn().session() + suite = session.query(Suite).filter_by(suite_name=suite_name).one() get_list(suite, session) else: - Logger = daklog.Logger(cnf.Cnf, "control-suite") - if file_list: - for f in file_list: - process_file(utils.open_file(f), suite, action, session, britney) - else: - process_file(sys.stdin, suite, action, session, britney) + Logger = daklog.Logger("control-suite") + + with ArchiveTransaction() as transaction: + session = transaction.session + suite = session.query(Suite).filter_by(suite_name=suite_name).one() + + if action == "set" and not suite.allowcsset: + if force: + utils.warn("Would not normally allow setting suite {0} (allowsetcs is FALSE), but --force used".format(suite_name)) + else: + utils.fubar("Will not reset suite {0} due to its database configuration (allowsetcs is FALSE)".format(suite_name)) + + if file_list: + for f in file_list: + process_file(utils.open_file(f), suite, action, transaction, britney, force) + else: + process_file(sys.stdin, suite, action, transaction, britney, force) + Logger.close() #######################################################################################