Merge "Adding checks for STACK_INSTALL"
diff --git a/cloud-update.groovy b/cloud-update.groovy
index 3fd7723..28c5005 100644
--- a/cloud-update.groovy
+++ b/cloud-update.groovy
@@ -658,9 +658,8 @@
 def restoreGalera(pepperEnv) {
     def salt = new com.mirantis.mk.Salt()
     def common = new com.mirantis.mk.Common()
-    def openstack = new com.mirantis.mk.Openstack()
-    salt.cmdRun(pepperEnv, 'I@xtrabackup:client', "rm -rf /var/lib/mysql/*")
-    openstack.restoreGaleraDb(pepperEnv)
+    def galera = new com.mirantis.mk.Galera()
+    galera.restoreGaleraDb(pepperEnv)
 }
 
 def backupZookeeper(pepperEnv) {
diff --git a/cvp-shaker.groovy b/cvp-shaker.groovy
index 85201ff..f845bc8 100644
--- a/cvp-shaker.groovy
+++ b/cvp-shaker.groovy
@@ -17,6 +17,7 @@
   SHAKER_SERVER_ENDPOINT: '10.13.0.15:5999'
   SHAKER_SCENARIOS: 'scenarios/essential'
   SKIP_LIST: ''
+  MATRIX: '{host:[ping.online.net, bouygues.iperf.fr]}'
   image_builder:
     - SHAKER_FLAVOR_DISK=4
     - SHAKER_FLAVOR_RAM=512
@@ -39,6 +40,11 @@
     scenarios/additional/cross_az
     scenarios/additional/external
     scenarios/additional/qos
+  "MATRIX" - Set the matrix of extra parameters for the scenario. The value is specified in JSON format.
+  To override a scenario duration one may provide: "{time: 10}", or to override list of hosts:
+  "{host:[ping.online.net, iperf.eenet.ee]}". When several parameters are overridden all combinations are
+  tested. It is a required field for some of external-category scenarios when the host name with iperf3
+  server needs to be provided as a command-line parameter, e.g. ``{host: 10.13.100.4}``.
   "SKIP_LIST" - Comma-separated list of Shaker scenarios to skip, directories or files inside scenarios/
   of cvp-shaker, e.g. "dense_l2.yaml,full_l2.yaml,l3"
   "image_builder" - shaker-image-builder env variables
@@ -52,6 +58,7 @@
     SCENARIO_AVAILABILITY_ZONE='nova,internal'
     SCENARIO_COMPUTE_NODES=2
     SHAKER_EXTERNAL_NET='public'
+
 For the more detailed description of the last two categories please refer to the shaker documentation
 https://pyshaker.readthedocs.io/en/latest/tools.html
 */
@@ -91,7 +98,8 @@
             def general_params = [
                 SHAKER_SERVER_ENDPOINT: (SHAKER_PARAMS.get('SHAKER_SERVER_ENDPOINT')),
                 SHAKER_SCENARIOS: (SHAKER_PARAMS.get('SHAKER_SCENARIOS')) ?: 'scenarios/essential',
-                SKIP_LIST: (SHAKER_PARAMS.get('SKIP_LIST'))
+                SKIP_LIST: (SHAKER_PARAMS.get('SKIP_LIST')),
+                MATRIX: (SHAKER_PARAMS.get('MATRIX'))
             ]
             if (! general_params['SHAKER_SERVER_ENDPOINT']) {
                 throw new Exception("SHAKER_SERVER_ENDPOINT address was not set in the SHAKER_PARAMS")
@@ -112,6 +120,11 @@
                 general_params['SKIP_LIST']
             )
 
+            // Override scenarios params:
+            if (general_params['MATRIX']) {
+                cmd_shaker_args += " --matrix '${general_params.MATRIX}'"
+            }
+
             // Define docker commands
             def commands = [
                 '001_build_image': "shaker-image-builder --debug",
diff --git a/docker-cleanup-pipeline.groovy b/docker-cleanup-pipeline.groovy
index 388793a..0f8fcf6 100644
--- a/docker-cleanup-pipeline.groovy
+++ b/docker-cleanup-pipeline.groovy
@@ -28,7 +28,7 @@
           """, false)
     }
     stage("Run docker system prune"){
-      salt.cmdRun(pepperEnv, 'I@jenkins:slave', "docker system prune -f", false) // dont verify the result
+      salt.cmdRun(pepperEnv, 'I@jenkins:slave', "docker system prune --all --force", false) // dont verify the result
     }
   }
 }
diff --git a/galera-cluster-verify-restore.groovy b/galera-cluster-verify-restore.groovy
index a5d4483..7a908cb 100644
--- a/galera-cluster-verify-restore.groovy
+++ b/galera-cluster-verify-restore.groovy
@@ -12,7 +12,7 @@
 
 def common = new com.mirantis.mk.Common()
 def salt = new com.mirantis.mk.Salt()
-def openstack = new com.mirantis.mk.Openstack()
+def galera = new com.mirantis.mk.Galera()
 def python = new com.mirantis.mk.Python()
 def pepperEnv = "pepperEnv"
 def resultCode = 99
@@ -31,11 +31,11 @@
             python.setupPepperVirtualenv(pepperEnv, SALT_MASTER_URL, SALT_MASTER_CREDENTIALS)
         }
         stage('Verify status')
-            resultCode = openstack.verifyGaleraStatus(pepperEnv, false, checkTimeSync)
+            resultCode = galera.verifyGaleraStatus(pepperEnv, false, checkTimeSync)
         stage('Restore') {
             if (resultCode == 128) {
                 common.errorMsg("Unable to connect to Galera Master. Trying slaves...")
-                resultCode = openstack.verifyGaleraStatus(pepperEnv, true, checkTimeSync)
+                resultCode = galera.verifyGaleraStatus(pepperEnv, true, checkTimeSync)
                 if (resultCode == 129) {
                     common.errorMsg("Unable to obtain Galera slave minions list". "Without fixing this issue, pipeline cannot continue in verification and restoration.")
                     currentBuild.result = "FAILURE"
@@ -74,7 +74,7 @@
             }
             try {
                 if((!askConfirmation && resultCode > 0) || askConfirmation){
-                  openstack.restoreGaleraDb(pepperEnv)
+                  galera.restoreGaleraDb(pepperEnv)
                 }
             } catch (Exception e) {
                 common.errorMsg("Restoration process has failed.")
@@ -82,7 +82,7 @@
         }
         stage('Verify restoration result') {
             common.retry(verificationRetries, 15) {
-                exitCode = openstack.verifyGaleraStatus(pepperEnv, false, false)
+                exitCode = galera.verifyGaleraStatus(pepperEnv, false, false)
                 if (exitCode >= 1) {
                     error("Verification attempt finished with an error. This may be caused by cluster not having enough time to come up or to sync. Next verification attempt in 15 seconds.")
                 } else {
diff --git a/generate-cookiecutter-products.groovy b/generate-cookiecutter-products.groovy
index 6f07593..fe85d37 100644
--- a/generate-cookiecutter-products.groovy
+++ b/generate-cookiecutter-products.groovy
@@ -36,10 +36,6 @@
             return true
         }
     }
-    if ("${context['salt_master_hostname']}.${context['cluster_domain']}".length() > 64) {
-        common.errorMsg("Cluster domain has too long name. Make ${context['cluster_domain']} shorter than 58 symbols.")
-        error('Invalid context provided')
-    }
     // Use mcpVersion git tag if not specified branch for cookiecutter-templates
     if (!context.get('cookiecutter_template_branch')) {
         context['cookiecutter_template_branch'] = gitGuessedVersion ?: context['mcp_version']
@@ -183,11 +179,13 @@
                     common.infoMsg("Attempt to run test against distribRevision: ${distribRevision}")
                     try {
                         def config = [
-                            'dockerHostname'     : "${context['salt_master_hostname']}.${context['cluster_domain']}",
+                            'dockerHostname'     : "${context['salt_master_hostname']}",
+                            'domain'             : "${context['cluster_domain']}",
                             'reclassEnv'         : testEnv,
                             'distribRevision'    : distribRevision,
                             'dockerContainerName': DockerCName,
-                            'testContext'        : 'salt-model-node'
+                            'testContext'        : 'salt-model-node',
+                            'dockerExtraOpts'    : [ '--memory=3g' ]
                         ]
                         testResult = saltModelTesting.testNode(config)
                         common.infoMsg("Test finished: SUCCESS")
diff --git a/opencontrail40-upgrade.groovy b/opencontrail40-upgrade.groovy
index 180ed85..2f89659 100644
--- a/opencontrail40-upgrade.groovy
+++ b/opencontrail40-upgrade.groovy
@@ -26,9 +26,9 @@
 def probe = 1
 def command = 'cmd.shell'
 
-def controlPkgs = 'contrail-config,contrail-config-openstack,contrail-control,contrail-dns,contrail-lib,contrail-nodemgr,contrail-utils,contrail-web-controller,contrail-web-core,neutron-plugin-contrail,python-contrail,contrail-database'
+def controlPkgs = 'contrail-config,contrail-config-openstack,contrail-control,contrail-dns,contrail-lib,contrail-nodemgr,contrail-utils,contrail-web-controller,contrail-web-core,neutron-plugin-contrail,contrail-database'
 def thirdPartyControlPkgsToRemove = 'zookeeper,libzookeeper-java,kafka,cassandra,redis-server,ifmap-server,supervisor'
-def analyticsPkgs = 'contrail-analytics,contrail-lib,contrail-nodemgr,contrail-utils,python-contrail,contrail-database'
+def analyticsPkgs = 'contrail-analytics,contrail-lib,contrail-nodemgr,contrail-utils,contrail-database'
 def thirdPartyAnalyticsPkgsToRemove = 'zookeeper,libzookeeper-java,kafka,cassandra,python-cassandra,cassandra-cpp-driver,redis-server,supervisor'
 def cmpPkgs = 'contrail-lib contrail-nodemgr contrail-utils contrail-vrouter-agent contrail-vrouter-utils python-contrail python-contrail-vrouter-api python-opencontrail-vrouter-netns contrail-vrouter-dkms'
 def neutronServerPkgs = 'neutron-plugin-contrail,contrail-heat,python-contrail'
diff --git a/restore-zookeeper.groovy b/restore-zookeeper.groovy
index 185f097..8afc5a5 100644
--- a/restore-zookeeper.groovy
+++ b/restore-zookeeper.groovy
@@ -12,6 +12,14 @@
 def python = new com.mirantis.mk.Python()
 
 def pepperEnv = "pepperEnv"
+
+def oc3SupervisorServices = ["supervisor-config", "supervisor-control"]
+def oc4ConfigServices = ["contrail-api", "contrail-schema", "contrail-svc-monitor", "contrail-device-manager", "contrail-config-nodemgr"]
+def oc4ControlServices = ["contrail-control", "contrail-named", "contrail-dns", "contrail-control-nodemgr"]
+def zkService = "zookeeper"
+def contrailStatusCheckCmd = "contrail-status | grep -v == | grep -v \'disabled on boot\' | grep -v nodemgr | grep -v active | grep -v backup"
+def zkDbPath = "/var/lib/zookeeper/version-2"
+
 timeout(time: 12, unit: 'HOURS') {
     node() {
 
@@ -20,65 +28,86 @@
         }
 
         stage('Restore') {
-            try {
-                salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.stop', ['supervisor-config'], null, true)
-            } catch (Exception er) {
-                common.warningMsg('Supervisor-config service already stopped')
+
+            def ocVersionPillarKey = salt.getReturnValues(salt.getPillar(pepperEnv, "I@opencontrail:control:role:primary", "_param:opencontrail_version"))
+
+            if (ocVersionPillarKey == '') {
+                throw new Exception("Cannot get value for _param:opencontrail_version key on I@opencontrail:control:role:primary target")
             }
-            try {
-                salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.stop', ['supervisor-control'], null, true)
-            } catch (Exception er) {
-                common.warningMsg('Supervisor-control service already stopped')
+
+            def ocVersion = ocVersionPillarKey.toString()
+
+            if (ocVersion >= "4.0") {
+
+                contrailStatusCheckCmd = "doctrail controller ${contrailStatusCheckCmd}"
+                zkDbPath = "/var/lib/config_zookeeper_data/version-2"
+
+                for (service in (oc4ConfigServices + oc4ControlServices + [zkService])) {
+                    try {
+                        salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'cmd.run', ["doctrail controller systemctl stop ${service}"])
+                    } catch (Exception er) {
+                        common.warningMsg("${service} cannot be stopped inside controller container")
+                    }
+                }
+                // wait until zookeeper service is down
+                salt.commandStatus(pepperEnv, 'I@opencontrail:control', "doctrail controller service ${zkService} status", 'Active: inactive')
+            } else {
+                for (service in (oc3SupervisorServices + [zkService])) {
+                    try {
+                        salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.stop', ["${service}"])
+                    } catch (Exception er) {
+                        common.warningMsg("${service} service cannot be stopped. It may be already stopped before.")
+                    }
+                }
+                // wait until zookeeper service is down
+                salt.commandStatus(pepperEnv, 'I@opencontrail:control', "service ${zkService} status", "stop")
             }
-            try {
-                salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.stop', ['zookeeper'], null, true)
-            } catch (Exception er) {
-                common.warningMsg('Zookeeper service already stopped')
-            }
-            //sleep(5)
-            // wait until zookeeper service is down
-            salt.commandStatus(pepperEnv, 'I@opencontrail:control', 'service zookeeper status', 'stop')
 
             try {
                 salt.cmdRun(pepperEnv, 'I@opencontrail:control', "mkdir -p /root/zookeeper/zookeeper.bak")
             } catch (Exception er) {
-                common.warningMsg('Directory already exists')
+                common.warningMsg('/root/zookeeper/zookeeper.bak directory already exists')
             }
 
             try {
-                salt.cmdRun(pepperEnv, 'I@opencontrail:control', "mv /var/lib/zookeeper/version-2/* /root/zookeeper/zookeeper.bak")
+                salt.cmdRun(pepperEnv, 'I@opencontrail:control', "mv ${zkDbPath}/* /root/zookeeper/zookeeper.bak")
             } catch (Exception er) {
                 common.warningMsg('Files were already moved')
             }
             try {
-                salt.cmdRun(pepperEnv, 'I@opencontrail:control', "rm -rf /var/lib/zookeeper/version-2/*")
+                salt.cmdRun(pepperEnv, 'I@opencontrail:control', "rm -rf ${zkDbPath}/*")
             } catch (Exception er) {
                 common.warningMsg('Directory already empty')
             }
 
-            _pillar = salt.getPillar(pepperEnv, "I@opencontrail:control", 'zookeeper:backup:backup_dir')
-            backup_dir = _pillar['return'][0].values()[0]
-            if(backup_dir == null || backup_dir.isEmpty()) { backup_dir='/var/backups/zookeeper' }
-            print(backup_dir)
-            salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'file.remove', ["${backup_dir}/dbrestored"], null, true)
+            backupDirPillarKey = salt.getPillar(pepperEnv, "I@opencontrail:control", 'zookeeper:backup:backup_dir')
+            backupDir = backupDirPillarKey['return'][0].values()[0]
+            if (backupDir == null || backupDir.isEmpty()) { backupDir='/var/backups/zookeeper' }
+            print(backupDir)
+            salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'file.remove', ["${backupDir}/dbrestored"])
 
             // performs restore
             salt.enforceState(pepperEnv, 'I@opencontrail:control', "zookeeper.backup")
 
-            salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.start', ['zookeeper'], null, true)
-            salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.start', ['supervisor-config'], null, true)
-            salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.start', ['supervisor-control'], null, true)
+            if (ocVersion >= "4.0") {
+                for (service in ([zkService] + oc4ConfigServices + oc4ControlServices)) {
+                    salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'cmd.run', ["doctrail controller systemctl start ${service}"])
+                }
+            } else {
+                for (service in ([zkService] + oc3SupervisorServices)) {
+                    salt.runSaltProcessStep(pepperEnv, 'I@opencontrail:control', 'service.start', ["${service}"])
+                }
+            }
 
             // wait until contrail-status is up
-            salt.commandStatus(pepperEnv, 'I@opencontrail:control', "contrail-status | grep -v == | grep -v \'disabled on boot\' | grep -v nodemgr | grep -v active | grep -v backup", null, false)
+            salt.commandStatus(pepperEnv, 'I@opencontrail:control', contrailStatusCheckCmd, null, false)
 
-            salt.cmdRun(pepperEnv, 'I@opencontrail:control', "ls /var/lib/zookeeper/version-2")
+            salt.cmdRun(pepperEnv, 'I@opencontrail:control', "ls ${zkDbPath}")
             try {
                 salt.cmdRun(pepperEnv, 'I@opencontrail:control', "echo stat | nc localhost 2181")
             } catch (Exception er) {
                 common.warningMsg('Check which node is zookeeper leader')
             }
-            salt.cmdRun(pepperEnv, 'I@opencontrail:control', "contrail-status")
         }
     }
 }
diff --git a/test-cookiecutter-reclass-chunk.groovy b/test-cookiecutter-reclass-chunk.groovy
index 15634bd..e0c9710 100644
--- a/test-cookiecutter-reclass-chunk.groovy
+++ b/test-cookiecutter-reclass-chunk.groovy
@@ -27,12 +27,14 @@
                 def content = readFile(file: extraVars.modelFile)
                 def templateContext = readYaml text: content
                 def config = [
-                    'dockerHostname': "cfg01.${templateContext.default_context.cluster_domain}",
+                    'dockerHostname': "cfg01",
+                    'domain': "${templateContext.default_context.cluster_domain}",
                     'clusterName': templateContext.default_context.cluster_name,
                     'reclassEnv': extraVars.testReclassEnv,
                     'distribRevision': extraVars.DISTRIB_REVISION,
                     'dockerContainerName': extraVars.DockerCName,
-                    'testContext': extraVars.modelFile
+                    'testContext': extraVars.modelFile,
+                    'dockerExtraOpts': [ '--memory=3g' ]
                 ]
                 if (extraVars.useExtraRepos) {
                     config['extraRepos'] = extraVars.extraRepos ? extraVars.extraRepos : [:]
diff --git a/test-salt-formulas-env.groovy b/test-salt-formulas-env.groovy
index 257c0ab..e007fe9 100644
--- a/test-salt-formulas-env.groovy
+++ b/test-salt-formulas-env.groovy
@@ -27,6 +27,8 @@
 def travisLess = false      /** TODO: Remove once formulas are witched to new config */
 def cleanEnv = ''           /** TODO: Remove once formulas are witched to new config */
 def testSuite = ''
+envOverrides = []
+kitchenFileName = ''
 
 throttle(['test-formula']) {
   timeout(time: 1, unit: 'HOURS') {
@@ -114,26 +116,27 @@
           } else {
             if (checkouted) {
               travisLess = true
-              if (fileExists(".kitchen.yml") || fileExists(".kitchen.openstack.yml")) {
-                if (fileExists(".kitchen.openstack.yml")) {
-                  common.infoMsg("Openstack Kitchen test configuration found, running Openstack kitchen tests.")
-                  if (fileExists(".kitchen.yml")) {
-                    common.infoMsg("Ignoring the docker Kitchen test configuration file.")
-                  }
-                  openstackTest = true
-                  withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: openstack_credentials_id,
-                  usernameVariable: 'OS_USERNAME', passwordVariable: 'OS_PASSWORD'], ]) {
-                    env.OS_USERNAME = OS_USERNAME
-                    env.OS_PASSWORD = OS_PASSWORD
-                    env.OS_AUTH_URL = OS_AUTH_URL
-                    env.OS_PROJECT_NAME = OS_PROJECT_NAME
-                    env.OS_DOMAIN_NAME = OS_DOMAIN_NAME
-                    env.OS_AZ = OS_AZ
-                  }
-                } else {
-                  common.infoMsg("Docker Kitchen test configuration found, running Docker kitchen tests.")
+              if (fileExists(".kitchen.openstack.yml")) {
+                common.infoMsg("Openstack Kitchen test configuration found, running Openstack kitchen tests.")
+                kitchenFileName = ".kitchen.openstack.yml"
+                envOverrides.add("KITCHEN_YAML=${kitchenFileName}")
+                rubyVersion = '2.5.0'
+                withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: openstack_credentials_id,
+                usernameVariable: 'OS_USERNAME', passwordVariable: 'OS_PASSWORD'], ]) {
+                  env.OS_USERNAME = OS_USERNAME
+                  env.OS_PASSWORD = OS_PASSWORD
+                  env.OS_AUTH_URL = OS_AUTH_URL
+                  env.OS_PROJECT_NAME = OS_PROJECT_NAME
+                  env.OS_DOMAIN_NAME = OS_DOMAIN_NAME
+                  env.OS_AZ = OS_AZ
                 }
-                ruby.ensureRubyEnv()
+              } else if (fileExists(".kitchen.yml")) {
+                common.infoMsg("Docker Kitchen test configuration found, running Docker kitchen tests.")
+                kitchenFileName = ".kitchen.yml"
+                rubyVersion = '2.4.1'
+              }
+              if (kitchenFileName) {
+                ruby.ensureRubyEnv(rubyVersion)
                 if (!fileExists("Gemfile")) {
                   sh("curl -s -o ./Gemfile 'https://gerrit.mcp.mirantis.com/gitweb?p=salt-formulas/salt-formulas-scripts.git;a=blob_plain;f=Gemfile;hb=refs/heads/master'")
                   ruby.installKitchen()
@@ -144,16 +147,15 @@
                 common.infoMsg("Running part of kitchen test")
                 if (KITCHEN_ENV != null && !KITCHEN_ENV.isEmpty() && KITCHEN_ENV != "") {
                   testSuite = KITCHEN_ENV.replaceAll("_", "-").trim()
-                  if (openstackTest) { testSuite = "KITCHEN_YAML=.kitchen.openstack.yml " + testSuite }
                   sh("grep apt.mirantis.com -Ril | xargs -I{} bash -c \"echo {}; sed -i 's/apt.mirantis.com/apt.mcp.mirantis.net/g' {}\"")
                   sh("grep apt-mk.mirantis.com -Ril | xargs -I{} bash -c \"echo {}; sed -i 's/apt-mk.mirantis.com/apt.mcp.mirantis.net/g' {}\"")
                   common.infoMsg("Running kitchen test with environment:" + testSuite)
-                  ruby.runKitchenTests("", testSuite)
+                  ruby.runKitchenTests(envOverrides.join(' '), testSuite)
                 } else {
                   throw new Exception("KITCHEN_ENV parameter is empty or invalid. This may indicate wrong env settings of initial test job or .travis.yml file.")
                 }
               } else {
-                throw new Exception(".kitchen.yml file not found, no kitchen tests triggered.")
+                throw new Exception(".kitchen.yml nor .kitchen.openstack.yml file not found, no kitchen tests triggered.")
               }
             }
           }
diff --git a/test-salt-formulas-pipeline.groovy b/test-salt-formulas-pipeline.groovy
index 088a744..4326433 100644
--- a/test-salt-formulas-pipeline.groovy
+++ b/test-salt-formulas-pipeline.groovy
@@ -19,8 +19,10 @@
 
 def checkouted = false
 
+envOverrides = []
 futureFormulas = []
 failedFormulas = []
+kitchenFileName = ''
 
 def setupRunner(defaultGitRef, defaultGitUrl) {
   def branches = [:]
@@ -173,17 +175,19 @@
         }/** TODO: End of block for removal */
         } else {
           if (checkouted) {
-            if (fileExists(".kitchen.yml") || fileExists(".kitchen.openstack.yml")) {
-              if (fileExists(".kitchen.openstack.yml")) {
-                common.infoMsg("Openstack Kitchen test configuration found, running Openstack kitchen tests.")
-                if (fileExists(".kitchen.yml")) {
-                  common.infoMsg("Ignoring the docker Kitchen test configuration file.")
-                }
-              } else {
-                common.infoMsg("Docker Kitchen test configuration found, running Docker kitchen tests.")
-              }
+            if (fileExists(".kitchen.openstack.yml")) {
+              common.infoMsg("Openstack Kitchen test configuration found, running Openstack kitchen tests.")
+              kitchenFileName = ".kitchen.openstack.yml"
+              envOverrides.add("KITCHEN_YAML=${kitchenFileName}")
+              rubyVersion = '2.5.0'
+            } else if (fileExists(".kitchen.yml")) {
+              common.infoMsg("Docker Kitchen test configuration found, running Docker kitchen tests.")
+              kitchenFileName = ".kitchen.yml"
+              rubyVersion = '2.4.1'
+            }
+            if (kitchenFileName) {
               def kitchenEnvs = []
-              ruby.ensureRubyEnv()
+              ruby.ensureRubyEnv(rubyVersion)
               if (!fileExists("Gemfile")) {
                 sh("curl -s -o ./Gemfile 'https://gerrit.mcp.mirantis.com/gitweb?p=salt-formulas/salt-formulas-scripts.git;a=blob_plain;f=Gemfile;hb=refs/heads/master'")
                 ruby.installKitchen()
@@ -191,8 +195,8 @@
                 common.infoMsg("Override Gemfile found in the kitchen directory, using it.")
                 ruby.installKitchen()
               }
-              common.infoMsg = ruby.runKitchenCommand("list -b")
-              kitchenEnvs = ruby.runKitchenCommand("list -b").split()
+              common.infoMsg = ruby.runKitchenCommand("list -b", envOverrides.join(' '))
+              kitchenEnvs = ruby.runKitchenCommand("list -b", envOverrides.join(' ')).split()
               common.infoMsg(kitchenEnvs)
               common.infoMsg("Running kitchen testing in parallel mode")
               if (CUSTOM_KITCHEN_ENVS != null && CUSTOM_KITCHEN_ENVS != '') {
@@ -207,9 +211,11 @@
                 }
                 setupRunner(defaultGitRef, defaultGitUrl)
               } else {
+                common.errorMsg("No enviroments defined in the Kitchen file: ${kitchenFileName}")
+              }
+            } else {
                 common.warningMsg(".kitchen.yml nor .kitchen.openstack.yml file not found, no kitchen tests triggered.")
               }
-            }
           }
         }
       }
diff --git a/test-salt-model-node.groovy b/test-salt-model-node.groovy
index 27e0909..154df3d 100644
--- a/test-salt-model-node.groovy
+++ b/test-salt-model-node.groovy
@@ -64,12 +64,16 @@
             common.infoMsg("Running salt model test for node ${NODE_TARGET} in cluster ${CLUSTER_NAME}")
 
             def DockerCName = "${env.JOB_NAME.toLowerCase()}_${env.BUILD_TAG.toLowerCase()}"
+            def dockerHostname = NODE_TARGET.tokenize('.')[0]
+            def domain = NODE_TARGET - "${dockerHostname}."
             def config = [
-              'dockerHostname': NODE_TARGET,
+              'dockerHostname': dockerHostname,
+              'domain': domain,
               'clusterName': CLUSTER_NAME,
               'reclassEnv': workspace,
               'distribRevision': distribRevision,
               'dockerMaxCpus': MAX_CPU_PER_JOB.toInteger(),
+              'dockerExtraOpts': [ '--memory=3g' ],
               'ignoreClassNotfound': RECLASS_IGNORE_CLASS_NOTFOUND,
               'aptRepoUrl': APT_REPOSITORY,
               'aptRepoGPG': APT_REPOSITORY_GPG,
diff --git a/test-salt-model-wrapper.groovy b/test-salt-model-wrapper.groovy
index 9913c5c..42aa4e9 100644
--- a/test-salt-model-wrapper.groovy
+++ b/test-salt-model-wrapper.groovy
@@ -55,9 +55,9 @@
 }
 
 // run needed job with params
-def runTests(String jobName, ArrayList jobParams, String threadName = '') {
+def runTests(String jobName, ArrayList jobParams, String threadName = '', Boolean voteOverride = null) {
     threadName = threadName ? threadName : jobName
-    def propagateStatus = voteMatrix.get(jobName, true)
+    def propagateStatus = voteOverride != null ? voteOverride : voteMatrix.get(jobName, true)
     return {
         def jobBuild = build job: jobName, propagate: false, parameters: jobParams
         jobResultComments[threadName] = ['url': jobBuild.absoluteUrl, 'status': jobBuild.result, 'job': jobName]
@@ -205,7 +205,9 @@
                         buildTestParamsOld['COOKIECUTTER_TEMPLATE_REF'] = ''
                         buildTestParamsOld['COOKIECUTTER_TEMPLATE_BRANCH'] = oldRef
                         String threadName = "${branchJobName}-${oldRef}"
-                        branches[threadName] = runTests(branchJobName, yamlJobParameters(buildTestParamsOld), threadName)
+                        // disable votes for release/2018.11.0 branch
+                        overrideVote = oldRef == 'release/2018.11.0' ? false : null
+                        branches[threadName] = runTests(branchJobName, yamlJobParameters(buildTestParamsOld), threadName, overrideVote)
                     }
                 }
                 if (gerritProject == cookiecutterTemplatesRepo) {