Merge "Fix enforceHighstate exclude"
diff --git a/src/com/mirantis/mcp/SlackNotification.groovy b/src/com/mirantis/mcp/SlackNotification.groovy
new file mode 100644
index 0000000..d78d63a
--- /dev/null
+++ b/src/com/mirantis/mcp/SlackNotification.groovy
@@ -0,0 +1,76 @@
+package com.mirantis.mcp
+
+/**
+ * Send POST request to url with some params
+ * @param urlString url
+ * @param paramString JSON string
+ */
+def sendPostRequest(String urlString, String paramString){
+    def url = new URL(urlString)
+    def conn = url.openConnection()
+    conn.setDoOutput(true)
+    def writer = new OutputStreamWriter(conn.getOutputStream())
+    writer.write(paramString)
+    writer.flush()
+    String line
+    def reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))
+    while ((line = reader.readLine()) != null) {
+        println line
+    }
+    writer.close()
+    reader.close()
+}
+
+/**
+ * Send slack notification
+ * @param buildStatusParam message type (success, warning, error)
+ * @param jobName job name param, if empty env.JOB_NAME will be used
+ * @param buildNumber build number param, if empty env.BUILD_NUM will be used
+ * @param buildUrl build url param, if empty env.BUILD_URL will be used
+ * @param channel param, default is '#mk-ci'
+ * @param credentialsId slack hook url credential, default is 'SLACK_WEBHOOK_URL'
+ */
+def jobResultNotification(String buildStatusParam, String channel = "#mk-ci",
+                          String jobName=null,
+                          Number buildNumber=null, String buildUrl=null,
+                          String credentialsId="SLACK_WEBHOOK_URL") {
+    def jobNameParam = jobName != null && jobName != "" ? jobName : env.JOB_NAME
+    def buildUrlParam = buildUrl != null && buildUrl != "" ? buildUrl : env.BUILD_URL
+    def buildNumberParam = buildNumber != null && buildNumber != "" ? buildNumber : env.BUILD_NUMBER
+
+
+    def common = new com.mirantis.mk.Common()
+    cred = common.getCredentialsById(credentialsId)
+    hook_url_parsed = cred.getSecret().toString()
+    if (buildStatusParam.toLowerCase().equals("success")) {
+        colorCode = "#00FF00"
+        colorName = "green"
+    } else if (buildStatusParam.toLowerCase().equals("unstable")) {
+        colorCode = "#FFFF00"
+        colorName = "yellow"
+    } else if (buildStatusParam.toLowerCase().equals("failure")) {
+        colorCode = "#FF0000"
+        colorName = "red"
+    }
+
+    queryString = 'payload={' +
+            "'text':'${buildStatusParam.toUpperCase()}: Job <${buildUrlParam}|${jobNameParam} [${buildNumberParam}]>', " +
+            "'color':'${colorCode}'," +
+            "'pretext': '', " +
+            '"icon_url": "https://cfr.slack-edge.com/ae7f/img/services/jenkins-ci_192.png",' +
+            "'channel': '${channel}', " +
+            '}'
+    sendPostRequest(hook_url_parsed, queryString)
+
+}
+
+/*
+node {
+    jobResultNotification(
+            "success",
+            "#test_reclass_notify",
+            "test-reclass-system",
+            44,
+            "https://ci.mcp.mirantis.net/",)
+}
+*/
diff --git a/src/com/mirantis/mk/Common.groovy b/src/com/mirantis/mk/Common.groovy
index 4097b3c..9a82fbe 100644
--- a/src/com/mirantis/mk/Common.groovy
+++ b/src/com/mirantis/mk/Common.groovy
@@ -513,3 +513,29 @@
     currentBuild.result = "FAILURE"
     throw new Exception("Failed after $times retries")
 }
+
+
+/**
+ * Wait for user input with timeout
+ *
+ * @param timeoutInSeconds Timeout
+ * @param options Options for input widget
+ */
+def waitForInputThenPass(timeoutInSeconds, options=[message: 'Ready to go?']) {
+  def userInput = true
+  try {
+    timeout(time: timeoutInSeconds, unit: 'SECONDS') {
+      userInput = input options
+    }
+  } catch(err) { // timeout reached or input false
+    def user = err.getCauses()[0].getUser()
+    if('SYSTEM' == user.toString()) { // SYSTEM means timeout.
+      println("Timeout, proceeding")
+    } else {
+      userInput = false
+      println("Aborted by: [${user}]")
+      throw err
+    }
+  }
+  return userInput
+}
diff --git a/src/com/mirantis/mk/Orchestrate.groovy b/src/com/mirantis/mk/Orchestrate.groovy
index b7c8b16..21c1101 100644
--- a/src/com/mirantis/mk/Orchestrate.groovy
+++ b/src/com/mirantis/mk/Orchestrate.groovy
@@ -613,6 +613,9 @@
         salt.enforceState(master, 'I@kubernetes:master and *01*', 'etcd.server.setup')
     }
 
+    // Run k8s master at *01* to simplify namespaces creation
+    salt.enforceStateWithExclude(master, 'I@kubernetes:master and *01*', "kubernetes.master", "kubernetes.master.setup")
+
     // Run k8s without master.setup
     salt.enforceStateWithExclude(master, 'I@kubernetes:master', "kubernetes", "kubernetes.master.setup")
 
@@ -668,14 +671,69 @@
 
 def installCicd(master) {
     def salt = new com.mirantis.mk.Salt()
-    salt.fullRefresh(master, 'I@jenkins:client or I@gerrit:client')
+    def common = new com.mirantis.mk.Common()
+    def gerrit_compound = 'I@gerrit:client and ci*'
+    def jenkins_compound = 'I@jenkins:client and ci*'
+
+    salt.fullRefresh(master, gerrit_compound)
+    salt.fullRefresh(master, jenkins_compound)
 
     if (salt.testTarget(master, 'I@aptly:publisher')) {
         salt.enforceState(master, 'I@aptly:publisher', 'aptly.publisher',true, null, false, -1, 2)
     }
 
     salt.enforceState(master, 'I@docker:swarm:role:master and I@jenkins:client', 'docker.client', true, true, null, false, -1, 2)
-    sleep(500)
+
+    // API timeout in minutes
+    def wait_timeout = 10
+
+    // Gerrit
+    def gerrit_master_url = salt.getPillar(master, gerrit_compound, '_param:gerrit_master_url')
+
+    if(!gerrit_master_url['return'].isEmpty()) {
+      gerrit_master_url = gerrit_master_url['return'][0].values()[0]
+    } else {
+      gerrit_master_url = ''
+    }
+
+    if (gerrit_master_url != '') {
+      common.infoMsg('Gerrit master url "' + gerrit_master_url + '" retrieved at _param:gerrit_master_url')
+    } else {
+
+      common.infoMsg('Gerrit master url could not be retrieved at _param:gerrit_master_url. Falling back to gerrit pillar')
+
+      def gerrit_host
+      def gerrit_http_port
+      def gerrit_http_scheme
+
+      def host_pillar = salt.getPillar(master, gerrit_compound, 'gerrit:client:server:host')
+      gerrit_host = salt.getReturnValues(host_pillar)
+
+      def port_pillar = salt.getPillar(master, gerrit_compound, 'gerrit:client:server:http_port')
+      gerrit_http_port = salt.getReturnValues(port_pillar)
+
+      def scheme_pillar = salt.getPillar(master, gerrit_compound, 'gerrit:client:server:protocol')
+      gerrit_http_scheme = salt.getReturnValues(scheme_pillar)
+
+      gerrit_master_url = gerrit_http_scheme + '://' + gerrit_host + ':' + gerrit_http_port
+
+    }
+
+    timeout(wait_timeout) {
+      common.infoMsg('Waiting for Gerrit to come up..')
+      def check_gerrit_cmd = 'while [ `curl -sI -m 3 -o /dev/null -w' + " '" + '%{http_code}' + "' " + gerrit_master_url + '/` -ne 200 ]; do sleep 0.5; done'
+      salt.cmdRun(master, gerrit_compound, 'timeout ' + (wait_timeout*60+3) + ' /bin/sh -c ' + '"' + check_gerrit_cmd + '"')
+    }
+
+    // Jenkins
+    def jenkins_master_url_pillar = salt.getPillar(master, jenkins_compound, '_param:jenkins_master_url')
+    jenkins_master_url = salt.getReturnValues(jenkins_master_url_pillar)
+
+    timeout(wait_timeout) {
+      common.infoMsg('Waiting for Jenkins to come up..')
+      def check_jenkins_cmd = 'while [ `curl -sI -m 3 -o /dev/null -w' + " '" + '%{http_code}' + "' " + jenkins_master_url + '/whoAmI/` -ne 200 ]; do sleep 0.5; done'
+      salt.cmdRun(master, jenkins_compound, 'timeout ' + (wait_timeout*60+3) + ' /bin/sh -c ' + '"' + check_jenkins_cmd + '"')
+    }
 
     if (salt.testTarget(master, 'I@aptly:server')) {
         salt.enforceState(master, 'I@aptly:server', 'aptly', true, true, null, false, -1, 2)
@@ -730,6 +788,11 @@
         }
     }
 
+    // Install MongoDB for Alerta
+    if (salt.testTarget(master, 'I@mongodb:server')) {
+        salt.enforceState(master, 'I@mongodb:server', 'mongodb')
+    }
+
     // Launch containers
     salt.enforceState(master, 'I@docker:swarm:role:master and I@prometheus:server', 'docker.client')
     salt.runSaltProcessStep(master, 'I@docker:swarm and I@prometheus:server', 'dockerng.ps')
@@ -1064,15 +1127,17 @@
     // connect Ceph to the env
     if (salt.testTarget(master, 'I@ceph:common and I@glance:server')) {
         salt.enforceState(master, 'I@ceph:common and I@glance:server', ['ceph.common', 'ceph.setup.keyring', 'glance'])
-        salt.runSaltProcessStep(master, 'I@ceph:common and I@glance:server', 'service.restart', ['glance-api', 'glance-glare', 'glance-registry'])
+        salt.runSaltProcessStep(master, 'I@ceph:common and I@glance:server', 'service.restart', ['glance-api'])
     }
     if (salt.testTarget(master, 'I@ceph:common and I@cinder:controller')) {
         salt.enforceState(master, 'I@ceph:common and I@cinder:controller', ['ceph.common', 'ceph.setup.keyring', 'cinder'])
+        salt.runSaltProcessStep(master, 'I@ceph:common and I@cinder:controller', 'service.restart', ['cinder-volume'])
     }
     if (salt.testTarget(master, 'I@ceph:common and I@nova:compute')) {
         salt.enforceState(master, 'I@ceph:common and I@nova:compute', ['ceph.common', 'ceph.setup.keyring'])
         salt.runSaltProcessStep(master, 'I@ceph:common and I@nova:compute', 'saltutil.sync_grains')
         salt.enforceState(master, 'I@ceph:common and I@nova:compute', ['nova'])
+        salt.runSaltProcessStep(master, 'I@ceph:common and I@nova:compute', 'service.restart', ['nova-compute'])
     }
 }