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: 01-Apr-2011 20:22:34 Branch: HEAD Handle: 2011040118223400 Modified files: mancoosi/framework rpmtest.py Log: - add patterns to capture --verify errors. - add more methods to the Uuut class. Summary: Revision Changes Path 1.2 +318 -245 mancoosi/framework/rpmtest.py ____________________________________________________________________________ patch -p0 <<'@@ .' Index: mancoosi/framework/rpmtest.py ============================================================================ $ cvs diff -u -r1.1 -r1.2 rpmtest.py --- mancoosi/framework/rpmtest.py 28 Mar 2011 14:30:19 -0000 1.1 +++ mancoosi/framework/rpmtest.py 1 Apr 2011 18:22:34 -0000 1.2 @@ -8,6 +8,7 @@ import glob import string import sys +import re import rpm import signal import optparse @@ -20,117 +21,285 @@ 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 +bin_rpm = "/bin/rpm" +var_lib_rpm = '/var/lib/rpm/' +usr_bin_dbrecover = '/usr/bin/db_recover' +usr_bin_dbstat = '/usr/bin/db_stat' + +class Uut: + def __init__(self, pkgset, niters=10, maxdelay=3.0, **kwargs): + self.pkgset = pkgset + self.npkgs = len(self.pkgset) + self.pkgs = [p for (p, fn) in pkgset] + print 'pkgs: ' + str(self.pkgs) + self.rpms = [fn for (p, fn) in pkgset] + print 'rpms: ' + str(self.rpms) + if niters: + self.niters = niters + if maxdelay: + self.maxdelay = maxdelay + self.ts = None + self.killtimes = [] + self.success_cases = 0 + self.details = [0,0] # (no. of transactions, no. of nop transactions) + + self.warning_pat = '.*warning: (.*)$' + self.error_pat = '.*error: (.*)$' + +# error: package libunixODBC1 is not installed + self.package_not_installed = '.*package ([^ ]*) is not installed$' +# Unsatisfied dependencies for openldap-clients-2.4.22-2mdv2010.1.i586: +# libldap2.4_2 = 2.4.22-2mdv2010.1 is needed by openldap-clients-2.4.22-2mdv2010.1.i586 +# liblber-2.4.so.2 is needed by openldap-clients-2.4.22-2mdv2010.1.i586 +# libldap-2.4.so.2 is needed by openldap-clients-2.4.22-2mdv2010.1.i586 + self.unsatisfied_deps = '^Unsatisfied dependencies for (.*):' + self.is_needed_by = '(.*) is needed by (.*)' +# warning: /etc/sysconfig/ldap created as /etc/sysconfig/ldap.rpmnew + self.created_as = '.*warning: (.*) created as (.*)' + + def bad(self, args): + print colored(args, 'red') + + def good(self, args): + print colored(args, 'green') + + def run(self, args): + print 'Running: ' + str(args) + po = subprocess.Popen(args, stdout=None, stderr=None) + res = po.wait() == 0 + return res + + def timed(self, args, delay): + print 'Running(' + str(delay) + '): ' + str(args) + po = subprocess.Popen(args, stdout=None, stderr=None) + pid = po.pid + time.sleep(delay) + res = subprocess.Popen.poll(po) == 0 + if not res: + try: + po.kill() + except OSError: + pass + sys.stderr.write('Killed pid('+str(pid)+') after %f\n' % delay) + return res + + def communicate(self, args): + print 'Running: ' + str(args) + xx = subprocess.Popen(args, stdout=subprocess.PIPE).communicate() + return xx + + def erase_allmatches(self, erase_list): + args = [bin_rpm, '-e', '--allmatches', '--nodeps' ] + erase_list + xx = self.run(args) + return xx + + def install_forced(self, install_list): + args = [bin_rpm, '-Uvh', '--force' ] + install_list + xx = self.run(args) + return xx + + # Calibrate maxdelay from actual install time + def calibrate(self): + xx = self.erase_allmatches(self.pkgs) + bgn = time.time() + self.install_forced(self.rpms) + self.maxdelay = (time.time() - bgn) * 1000. + xx = self.erase_allmatches(self.pkgs) + return xx + + def rollback(self, rollback_list): + print "--> rollback(" + str(rollback_list) + ")" + + # rollback_list must be really a list !! + if not self.install_forced(rollback_list): + self.bad('Rollback failed!') + + def test_case(self, opts): + args = [bin_rpm, opts, '--force' ] \ + + random.sample(self.rpms, self.npkgs) + + print 'Running: ' + str(args) + po = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr) + pid = po.pid + delay = (random.random() * self.maxdelay) / 1000. + self.killtimes.append(delay) + time.sleep(delay) + po.kill() + sys.stderr.write('Killed pid('+str(pid)+') after %f\n' % delay) + xx = True + return xx + + def test_case_single_pkg(self, pkg): + args = [bin_rpm, '-Uvh', '--force' ] + (p, filename) = pkg + args.append(filename) + + delay = (random.random() * self.maxdelay) / 1000. + print 'Running: ' + str(args) + po = subprocess.Popen(args, stdout=sys.stdout, stderr=sys.stderr) + pid = po.pid + self.killtimes.append(delay) + time.sleep(delay) + po.kill() + sys.stderr.write('Killed pid('+str(pid)+') after %f\n' % delay) + xx = True + return xx + + def queryByPkgName(self, name): + if not self.ts: + self.ts = rpm.TransactionSet() + match = self.ts.dbMatch('name', name) + h = None; + if match: + for h in match: + break # Assumes single match + return h + + def installTID(self, name): + h = self.queryByPkgName(name) + res = '' # None instead? + if h: + res = h[rpm.RPMTAG_INSTALLTID] + return res + + def printNEVRA(self, name): + h = self.queryByPkgName(name) + res = '' # None instead? + if h: + E = (str(h['epoch']) + ':' if h['epoch'] else '') + res = '%s-%s%s-%s.%s' % (h['name'], E, h['version'], + h['release'], h['arch']) + return res + + # Start with known pre-conditions. + def prenatal(self): + return self.erase_allmatches(self.pkgs) + + # See if a stale lock stops rpmdb queries (must be run as root). + def check_query(self): + args = [bin_rpm, '-q'] + self.pkgs + watchdog_delay = 2.0 + xx = self.timed(args, watchdog_delay) + return xx + + # See if rpmdb access is recoverable. Remove stale locks to proceed. + def check_recover(self): + watchdog_delay = 2.0 + args = [usr_bin_dbrecover, '-h', var_lib_rpm] + xx = self.timed(args, watchdog_delay) + if not xx: + args = [usr_bin_dbstat, '-Cl', '-h', var_lib_rpm] + self.run(args) + args = ['/bin/rm', '-f', var_lib_rpm + '__db*'] + self.run(args) + return xx + + # Verify a single package file contents + def verify(self, opts, pkgname): + args = [bin_rpm, opts, pkgname] + (vfyout, vfyerr) = self.communicate(args) + if vfyout: + while True: + mo = re.match(self.package_not_installed, vfyout) + if mo: + break + mo = re.match(self.unsatisfied_deps, vfyout) + if mo: + break + print '=== verify(%s) stdout ===' % pkgname + print vfyout + break + if vfyerr: + print '=== verify(%s) stderr ===' % pkgname + print vfyerr + if (vfyout and len(vfyout)) or (vfyerr and len(vfyerr)): + res = False + else: + res = True + return res + # Post-mortem test results analysis + def postmortem(self): -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) + version_wrong = 0 + files_wrong = 0 + was_updated = 0 + not_updated = 0 + + # See if a stale lock stops rpmdb queries. + xx = self.check_query() + + # See if rpmdb access is recoverable. Remove stale locks to proceed. + xx = self.check_recover() + + for (p, file) in self.pkgset: + iniNEVRA = initial_versions[p] + curNEVRA = self.printNEVRA(p) + if iniNEVRA and curNEVRA and iniNEVRA != curNEVRA: + print '''Initial version: %s +Current version: %s +''' % (iniNEVRA, curNEVRA) + if iniNEVRA and iniNEVRA != curNEVRA: + self.bad('Version for pkg %s is different' % p) + was_updated += 1 + else: + self.good('Version for pkg %s is the same' % p) + not_updated += 1 + xx = self.verify('-V', p) + + del self.ts + self.ts = None + + if was_updated > 0 and not_updated > 0: + self.bad('Multiple-package test failed') + res = False + else: + self.good('Multiple-package test success') + if was_updated == 0: + self.details[1] += 1 + else: + self.details[0] += 1 + + res = True + self.success_cases += 1 + + print '''Within this transaction %d pkgs were updated and %d were not updated''' \ + % (was_updated, not_updated) + # 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, self.npkgs,files_wrong, + # .... self.npkgs) + # return [prettyPrintPkgList(pkgs), str(self.npkgs), str(version_wrong), str(files_wrong), str(self.killtimes[-1])] + + return res -def listPkgContent(ts, pkgname): - h = queryByPkgName(ts, pkgname) +def listPkgContent(ts, name): + match = ts.dbMatch('name', name) + h = None; + if match: + for h in match: + break # Assumes single match if h and h[rpm.RPMTAG_FILENAMES]: for file in h[rpm.RPMTAG_FILENAMES]: -# print "<-- listPkgContent(ts, " + pkgname + ") fn " + file +# print "<-- listPkgContent(ts, " + name + ") 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... +def fileTimestamp(filePath): + try: + timestamp = os.stat(filePath).st_mtime +# print "<-- fileTimestamp(" + filePath + ")" + return timestamp + except OSError: - for p in ts.dbMatch(): - if p['name'].startswith(pkgname): - return p - else: - return None + # uut.bad("Filepath %s not found" % filePath) - for m in match: - return m # Assume just one match + pass def dumpResultsToCsv(results, filename): @@ -154,40 +323,15 @@ return res -def NEVRA(ts, name): - print "--> NEVRA(ts, name)" +def printNVRA(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 + h = None; + if match: + for h in match: + break # Assumes single match + res = '' # None instead? + if h: + res = '%s-%s-%s.%s' % (h['name'], h['version'], h['release'], h['arch']) return res @@ -198,48 +342,27 @@ 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 +def checkResultsSinglePkg(uut, pkg_test): print 'Single Pkg test for upgrading %s, waiting time %f seg.' \ - % (pkg_test, killtimes[-1]) + % (pkg_test, uut.killtimes[-1]) res = True - # After all the messup caused by (failed) installs we can't reuse the ts + uut.run([usr_bin_dbstat, '-Cl', '-h', var_lib_rpm]) + uut.run(['/bin/rm', '-f', var_lib_rpm + '__db*']) - global_transaction.closeDB() - global_transaction = rpm.TransactionSet() - res = doublePkgEntry(global_transaction, pkg_test) + # After all the messup caused by (failed) installs we can't reuse the ts + ts = rpm.TransactionSet() + res = doublePkgEntry(ts, pkg_test) + del ts if res > 1: - print colored('Two versions detected for package %s' - % pkg_test, 'red') + uut.bad('Two versions detected for package %s' % pkg_test) res = False elif res == 0: - print colored('No version of package %s was found' % pkg_test, - 'green') + uut.good('No version of package %s was found' % pkg_test) else: - print colored('Just one version of package %s was found' - % pkg_test, 'green') + uut.good('Just one version of package %s was found' % pkg_test) - print "<-- checkResultsSinglePkg(ts, " + str(pkg_test) + ") res " + str(res) + print "<-- checkResultsSinglePkg(uut, " + str(pkg_test) + ") res " + str(res) return res @@ -253,10 +376,10 @@ if fileTimestamp(f[0]) > f[1]: print 'File was modified' else: - print colored('File %s not touched' % f[0], 'red') + uut.bad('File %s not touched' % f[0]) res = False else: - print colored('Non-existing file %s ' % f[0], 'red') + uut.bad('Non-existing file %s ' % f[0]) res = False else: @@ -274,10 +397,10 @@ return h -def compareWithInstalledVersion(ts, pkgfile): +def compareWithInstalledVersion(uut, ts, pkgfile): h = readRpmHeader(ts, pkgfile) pkg_ds = h.dsOfHeader() - inst_h = queryByPkgName(h['name']) + inst_h = uut.queryByPkgName(h['name']) inst_ds = inst_h.dsOfHeader() pkg_evr = pkg_ds.EVR() instr_evr = inst_ds.EVR() @@ -292,58 +415,6 @@ 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: @@ -376,8 +447,6 @@ parser.print_help() sys.exit(-1) - ts = rpm.TransactionSet() - global_transaction = ts to_install = [] to_upgrade = [] to_upgrade_multi = set() @@ -387,7 +456,7 @@ 'Failures timestamps', 'Kill interval (sec)']] package_sets = [ - 'openldap,openldap-clients,openldap-doc,openldap-servers', + 'openldap,libldap2.4_2,openldap-clients,openldap-doc,openldap-extra-schemas,openldap-servers,libunixODBC1', 'postgresql8.3,postgresql8.3-contrib,postgresql8.3-devel,postgresql8.3-docs' , 'ghostscript,ghostscript-X,ghostscript-common,ghostscript-doc', @@ -402,6 +471,7 @@ ] if opts.package_set and not opts.singlepkg: + ts = rpm.TransactionSet() for pkgname in package_sets[opts.package_set - 1].split(','): pat = args[0] + os.path.sep + pkgname + '-' if string.find(pkgname, '-') < 0: @@ -413,23 +483,21 @@ 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)) + del ts else: + ts = rpm.TransactionSet() 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) + del ts if opts.debug: print 'Selected %d Packages for Upgrades: \n%s' \ @@ -439,15 +507,19 @@ 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 + n_iters = (int(args[2]) if len(args) > 2 else 2) + max_delay = (args[1] if len(args) > 1 else 3000.) + + uut = Uut(to_upgrade_multi, niters=n_iters, maxdelay=max_delay) 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): + else: # Calibrate maxdelay from actual install time + xx = uut.calibrate() + + for n in range(uut.niters): if opts.singlepkg: # pkg_test = args[3] if len(args) > 3 else random.choice(to_upgrade)[1] @@ -455,38 +527,39 @@ # 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 + xx = uut.erase_allmatches(p) + uut.test_case_single_pkg(pkg_test) + if checkResultsSinglePkg(uut, p): + uut.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) + uut.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 + if uut.npkgs > 0: + xx = uut.prenatal() + xx = uut.test_case('-Uvh') + xx = uut.postmortem() + to_rollback = [] - for (p, filename) in to_upgrade_multi: + for (p, filename) in uut.pkgset: if initial_versions[p]: to_rollback.append(precondition_dir + initial_versions[p] + '.rpm') if to_rollback: - do_rollback(to_rollback) + uut.rollback(to_rollback) - print 'Test aggregate results: %d/%d pkgs' % (success_cases, - n_iters) + print 'Test aggregate results: %d/%d pkgs' \ + % (uut.success_cases, uut.niters) if not opts.singlepkg: print 'Multiple package test details: %d transactions to completion /%d initial state' \ - % (multi_test_details[0], multi_test_details[1]) + % (uut.details[0], uut.details[1]) - print 'Average killtimes: %f' % (sum(killtimes) / len(killtimes)) + print 'Average killtimes: %f' % (sum(uut.killtimes) / len(uut.killtimes)) # dumpResultsToCsv(global_results, output_file) @@ . ______________________________________________________________________ RPM Package Manager http://rpm5.org CVS Sources Repository rpm-cvs@rpm5.org