#!/usr/bin/env python
# Manipulate suite tags
-# Copyright (C) 2000 James Troup <james@nocrew.org>
-# $Id: heidi,v 1.3 2001-01-10 06:08:03 troup Exp $
+# Copyright (C) 2000, 2001, 2002, 2003, 2004 James Troup <james@nocrew.org>
+# $Id: heidi,v 1.18 2004-03-11 00:20:51 troup Exp $
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# Adds or removes packages from a suite. Takes the list of files
# either from stdin or as a command line argument. Special action
-# "set", will reset the suite (!) and add all packages from scratch.
+# "set", will reset the suite (!) and add all packages from scratch.
#######################################################################################
-import os, pg, string, sys;
+import pg, sys;
import apt_pkg;
-import utils, db_access;
+import utils, db_access, logging;
#######################################################################################
Cnf = None;
projectB = None;
+Logger = None;
+
+################################################################################
+
+def usage (exit_code=0):
+ print """Usage: heidi [OPTIONS] [FILE]
+Display or alter the contents of a suite using FILE(s), or stdin.
+
+ -a, --add=SUITE add to SUITE
+ -h, --help show this help and exit
+ -l, --list=SUITE list the contents of SUITE
+ -r, --remove=SUITE remove from SUITE
+ -s, --set=SUITE set SUITE"""
+
+ sys.exit(exit_code)
#######################################################################################
-def process_file (file, suite_id, action):
+def get_id (package, version, architecture):
+ if architecture == "source":
+ q = projectB.query("SELECT id FROM source WHERE source = '%s' AND version = '%s'" % (package, version))
+ else:
+ q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all') AND b.architecture = a.id" % (package, version, architecture))
+
+ ql = q.getresult();
+ if not ql:
+ utils.warn("Couldn't find '%s~%s~%s'." % (package, version, architecture));
+ return None;
+ if len(ql) > 1:
+ utils.warn("Found more than one match for '%s~%s~%s'." % (package, version, architecture));
+ return None;
+ id = ql[0][0];
+ return id;
- if action == "set":
- projectB.query("DELETE FROM bin_associations WHERE suite = %s" % (suite_id));
- projectB.query("DELETE FROM src_associations WHERE suite = %s" % (suite_id));
- action = "add";
-
- for line in file.readlines():
- split_line = string.split(string.strip(line[:-1]));
+#######################################################################################
+
+def set_suite (file, suite_id):
+ lines = file.readlines();
+
+ projectB.query("BEGIN WORK");
+
+ # Build up a dictionary of what is currently in the suite
+ current = {};
+ q = projectB.query("SELECT b.package, b.version, a.arch_string, ba.id FROM binaries b, bin_associations ba, architecture a WHERE ba.suite = %s AND ba.bin = b.id AND b.architecture = a.id" % (suite_id));
+ ql = q.getresult();
+ for i in ql:
+ key = " ".join(i[:3]);
+ current[key] = i[3];
+ q = projectB.query("SELECT s.source, s.version, sa.id FROM source s, src_associations sa WHERE sa.suite = %s AND sa.source = s.id" % (suite_id));
+ ql = q.getresult();
+ for i in ql:
+ key = " ".join(i[:2]) + " source";
+ current[key] = i[2];
+
+ # Build up a dictionary of what should be in the suite
+ desired = {};
+ for line in lines:
+ split_line = line.strip().split();
if len(split_line) != 3:
- sys.stderr.write("W: '%s' does not break into 'package version architecture'.\n" % (line[:-1]));
+ utils.warn("'%s' does not break into 'package version architecture'." % (line[:-1]));
continue;
-
- (package, version, architecture) = split_line;
+ key = " ".join(split_line);
+ desired[key] = "";
+
+ # 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();
+ id = current[key];
+ if architecture == "source":
+ q = projectB.query("DELETE FROM src_associations WHERE id = %s" % (id));
+ else:
+ q = projectB.query("DELETE FROM bin_associations WHERE id = %s" % (id));
+ Logger.log(["removed",key,id]);
+
+ # 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();
+ id = get_id (package, version, architecture);
+ if not id:
+ continue;
+ if architecture == "source":
+ q = projectB.query("INSERT INTO src_associations (suite, source) VALUES (%s, %s)" % (suite_id, id));
+ else:
+ q = projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%s, %s)" % (suite_id, id));
+ Logger.log(["added",key,id]);
- if architecture == "source":
- q = projectB.query("SELECT id FROM source WHERE source = '%s' AND version = '%s'" % (package, version))
- else:
- q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all') AND b.architecture = a.id" % (package, version, architecture))
+ projectB.query("COMMIT WORK");
+
+#######################################################################################
+
+def process_file (file, suite, action):
+
+ suite_id = db_access.get_suite_id(suite);
+
+ if action == "set":
+ set_suite (file, suite_id);
+ return;
+
+ lines = file.readlines();
+
+ projectB.query("BEGIN WORK");
- ql = q.getresult();
- if ql == []:
- sys.stderr.write("W: Couldn't find '%s~%s~%s'.\n" % (package, version, architecture));
+ 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;
- if len(ql) > 1:
- sys.stderr.write("E: Found more than one match for '%s~%s~%s'.\n" % (package, version, architecture));
+
+ (package, version, architecture) = split_line;
+
+ id = get_id(package, version, architecture);
+ if not id:
continue;
- id = ql[0][0];
- if architecture == "source":
+ if architecture == "source":
# Find the existing assoications ID, if any
q = projectB.query("SELECT id FROM src_associations WHERE suite = %s and source = %s" % (suite_id, id));
ql = q.getresult();
- if ql == []:
+ if not ql:
assoication_id = None;
else:
assoication_id = ql[0][0];
# Take action
if action == "add":
- if assoication_id != None:
- sys.stderr.write("W: '%s~%s~%s' already exists in suite %s.\n" % (package, version, architecture, suite_id));
+ if assoication_id:
+ utils.warn("'%s~%s~%s' already exists in suite %s." % (package, version, architecture, suite));
continue;
else:
q = projectB.query("INSERT INTO src_associations (suite, source) VALUES (%s, %s)" % (suite_id, id));
elif action == "remove":
if assoication_id == None:
- sys.stderr.write("W: '%s~%s~%s' doesn't exist in suite %s.\n" % (package, version, architecture, suite_id));
+ utils.warn("'%s~%s~%s' doesn't exist in suite %s." % (package, version, architecture, suite));
continue;
else:
q = projectB.query("DELETE FROM src_associations WHERE id = %s" % (assoication_id));
# Find the existing assoications ID, if any
q = projectB.query("SELECT id FROM bin_associations WHERE suite = %s and bin = %s" % (suite_id, id));
ql = q.getresult();
- if ql == []:
+ if not ql:
assoication_id = None;
else:
assoication_id = ql[0][0];
# Take action
if action == "add":
- if assoication_id != None:
- sys.stderr.write("W: '%s~%s~%s' already exists in suite %s.\n" % (package, version, architecture, suite_id));
+ if assoication_id:
+ utils.warn("'%s~%s~%s' already exists in suite %s." % (package, version, architecture, suite));
continue;
else:
q = projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%s, %s)" % (suite_id, id));
elif action == "remove":
if assoication_id == None:
- sys.stderr.write("W: '%s~%s~%s' doesn't exist in suite %s.\n" % (package, version, architecture, suite_id));
+ utils.warn("'%s~%s~%s' doesn't exist in suite %s." % (package, version, architecture, suite));
continue;
else:
q = projectB.query("DELETE FROM bin_associations WHERE id = %s" % (assoication_id));
-
+
+ projectB.query("COMMIT WORK");
+
#######################################################################################
-def get_list (suite_id):
+def get_list (suite):
+ suite_id = db_access.get_suite_id(suite);
# List binaries
q = projectB.query("SELECT b.package, b.version, a.arch_string FROM binaries b, bin_associations ba, architecture a WHERE ba.suite = %s AND ba.bin = b.id AND b.architecture = a.id" % (suite_id));
ql = q.getresult();
for i in ql:
- print string.join(i);
+ print " ".join(i);
# List source
q = projectB.query("SELECT s.source, s.version FROM source s, src_associations sa WHERE sa.suite = %s AND sa.source = s.id" % (suite_id));
ql = q.getresult();
for i in ql:
- print string.join(i) + " source";
+ print " ".join(i) + " source";
#######################################################################################
def main ():
- global Cnf, projectB;
+ global Cnf, projectB, Logger;
- apt_pkg.init();
-
- Cnf = apt_pkg.newConfiguration();
- apt_pkg.ReadConfigFileISC(Cnf,utils.which_conf_file());
+ Cnf = utils.get_conf()
Arguments = [('a',"add","Heidi::Options::Add", "HasArg"),
- ('d',"debug","Heidi::Options::Debug", "IntVal"),
('h',"help","Heidi::Options::Help"),
('l',"list","Heidi::Options::List","HasArg"),
('r',"remove", "Heidi::Options::Remove", "HasArg"),
- ('s',"set", "Heidi::Options::Set", "HasArg"),
- ('v',"version","Heidi::Options::Version")];
+ ('s',"set", "Heidi::Options::Set", "HasArg")];
+
+ for i in ["add", "help", "list", "remove", "set", "version" ]:
+ if not Cnf.has_key("Heidi::Options::%s" % (i)):
+ Cnf["Heidi::Options::%s" % (i)] = "";
file_list = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv);
+ Options = Cnf.SubTree("Heidi::Options")
+
+ if Options["Help"]:
+ usage();
- projectB = pg.connect('projectb', 'localhost');
+ projectB = pg.connect(Cnf["DB::Name"], Cnf["DB::Host"],int(Cnf["DB::Port"]));
db_access.init(Cnf, projectB);
action = None;
for i in ("add", "list", "remove", "set"):
- suite = Cnf["Heidi::Options::%s" % (i)];
- if suite !="":
+ if Cnf["Heidi::Options::%s" % (i)] != "":
+ suite = Cnf["Heidi::Options::%s" % (i)];
if not Cnf.has_key("Suite::%s" % (suite)):
- sys.stderr.write("Unknown suite %s.\n" %(suite));
- sys.exit(2);
+ utils.fubar("Unknown suite '%s'." %(suite));
else:
- suite_id = db_access.get_suite_id(suite);
- if action != None:
- sys.stderr.write("Can only do one action at a time.\n");
- sys.exit(2);
+ if action:
+ utils.fubar("Can only perform one action at a time.");
action = i;
# Need an action...
if action == None:
- sys.stderr.write("No action specified.\n");
- sys.exit(2);
+ utils.fubar("No action specified.");
# Safety/Sanity check
if action == "set" and suite != "testing":
- sys.stderr.write("Will not reset a suite other than testing...\n");
- sys.exit(2);
+ utils.fubar("Will not reset a suite other than testing.");
if action == "list":
- get_list(suite_id);
+ get_list(suite);
else:
- if file_list != []:
+ Logger = logging.Logger(Cnf, "heidi");
+ if file_list:
for file in file_list:
- process_file(utils.open_file(file,'r'), suite_id, action);
+ process_file(utils.open_file(file), suite, action);
else:
- process_file(sys.stdin, suite_id, action);
+ process_file(sys.stdin, suite, action);
+ Logger.close();
#######################################################################################