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