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/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