Merge "Added lvm support for removing partitions"
diff --git a/src/com/mirantis/mcp/Validate.groovy b/src/com/mirantis/mcp/Validate.groovy
index b1755b2..9c7a5b6 100644
--- a/src/com/mirantis/mcp/Validate.groovy
+++ b/src/com/mirantis/mcp/Validate.groovy
@@ -60,7 +60,7 @@
                       "/tmp/": "/tmp/",
                       "/etc/hosts": "/etc/hosts"]
     params.mounts = default_mounts + params.mounts
-    if ( salt.cmdRun(params.master, params.target, "docker ps -f name=${params.name} -q", false, null, false)['return'][0].values()[0] ) {
+    if ( salt.cmdRun(params.master, params.target, "docker ps -f name=^${params.name}\$ -q", false, null, false)['return'][0].values()[0] ) {
         salt.cmdRun(params.master, params.target, "docker rm -f ${params.name}")
     }
     if (params.env_var.size() > 0) {
@@ -926,7 +926,7 @@
  */
 def runCleanup(master, target, name='cvp') {
     def salt = new com.mirantis.mk.Salt()
-    if ( salt.cmdRun(master, target, "docker ps -f name=${name} -q", false, null, false)['return'][0].values()[0] ) {
+    if ( salt.cmdRun(master, target, "docker ps -f name=^${name}\$ -q", false, null, false)['return'][0].values()[0] ) {
         salt.cmdRun(master, target, "docker rm -f ${name}")
     }
 }
diff --git a/src/com/mirantis/mk/Debian.groovy b/src/com/mirantis/mk/Debian.groovy
index 6bb1f50..5ca197b 100644
--- a/src/com/mirantis/mk/Debian.groovy
+++ b/src/com/mirantis/mk/Debian.groovy
@@ -258,15 +258,15 @@
 * @param env    Salt Connection object or env  Salt command map
 * @param target Salt target to upgrade packages on.
 */
-def osUpgrade(env, target){
-  def common = new com.mirantis.mk.Common()
-  def salt = new com.mirantis.mk.Salt()
+def osUpgrade(env, target, batch=null) {
+    def common = new com.mirantis.mk.Common()
+    def salt = new com.mirantis.mk.Salt()
 
-  common.infoMsg("Running upgrade on ${target}")
+    common.infoMsg("Running upgrade on ${target}")
 
-  salt.runSaltProcessStep(env, target, 'pkg.refresh_db', [], null, true)
-  def cmd = 'export DEBIAN_FRONTEND=noninteractive; apt-get -y -q --allow-downgrades -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" upgrade'
-  salt.cmdRun(env, target, cmd)
+    salt.runSaltProcessStep(env, target, 'pkg.refresh_db', [], batch, true)
+    def cmd = 'export DEBIAN_FRONTEND=noninteractive; apt-get -y -q --allow-downgrades -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" upgrade'
+    salt.cmdRun(env, target, cmd, true, batch)
 }
 
 /**
@@ -275,14 +275,14 @@
 * @param env    Salt Connection object or env  Salt command map
 * @param target Salt target to upgrade packages on.
 */
-def osDistUpgrade(env, target){
-  def salt = new com.mirantis.mk.Salt()
-  def common = new com.mirantis.mk.Common()
+def osDistUpgrade(env, target, batch=null) {
+    def salt = new com.mirantis.mk.Salt()
+    def common = new com.mirantis.mk.Common()
 
-  common.infoMsg("Running dist-upgrade on ${target}")
-  salt.runSaltProcessStep(env, target, 'pkg.refresh_db', [], null, true)
-  def cmd = 'export DEBIAN_FRONTEND=noninteractive; apt-get -y -q --allow-downgrades -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" dist-upgrade'
-  salt.cmdRun(env, target, cmd)
+    common.infoMsg("Running dist-upgrade on ${target}")
+    salt.runSaltProcessStep(env, target, 'pkg.refresh_db', [], batch, true)
+    def cmd = 'export DEBIAN_FRONTEND=noninteractive; apt-get -y -q --allow-downgrades -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\" dist-upgrade'
+    salt.cmdRun(env, target, cmd, true, batch)
 }
 
 /**
@@ -293,18 +293,18 @@
 * @param timeout   Sleep timeout when doing retries.
 * @param attempts  Number of attemps to wait for.
 */
-def osReboot(env, target, timeout=30, attempts=10){
-  def salt = new com.mirantis.mk.Salt()
-  def common = new com.mirantis.mk.Common()
+def osReboot(env, target, timeout=30, attempts=10) {
+    def salt = new com.mirantis.mk.Salt()
+    def common = new com.mirantis.mk.Common()
 
-  salt.runSaltProcessStep(env, target, 'cmd.run', ["touch /tmp/rebooting"])
-  salt.runSaltProcessStep(env, target, 'system.reboot', [], null, true, 5)
+    salt.runSaltProcessStep(env, target, 'cmd.run', ["touch /tmp/rebooting"])
+    salt.runSaltProcessStep(env, target, 'system.reboot', [], null, true, 5)
 
-  common.retry(timeout, attempts){
-    if (salt.runSaltProcessStep(env, target, 'file.file_exists', ['/tmp/rebooting'], null, true, 5)['return'][0].values()[0].toBoolean()){
-      error("The system is still rebooting...")
+    common.retry(timeout, attempts) {
+        if (salt.runSaltProcessStep(env, target, 'file.file_exists', ['/tmp/rebooting'], null, true, 5)['return'][0].values()[0].toBoolean()) {
+            error("The system is still rebooting...")
+        }
     }
-  }
 }
 
 /**
@@ -317,23 +317,23 @@
 * @param timeout   Sleep timeout when doing retries.
 * @param attempts  Number of attemps to wait for.
 */
-def osUpgradeNode(env, target, mode, postponeReboot=false, timeout=30, attempts=10){
+def osUpgradeNode(env, target, mode, postponeReboot=false, timeout=30, attempts=10, batch=null) {
     def common = new com.mirantis.mk.Common()
     def salt = new com.mirantis.mk.Salt()
 
     def rebootRequired = false
-    if (mode == 'dist-upgrade'){
-      osDistUpgrade(env, target)
-    } else if (mode == 'upgrade'){
-      osUpgrade(env, target)
+    if (mode == 'dist-upgrade') {
+        osDistUpgrade(env, target, batch)
+    } else if (mode == 'upgrade') {
+        osUpgrade(env, target, batch)
     }
-    rebootRequired = salt.runSaltProcessStep(env, target, 'file.file_exists', ['/var/run/reboot-required'], null, true, 5)['return'][0].values()[0].toBoolean()
+    rebootRequired = salt.runSaltProcessStep(env, target, 'file.file_exists', ['/var/run/reboot-required'], batch, true, 5)['return'][0].values()[0].toBoolean()
     if (rebootRequired) {
-      if (!postponeReboot){
-        common.infoMsg("Reboot is required after upgrade on ${target} Rebooting...")
-        osReboot(env, target, timeout, attempts)
-      } else {
-        common.infoMsg("Postponing reboot on node ${target}")
-      }
+        if (!postponeReboot) {
+            common.infoMsg("Reboot is required after upgrade on ${target} Rebooting...")
+            osReboot(env, target, timeout, attempts)
+        } else {
+            common.infoMsg("Postponing reboot on node ${target}")
+        }
     }
 }
diff --git a/src/com/mirantis/mk/Openstack.groovy b/src/com/mirantis/mk/Openstack.groovy
index 0e02f2a..a57256a 100644
--- a/src/com/mirantis/mk/Openstack.groovy
+++ b/src/com/mirantis/mk/Openstack.groovy
@@ -41,7 +41,7 @@
         'oslo.i18n>=2.3.0,<2.4.0',
         'oslo.serialization>=1.8.0,<1.9.0',
         'oslo.utils>=1.4.0,<1.5.0',
-        'docutils'
+        'docutils==0.16'
     ]
 
     def openstack_latest_packages = [
@@ -58,10 +58,12 @@
         'warlock<=1.3.1;python_version=="2.7"',
         'warlock>1.3.1;python_version=="3.4"',
         'warlock>1.3.1;python_version=="3.5"',
-        'python-openstackclient',
-        'python-octaviaclient',
-        'python-heatclient',
-        'docutils'
+        // NOTE: pin client packages to current latest to prevent
+        // downloading packages which are not support Python 2.7
+        'python-openstackclient==4.0.0',
+        'python-octaviaclient==1.11.0',
+        'python-heatclient==1.18.0',
+        'docutils==0.16'
     ]
 
     if (version == 'kilo') {
diff --git a/src/com/mirantis/mk/Orchestrate.groovy b/src/com/mirantis/mk/Orchestrate.groovy
index 13fee98..429e8a2 100644
--- a/src/com/mirantis/mk/Orchestrate.groovy
+++ b/src/com/mirantis/mk/Orchestrate.groovy
@@ -194,10 +194,8 @@
 
     // If galera is not enabled check if we need to install mysql:server
     } else {
-
-    salt.enforceStateWithTest([saltId: master, target: "I@mysql:server ${extra_tgt}", state: 'mysql.server'])
-    salt.enforceStateWithTest([saltId: master, target: "I@mysql:client ${extra_tgt}", state: 'mysql.client'])
-
+        salt.enforceStateWithTest([saltId: master, target: "I@mysql:server ${extra_tgt}", state: 'mysql.server'])
+        salt.enforceStateWithTest([saltId: master, target: "I@mysql:client ${extra_tgt}", state: 'mysql.client'])
     }
     installBackup(master, 'mysql', extra_tgt)
 
@@ -811,6 +809,8 @@
 
     salt.enforceStateWithTest([saltId: master, target: "I@openldap:client ${extra_tgt}", state: 'openldap', retries: 2])
 
+    salt.enforceStateWithTest([saltId: master, target: "( I@keycloak:server or I@keycloak:proxy ) ${extra_tgt}", state: 'keycloak', retries: 2])
+
     salt.enforceStateWithTest([saltId: master, target: "I@python:environment ${extra_tgt}", state: 'python'])
 
     withEnv(['ASK_ON_ERROR=false']){
diff --git a/src/com/mirantis/mk/Python.groovy b/src/com/mirantis/mk/Python.groovy
index 9d07d47..9c9bf88 100644
--- a/src/com/mirantis/mk/Python.groovy
+++ b/src/com/mirantis/mk/Python.groovy
@@ -159,7 +159,7 @@
  */
 def setupDocutilsVirtualenv(path, python="python2") {
     requirements = [
-        'docutils',
+        'docutils==0.16',
     ]
     setupVirtualenv(path, python, requirements)
 }
@@ -453,8 +453,8 @@
  */
 def setupJinjaVirtualenv(path) {
     requirements = [
-        'jinja2-cli',
-        'pyyaml',
+        'jinja2-cli==0.7.0',
+        'pyyaml==5.3',
     ]
     setupVirtualenv(path, 'python2', requirements)
 }
diff --git a/src/com/mirantis/mk/ReleaseWorkflow.groovy b/src/com/mirantis/mk/ReleaseWorkflow.groovy
index c27fa8a..032f8dc 100644
--- a/src/com/mirantis/mk/ReleaseWorkflow.groovy
+++ b/src/com/mirantis/mk/ReleaseWorkflow.groovy
@@ -109,6 +109,7 @@
     String gitCredentialsId     = params.get('metadataCredentialsId', 'mcp-ci-gerrit')
     String metadataRepoUrl      = params.get('metadataGitRepoUrl', "ssh://${gitCredentialsId}@gerrit.mcp.mirantis.net:29418/mcp/artifact-metadata")
     String metadataGerritBranch = params.get('metadataGitRepoBranch', 'master')
+    String toxDockerImage       = params.get('toxDockerImage', 'docker-prod-virtual.docker.mirantis.net/mirantis/external/tox')
     String repoDir              = params.get('repoDir', 'artifact-metadata')
     String comment              = params.get('comment', '')
     String crTopic              = params.get('crTopic', '')
@@ -161,11 +162,12 @@
         def keyArr = key.split(';')
         def valueArr = value.split(';')
         if (keyArr.size() == valueArr.size()) {
-            for (i in 0..keyArr.size()-1) {
-                precreateKeyReleaseMetadataFile(keyArr[i], metadataDir, dirdepth)
-
-                cmdText = "python '${repoDir}/utils/app.py' --path '${metadataDir}' update --key '${keyArr[i]}' --value '${valueArr[i]}'"
-                python.runVirtualenvCommand(venvDir, cmdText)
+            docker.image(toxDockerImage).inside {
+                for (i in 0..keyArr.size()-1) {
+                    // TODO remove/refactor it as app.py will have this functionality
+                    precreateKeyReleaseMetadataFile(keyArr[i], metadataDir, dirdepth)
+                    sh "cd ${repoDir} && tox -qq -e metadata -- update --key '${keyArr[i]}' --value '${valueArr[i]}'"
+                }
             }
         }
 
diff --git a/src/com/mirantis/mk/Workflow.groovy b/src/com/mirantis/mk/Workflow.groovy
index a3502bd..681d204 100644
--- a/src/com/mirantis/mk/Workflow.groovy
+++ b/src/com/mirantis/mk/Workflow.groovy
@@ -233,8 +233,20 @@
         runSteps(scenario['finally'], global_variables, failed_jobs)
 
         if (failed_jobs) {
+            statuses = []
+            failed_jobs.each {
+                statuses += it.value
+                }
+            if (statuses.contains('FAILURE')) {
+                currentBuild.result = 'FAILURE'
+            }
+            else if (statuses.contains('UNSTABLE')) {
+                currentBuild.result = 'UNSTABLE'
+            }
+            else {
+                currentBuild.result = 'FAILURE'
+            }
             println "Failed jobs: ${failed_jobs}"
-            currentBuild.result = "FAILED"
         }
-    } // try
+    } // finally
 }