Add virtlet deploy step

Added fixture, manager and step for deploy virtlet.
Added test for deploy virtlet.

Change-Id: I63a2c4dcd95ba78af60b610a9ae621a784d87fd8
Reviewed-on: https://review.gerrithub.io/362146
Reviewed-by: Sergii Golovatiuk <holser@gmail.com>
Reviewed-by: Tatyanka Leontovich <tleontovich@mirantis.com>
Reviewed-by: Dennis Dmitriev <dis.xcom@gmail.com>
Tested-by: Dennis Dmitriev <dis.xcom@gmail.com>
diff --git a/tcp_tests/fixtures/virtlet_fixtures.py b/tcp_tests/fixtures/virtlet_fixtures.py
new file mode 100644
index 0000000..f70a201
--- /dev/null
+++ b/tcp_tests/fixtures/virtlet_fixtures.py
@@ -0,0 +1,84 @@
+#    Copyright 2017 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
+import yaml
+
+from tcp_tests import logger
+from tcp_tests.helpers import ext
+from tcp_tests import settings
+from tcp_tests.managers import virtlet_manager
+
+LOG = logger.logger
+
+
+@pytest.fixture(scope='function')
+def virtlet_actions(config, underlay, salt_actions):
+    """Fixture that provides various actions for Virtlet project
+
+    :param config: fixture provides oslo.config
+    :param underlay: fixture provides underlay manager
+    :rtype: VirtletManager
+    """
+    return virtlet_manager.VirtletManager(config, underlay, salt_actions)
+
+
+@pytest.mark.revert_snapshot(ext.SNAPSHOT.virtlet_deployed)
+@pytest.fixture(scope='function')
+def virtlet_deployed(revert_snapshot, request, config,
+                             hardware, underlay, common_services_deployed,
+                             virtlet_actions):
+    """Fixture to get or install Virtlet project on the environment
+
+    :param revert_snapshot: fixture that reverts snapshot that is specified
+                            in test with @pytest.mark.revert_snapshot(<name>)
+    :param request: fixture provides pytest data
+    :param config: fixture provides oslo.config
+    :param hardware: fixture provides enviromnet manager
+    :param underlay: fixture provides underlay manager
+    :param virtlet_actions: fixture provides VirtletManager instance
+    :rtype: VirtletManager
+
+    If config.virtlet.virtlet_installed is not set, this
+    fixture assumes that the Virtlet project was not installed,
+    and do the following:
+    - install Virtlet project
+    - make snapshot with name 'virtlet_deployed'
+    - return VirtletManager
+
+    If config.virtlet.virtlet_installed was set, this fixture assumes that
+    the Virtlet project was already installed, and do the following:
+    - return VirtletManager instance
+
+    If you want to revert 'virtlet_deployed' snapshot, please use mark:
+    @pytest.mark.revert_snapshot("virtlet_deployed")
+    """
+    # Create Salt cluster
+    if not config.virtlet.virtlet_installed:
+        steps_path = config.virtlet_deploy.virtlet_steps_path
+        commands = underlay.read_template(steps_path)
+        virtlet_actions.install(commands)
+        hardware.create_snapshot(ext.SNAPSHOT.virtlet_deployed)
+
+    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)
+        # 3. config.tcp.* options contain access credentials to the already
+        #    installed TCP API endpoint
+        pass
+
+    return virtlet_actions
diff --git a/tcp_tests/helpers/ext.py b/tcp_tests/helpers/ext.py
index 6d6ee2f..748efee 100644
--- a/tcp_tests/helpers/ext.py
+++ b/tcp_tests/helpers/ext.py
@@ -40,6 +40,7 @@
     'common_services_deployed',
     'openstack_deployed',
     'sl_deployed',
+    'virtlet_deployed',
 )
 
 LOG_LEVELS = enum(
diff --git a/tcp_tests/managers/virtlet_manager.py b/tcp_tests/managers/virtlet_manager.py
new file mode 100644
index 0000000..c696ed4
--- /dev/null
+++ b/tcp_tests/managers/virtlet_manager.py
@@ -0,0 +1,34 @@
+#    Copyright 2017 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 tcp_tests.managers.execute_commands import ExecuteCommandsMixin
+
+
+class VirtletManager(ExecuteCommandsMixin):
+    """docstring for VirtletManager"""
+
+    __config = None
+    __underlay = None
+
+    def __init__(self, config, underlay, salt):
+        self.__config = config
+        self.__underlay = underlay
+        self._salt = salt
+        super(VirtletManager, self).__init__(
+            config=config, underlay=underlay)
+
+    def install(self, commands):
+        self.execute_commands(commands,
+                              label='Install Virtlet project')
+        self.__config.virtlet.virtlet_installed = True
diff --git a/tcp_tests/settings_oslo.py b/tcp_tests/settings_oslo.py
index 262f491..c6b5b74 100644
--- a/tcp_tests/settings_oslo.py
+++ b/tcp_tests/settings_oslo.py
@@ -43,7 +43,9 @@
 _default_sl_prepare_tests_steps_path = pkg_resources.resource_filename(
     __name__, 'templates/{0}/sl.yaml'.format(
         settings.LAB_CONFIG_NAME))
-
+_default_virtlet_prepare_tests_steps_path = pkg_resources.resource_filename(
+    __name__, 'templates/{0}/virtlet.yaml'.format(
+        settings.LAB_CONFIG_NAME))
 
 hardware_opts = [
     ct.Cfg('manager', ct.String(),
@@ -147,6 +149,16 @@
            help="", default=False),
 ]
 
+virtlet_deploy_opts = [
+    ct.Cfg('virtlet_steps_path', ct.String(),
+           help="Path to YAML with steps to deploy virtlet",
+           default=_default_virtlet_prepare_tests_steps_path)
+]
+
+virtlet_opts = [
+    ct.Cfg('virtlet_installed', ct.Boolean(),
+           help="", default=False),
+]
 
 _group_opts = [
     ('hardware', hardware_opts),
@@ -160,6 +172,8 @@
     ('opencontrail', opencontrail_opts),
     ('stack_light', sl_opts),
     ('sl_deploy', sl_deploy_opts),
+    ('virtlet_deploy', virtlet_deploy_opts),
+    ('virtlet', virtlet_opts),
 ]
 
 
@@ -212,7 +226,12 @@
                  title="SL deploy config and credentials",
                  help=""))
     config.register_opts(group='sl_deploy', opts=sl_deploy_opts)
-
+    config.register_group(cfg.OptGroup(name='virtlet_deploy',
+                                       title='Virtlet deploy config', help=""))
+    config.register_opts(group='virtlet_deploy', opts=virtlet_deploy_opts)
+    config.register_group(cfg.OptGroup(name='virtlet',
+                                       title='Virtlet config', help=""))
+    config.register_opts(group='virtlet', opts=virtlet_opts)
     return config
 
 
diff --git a/tcp_tests/templates/virtual-mcp11-k8s-calico/virtlet.yaml b/tcp_tests/templates/virtual-mcp11-k8s-calico/virtlet.yaml
new file mode 100644
index 0000000..4f2abb3
--- /dev/null
+++ b/tcp_tests/templates/virtual-mcp11-k8s-calico/virtlet.yaml
@@ -0,0 +1,72 @@
+{% from 'virtual-mcp11-k8s-calico/underlay.yaml' import HOSTNAME_CTL02 with context %}
+
+# Clone virtlet project from git to the ctl01 node for start virtlet pod from yaml
+- description: Cloning virtlet project on ctl02
+  cmd:  git clone -b master https://github.com/Mirantis/virtlet.git
+  node_name: {{ HOSTNAME_CTL02 }}
+  retry: {count: 1, delay: 1}
+  skip_fail: false
+
+# Add 'virtlet' label for ctl02
+- description: Adding virtlet label for ctl02
+  cmd:  kubectl label node ctl02 extraRuntime=virtlet
+  node_name: {{ HOSTNAME_CTL02 }}
+  retry: {count: 1, delay: 1}
+  skip_fail: false
+
+# Add extra parameter for kubelet service on virtlet node
+- description: Adding extra parameter for kubelet service on virtlet node
+  cmd:  sed -i.bak "s|^\"|--feature-gates=DynamicKubeletConfig=true \\\\\\n\"|" /etc/default/kubelet
+  node_name: {{ HOSTNAME_CTL02 }}
+  retry: {count: 1, delay: 1}
+  skip_fail: false
+
+# Restart kubelet and kube-api services on virtlet node
+- description: Restart kubelet and kube-api services on ctl02
+  cmd:  systemctl restart kube-apiserver kubelet
+  node_name: {{ HOSTNAME_CTL02 }}
+  retry: {count: 1, delay: 1}
+  skip_fail: false
+
+# Create virtlet pod
+- description: Creating virtlet pod
+  cmd:  kubectl create -f virtlet/deploy/virtlet-ds.yaml
+  node_name: {{ HOSTNAME_CTL02 }}
+  retry: {count: 1, delay: 1}
+  skip_fail: false
+
+# Virtlet pod will likely stay in Init:0/1 state because there's a problem
+# with automatic kubelet restart after applying the configmap.
+# As of now, you'll need to restart kubelet after ~30-60 seconds.
+- description: Restarting kubelet service on virtlet node
+  cmd: |
+    COUNTER=0
+    while [[ $(kubectl get pods -n kube-system | awk '/virtlet/{print $3}') != 'Init:0/1' ]]; do
+      COUNTER=$((COUNTER+1))
+      sleep 5
+      if [[ $COUNTER -eq 36 ]]; then
+        echo "We havenot Init:0/1 state for virtlet pod. Aborting.";
+        exit 1
+      fi
+    done
+    sleep 60
+    systemctl restart kubelet
+  node_name: {{ HOSTNAME_CTL02 }}
+  retry: {count: 1, delay: 1}
+  skip_fail: false
+
+# Wait Active state for virtlet pod
+- description: Waiting 'Active' state for virtlet pod
+  cmd: |
+    COUNTER=0
+    while [[ $(kubectl get pods -n kube-system | awk '/virtlet/{print $3}') != 'Running' ]]; do
+      COUNTER=$((COUNTER+1))
+      sleep 5
+      if [[ $COUNTER -eq 36 ]]; then
+        echo "We havenot Active state for virtlet pod. Aborting.";
+        exit 1
+      fi
+    done
+  node_name: {{ HOSTNAME_CTL02 }}
+  retry: {count: 1, delay: 1}
+  skip_fail: false
diff --git a/tcp_tests/tests/system/conftest.py b/tcp_tests/tests/system/conftest.py
index 93aae01..e8ffb8d 100644
--- a/tcp_tests/tests/system/conftest.py
+++ b/tcp_tests/tests/system/conftest.py
@@ -22,6 +22,7 @@
 from tcp_tests.fixtures.openstack_fixtures import *
 from tcp_tests.fixtures.opencontrail_fixtures import *
 from tcp_tests.fixtures.stacklight_fixtures import *
+from tcp_tests.fixtures.virtlet_fixtures import *
 
 __all__ = sorted([  # sort for documentation
     # common_fixtures
@@ -49,4 +50,6 @@
     # stacklight_fixtures
     'sl_actions',
     'sl_deployed',
+    'virtlet_actions',
+    'virtlet_deployed',
 ])
diff --git a/tcp_tests/tests/system/test_install_virtlet.py b/tcp_tests/tests/system/test_install_virtlet.py
new file mode 100644
index 0000000..7976761
--- /dev/null
+++ b/tcp_tests/tests/system/test_install_virtlet.py
@@ -0,0 +1,50 @@
+#    Copyright 2017 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 copy
+import time
+
+import pytest
+
+from tcp_tests import settings
+from tcp_tests.helpers import ext
+from tcp_tests import logger
+
+LOG = logger.logger
+
+
+@pytest.mark.deploy
+class TestVirtletInstall(object):
+    """Test class for testing Virtlet deploy"""
+
+    #salt_cmd = 'salt -l debug '  # For debug output
+    #salt_call_cmd = 'salt-call -l debug '  # For debug output
+    salt_cmd = 'salt --hard-crash --state-output=mixed --state-verbose=False '  # For cause only output
+    salt_call_cmd = 'salt-call --hard-crash --state-output=mixed --state-verbose=False '  # For cause only output
+    #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.snapshot_needed
+    # @pytest.mark.fail_snapshot
+    def test_virtlet_install(self, underlay, virtlet_deployed,
+                                     show_step):
+        """Test for deploying an mcp environment with virtlet
+
+        Scenario:
+            1. Prepare salt on hosts
+            2. Setup controller nodes
+            3. Setup compute nodes
+            4. Setup virtlet
+
+        """
+        LOG.info("*************** DONE **************")