Merge "getFirstMinion should return minion id"
diff --git a/src/com/mirantis/mk/Common.groovy b/src/com/mirantis/mk/Common.groovy
index a44bcdf..336b4dc 100644
--- a/src/com/mirantis/mk/Common.groovy
+++ b/src/com/mirantis/mk/Common.groovy
@@ -188,30 +188,30 @@
     }
 }
 
+def getColorizedString(msg, color){
+  def  colorMap = [
+    'red'   : '\u001B[31m',
+    'black' : '\u001B[30m',
+    'green' : '\u001B[32m',
+    'yellow': '\u001B[33m',
+    'blue'  : '\u001B[34m',
+    'purple': '\u001B[35m',
+    'cyan'  : '\u001B[36m',
+    'white' : '\u001B[37m',
+    'reset' : '\u001B[0m'
+  ]
+
+  return "${colorMap[color]}${msg}${colorMap.reset}"
+}
+
 /**
  * Print message
  *
  * @param msg Message to be printed
- * @param level Level of message (default INFO)
- * @param color Color to use for output or false (default)
+ * @param color Color to use for output
  */
-def printMsg(msg, color = false) {
-    colors = [
-        'red'   : '\u001B[31m',
-        'black' : '\u001B[30m',
-        'green' : '\u001B[32m',
-        'yellow': '\u001B[33m',
-        'blue'  : '\u001B[34m',
-        'purple': '\u001B[35m',
-        'cyan'  : '\u001B[36m',
-        'white' : '\u001B[37m',
-        'reset' : '\u001B[0m'
-    ]
-    if (color != false) {
-        print "${colors[color]}${msg}${colors.reset}"
-    } else {
-        print "[${level}] ${msg}"
-    }
+def printMsg(msg, color) {
+    print getColorizedString(msg, color)
 }
 
 /**
@@ -714,4 +714,89 @@
         filename = filename.take(filename.lastIndexOf(remove_ext.toString()))
     }
     return filename
-}
\ No newline at end of file
+}
+
+/**
+* Return colored string of specific stage in stageMap
+*
+* @param stageMap  LinkedHashMap object.
+* @param stageName The name of current stage we are going to execute.
+* @param color     Text color
+**/
+def getColoredStageView(stageMap, stageName, color){
+  def stage = stageMap[stageName]
+  def banner = []
+  def currentStageIndex = new ArrayList<String>(stageMap.keySet()).indexOf(stageName)
+  def numberOfStages = stageMap.keySet().size() - 1
+
+  banner.add(getColorizedString(
+    "=========== Stage ${currentStageIndex}/${numberOfStages}: ${stageName} ===========", color))
+  for (stage_item in stage.keySet()){
+    banner.add(getColorizedString(
+    "${stage_item}: ${stage[stage_item]}", color))
+  }
+  banner.add('\n')
+
+  return banner
+}
+
+/**
+* Pring stageMap to console with specified color
+*
+* @param stageMap        LinkedHashMap object with stages information.
+* @param currentStage    The name of current stage we are going to execute.
+*
+**/
+def printCurrentStage(stageMap, currentStage){
+  print getColoredStageView(stageMap, currentStage, "cyan").join('\n')
+}
+
+/**
+* Pring stageMap to console with specified color
+*
+* @param stageMap   LinkedHashMap object.
+* @param baseColor  Text color (default white)
+**/
+def printStageMap(stageMap, baseColor="white"){
+  def banner = []
+  def index = 0
+  for (stage_name in stageMap.keySet()){
+    banner.addAll(getColoredStageView(stageMap, stage_name,  baseColor))
+  }
+  print banner.join('\n')
+}
+
+/**
+* Wrap provided code in stage, and do interactive retires if needed.
+*
+* @param stageMap        LinkedHashMap object with stages information.
+* @param currentStage    The name of current stage we are going to execute.
+* @param target          Target host to execute stage on.
+* @param interactive     Boolean flag to specify if interaction with user is enabled.
+* @param body            Command to be in stage block.
+**/
+def stageWrapper(stageMap, currentStage, target, interactive=true, Closure body) {
+  def common = new com.mirantis.mk.Common()
+  def banner = []
+
+  printCurrentStage(stageMap, currentStage)
+
+  stage(currentStage){
+    input message: getColorizedString("We are going to execute stage \'${currentStage}\' on the following target ${target}.\nPlease review stage information above.", "yellow")
+    try {
+      return body.call()
+      stageMap[currentStage]['status'] = "SUCCESS"
+    } catch (Exception err) {
+      def msg = "Stage ${currentStage} failed with the following exception:\n${err}"
+      print getColorizedString(msg, "yellow")
+      common.errorMsg(err)
+      if (interactive){
+        input message: getColorizedString("Please make sure problem is fixed to proceed with retry. Ready to proceed?", "yellow")
+        stageMap[currentStage]['status'] = "RETRYING"
+        stageWrapper(stageMap, currentStage, target, interactive, body)
+      } else {
+        error(msg)
+      }
+    }
+  }
+}
diff --git a/src/com/mirantis/mk/Orchestrate.groovy b/src/com/mirantis/mk/Orchestrate.groovy
index 1157083..b2dfe18 100644
--- a/src/com/mirantis/mk/Orchestrate.groovy
+++ b/src/com/mirantis/mk/Orchestrate.groovy
@@ -255,6 +255,15 @@
         }
         salt.enforceState(master, "I@redis:server ${extra_tgt}", 'redis')
     }
+
+    // Install DNS services
+    if (salt.testTarget(master, "I@bind:server ${extra_tgt}")) {
+        salt.enforceState(master, "I@bind:server ${extra_tgt}", 'bind.server')
+    }
+    if (salt.testTarget(master, "I@powerdns:server ${extra_tgt}")) {
+        salt.enforceState(master, "I@powerdns:server ${extra_tgt}", 'powerdns.server')
+    }
+
     installBackup(master, 'common', extra_tgt)
 }
 
@@ -397,12 +406,6 @@
 
     // Install designate services
     if (salt.testTarget(master, "I@designate:server:enabled ${extra_tgt}")) {
-        if (salt.testTarget(master, "I@designate:server:backend:bind9 ${extra_tgt}")) {
-            salt.enforceState(master, "I@bind:server ${extra_tgt}", 'bind.server')
-        }
-        if (salt.testTarget(master, "I@designate:server:backend:pdns4 ${extra_tgt}")) {
-            salt.enforceState(master, "I@powerdns:server ${extra_tgt}", 'powerdns.server')
-        }
         salt.enforceState(master, "I@designate:server and *01* ${extra_tgt}", 'designate.server')
         salt.enforceState(master, "I@designate:server ${extra_tgt}", 'designate')
     }