Revert only the latest snapshot that matches the test requirements
- add pytest marks 'revert_snapshot' to all required fixtures instead
of reverting the snapshots inside them
- add get_top_fixtures_marks() that extracts all the marks
'revert_snapshot' from the test and it's fixtures, order the marks
in the same way as the fixtures depends on each other,
- in the fixture 'revert_snapshot' try to find the most suitable
snapshot for reverting, from latest to earliest.
Co-Authored-By: Dmitry Tyzhnenko <dtyzhnenko@mirantis.com>
diff --git a/tcp_tests/fixtures/common_services_fixtures.py b/tcp_tests/fixtures/common_services_fixtures.py
index ed00574..2547a08 100644
--- a/tcp_tests/fixtures/common_services_fixtures.py
+++ b/tcp_tests/fixtures/common_services_fixtures.py
@@ -36,6 +36,7 @@
return common_services_manager.CommonServicesManager(config, underlay)
+@pytest.mark.revert_snapshot(ext.SNAPSHOT.common_services_deployed)
@pytest.fixture(scope='function')
def common_services_deployed(revert_snapshot, request, config,
hardware, underlay, salt_deployed,
@@ -67,15 +68,6 @@
If you want to revert 'common_services_deployed' snapshot, please use mark:
@pytest.mark.revert_snapshot("common_services_deployed")
"""
- # If no snapshot was reverted, then try to revert the snapshot
- # that belongs to the fixture.
- # Note: keep fixtures in strict dependences from each other!
- if not revert_snapshot:
- if hardware.has_snapshot(ext.SNAPSHOT.common_services_deployed) and \
- hardware.has_snapshot_config(
- ext.SNAPSHOT.common_services_deployed):
- hardware.revert_snapshot(ext.SNAPSHOT.common_services_deployed)
-
# Create Salt cluster
if not config.common_services.common_services_installed:
steps_path = config.common_services_deploy.common_services_steps_path
diff --git a/tcp_tests/fixtures/openstack_fixtures.py b/tcp_tests/fixtures/openstack_fixtures.py
index c7e648e..f0a9024 100644
--- a/tcp_tests/fixtures/openstack_fixtures.py
+++ b/tcp_tests/fixtures/openstack_fixtures.py
@@ -38,6 +38,7 @@
return openstack_manager.OpenstackManager(config, underlay)
+@pytest.mark.revert_snapshot(ext.SNAPSHOT.openstack_deployed)
@pytest.fixture(scope='function')
def openstack_deployed(revert_snapshot, request, config,
hardware, underlay, common_services_deployed,
@@ -66,14 +67,6 @@
If you want to revert 'openstack_deployed' snapshot, please use mark:
@pytest.mark.revert_snapshot("openstack_deployed")
"""
- # If no snapshot was reverted, then try to revert the snapshot
- # that belongs to the fixture.
- # Note: keep fixtures in strict dependences from each other!
- if not revert_snapshot:
- if hardware.has_snapshot(ext.SNAPSHOT.openstack_deployed) and \
- hardware.has_snapshot_config(ext.SNAPSHOT.openstack_deployed):
- hardware.revert_snapshot(ext.SNAPSHOT.openstack_deployed)
-
# Create Salt cluster
if not config.openstack.openstack_installed:
steps_path = config.openstack_deploy.openstack_steps_path
diff --git a/tcp_tests/fixtures/salt_fixtures.py b/tcp_tests/fixtures/salt_fixtures.py
index 047f106..977b38d 100644
--- a/tcp_tests/fixtures/salt_fixtures.py
+++ b/tcp_tests/fixtures/salt_fixtures.py
@@ -36,6 +36,7 @@
return saltmanager.SaltManager(config, underlay)
+@pytest.mark.revert_snapshot(ext.SNAPSHOT.salt_deployed)
@pytest.fixture(scope='function')
def salt_deployed(revert_snapshot, request, config,
hardware, underlay, salt_actions):
@@ -63,14 +64,6 @@
If you want to revert 'salt_deployed' snapshot, please use mark:
@pytest.mark.revert_snapshot("salt_deployed")
"""
- # If no snapshot was reverted, then try to revert the snapshot
- # that belongs to the fixture.
- # Note: keep fixtures in strict dependences from each other!
- if not revert_snapshot:
- if hardware.has_snapshot(ext.SNAPSHOT.salt_deployed) and \
- hardware.has_snapshot_config(ext.SNAPSHOT.salt_deployed):
- hardware.revert_snapshot(ext.SNAPSHOT.salt_deployed)
-
# Create Salt cluster
if config.salt.salt_master_host == '0.0.0.0':
with underlay.yaml_editor(
diff --git a/tcp_tests/fixtures/underlay_fixtures.py b/tcp_tests/fixtures/underlay_fixtures.py
index a19b6d1..d7351bf 100644
--- a/tcp_tests/fixtures/underlay_fixtures.py
+++ b/tcp_tests/fixtures/underlay_fixtures.py
@@ -16,6 +16,7 @@
from datetime import datetime
from tcp_tests.helpers import ext
+from tcp_tests.helpers import utils
from tcp_tests import logger
from tcp_tests import settings
from tcp_tests.managers import envmanager_devops
@@ -25,20 +26,6 @@
LOG = logger.logger
-def extract_name_from_mark(mark):
- """Simple function to extract name from mark
-
- :param mark: pytest.mark.MarkInfo
- :rtype: string or None
- """
- if mark:
- if len(mark.args) > 0:
- return mark.args[0]
- elif 'name' in mark.kwargs:
- return mark.kwargs['name']
- return None
-
-
@pytest.fixture(scope="session")
def hardware(request, config):
"""Fixture for manage the hardware layer.
@@ -110,17 +97,19 @@
:rtype string: name of the reverted snapshot or None
"""
- revert_snapshot = request.keywords.get('revert_snapshot', None)
- snapshot_name = extract_name_from_mark(revert_snapshot)
+ top_fixtures_snapshots = utils.get_top_fixtures_marks(
+ request, 'revert_snapshot')
- if snapshot_name and \
- hardware.has_snapshot(snapshot_name) and \
- hardware.has_snapshot_config(snapshot_name):
- hardware.revert_snapshot(snapshot_name)
- return snapshot_name
- else:
- hardware.revert_snapshot(ext.SNAPSHOT.hardware)
- return None
+ # Try to revert the best matches snapshot for the test
+ for snapshot_name in top_fixtures_snapshots:
+ if hardware.has_snapshot(snapshot_name) and \
+ hardware.has_snapshot_config(snapshot_name):
+ hardware.revert_snapshot(snapshot_name)
+ return snapshot_name
+
+ # Fallback to the basic snapshot
+ hardware.revert_snapshot(ext.SNAPSHOT.hardware)
+ return None
@pytest.fixture(scope='function', autouse=True)
@@ -146,7 +135,7 @@
request.node.function.__name__)
if hasattr(request.node, 'rep_call') and request.node.rep_call.passed \
and snapshot_needed:
- snapshot_name = extract_name_from_mark(snapshot_needed) or \
+ snapshot_name = utils.extract_name_from_mark(snapshot_needed) or \
"{}_passed".format(default_snapshot_name)
hardware.create_snapshot(snapshot_name)
@@ -163,6 +152,7 @@
request.addfinalizer(test_fin)
+@pytest.mark.revert_snapshot(ext.SNAPSHOT.underlay)
@pytest.fixture(scope="function")
def underlay(revert_snapshot, config, hardware):
"""Fixture that should provide SSH access to underlay objects.
@@ -179,14 +169,6 @@
- provide SSH access to underlay nodes using
node names or node IPs.
"""
- # If no snapshot was reverted, then try to revert the snapshot
- # that belongs to the fixture.
- # Note: keep fixtures in strict dependences from each other!
- if not revert_snapshot:
- if hardware.has_snapshot(ext.SNAPSHOT.underlay) and \
- hardware.has_snapshot_config(ext.SNAPSHOT.underlay):
- hardware.revert_snapshot(ext.SNAPSHOT.underlay)
-
# Create Underlay
if not config.underlay.ssh:
# If config.underlay.ssh wasn't provided from external config, then
diff --git a/tcp_tests/helpers/utils.py b/tcp_tests/helpers/utils.py
index c60e1b9..f913544 100644
--- a/tcp_tests/helpers/utils.py
+++ b/tcp_tests/helpers/utils.py
@@ -315,3 +315,75 @@
if self.content == self.__original_content:
return
self.write_content()
+
+
+def extract_name_from_mark(mark):
+ """Simple function to extract name from pytest mark
+
+ :param mark: pytest.mark.MarkInfo
+ :rtype: string or None
+ """
+ if mark:
+ if len(mark.args) > 0:
+ return mark.args[0]
+ elif 'name' in mark.kwargs:
+ return mark.kwargs['name']
+ return None
+
+
+def get_top_fixtures_marks(request, mark_name):
+ """Order marks according to fixtures order
+
+ When a test use fixtures that depend on each other in some order,
+ that fixtures can have the same pytest mark.
+
+ This method extracts such marks from fixtures that are used in the
+ current test and return the content of the marks ordered by the
+ fixture dependences.
+ If the test case have the same mark, than the content of this mark
+ will be the first element in the resulting list.
+
+ :param request: pytest 'request' fixture
+ :param mark_name: name of the mark to search on the fixtures and the test
+
+ :rtype list: marks content, from last to first executed.
+ """
+
+ fixtureinfo = request.session._fixturemanager.getfixtureinfo(
+ request.node, request.function, request.cls)
+
+ top_fixtures_names = []
+ for _ in enumerate(fixtureinfo.name2fixturedefs):
+ parent_fixtures = set()
+ child_fixtures = set()
+ for name in sorted(fixtureinfo.name2fixturedefs):
+ if name in top_fixtures_names:
+ continue
+ parent_fixtures.add(name)
+ child_fixtures.update(
+ fixtureinfo.name2fixturedefs[name][0].argnames)
+ top_fixtures_names.extend(list(parent_fixtures - child_fixtures))
+
+ top_fixtures_marks = []
+
+ if mark_name in request.function.func_dict:
+ # The top priority is the 'revert_snapshot' mark on the test
+ top_fixtures_marks.append(
+ extract_name_from_mark(
+ request.function.func_dict[mark_name]))
+
+ for top_fixtures_name in top_fixtures_names:
+ fd = fixtureinfo.name2fixturedefs[top_fixtures_name][0]
+ if mark_name in fd.func.func_dict:
+ fixture_mark = extract_name_from_mark(
+ fd.func.func_dict[mark_name])
+ # Append the snapshot names in the order that fixtures are called
+ # starting from the last called fixture to the first one
+ top_fixtures_marks.append(fixture_mark)
+
+ LOG.debug("Fixtures ordered from last to first called: {0}"
+ .format(top_fixtures_names))
+ LOG.debug("Marks ordered from most to least preffered: {0}"
+ .format(top_fixtures_marks))
+
+ return top_fixtures_marks
diff --git a/tcp_tests/tests/system/test_opencontrail.py b/tcp_tests/tests/system/test_opencontrail.py
index 3746a3b..661b298 100644
--- a/tcp_tests/tests/system/test_opencontrail.py
+++ b/tcp_tests/tests/system/test_opencontrail.py
@@ -27,7 +27,6 @@
class TestOpenContrail(object):
"""Test class for testing OpenContrail on a TCP lab"""
- @pytest.mark.revert_snapshot(ext.SNAPSHOT.openstack_deployed)
# @pytest.mark.snapshot_needed
# @pytest.mark.fail_snapshot
def test_opencontrail(self, config, openstack_deployed,
diff --git a/tcp_tests/tests/system/test_tcp_install.py b/tcp_tests/tests/system/test_tcp_install.py
index dcb31d6..794afb2 100644
--- a/tcp_tests/tests/system/test_tcp_install.py
+++ b/tcp_tests/tests/system/test_tcp_install.py
@@ -34,7 +34,6 @@
#salt_cmd = 'salt --state-output=terse --state-verbose=False ' # For reduced output
#salt_call_cmd = 'salt-call --state-output=terse --state-verbose=False ' # For reduced output
- @pytest.mark.revert_snapshot(ext.SNAPSHOT.openstack_deployed)
# @pytest.mark.snapshot_needed
# @pytest.mark.fail_snapshot
def test_tcp_install_default(self, underlay, openstack_deployed,
@@ -59,7 +58,6 @@
fail_msg = 'Tempest verification fails {}'.format(res)
assert res['failures'] == 0, fail_msg
- @pytest.mark.revert_snapshot(ext.SNAPSHOT.salt_deployed)
# @pytest.mark.snapshot_needed
# @pytest.mark.fail_snapshot
def test_tcp_install_with_scripts(self, config, underlay, salt_deployed,