RPM Package Manager, CVS Repository http://rpm5.org/cvs/ ____________________________________________________________________________
Server: rpm5.org Name: Jeff Johnson Root: /v/rpm/cvs Email: j...@rpm5.org Module: mancoosi Date: 28-Mar-2011 16:30:20 Branch: HEAD Handle: 2011032814301900 Added files: mancoosi/framework rpmtest.py Log: - swipe a copy of rpm4_test.py for developpment/extension. Summary: Revision Changes Path 1.1 +492 -0 mancoosi/framework/rpmtest.py ____________________________________________________________________________ patch -p0 <<'@@ .' Index: mancoosi/framework/rpmtest.py ============================================================================ $ cvs diff -u -r0 -r1.1 rpmtest.py --- /dev/null 2011-03-28 16:30:19.000000000 +0200 +++ rpmtest.py 2011-03-28 16:30:20.133414672 +0200 @@ -0,0 +1,492 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import random +import time +import os +import fnmatch +import glob +import string +import sys +import rpm +import signal +import optparse +import subprocess +import csvwriter + +from termcolor import colored + +# Globals + +initial_contents = {} # PKGNAME: [(FILE, mtime),...] +initial_versions = {} # PKGNAME: (NEVRA, INSTALLTID) +killtimes = [] # list of killtimes +rollback_list = [] +#mainrelease_dir = '/tmp/rpms/baseline/' +#multi_updates_dir = '/home/agrr/pacotes/multi-updates/' +#multi_release_dir = '/home/agrr/pacotes/multi-base/' +precondition_dir = "CM15/RPMS/" +multi_test_details = [0, 0] # (Number of all upgraded transactions, number of nop transactions) +global_transaction = None + +def test_case(list_of_pkgs, max_delay): + global killtimes + print "--> test_case(list_of_pkgs, " + str(max_delay) + ")" + args = ['rpm', '-Uvh', '--force', '--nodeps', '--noscripts'] + print 'Test Case: ' + str(list_of_pkgs) + + args_final = args + list_of_pkgs + + print 'About to run ' + str(args_final) + rpm_pid = os.spawnv(os.P_NOWAIT, '/bin/rpm', args_final) + mult = random.random() + actual_killtime = mult * max_delay * len(list_of_pkgs) + print 'Killtime actual: ' + str(actual_killtime) + ' ' + + time.sleep(actual_killtime) + + killtimes.append(actual_killtime) + sys.stderr.write('Killing rpm after %f\n' % actual_killtime) + os.kill(rpm_pid, signal.SIGKILL) + + +def test_case_single_pkg(pkg, max_delay): + global killtimes + print "--> test_case_single_pkg(pkg, " + str(max_delay) + ")" + args = ['rpm', '-Uvh', '--force', '--nodeps', '--quiet'] + (blah, filename) = pkg + args.append(filename) + + print 'About to run rpm -Uvh ' + str(args) + rpm_pid = os.spawnv(os.P_NOWAIT, '/bin/rpm', args) + mult = random.random() * max_delay + killtimes.append(mult) + + time.sleep(mult) + + sys.stderr.write('Killing rpm after %f\n' % mult) + os.kill(rpm_pid, signal.SIGKILL) + + +def install_forced(install_list): + args = ['/bin/rpm', '-Uvh', '--force', '--nodeps', '--quiet'] \ + + install_list + p = subprocess.Popen(args) + ret = p.wait() + res = ret == 0 + print "<-- install_forced(" + str(install_list) + ") res " + str(res) + return res + + +def do_rollback(rollback_list): + print "--> do_rollback(" + str(rollback_list) + ")" + print 'Rollback for ' + str(rollback_list) + + # rollback_list must be really a list !! + + if not install_forced(rollback_list): + print colored('Rollback failed!', 'red') + recoverRPMdb() + + +def recoverRPMdb(): + rpmdb_dir = '/var/lib/rpm/' + args = ['sudo', '/usr/bin/db_recover', '-e', '-h', rpmdb_dir] + p = subprocess.Popen(args).communicate()[0] + print "<-- recoverRPMdb()" + + +def rpmVerify(pkgname): + args = ['rpm', '-V', pkgname] + verify_output = subprocess.Popen(args, + stdout=subprocess.PIPE).communicate()[0] + if len(0): + print 'Package %s is not OK' % pkgname + res = False + else: + res = True + print "<-- rpmVerify(" + pkgname + ") res " + str(res) + + +def listPkgContent(ts, pkgname): + h = queryByPkgName(ts, pkgname) + if h and h[rpm.RPMTAG_FILENAMES]: + for file in h[rpm.RPMTAG_FILENAMES]: +# print "<-- listPkgContent(ts, " + pkgname + ") fn " + file + yield file + + +def queryByPkgName(ts, name): +# print "--> queryByPkgName(ts, " + name + ")" + match = ts.dbMatch('name', name) + if not match: + + # We need to catch the 'timezone' case... + + for p in ts.dbMatch(): + if p['name'].startswith(pkgname): + return p + else: + return None + + for m in match: + return m # Assume just one match + + +def dumpResultsToCsv(results, filename): + print "--> dumpResultsToCsv(results, filename)" + outfile = open(filename, 'w') + writer = csvwriter.UnicodeWriter(f=outfile) + writer.writerows(results) + + +def pkgNameFromNEVRA(nevra): + list = nevra.split('-') + res = '-'.join(list[0:-2]) + print "<-- pkgNameFromNEVRA(" + nevra + ") res " + res + return res + + +def splitNEVR(nevra): + list = nevra.split('-') + res = ('-'.join(list[0:-2]), list[-2], list[-1]) + print "<-- splitNEVR(" + nevra + ") res " + res + return res + + +def NEVRA(ts, name): + print "--> NEVRA(ts, name)" + match = ts.dbMatch('name', name) + for h in match: + return printNEVRA(ts, h) + + +def installTID(ts, name): + h = queryByPkgName(ts, name) + res = h[rpm.RPMTAG_INSTALLTID] + print "<-- installTID(ts, " + name + ") res " + str(res) + return res + + +def printNEVRA(ts, pkgname): + h = queryByPkgName(ts, pkgname) + if not h: + print 'Version not found' + return '' + epoch = (str(h['epoch']) + ':' if h['epoch'] else '') + res = '%s-%s%s-%s.%s' % (h['name'], epoch, h['version'], + h['release'], h['arch']) # Assume just one match + print "<-- printNEVRA(ts, " + pkgname + ") res " + res + return res + + +def printNVRA(ts, pkgname): + h = queryByPkgName(ts, pkgname) + if not h: + print 'Version not found' + return '' + res = '%s-%s-%s.%s' % (h['name'], h['version'], h['release'], + h['arch']) # Assume just one match + print "<-- printNVRA(ts, " + pkgname + ") res " + res + return res + + +def doublePkgEntry(ts, name): + match = ts.dbMatch('name', name) + res = match.count() + print "<-- doublePkgEntry(ts, " + name + ") res " + str(res) + return res + + +def fileTimestamp(filePath): + try: + timestamp = os.stat(filePath).st_mtime +# print "<-- fileTimestamp(" + filePath + ")" + return timestamp + except OSError: + + # print colored("Filepath %s not found" % filePath, 'red') + + pass + + +def rpmqa(): + print "--> rpmqa()" + ts = rpm.TransactionSet() + for h in ts.dbMatch(): + print h + + +def checkResultsSinglePkg(ts, pkg_test): + global global_transaction + print 'Single Pkg test for upgrading %s, waiting time %f seg.' \ + % (pkg_test, killtimes[-1]) + res = True + + # After all the messup caused by (failed) installs we can't reuse the ts + + global_transaction.closeDB() + global_transaction = rpm.TransactionSet() + res = doublePkgEntry(global_transaction, pkg_test) + if res > 1: + print colored('Two versions detected for package %s' + % pkg_test, 'red') + res = False + elif res == 0: + print colored('No version of package %s was found' % pkg_test, + 'green') + else: + print colored('Just one version of package %s was found' + % pkg_test, 'green') + + print "<-- checkResultsSinglePkg(ts, " + str(pkg_test) + ") res " + str(res) + return res + + +def check_fs_consistency(pkgname): + contents = initial_contents[pkgname] + + # contents: List of (file, timestamp) tuples + + for f in contents: + if os.path.exists(f[0]): + if fileTimestamp(f[0]) > f[1]: + print 'File was modified' + else: + print colored('File %s not touched' % f[0], 'red') + res = False + else: + print colored('Non-existing file %s ' % f[0], 'red') + res = False + else: + + res = True + print "<-- check_fs_consistency(" + pkgname + ") res " + str(res) + return res + + +def readRpmHeader(ts, filename): + fd = os.open(filename, os.O_RDONLY) + ts.setVSFlags(rpm.RPMVSF_NODSA | rpm._RPMVSF_NOSIGNATURES) # Ignore signatures, please + h = ts.hdrFromFdno(fd) + os.close(fd) + print "<-- readRpmHeader(ts, " + filename + ")" + return h + + +def compareWithInstalledVersion(ts, pkgfile): + h = readRpmHeader(ts, pkgfile) + pkg_ds = h.dsOfHeader() + inst_h = queryByPkgName(h['name']) + inst_ds = inst_h.dsOfHeader() + pkg_evr = pkg_ds.EVR() + instr_evr = inst_ds.EVR() + + if pkg_evr == instr_evr: + res = 0 + elif pkg_evr > instr_evr: + res = 1 + else: + res = -1 + print "<-- compareWithInstalledVersion(ts, " + pkgfile + ") res " + str(res) + return res + + +# TODO: Review and fix + + +def checkResults(ts, pkgs): + global global_transaction + global multi_test_details + + total_pkgs = len(pkgs) + version_wrong = 0 + files_wrong = 0 + was_updated = not_updated = 0 + + global_transaction.closeDB() + global_transaction = rpm.TransactionSet() + + for (p, file) in pkgs: + print '''Initial version: %s +Current version: %s +''' \ + % (initial_versions[p], printNEVRA(global_transaction, p)) + if printNEVRA(global_transaction, p) != initial_versions[p]: + print colored('Version for pkg %s is different' % p, 'red') + was_updated += 1 + else: + print colored('Version for pkg %s is the same' % p, 'red') + not_updated += 1 + + if was_updated > 0 and not_updated > 0: + print colored('Multiple-package test failed', 'red') + res = False + else: + print colored('Multiple-package test success', 'green') + if was_updated == 0: + multi_test_details[1] += 1 + else: + multi_test_details[0] += 1 + + res = True + + print '''Within this transaction %d pkgs were updated and %d were not updated''' \ + % (was_updated, not_updated) + + print "<-- checkResults(ts, " + str(pkgs) + ") res " + str(res) + return res + + + # print '''Results:\n\t %d out of %d Packages failed the database test\n\t + # %d out of %d failed the timestamps test''' % (version_wrong, total_pkgs,files_wrong, + # .... total_pkgs) + # return [prettyPrintPkgList(pkgs), str(total_pkgs), str(version_wrong), str(files_wrong), str(killtimes[-1])] + + +def prettyPrintPkgList(list): + out = '' + for p in list: + out += ', ' + p[0] + return out + + +if __name__ == '__main__': + + parser = \ + optparse.OptionParser(usage='Usage: %prog [options] PackageDir [MaxDelay] [#Iters]...' + , version='%prog-0.1', + conflict_handler='resolve') + + parser.add_option('-s', '--singlepkg', action='store_true', + dest='singlepkg', + help='Run test for single package transactions') + parser.add_option('-d', '--debug', action='store_true', dest='debug' + , + help='Dont actually run the tests, just print what would be done' + ) + parser.add_option('-p', '--package-set', type='int', + dest='package_set', + help='''Choose which package set to use for the multi-package test: [1-10] ''' + ) + + (opts, args) = parser.parse_args() + + if len(args) < 1: + parser.print_help() + sys.exit(-1) + + ts = rpm.TransactionSet() + global_transaction = ts + to_install = [] + to_upgrade = [] + to_upgrade_multi = set() + + output_file = time.strftime('%d%m%Y-%H%M%S', time.gmtime()) + '.csv' + global_results = [['List of packages', '# packages', 'Failures DB', + 'Failures timestamps', 'Kill interval (sec)']] + + package_sets = [ + 'openldap,openldap-clients,openldap-doc,openldap-servers', + 'postgresql8.3,postgresql8.3-contrib,postgresql8.3-devel,postgresql8.3-docs' + , + 'ghostscript,ghostscript-X,ghostscript-common,ghostscript-doc', + 'ruby,ruby-devel,ruby-doc', + 'apache-mod_dbd,apache-mod_disk_cache,apache-mod_deflate,apache-mod_mod_proxy' + , + 'beagle,beagle-crawl-system,beagle-evolution', + 'bind,bind-devel,bind-doc', + 'evolution,evolution-data-server,evolution-exchange', + 'git,git-core,git-cvs,git-svn', + 'mysql,mysql-bench,mysql-common,mysql-doc', + ] + + if opts.package_set and not opts.singlepkg: + for pkgname in package_sets[opts.package_set - 1].split(','): + pat = args[0] + os.path.sep + pkgname + '-' + if string.find(pkgname, '-') < 0: + pat += '[0-9]' + pat += '*.rpm' + flist = glob.glob(pat) + for file in flist: + if pkgname not in initial_versions.keys(): + initial_contents[pkgname] = [(f, fileTimestamp(f)) + for f in listPkgContent(ts, pkgname)] + initial_versions[pkgname] = printNVRA(ts, pkgname) + print "+++ add(" + pkgname + ", " + file + ")" + to_upgrade_multi.add((pkgname, file)) + else: + flist = os.listdir(args[0]) + for file in flist: + if fnmatch.fnmatch(file, '*.rpm'): + basename = os.path.splitext(file)[0] # Remove extension + pkgname = pkgNameFromNEVRA(basename) + filename = args[0] + os.path.sep + file + installed_NEVR = queryByPkgName(ts, pkgname) + to_upgrade.append((pkgname, filename)) + initial_contents[pkgname] = [(f, fileTimestamp(f)) + for f in listPkgContent(ts, pkgname)] + initial_versions[pkgname] = printNVRA(ts, pkgname) + + n_iters = (int(args[2]) if len(args) > 2 else 10) + max_delay = (args[1] if len(args) > 1 else 3) + + if opts.debug: + print 'Selected %d Packages for Upgrades: \n%s' \ + % (len(to_upgrade), str(to_upgrade)) + print 'Selected %d Packages for New Install: \n%s' \ + % (len(to_install), str(to_install)) + for p in to_upgrade: + print 'Dry run of filesystem checks for ' + p[0] + print str(check_fs_consistency(p[0])) + + sys.exit(0) + + success_cases = 0 + + if opts.singlepkg: + pkg_test = random.choice(to_upgrade) # Same-pkg test + total_dashes = int(n_iters * 100 / n_iters) + for n in range(n_iters): + if opts.singlepkg: + + # pkg_test = args[3] if len(args) > 3 else random.choice(to_upgrade)[1] + # pkg_test = random.choice(to_upgrade) #Random package test + # to_upgrade.remove(pkg_test) #Avoid repeating the choice + + (p, filename) = pkg_test + test_case_single_pkg(pkg_test, float(max_delay)) + if checkResultsSinglePkg(ts, p): + success_cases += 1 + to_rollback = [] + if initial_versions[p]: + to_rollback.append(precondition_dir + + initial_versions[p] + '.rpm') + if to_rollback: + do_rollback(to_rollback) + else: + + if len(to_upgrade_multi) > 0: + filenames_multi = [filename for (p, filename) in + to_upgrade_multi] + test_case(filenames_multi, float(max_delay)) + if checkResults(ts, to_upgrade_multi): + success_cases += 1 + to_rollback = [] + for (p, filename) in to_upgrade_multi: + if initial_versions[p]: + to_rollback.append(precondition_dir + + initial_versions[p] + '.rpm') + + if to_rollback: + do_rollback(to_rollback) + + print 'Test aggregate results: %d/%d pkgs' % (success_cases, + n_iters) + if not opts.singlepkg: + print 'Multiple package test details: %d transactions to completion /%d initial state' \ + % (multi_test_details[0], multi_test_details[1]) + + print 'Average killtimes: %f' % (sum(killtimes) / len(killtimes)) + + # dumpResultsToCsv(global_results, output_file) @@ . ______________________________________________________________________ RPM Package Manager http://rpm5.org CVS Sources Repository rpm-cvs@rpm5.org