Collect logs from tests

After each test, and after class setups and teardowns, the BeakerLib
integration plugin now downloads log files from the remote masters
and submits them using rlFileSubmit.

Part of the work for: https://fedorahosted.org/freeipa/ticket/3621
This commit is contained in:
Petr Viktorin
2013-06-13 06:22:14 -04:00
parent 00f133458b
commit 5365e1b81b
2 changed files with 69 additions and 12 deletions

View File

@@ -20,10 +20,10 @@
"""A Nose plugin that integrates with BeakerLib""" """A Nose plugin that integrates with BeakerLib"""
import os import os
import sys
import subprocess import subprocess
import traceback import traceback
import logging import logging
import tempfile
import nose import nose
from nose.plugins import Plugin from nose.plugins import Plugin
@@ -56,6 +56,10 @@ class BeakerLibPlugin(Plugin):
# See nose.plugins.base.IPluginInterface for Nose plugin interface docs # See nose.plugins.base.IPluginInterface for Nose plugin interface docs
name = 'beakerlib' name = 'beakerlib'
def __init__(self):
super(BeakerLibPlugin, self).__init__()
self.log = log_mgr.get_logger(self)
def options(self, parser, env=os.environ): def options(self, parser, env=os.environ):
super(BeakerLibPlugin, self).options(parser, env=env) super(BeakerLibPlugin, self).options(parser, env=env)
self.env = env self.env = env
@@ -76,9 +80,9 @@ class BeakerLibPlugin(Plugin):
source_path = os.path.join(self.env['BEAKERLIB'], 'beakerlib.sh') source_path = os.path.join(self.env['BEAKERLIB'], 'beakerlib.sh')
self.run_beakerlib_command(['.', source_path]) self.run_beakerlib_command(['.', source_path])
# _in_class is set when we are in setup_class, so its rlPhaseEnd can # _in_class_setup is set when we are in setup_class, so logs can be
# be called when the first test starts # collected just before the first test starts
self._in_class = False self._in_class_setup = False
# Redirect logging to our own handlers # Redirect logging to our own handlers
self.setup_log_handler(BeakerLibLogHandler(self.run_beakerlib_command)) self.setup_log_handler(BeakerLibLogHandler(self.run_beakerlib_command))
@@ -113,24 +117,28 @@ class BeakerLibPlugin(Plugin):
""" """
if not isinstance(context, type): if not isinstance(context, type):
return return
message = 'Class setup: %s' % context.__name__ message = 'Nose Test Class: %s' % context.__name__
self.run_beakerlib_command(['rlPhaseStart', 'FAIL', message]) self.run_beakerlib_command(['rlPhaseStart', 'FAIL', message])
self._in_class = True self._in_class_setup = True
def stopContext(self, context): def stopContext(self, context):
"""End a test context""" """End a test context"""
if self._in_class: if not isinstance(context, type):
return
self.collect_logs(context)
self.run_beakerlib_command(['rlPhaseEnd']) self.run_beakerlib_command(['rlPhaseEnd'])
def startTest(self, test): def startTest(self, test):
"""Start a test phase""" """Start a test phase"""
if self._in_class: if self._in_class_setup:
self.run_beakerlib_command(['rlPhaseEnd']) self.collect_logs(test.context)
self.log.info('Running test: %s', test.id())
self.run_beakerlib_command(['rlPhaseStart', 'FAIL', self.run_beakerlib_command(['rlPhaseStart', 'FAIL',
'Nose test: %s' % test]) 'Nose test: %s' % test])
def stopTest(self, test): def stopTest(self, test):
"""End a test phase""" """End a test phase"""
self.collect_logs(test.context)
self.run_beakerlib_command(['rlPhaseEnd']) self.run_beakerlib_command(['rlPhaseEnd'])
def addSuccess(self, test): def addSuccess(self, test):
@@ -158,3 +166,50 @@ class BeakerLibPlugin(Plugin):
def addFailure(self, test, err): def addFailure(self, test, err):
self.log_exception(err) self.log_exception(err)
self.run_beakerlib_command(['rlFail', 'Test failed']) self.run_beakerlib_command(['rlFail', 'Test failed'])
def collect_logs(self, test):
"""Collect logs specified in test's logs_to_collect attribute
"""
try:
logs_to_collect = test.logs_to_collect
except AttributeError:
self.log.debug('No logs to collect')
else:
for host, logs in logs_to_collect.items():
self.log.info('Collecting logs from: %s', host.hostname)
# Tar up the logs on the remote server
cmd = host.run_command(['tar', 'cJv'] + logs, log_stdout=False,
raiseonerr=False)
if cmd.returncode:
self.run_beakerlib_command(
['rlFail', 'Could not collect all requested logs'])
# Copy and unpack on the local side
topdirname = tempfile.mkdtemp()
dirname = os.path.join(topdirname, host.hostname)
os.mkdir(dirname)
tarname = os.path.join(dirname, 'logs.tar.xz')
with open(tarname, 'w') as f:
f.write(cmd.stdout_text)
self.log.info('%s', dirname)
ipautil.run(['tar', 'xJvf', 'logs.tar.xz'], cwd=dirname)
os.unlink(tarname)
# Use BeakerLib's rlFileSubmit on the indifidual files
# The resulting submitted filename will be
# $HOSTNAME-$FILENAME (with '/' replaced by '-')
self.run_beakerlib_command(['pushd', topdirname])
for dirpath, dirnames, filenames in os.walk(topdirname):
for filename in filenames:
fullname = os.path.relpath(
os.path.join(dirpath, filename), topdirname)
self.log.info('Submitting file: %s', fullname)
self.run_beakerlib_command(['rlFileSubmit', fullname])
self.run_beakerlib_command(['popd'])
# The BeakerLib process runs asynchronously, let it clean up
# after it's done with the directory
self.run_beakerlib_command(['rm', '-rvf', topdirname])
test.logs_to_collect.clear()

View File

@@ -98,17 +98,19 @@ class IntegrationTest(object):
try: try:
cls.uninstall() cls.uninstall()
finally: finally:
del cls.logs_to_collect
del cls.master del cls.master
del cls.replicas del cls.replicas
del cls.clients del cls.clients
@classmethod @classmethod
def uninstall(cls): def uninstall(cls):
cls.collect_log(cls.master, '/var/log/ipaserver-uninstall.log')
cls.master.run_command(['ipa-server-install', '--uninstall', '-U']) cls.master.run_command(['ipa-server-install', '--uninstall', '-U'])
for replica in cls.replicas: for replica in cls.replicas:
cls.collect_log(replica, '/var/log/ipaserver-uninstall.log')
replica.run_command(['ipa-server-install', '--uninstall', '-U']) replica.run_command(['ipa-server-install', '--uninstall', '-U'])
for client in cls.clients: for client in cls.clients:
cls.collect_log(replica, '/var/log/ipaclient-uninstall.log')
client.run_command(['ipa-client-install', '--uninstall', '-U']) client.run_command(['ipa-client-install', '--uninstall', '-U'])
@classmethod @classmethod