Merge "Add virtual_mcp11_k8s_calico template"
diff --git a/.gitreview b/.gitreview
index 7dfb0a5..22fa62c 100644
--- a/.gitreview
+++ b/.gitreview
@@ -1,4 +1,4 @@
 [gerrit]
-host=review.openstack.org
+host=review.gerrithub.io
 port=29418
 project=Mirantis/tcp-qa.git
diff --git a/tcp_tests/fixtures/underlay_fixtures.py b/tcp_tests/fixtures/underlay_fixtures.py
index d7351bf..1e17518 100644
--- a/tcp_tests/fixtures/underlay_fixtures.py
+++ b/tcp_tests/fixtures/underlay_fixtures.py
@@ -73,7 +73,6 @@
     # for empty manager: do nothing
     # for maas manager: provision nodes and wait for SSH
     if not env.has_snapshot(ext.SNAPSHOT.hardware):
-        env.start()
         env.create_snapshot(ext.SNAPSHOT.hardware)
 
     def fin():
@@ -173,6 +172,13 @@
     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)
+
+        # for devops manager: power on nodes and wait for SSH
+        # for empty manager: do nothing
+        # for maas manager: provision nodes and wait for SSH
+        hardware.start(underlay_node_roles=config.underlay.roles,
+                       timeout=config.underlay.bootstrap_timeout)
+
         config.underlay.ssh = hardware.get_ssh_data(
             roles=config.underlay.roles)
 
diff --git a/tcp_tests/helpers/oslo_cfg_types.py b/tcp_tests/helpers/oslo_cfg_types.py
index 962f2d3..135798f 100644
--- a/tcp_tests/helpers/oslo_cfg_types.py
+++ b/tcp_tests/helpers/oslo_cfg_types.py
@@ -104,7 +104,10 @@
 
         env_var_name = args[0].upper()
         if 'default' in kwargs:
-            kwargs['default'] = os.environ.get(env_var_name, kwargs.get('default', None))
+            # Load a default environment variable with expected type
+            kwargs['default'] = args[1](
+                os.environ.get(env_var_name, kwargs.get('default', None))
+            )
         super(Cfg, self).__init__(*args, **kwargs)
 
         # Print info about default environment variables to console
diff --git a/tcp_tests/managers/envmanager_devops.py b/tcp_tests/managers/envmanager_devops.py
index 2116e29..c4a0642 100644
--- a/tcp_tests/managers/envmanager_devops.py
+++ b/tcp_tests/managers/envmanager_devops.py
@@ -275,7 +275,7 @@
             'Environment "{0}" created'.format(env_name)
         )
 
-    def start(self):
+    def start(self, underlay_node_roles, timeout=480):
         """Method for start environment
 
         """
@@ -283,9 +283,8 @@
             raise exceptions.EnvironmentIsNotSet()
         self.__env.start()
         LOG.info('Environment "{0}" started'.format(self.__env.name))
-        for node in self.__env.get_nodes(role__in=ext.UNDERLAY_NODE_ROLES):
+        for node in self.__env.get_nodes(role__in=underlay_node_roles):
             LOG.info("Waiting for SSH on node '{}...'".format(node.name))
-            timeout = 480
             helpers.wait(
                 lambda: helpers.tcp_ping(self.node_ip(node), 22),
                 timeout=timeout,
diff --git a/tcp_tests/settings_oslo.py b/tcp_tests/settings_oslo.py
index 609fd69..f51f8ce 100644
--- a/tcp_tests/settings_oslo.py
+++ b/tcp_tests/settings_oslo.py
@@ -69,6 +69,9 @@
            help="Node roles managed by underlay in the environment",
            default=[ext.UNDERLAY_NODE_ROLES.salt_master,
                     ext.UNDERLAY_NODE_ROLES.salt_minion, ]),
+    ct.Cfg('bootstrap_timeout', ct.Integer(),
+           help="Timeout of waiting SSH for nodes with specified roles",
+           default=480),
     ct.Cfg('nameservers', ct.JSONList(),
            help="IP addresses of DNS servers",
            default=[]),
diff --git a/tcp_tests/templates/ironic_standalone/underlay--meta-data.yaml b/tcp_tests/templates/ironic_standalone/underlay--meta-data.yaml
new file mode 100644
index 0000000..3699401
--- /dev/null
+++ b/tcp_tests/templates/ironic_standalone/underlay--meta-data.yaml
@@ -0,0 +1,4 @@
+| # All the data below will be stored as a string object
+  instance-id: iid-local1
+  hostname: {hostname}
+  local-hostname: {hostname}
diff --git a/tcp_tests/templates/ironic_standalone/underlay--user-data-ironic.yaml b/tcp_tests/templates/ironic_standalone/underlay--user-data-ironic.yaml
new file mode 100644
index 0000000..54a7f79
--- /dev/null
+++ b/tcp_tests/templates/ironic_standalone/underlay--user-data-ironic.yaml
@@ -0,0 +1,130 @@
+| # All the data below will be stored as a string object
+  #cloud-config, see http://cloudinit.readthedocs.io/en/latest/topics/examples.html
+
+  ssh_pwauth: True
+  users:
+   - name: root
+     sudo: ALL=(ALL) NOPASSWD:ALL
+     shell: /bin/bash
+     ssh_authorized_keys:
+      - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCp0evjOaK8c8SKYK4r2+0BN7g+8YSvQ2n8nFgOURCyvkJqOHi1qPGZmuN0CclYVdVuZiXbWw3VxRbSW3EH736VzgY1U0JmoTiSamzLHaWsXvEIW8VCi7boli539QJP0ikJiBaNAgZILyCrVPN+A6mfqtacs1KXdZ0zlMq1BPtFciR1JTCRcVs5vP2Wwz5QtY2jMIh3aiwkePjMTQPcfmh1TkOlxYu5IbQyZ3G1ahA0mNKI9a0dtF282av/F6pwB/N1R1nEZ/9VtcN2I1mf1NW/tTHEEcTzXYo1R/8K9vlqAN8QvvGLZtZduGviNVNoNWvoxaXxDt8CPv2B2NCdQFZp
+
+  disable_root: false
+  chpasswd:
+   list: |
+    root:r00tme
+   expire: False
+
+  bootcmd:
+   # Block access to SSH while node is preparing
+   - cloud-init-per once sudo iptables -A INPUT -p tcp --dport 22 -j DROP
+   # Enable root access
+   - sed -i -e '/^PermitRootLogin/s/^.*$/PermitRootLogin yes/' /etc/ssh/sshd_config
+   - service sshd restart
+  output:
+    all: '| tee -a /var/log/cloud-init-output.log /dev/tty0'
+
+  runcmd:
+   # Configure dhclient
+   - sudo echo "nameserver {gateway}" >> /etc/resolvconf/resolv.conf.d/base
+   - sudo resolvconf -u
+
+   # Prepare network connection
+   - sudo ifup ens3
+   #- sudo route add default gw {gateway} {interface_name}
+   - sudo ifup ens4
+
+   # Create swap
+   - fallocate -l 4G /swapfile
+   - chmod 600 /swapfile
+   - mkswap /swapfile
+   - swapon /swapfile
+   - echo "/swapfile   none    swap    defaults    0   0" >> /etc/fstab
+
+   ############## TCP Cloud cfg01 node ##################
+   #- sleep 120
+   - echo "Preparing base OS"
+
+   - echo "172.18.248.114    jenkins.mcp.mirantis.net  gerrit.mcp.mirantis.net" >> /etc/hosts;
+   - echo "185.135.196.10    apt-mk.mirantis.com" >> /etc/hosts;
+   - echo "nameserver 172.18.208.44 >> /etc/resolv.conf;
+   - echo "nameserver 8.8.8.8 >> /etc/resolv.conf;
+   - which wget >/dev/null || (apt-get update; apt-get install -y wget);
+
+   - export IRONIC_PXE_MANAGER=dnsmasq
+   - export IRONIC_PXE_INTERFACE_NAME=ens4
+   - export IRONIC_PXE_INTERFACE_ADDRESS=10.0.175.2
+   - export IRONIC_DHCP_POOL_NETMASK_PREFIX=24
+   - export IRONIC_DHCP_POOL_START=10.0.175.100
+   - export IRONIC_DHCP_POOL_END=10.0.175.200
+   - export DNSMASQ_USE_EXTERNAL_DNS=true
+   - ip a a $IRONIC_PXE_INTERFACE_ADDRESS/$IRONIC_DHCP_POOL_NETMASK_PREFIX dev $IRONIC_PXE_INTERFACE_NAME
+   - curl https://raw.githubusercontent.com/dis-xcom/underpillar/master/bootstrap.sh -o ./bootstrap.sh && bash ./bootstrap.sh
+
+   - echo "Building ironic agent image (stable/newton) ..."
+   - apt-get install -y docker.io gzip uuid-runtime cpio findutils grep gnupg make
+   - service docker start
+   - git clone https://git.openstack.org/openstack/ironic-python-agent /tmp/ironic-python-agent
+   - cd /tmp/ironic-python-agent/imagebuild/coreos; git checkout stable/newton; make
+   - cp /tmp/ironic-python-agent/imagebuild/coreos/UPLOAD/coreos_production_pxe_image-oem.cpio.gz /httpboot/
+   - cp /tmp/ironic-python-agent/imagebuild/coreos/UPLOAD/coreos_production_pxe.vmlinuz /httpboot/
+   - chmod a+r /httpboot/coreos_production_pxe*
+
+   - echo "Download ubuntu cloudinit image"
+   - wget https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img -O /httpboot/xenial-server-cloudimg-amd64.qcow2
+
+   ########################################################
+   # Node is ready, allow SSH access
+   - echo "Allow SSH access ..."
+   - sudo iptables -D INPUT -p tcp --dport 22 -j DROP
+   ########################################################
+
+  write_files:
+   - path: /etc/network/interfaces
+     content: |
+          auto ens3
+          iface ens3 inet dhcp
+          auto ens4
+          iface ens4 inet manual
+
+   - path: /root/.ssh/id_rsa
+     owner: root:root
+     permissions: '0600'
+     content: |
+         -----BEGIN RSA PRIVATE KEY-----
+         MIIEogIBAAKCAQEAqdHr4zmivHPEimCuK9vtATe4PvGEr0Np/JxYDlEQsr5Cajh4
+         tajxmZrjdAnJWFXVbmYl21sN1cUW0ltxB+9+lc4GNVNCZqE4kmpsyx2lrF7xCFvF
+         Qou26JYud/UCT9IpCYgWjQIGSC8gq1TzfgOpn6rWnLNSl3WdM5TKtQT7RXIkdSUw
+         kXFbObz9lsM+ULWNozCId2osJHj4zE0D3H5odU5DpcWLuSG0MmdxtWoQNJjSiPWt
+         HbRdvNmr/xeqcAfzdUdZxGf/VbXDdiNZn9TVv7UxxBHE812KNUf/Cvb5agDfEL7x
+         i2bWXbhr4jVTaDVr6MWl8Q7fAj79gdjQnUBWaQIDAQABAoIBAFU3kU6yIna9BViH
+         UX+S2ijtRBjZ68JjavEnp4xvo5h+nydcdT57q9lv/0nAi3g3gmXm/oJH+/ZU87HV
+         zy+zP+t+umDSChUkPBZFL5jxpKyN7BhMrP1KzRuEGYd6vJE/nfY5g095P5vDgnpX
+         o+SNg/YqrY1u8zgr/hnfRaV2/XyIDEEcQXTHseWTnnMQnULFU88xL8yq8ACT5GhK
+         7A9m5ukfcU6d/fs/psz5Yqw5IQsWbv1yJ3/FKufPHlo2Nzh3/3eDAZUXvaBgf1so
+         FWFpHtkry3OXOGaZ98HgF9hL0twS0pzMvuypdGUQAt6nyB1N5re4LK/MAOddqwEc
+         1+NQzfECgYEA2ryEf0GLJdtiYs3F4HbwTwJVIXdyWv7kjYGeMkutzzAjXl6wx8aq
+         kfqLJ7x7UkR5unZ1ajEbKBciAlSuFA+Gikn6a4Lv8h87aSnHpPd/2VSitRlI/gW7
+         w4U4CL3Br1JyonU5WA7VYfTow7KnHBhdwm27RMA9uosyIpveQRpqSG0CgYEAxsAS
+         wCQKrhuPq2YtGtFR7K4BL+N+0E1Vq6h49u1ukcgUe0GHVD3VzBypNCv7rWEVHzAg
+         biCVi7PCjzZYW4fYZmzVD4JbFLVGOUu7aJwLaE4wDe72DNr6YZhcS+Ta98BP+x0q
+         Wt34JNPDabRPfhXfhiCqnWjjod+4Zqx4VJVNgG0CgYB5EXL8xJhyAbW5Hk/x56Mm
+         +BGKjoR7HS3/rMiU6hJv5SMObrbGPI3YcqZm/gn8BO6jaEGg30E6tWMbiyc270j2
+         be/vZe/NQcAuevOHuX3IGvJb7nzaLO46UBgtrmnv0mCkzuFIfh1ZNKdI+i9Ie6wZ
+         m4bVjNod0EGVqlQgELDXGQKBgB+NNmzSS++/6FrpaZesSzkrlnynvOYMoOETacCp
+         iLgT70xx5q308w/oLORfZyDrHJNK7JsPCS6YZvadRgGh2zTHajuAEj2DWZaW8zV0
+         MEtqvi44FU+NI9qCeYSC3FAgc5IF20d5nX8bLxaEzWnSxx1f6jX7BMgZ4AhMsP2c
+         hiUxAoGAFaxn+t9blIjqUiuh0smSYFhLBVPZveYHQDmQYERjktptBd3X95fGnSKh
+         iDe2iPGyud2+Yu4X/VjHLh/MRru+ZXvPXw1XwEqX93q8a1n283ul0Rl9+KKKOVHR
+         eecTjI/BfXBf33mPRKny3xuHw6uwta2T3OXky9IhqYS1kkHiZWA=
+         -----END RSA PRIVATE KEY-----
+
+   - path: /root/.ssh/config
+     owner: root:root
+     permissions: '0600'
+     content: |
+          Host *
+            ServerAliveInterval 300
+            ServerAliveCountMax 10
+            StrictHostKeyChecking no
+            UserKnownHostsFile /dev/null
diff --git a/tcp_tests/templates/ironic_standalone/underlay.yaml b/tcp_tests/templates/ironic_standalone/underlay.yaml
new file mode 100644
index 0000000..6a25ced
--- /dev/null
+++ b/tcp_tests/templates/ironic_standalone/underlay.yaml
@@ -0,0 +1,111 @@
+# Set the repository suite, one of the: 'nightly', 'testing', 'stable', or any other required
+{% set REPOSITORY_SUITE = os_env('REPOSITORY_SUITE', 'stable') %}
+
+{% import 'ironic_standalone/underlay--meta-data.yaml' as CLOUDINIT_META_DATA with context %}
+{% import 'ironic_standalone/underlay--user-data-ironic.yaml' as CLOUDINIT_USER_DATA_IRONIC with context %}
+
+---
+aliases:
+ - &interface_model {{ os_env('INTERFACE_MODEL', 'virtio') }}
+ - &cloudinit_meta_data {{ CLOUDINIT_META_DATA }}
+ - &cloudinit_user_data_ironic {{ CLOUDINIT_USER_DATA_IRONIC }}
+
+{% set DOMAIN_NAME = os_env('LAB_CONFIG_NAME', 'standalone') + '.local' %}
+{% set HOSTNAME_IRONIC = os_env('HOSTNAME_IRONIC', 'ironic_master') %}
+
+
+template:
+  devops_settings:
+    env_name: {{ os_env('IRONIC_ENV_NAME', 'ironic_' + DOMAIN_NAME + '_' + REPOSITORY_SUITE + '_' + os_env('BUILD_NUMBER', '')) }}
+
+    address_pools:
+
+      ironic-pool01:
+        net: {{ os_env('IRONIC_ADDRESS_POOL01', '10.50.0.0/16:24') }}
+        params:
+          ip_reserved:
+            gateway: +1
+            l2_network_device: +1
+            default_{{ HOSTNAME_IRONIC }}: +2
+          ip_ranges:
+            dhcp: [+90, -10]
+
+      admin-pool01:
+        net: {{ os_env('ADMIN_ADDRESS_POOL01', '10.70.0.0/16:24') }}
+        params:
+          ip_reserved:
+            gateway: +1
+            l2_network_device: +1
+            default_{{ HOSTNAME_IRONIC }}: +2
+
+    groups:
+      - name: default
+        driver:
+          name: devops.driver.libvirt
+          params:
+            connection_string: !os_env CONNECTION_STRING, qemu:///system
+            storage_pool_name: !os_env STORAGE_POOL_NAME, default
+            stp: False
+            hpet: False
+            enable_acpi: true
+            use_host_cpu: !os_env DRIVER_USE_HOST_CPU, true
+
+        network_pools:
+          admin: admin-pool01
+
+        l2_network_devices:
+          # Ironic management interface
+          ironic:
+            address_pool: ironic-pool01
+            dhcp: true
+            forward:
+              mode: nat
+
+          # PXE / Salt-master admin network
+          admin:
+            address_pool: admin-pool01
+            dhcp: false
+            parent_iface:
+              phys_dev: !os_env BAREMETAL_ADMIN_IFACE, enp8s0f1
+
+        group_volumes:
+         - name: cloudimage1604    # This name is used for 'backing_store' option for node volumes.
+           source_image: !os_env IMAGE_PATH1604  # https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-disk1.img or
+                                             # http://apt.tcpcloud.eu/images/ubuntu-16-04-x64-201608231004.qcow2
+           format: qcow2
+
+        nodes:
+          - name: {{ HOSTNAME_IRONIC }}
+            role: ironic_master
+            params:
+              vcpu: !os_env SLAVE_NODE_CPU, 1
+              memory: !os_env SLAVE_NODE_MEMORY, 2048
+              boot:
+                - hd
+              cloud_init_volume_name: iso
+              cloud_init_iface_up: ens3
+              volumes:
+                - name: system
+                  capacity: !os_env NODE_VOLUME_SIZE, 150
+                  backing_store: cloudimage1604
+                  format: qcow2
+                - name: iso  # Volume with name 'iso' will be used
+                             # for store image with cloud-init metadata.
+                  capacity: 1
+                  format: raw
+                  device: cdrom
+                  bus: ide
+                  cloudinit_meta_data: *cloudinit_meta_data
+                  cloudinit_user_data: *cloudinit_user_data_ironic
+
+              interfaces:
+                - label: ens3
+                  l2_network_device: ironic
+                  interface_model: *interface_model
+                - label: ens4
+                  l2_network_device: admin
+                  interface_model: *interface_model
+              network_config:
+                ens4:
+                  networks:
+                    - admin
diff --git a/tcp_tests/tests/environment/conftest.py b/tcp_tests/tests/environment/conftest.py
new file mode 100644
index 0000000..90f0fd4
--- /dev/null
+++ b/tcp_tests/tests/environment/conftest.py
@@ -0,0 +1,48 @@
+#    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 tcp_tests.fixtures import *
+from tcp_tests.fixtures.common_fixtures import *
+from tcp_tests.fixtures.config_fixtures import *
+from tcp_tests.fixtures.underlay_fixtures import *
+from tcp_tests.fixtures.rally_fixtures import *
+from tcp_tests.fixtures.salt_fixtures import *
+from tcp_tests.fixtures.common_services_fixtures import *
+from tcp_tests.fixtures.openstack_fixtures import *
+from tcp_tests.fixtures.opencontrail_fixtures import *
+
+__all__ = sorted([  # sort for documentation
+    # common_fixtures
+    'show_step',
+    'revert_snapshot',
+    'snapshot',
+    # config_fixtures
+    'config',
+    #underlay_fixtures
+    'hardware',
+    'underlay',
+    # rally_fixtures
+    'rally',
+    # salt_fixtures
+    'salt_actions',
+    'salt_deployed',
+    # common_services_fixtures
+    'common_services_actions',
+    'common_services_deployed',
+    # openstack_fixtures
+    'openstack_actions',
+    'openstack_deployed',
+    # component fixtures
+    'opencontrail',
+])
diff --git a/tcp_tests/tests/environment/test_ironic_standalone.py b/tcp_tests/tests/environment/test_ironic_standalone.py
new file mode 100644
index 0000000..8d60279
--- /dev/null
+++ b/tcp_tests/tests/environment/test_ironic_standalone.py
@@ -0,0 +1,60 @@
+#    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 copy
+import os
+import time
+
+import pytest
+
+from tcp_tests import settings
+from tcp_tests.helpers import ext
+from tcp_tests import logger
+
+LOG = logger.logger
+
+
+class TestIronicStandalone(object):
+    """Test class for testing TCP deployment"""
+
+    @pytest.mark.fail_snapshot
+    def test_install_ironic_standalone(self, config, underlay):
+        """Install a VM with standalone ironic
+
+        Before using, please set the correct roles and timeout:
+
+            export ROLES='["ironic_master"]'
+            export BOOTSTRAP_TIMEOUT=900
+
+        , and unset these variables after the bootstrap is completed.
+
+        Scenario:
+            1. Install Ironic service and helper services
+            2. Build ironic agent image
+            3. Download Ubuntu cloud image and calculate MD5
+            4. export environment variables to further use
+
+        """
+        cmd = "md5sum /httpboot/xenial-server-cloudimg-amd64.qcow2 | awk '{print $1}'"
+        res = underlay.check_call(cmd, host=config.salt.salt_master_host, verbose=True)
+
+        ironic_url = 'http://{0}:6385/'.format(config.salt.salt_master_host)
+        os.environ['CLOUDINIT_IMAGE_MD5'] = res['stdout']
+        os.environ['OS_AUTH_TOKEN'] = 'fake-token'
+        os.environ['IRONIC_URL'] = ironic_url
+
+        LOG.info("Ironic standalone server installed, to use it:\n"
+                 "    export OS_AUTH_TOKEN=fake-token\n"
+                 "    export IRONIC_URL={0}\n"
+                 "    export CLOUDINIT_IMAGE_MD5={1}"
+                 .format(ironic_url, res['stdout']))