Initial commit with fixtures
- add fixtures for hardware and underlay
- add fuel-devops template tcpcloud-default.yaml
* Migration of fixtures is not finished yet
diff --git a/tcp_tests/fixtures/__init__.py b/tcp_tests/fixtures/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tcp_tests/fixtures/__init__.py
diff --git a/tcp_tests/fixtures/common_fixtures.py b/tcp_tests/fixtures/common_fixtures.py
new file mode 100644
index 0000000..9d121e5
--- /dev/null
+++ b/tcp_tests/fixtures/common_fixtures.py
@@ -0,0 +1,75 @@
+# Copyright 2016 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from __future__ import division
+import time
+
+import pytest
+
+from tcp_tests import logger
+from tcp_tests.helpers import log_step
+from tcp_tests.helpers import utils
+
+
+LOG = logger.logger
+
+
+@pytest.yield_fixture(scope='session')
+def ssh_keys_dir(request):
+ ssh_keys_dir = utils.generate_keys()
+ LOG.info("SSH keys were generated in {}".format(ssh_keys_dir))
+ yield ssh_keys_dir
+ utils.clean_dir(ssh_keys_dir)
+ LOG.info("Tmp dir {} with generated ssh keys was cleaned".format(
+ ssh_keys_dir))
+
+
+@pytest.hookimpl(tryfirst=True, hookwrapper=True)
+def pytest_runtest_makereport(item, call):
+ outcome = yield
+ rep = outcome.get_result()
+ setattr(item, "rep_" + rep.when, rep)
+
+
+def pytest_runtest_setup(item):
+ if item.cls is not None:
+ item.cls._current_test = item.function
+ item._start_time = time.time()
+ head = "<" * 5 + "#" * 30 + "[ {} ]" + "#" * 30 + ">" * 5
+ head = head.format(item.function.__name__)
+ start_step = "\n{head}".format(head=head)
+ LOG.info(start_step)
+
+
+def pytest_runtest_teardown(item):
+ step_name = item.function.__name__
+ if hasattr(item, '_start_time'):
+ spent_time = time.time() - item._start_time
+ else:
+ spent_time = 0
+ minutes = spent_time // 60
+ seconds = int(round(spent_time)) % 60
+ finish_step = "FINISH {} TEST. TOOK {} min {} sec".format(
+ step_name, minutes, seconds
+ )
+ foot = "\n" + "<" * 5 + "#" * 30 + "[ {} ]" + "#" * 30 + ">" * 5
+ foot = foot.format(finish_step)
+ LOG.info(foot)
+
+
+@pytest.fixture(scope='function')
+def show_step(request):
+ def _show_step(step_number):
+ return log_step.log_step(request.function, step_number)
+ return _show_step
diff --git a/tcp_tests/fixtures/config_fixtures.py b/tcp_tests/fixtures/config_fixtures.py
new file mode 100644
index 0000000..ffcaea9
--- /dev/null
+++ b/tcp_tests/fixtures/config_fixtures.py
@@ -0,0 +1,33 @@
+# Copyright 2016 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+import os
+
+import pytest
+
+from tcp_tests import settings_oslo
+
+
+@pytest.fixture(scope='session')
+def config():
+
+ config_files = []
+
+ tests_configs = os.environ.get('TESTS_CONFIGS', None)
+ if tests_configs:
+ for test_config in tests_configs.split(','):
+ config_files.append(test_config)
+
+ config_opts = settings_oslo.load_config(config_files)
+
+ return config_opts
diff --git a/tcp_tests/fixtures/rally_fixtures.py b/tcp_tests/fixtures/rally_fixtures.py
new file mode 100644
index 0000000..45e828a
--- /dev/null
+++ b/tcp_tests/fixtures/rally_fixtures.py
@@ -0,0 +1,30 @@
+# Copyright 2016 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import pytest
+
+from tcp_tests.managers import rallymanager
+
+
+@pytest.fixture(scope='function')
+def rally(underlay):
+ """Fixture that provides various actions for TCP
+
+ :param config: fixture provides oslo.config
+ :param underlay: fixture provides underlay manager
+ :rtype: RallyManager
+
+ For use in tests or fixtures to deploy a custom TCP
+ """
+ return rallymanager.RallyManager(underlay, 'cfg01')
diff --git a/tcp_tests/fixtures/underlay_fixtures.py b/tcp_tests/fixtures/underlay_fixtures.py
new file mode 100644
index 0000000..e7aaaf8
--- /dev/null
+++ b/tcp_tests/fixtures/underlay_fixtures.py
@@ -0,0 +1,211 @@
+# Copyright 2016 Mirantis, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import pytest
+from datetime import datetime
+
+from tcp_tests.helpers import ext
+from tcp_tests import logger
+from tcp_tests import settings
+from tcp_tests.managers import envmanager_devops
+from tcp_tests.managers import envmanager_empty
+from tcp_tests.managers import underlay_ssh_manager
+
+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.
+
+ - start/stop/reboot libvirt/IPMI(/MaaS?) nodes
+ - snapshot/revert libvirt nodes (fuel-devops only)
+ - block/unblock libvirt networks/interfaces (fuel-devops only)
+
+ This fixture should get a hardware configuration from
+ 'config' object or create a virtual/baremetal underlay
+ using EnvironmentManager.
+
+ Creates a snapshot 'hardware' with ready-to-use virtual environment
+ (Only for config.hardware.manager='devops'):
+ - just created virtual nodes in power-on state
+ - node volumes filled with necessary content
+ - node network interfaces connected to necessary devices
+
+ config.hardware.manager: one of ('devops', 'maas', None)
+ config.hardware.config: path to the config file for the manager
+ config.hardware.current_snapshot = Latest created or reverted snapshot
+
+ :rtype EnvironmentModel: if config.hardware.manager == 'devops'
+ :rtype EnvironmentManagerEmpty: if config.hardware.manager == 'empty'
+ """
+ env = None
+
+ manager = config.hardware.manager
+
+ if manager == 'empty':
+ # No environment manager is used.
+ # 'config' should contain config.underlay.ssh settings
+ # 'config' should contain config.underlay.current_snapshot setting
+ env = envmanager_empty.EnvironmentManagerEmpty(config=config)
+
+ elif manager == 'devops':
+ # fuel-devops environment manager is used.
+ # config.underlay.ssh settings can be empty or witn SSH to existing env
+ # config.underlay.current_snapshot
+ env = envmanager_devops.EnvironmentManager(config=config)
+ else:
+ raise Exception("Unknown hardware manager: '{}'".format(manager))
+
+ # for devops manager: power on nodes and wait for SSH
+ # for empty manager: do nothing
+ # for maas manager: provision nodes and wait for SSH
+ env.start()
+ if not env.has_snapshot(ext.SNAPSHOT.hardware):
+ env.create_snapshot(ext.SNAPSHOT.hardware)
+
+ def fin():
+ if settings.SHUTDOWN_ENV_ON_TEARDOWN:
+ LOG.info("Shutdown environment...")
+ env.stop()
+
+ request.addfinalizer(fin)
+ return env
+
+
+@pytest.fixture(scope='function')
+def revert_snapshot(request, hardware):
+ """Revert snapshot for the test case
+
+ Usage:
+ @pytest.mark.revert_snapshot(name='<required_snapshot_name>')
+
+ If the mark 'revert_snapshot' is absend, or <required_snapshot_name>
+ not found, then an initial 'hardware' snapshot will be reverted.
+
+ :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)
+
+ 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
+
+
+@pytest.fixture(scope='function', autouse=True)
+def snapshot(request, hardware):
+ """Fixture for creating snapshot at the end of test if it's needed
+
+ Marks:
+ snapshot_needed(name=None) - make snapshot if test is passed. If
+ name argument provided, it will be used for creating snapshot,
+ otherwise, test function name will be used
+
+ fail_snapshot - make snapshot if test failed
+
+ :param request: pytest.python.FixtureRequest
+ :param env: envmanager.EnvironmentManager
+ """
+ snapshot_needed = request.keywords.get('snapshot_needed', None)
+ fail_snapshot = request.keywords.get('fail_snapshot', None)
+
+ def test_fin():
+ default_snapshot_name = getattr(request.node.function,
+ '_snapshot_name',
+ 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 \
+ "{}_passed".format(default_snapshot_name)
+ hardware.create_snapshot(snapshot_name)
+
+ elif hasattr(request.node, 'rep_setup') and \
+ request.node.rep_setup.failed and fail_snapshot:
+ snapshot_name = "{0}_prep_failed".format(default_snapshot_name)
+ hardware.create_snapshot(snapshot_name)
+
+ elif hasattr(request.node, 'rep_call') and \
+ request.node.rep_call.failed and fail_snapshot:
+ snapshot_name = "{0}_failed".format(default_snapshot_name)
+ hardware.create_snapshot(snapshot_name)
+
+ request.addfinalizer(test_fin)
+
+
+@pytest.fixture(scope="function")
+def underlay(revert_snapshot, config, hardware):
+ """Fixture that should provide SSH access to underlay objects.
+
+ - Starts the 'hardware' environment and creates 'underlay' with required
+ configuration.
+ - Fills the following object using the 'hardware' fixture:
+ config.underlay.ssh = JSONList of SSH access credentials for nodes.
+ This list will be used for initialization the
+ model UnderlaySSHManager, see it for details.
+
+ :rtype UnderlaySSHManager: Object that encapsulate SSH credentials;
+ - provide list of underlay nodes;
+ - 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
+ # try to get necessary data from hardware manager (fuel-devops)
+ config.underlay.ssh = hardware.get_ssh_data(
+ roles=config.underlay.roles)
+
+ underlay = underlay_ssh_manager.UnderlaySSHManager(config.underlay.ssh)
+
+ if not config.underlay.lvm:
+ underlay.enable_lvm(hardware.lvm_storages())
+ config.underlay.lvm = underlay.config_lvm
+
+ hardware.create_snapshot(ext.SNAPSHOT.underlay)
+
+ else:
+ # 1. hardware environment created and powered on
+ # 2. config.underlay.ssh contains SSH access to provisioned nodes
+ # (can be passed from external config with TESTS_CONFIGS variable)
+ underlay = underlay_ssh_manager.UnderlaySSHManager(config.underlay.ssh)
+
+ return underlay