summaryrefslogtreecommitdiffstats
path: root/ipatests/pytest_plugins/integration/__init__.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipatests/pytest_plugins/integration/__init__.py')
-rw-r--r--ipatests/pytest_plugins/integration/__init__.py239
1 files changed, 239 insertions, 0 deletions
diff --git a/ipatests/pytest_plugins/integration/__init__.py b/ipatests/pytest_plugins/integration/__init__.py
new file mode 100644
index 000000000..ae94dbcb1
--- /dev/null
+++ b/ipatests/pytest_plugins/integration/__init__.py
@@ -0,0 +1,239 @@
+# Authors:
+# Petr Viktorin <pviktori@redhat.com>
+#
+# Copyright (C) 2011 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+"""Pytest plugin for IPA Integration tests"""
+
+from __future__ import print_function
+
+import os
+import tempfile
+import shutil
+
+import pytest
+from pytest_multihost import make_multihost_fixture
+
+from ipapython import ipautil
+from ipapython.ipa_log_manager import log_mgr
+
+log = log_mgr.get_logger(__name__)
+
+
+def pytest_addoption(parser):
+ group = parser.getgroup("IPA integration tests")
+
+ group.addoption(
+ '--logfile-dir', dest="logfile_dir", default=None,
+ help="Directory to store integration test logs in.")
+
+
+def collect_test_logs(node, logs_dict, test_config):
+ """Collect logs from a test
+
+ Calls collect_logs
+
+ :param node: The pytest collection node (request.node)
+ :param logs_dict: Mapping of host to list of log filnames to collect
+ :param test_config: Pytest configuration
+ """
+ collect_logs(
+ name=node.nodeid.replace('/', '-').replace('::', '-'),
+ logs_dict=logs_dict,
+ logfile_dir=test_config.getoption('logfile_dir'),
+ beakerlib_plugin=test_config.pluginmanager.getplugin('BeakerLibPlugin'),
+ )
+
+
+def collect_logs(name, logs_dict, logfile_dir=None, beakerlib_plugin=None):
+ """Collect logs from remote hosts
+
+ Calls collect_logs
+
+ :param name: Name under which logs arecollected, e.g. name of the test
+ :param logs_dict: Mapping of host to list of log filnames to collect
+ :param logfile_dir: Directory to log to
+ :param beakerlib_plugin:
+ BeakerLibProcess or BeakerLibPlugin used to collect tests for BeakerLib
+
+ If neither logfile_dir nor beakerlib_plugin is given, no tests are
+ collected.
+ """
+ if logs_dict and (logfile_dir or beakerlib_plugin):
+
+ if logfile_dir:
+ remove_dir = False
+ else:
+ logfile_dir = tempfile.mkdtemp()
+ remove_dir = True
+
+ topdirname = os.path.join(logfile_dir, name)
+
+ for host, logs in logs_dict.items():
+ log.info('Collecting logs from: %s', host.hostname)
+
+ # Tar up the logs on the remote server
+ cmd = host.run_command(
+ ['tar', '-c', '--ignore-failed-read', '-J', '-v'] + logs,
+ log_stdout=False, raiseonerr=False)
+ if cmd.returncode:
+ log.warning('Could not collect all requested logs')
+
+ # Unpack on the local side
+ dirname = os.path.join(topdirname, host.hostname)
+ try:
+ os.makedirs(dirname)
+ except OSError:
+ pass
+ tarname = os.path.join(dirname, 'logs.tar.xz')
+ with open(tarname, 'w') as f:
+ f.write(cmd.stdout_text)
+ ipautil.run(['tar', 'xJvf', 'logs.tar.xz'], cwd=dirname,
+ raiseonerr=False)
+ os.unlink(tarname)
+
+ if beakerlib_plugin:
+ # Use BeakerLib's rlFileSubmit on the indifidual files
+ # The resulting submitted filename will be
+ # $HOSTNAME-$FILENAME (with '/' replaced by '-')
+ beakerlib_plugin.run_beakerlib_command(['pushd', topdirname])
+ try:
+ for dirpath, _dirnames, filenames in os.walk(topdirname):
+ for filename in filenames:
+ fullname = os.path.relpath(
+ os.path.join(dirpath, filename), topdirname)
+ log.debug('Submitting file: %s', fullname)
+ beakerlib_plugin.run_beakerlib_command(
+ ['rlFileSubmit', fullname])
+ finally:
+ beakerlib_plugin.run_beakerlib_command(['popd'])
+
+ if remove_dir:
+ if beakerlib_plugin:
+ # The BeakerLib process runs asynchronously, let it clean up
+ # after it's done with the directory
+ beakerlib_plugin.run_beakerlib_command(
+ ['rm', '-rvf', topdirname])
+ else:
+ shutil.rmtree(topdirname)
+
+ logs_dict.clear()
+
+
+@pytest.fixture(scope='class')
+def class_integration_logs():
+ """Internal fixture providing class-level logs_dict"""
+ return {}
+
+
+@pytest.yield_fixture
+def integration_logs(class_integration_logs, request):
+ """Provides access to test integration logs, and collects after each test
+ """
+ yield class_integration_logs
+ collect_test_logs(request.node, class_integration_logs, request.config)
+
+
+@pytest.yield_fixture(scope='class')
+def mh(request, class_integration_logs):
+ """IPA's multihost fixture object
+ """
+ # TODO: cleanup modules
+ from ipatests.test_integration import tasks
+ from ipatests.test_integration.config import Config
+ from ipatests.test_integration.env_config import get_global_config
+
+ cls = request.cls
+
+ domain_description = {
+ 'type': 'IPA',
+ 'hosts': {
+ 'master': 1,
+ 'replica': cls.num_replicas,
+ 'client': cls.num_clients,
+ },
+ }
+ domain_description['hosts'].update(
+ {role: 1 for role in cls.required_extra_roles})
+
+ domain_descriptions = [domain_description]
+ for _i in range(cls.num_ad_domains):
+ domain_descriptions.append({
+ 'type': 'AD',
+ 'hosts': {'ad': 1, 'ad_subdomain': 1, 'ad_treedomain': 1},
+ })
+
+ mh = make_multihost_fixture(
+ request,
+ domain_descriptions,
+ config_class=Config,
+ _config=get_global_config(),
+ )
+
+ mh.domain = mh.config.domains[0]
+ [mh.master] = mh.domain.hosts_by_role('master')
+ mh.replicas = mh.domain.hosts_by_role('replica')
+ mh.clients = mh.domain.hosts_by_role('client')
+
+ cls.logs_to_collect = class_integration_logs
+
+ def collect_log(host, filename):
+ log.info('Adding %s:%s to list of logs to collect' %
+ (host.external_hostname, filename))
+ class_integration_logs.setdefault(host, []).append(filename)
+
+ print(mh.config)
+ for host in mh.config.get_all_hosts():
+ host.add_log_collector(collect_log)
+ cls.log.info('Preparing host %s', host.hostname)
+ tasks.prepare_host(host)
+
+ setup_class(cls, mh)
+ mh._pytestmh_request.addfinalizer(lambda: teardown_class(cls))
+
+ yield mh.install()
+
+ for host in cls.get_all_hosts():
+ host.remove_log_collector(collect_log)
+
+ collect_test_logs(request.node, class_integration_logs, request.config)
+
+
+def setup_class(cls, mh):
+ """Add convenience attributes to the test class
+
+ This is deprecated in favor of the mh fixture.
+ To be removed when no more tests using this.
+ """
+ cls.domain = mh.domain
+ cls.master = mh.master
+ cls.replicas = mh.replicas
+ cls.clients = mh.clients
+ cls.ad_domains = mh.config.ad_domains
+
+
+def teardown_class(cls):
+ """Remove convenience attributes from the test class
+
+ This is deprecated in favor of the mh fixture.
+ To be removed when no more tests using this.
+ """
+ del cls.master
+ del cls.replicas
+ del cls.clients
+ del cls.ad_domains
+ del cls.domain