Merge "Remove redundant installInfra call from installOssInfra"
diff --git a/src/com/mirantis/mcp/Validate.groovy b/src/com/mirantis/mcp/Validate.groovy
index a345165..43d1869 100644
--- a/src/com/mirantis/mcp/Validate.groovy
+++ b/src/com/mirantis/mcp/Validate.groovy
@@ -210,7 +210,7 @@
     salt.runSaltProcessStep(master, target, 'file.mkdir', ["${results}", "mode=777"])
     def _pillar = salt.getPillar(master, 'I@keystone:server', 'keystone:server')
     def keystone = _pillar['return'][0].values()[0]
-    def env_vars = ["tempest_version=${version}",
+    def env_vars = ['tempest_version=15.0.0',
                     "OS_USERNAME=${keystone.admin_name}",
                     "OS_PASSWORD=${keystone.admin_password}",
                     "OS_TENANT_NAME=${keystone.admin_tenant}",
@@ -222,7 +222,7 @@
         cmd = "git clone -b ${confBranch ?: 'master'} ${confRepository} test_config; " +
             'rally deployment create --fromenv --name=tempest; rally deployment config; ' +
             'rally verify create-verifier --name tempest_verifier --type tempest ' +
-            "--source ${repository ?: '/tmp/tempest/'} --version ${version}; " +
+            "--source ${repository ?: '/tmp/tempest/'} --version ${version: '15.0.0'}; " +
             'rally verify configure-verifier --extend test_config/tempest/tempest.conf --show; '
         skip_list = '--skip-list test_config/tempest/skip-list.yaml'
     }
diff --git a/src/com/mirantis/mk/Common.groovy b/src/com/mirantis/mk/Common.groovy
index 9a82fbe..7ad1834 100644
--- a/src/com/mirantis/mk/Common.groovy
+++ b/src/com/mirantis/mk/Common.groovy
@@ -539,3 +539,14 @@
   }
   return userInput
 }
+
+/**
+ * Function receives Map variable as input and sorts it
+ * by values ascending. Returns sorted Map
+ * @param _map Map variable
+ */
+@NonCPS
+def SortMapByValueAsc(_map) {
+    def sortedMap = _map.sort {it.value}
+    return sortedMap
+}
diff --git a/src/com/mirantis/mk/Orchestrate.groovy b/src/com/mirantis/mk/Orchestrate.groovy
index fe8caba..d513822 100644
--- a/src/com/mirantis/mk/Orchestrate.groovy
+++ b/src/com/mirantis/mk/Orchestrate.groovy
@@ -1269,3 +1269,31 @@
   }
   salt.enforceState(master, "I@elasticsearch:client ${extra_tgt}", 'elasticsearch.client')
 }
+
+/**
+ * Function receives connection string, target and configuration yaml pattern
+ * and retrieves config fom salt minion according to pattern. After that it
+ * sorts applications according to priorities and runs orchestration states
+ * @param master Salt Connection object or pepperEnv
+ * @param tgt Target
+ * @param conf Configuration pattern
+ */
+def OrchestrateApplications(master, tgt, conf) {
+    def salt = new com.mirantis.mk.Salt()
+    def common = new com.mirantis.mk.Common()
+    def _orch = salt.getConfig(master, tgt, conf)
+    if ( !_orch['return'][0].values()[0].isEmpty() ) {
+      Map<String,Integer> _orch_app = [:]
+      for (k in _orch['return'][0].values()[0].keySet()) {
+        _orch_app[k] = _orch['return'][0].values()[0][k].values()[0].toInteger()
+      }
+      def _orch_app_sorted = common.SortMapByValueAsc(_orch_app)
+      common.infoMsg("Applications will be deployed in following order:"+_orch_app_sorted.keySet())
+      for (app in _orch_app_sorted.keySet()) {
+        salt.orchestrateSystem(master, ['expression': tgt, 'type': 'compound'], "${app}.orchestrate.deploy")
+      }
+    }
+    else {
+      common.infoMsg("No applications found for orchestration")
+    }
+}
diff --git a/src/com/mirantis/mk/Salt.groovy b/src/com/mirantis/mk/Salt.groovy
index 88146fa..6530062 100644
--- a/src/com/mirantis/mk/Salt.groovy
+++ b/src/com/mirantis/mk/Salt.groovy
@@ -132,6 +132,16 @@
     }
 }
 
+/**
+ * Return config items for given saltId and target
+ * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method)
+ * @param target Get grain target
+ * @param config grain name (optional)
+ * @return output of salt command
+ */
+def getConfig(saltId, target, config) {
+    return runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'config.get', null, [config.replace('.', ':')], '--out=json')
+}
 
 /**
  * Enforces state on given saltId and target
@@ -671,6 +681,9 @@
  * @return output of salt command
  */
 def orchestrateSystem(saltId, target, orchestrate=[], kwargs = null) {
+    //Since the runSaltCommand uses "arg" (singular) for "runner" client this won`t work correctly on old salt 2016
+    //cause this version of salt used "args" (plural) for "runner" client, see following link for reference:
+    //https://github.com/saltstack/salt/pull/32938
     return runSaltCommand(saltId, 'runner', target, 'state.orchestrate', true, orchestrate, kwargs, 7200, 7200)
 }