Merge "[CVP,master] Fix cvp-perf job for xrally 0.11.2"
diff --git a/src/com/mirantis/mk/Artifactory.groovy b/src/com/mirantis/mk/Artifactory.groovy
index d1eb218..73ed80f 100644
--- a/src/com/mirantis/mk/Artifactory.groovy
+++ b/src/com/mirantis/mk/Artifactory.groovy
@@ -394,3 +394,66 @@
)
}
}
+
+/**
+ * Get Helm repo for Artifactory
+ *
+ * @param art Artifactory connection object
+ * @param repoName Chart repository name
+ */
+def getArtifactoryProjectByName(art, repoName){
+ return restGet(art, "/repositories/${repoName}")
+}
+
+/**
+ * Get repo by packageType for Artifactory
+ *
+ * @param art Artifactory connection object
+ * @param packageType Repository package type
+ */
+def getArtifactoryProjectByPackageType(art, repoName){
+ return restGet(art, "/repositories?${packageType}")
+}
+
+/**
+ * Create Helm repo for Artifactory
+ *
+ * @param art Artifactory connection object
+ * @param repoName Chart repository name
+ * @param data Transmitted data
+ */
+def createArtifactoryChartRepo(art, repoName){
+ return restPut(art, "/repositories/${repoName}", '{"rclass": "local","handleSnapshots": false,"packageType": "helm"}')
+}
+
+/**
+ * Delete Helm repo for Artifactory
+ *
+ * @param art Artifactory connection object
+ * @param repoName Chart repository name
+ */
+def deleteArtifactoryChartRepo(art, repoName){
+ return restDelete(art, "/repositories/${repoName}")
+}
+
+/**
+ * Create Helm repo for Artifactory
+ *
+ * @param art Artifactory connection object
+ * @param repoName Repository Chart name
+ * @param chartName Chart name
+ */
+def publishArtifactoryHelmChart(art, repoName, chartName){
+ return restPut(art, "/repositories/${repoName}", "${chartName}")
+}
+
+/**
+ * Create Helm repo for Artifactory
+ *
+ * @param art Artifactory connection object
+ * @param repoName Repository Chart name
+ * @param chartName Chart name
+ */
+def deleteArtifactoryHelmChart(art, repoName, chartName){
+ return restDelete(art, "/repositories/${repoName}", "${chartName}")
+}
diff --git a/src/com/mirantis/mk/Common.groovy b/src/com/mirantis/mk/Common.groovy
index b5760da..c0059bb 100644
--- a/src/com/mirantis/mk/Common.groovy
+++ b/src/com/mirantis/mk/Common.groovy
@@ -460,10 +460,14 @@
}
else if (overrides.length == 1) {
overrides[0]?.each { k, v ->
- if (v in Map && onto[k] in Map){
- mergeMaps((Map) onto[k], (Map) v)
- } else if (v in List) {
- onto[k] += v
+ if (k in onto.keySet()) {
+ if (v in Map && onto[k] in Map){
+ mergeMaps((Map) onto[k], (Map) v)
+ } else if (v in List) {
+ onto[k] += v
+ } else {
+ onto[k] = v
+ }
} else {
onto[k] = v
}
@@ -1006,3 +1010,16 @@
Date parseDate(String date, String format) {
return Date.parse(format, date)
}
+
+/**
+ * Generate Random Hash string
+ * @param n Hash length
+ * @param pool Pool to use for hash generation
+*/
+def generateRandomHashString(int n, ArrayList pool = []) {
+ if (!pool) {
+ pool = ['a'..'z','A'..'Z',0..9,'_','+','='].flatten()
+ }
+ Random rand = new Random(System.currentTimeMillis())
+ return (1..n).collect { pool[rand.nextInt(pool.size())] }.join()
+}
diff --git a/src/com/mirantis/mk/Galera.groovy b/src/com/mirantis/mk/Galera.groovy
index 733914c..382a72f 100644
--- a/src/com/mirantis/mk/Galera.groovy
+++ b/src/com/mirantis/mk/Galera.groovy
@@ -105,8 +105,8 @@
}
for (int i = 0; i < iostatRes.size(); i++) {
def diskKey = iostatRes.keySet()[i]
- if (!(iostatRes[diskKey].toString().isBigDecimal() && (iostatRes[diskKey].toBigDecimal() < 0.5 ))) {
- common.errorMsg("Disk ${diskKey} has to high i/o utilization. Maximum value is 0.5 and current value is ${iostatRes[diskKey]}.")
+ if (!(iostatRes[diskKey].toString().isBigDecimal() && (iostatRes[diskKey].toBigDecimal() < 50 ))) {
+ common.errorMsg("Disk ${diskKey} has to high i/o utilization. Maximum value is 50 and current value is ${iostatRes[diskKey]}.")
return 141
}
}
@@ -347,23 +347,38 @@
common.warningMsg('File is not present')
}
+ // make sure that gcom parameter is empty
salt.cmdRun(env, lastNodeTarget, "sed -i '/gcomm/c\\wsrep_cluster_address=\"gcomm://\"' /etc/mysql/my.cnf")
+ // run restore of DB
if (runRestoreDb) {
restoreGaleraDb(env, lastNodeTarget)
}
- salt.enforceState(env, lastNodeTarget, 'galera')
+ // start mysql service on the last node
+ salt.runSaltProcessStep(env, lastNodeTarget, 'service.start', ['mysql'])
- // wait until mysql service on galera master is up
+ // wait until mysql service on the last node is up
try {
salt.commandStatus(env, lastNodeTarget, 'service mysql status', 'running')
} catch (Exception er) {
input message: "Database is not running please fix it first and only then click on PROCEED."
}
+ // start mysql services on the rest of the nodes
salt.runSaltProcessStep(env, "I@galera:master and not ${lastNodeTarget}", 'service.start', ['mysql'])
salt.runSaltProcessStep(env, "I@galera:slave and not ${lastNodeTarget}", 'service.start', ['mysql'])
+
+ // wait until mysql service on the rest of the nodes is up
+ try {
+ salt.commandStatus(env, "( I@galera:master or I@galera:slave ) and not ${lastNodeTarget}", 'service mysql status', 'running')
+ } catch (Exception er) {
+ input message: "Database is not running please fix it first and only then click on PROCEED."
+ }
+
+ // apply any changes in configuration
+ salt.enforceState(env, lastNodeTarget, 'galera')
+
}
/**
diff --git a/src/com/mirantis/mk/GoogleCloudStorage.groovy b/src/com/mirantis/mk/GoogleCloudStorage.groovy
new file mode 100644
index 0000000..5bf5ad7
--- /dev/null
+++ b/src/com/mirantis/mk/GoogleCloudStorage.groovy
@@ -0,0 +1,126 @@
+package com.mirantis.mk
+
+/**
+ Work with Google Cloud Storage
+**/
+
+/** Exists or not gcloud binary file
+ *
+ * @param gcloudBinDir Path to check
+*/
+def checkGcloudBinary(String gcloudBinDir) {
+ def status = sh(script: "${gcloudBinDir}/google-cloud-sdk/bin/gcloud info > /dev/null", returnStatus: true)
+ return "${status}" == "0"
+}
+
+/** Download gcloud archive with binarties
+ *
+ * @param gcloudBinDir Path to save binaries
+ * @param url Specific URL to use to download
+*/
+def downloadGcloudUtil(String gcloudBinDir, String url="") {
+ if (!url) {
+ url="https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-253.0.0-linux-x86_64.tar.gz"
+ }
+ dir(gcloudBinDir) {
+ def archiveName='google-cloud-sdk'
+ sh """
+ wget -O ${archiveName}.tar.gz ${url}
+ tar -xf ${archiveName}.tar.gz
+ """
+ }
+}
+
+/** Auth gcloud util with provided creds
+ *
+ * @param gcloudBinDir Path to save binaries
+ * @param creds Creds name to use, saved as secret file
+ * @param projectName Project name to use
+*/
+def authGcloud(String gcloudBinDir, String creds, String projectName) {
+ ws {
+ withCredentials([
+ file(credentialsId: creds,
+ variable: 'key_file')
+ ]) {
+ sh "${gcloudBinDir}/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file $key_file --project ${projectName}"
+ }
+ }
+}
+
+/** Revoke gcloud auth account
+ *
+ * @param gcloudBinDir Path to save binaries
+*/
+def revokeGcloud(String gcloudBinDir) {
+ sh "${gcloudBinDir}/google-cloud-sdk/bin/gcloud auth revoke"
+}
+
+/** Copy file to Google Storage Bucket
+ *
+ * @param gcloudBinDir Path to save binaries
+ * @param src Source file
+ * @param dst Destination path in storage
+ * @param entireTree Copy entire directory tree
+*/
+def cpFile(String gcloudBinDir, String src, String dst, Boolean entireTree=false) {
+ def fileURL = ''
+ if (entireTree) {
+ sh "${gcloudBinDir}/google-cloud-sdk/bin/gsutil cp -r ${src} ${dst}"
+ return dst
+ } else {
+ def fileBaseName = sh(script:"basename ${src}", returnStdout: true).trim()
+ sh "${gcloudBinDir}/google-cloud-sdk/bin/gsutil cp ${src} ${dst}/${fileBaseName}"
+ return "${dst}/${fileBaseName}"
+ }
+}
+
+/** Set ACL on files in bucket
+ *
+ * @param gcloudBinDir Path to save binaries
+ * @param path Path to file in bucket
+ * @param acls ACLs to be set for file
+*/
+def setAcl(String gcloudBinDir, String path, ArrayList acls) {
+ for(String acl in acls) {
+ sh "${gcloudBinDir}/google-cloud-sdk/bin/gsutil acl ch -u ${acl} ${path}"
+ }
+}
+
+/** Upload files to Google Cloud Storage Bucket
+ *
+ * @param config LinkedHashMap with next parameters:
+ * @param gcloudBinDir Path to save binaries
+ * @param creds Creds name to use, saved as secret file
+ * @param projectName Project name to use
+ * @param sources List of file to upload
+ * @param dest Destination path in Google Storage, in format: gs://<path>
+ * @param acls ACLs for uploaded files
+ * @param entireTree Copy entire directory to bucket
+ *
+ * Returns URLs list of uploaded files
+*/
+def uploadArtifactToGoogleStorageBucket(Map config) {
+ def gcloudDir = config.get('gcloudDir', '/tmp/gcloud')
+ def creds = config.get('creds')
+ def project = config.get('project')
+ def acls = config.get('acls', ['AllUsers:R'])
+ def sources = config.get('sources')
+ def dest = config.get('dest')
+ def entireTree = config.get('entireTree', false)
+ def fileURLs = []
+ if (!checkGcloudBinary(gcloudDir)) {
+ downloadGcloudUtil(gcloudDir)
+ }
+ try {
+ authGcloud(gcloudDir, creds, project)
+ for(String src in sources) {
+ def fileURL = cpFile(gcloudDir, src, dest, entireTree)
+ setAcl(gcloudDir, fileURL, acls)
+ fileURLs << fileURL
+ }
+ } finally {
+ revokeGcloud(gcloudDir)
+ }
+ return fileURLs
+}
\ No newline at end of file
diff --git a/src/com/mirantis/mk/Salt.groovy b/src/com/mirantis/mk/Salt.groovy
index 21bb4c9..daff9fc 100644
--- a/src/com/mirantis/mk/Salt.groovy
+++ b/src/com/mirantis/mk/Salt.groovy
@@ -458,32 +458,45 @@
* You can call this function when salt-master already contains salt keys of the target_nodes
* @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method)
* @param target Should always be salt-master
- * @param target_nodes unique identification of a minion or group of salt minions
+ * @param targetNodes unique identification of a minion or group of salt minions
* @param batch salt batch parameter integer or string with percents (optional, default null - disable batch)
- * @param wait timeout for the salt command if minions do not return (default 10)
+ * @param cmdTimeout timeout for the salt command if minions do not return (default 10)
* @param maxRetries finite number of iterations to check status of a command (default 200)
* @return output of salt command
*/
-def minionsReachable(saltId, target, target_nodes, batch=null, wait = 10, maxRetries = 200) {
+
+def minionsReachable(saltId, target, targetNodes, batch=null, cmdTimeout = 10, maxRetries = 200) {
def common = new com.mirantis.mk.Common()
- def cmd = "salt -t${wait} -C '${target_nodes}' test.ping"
- common.infoMsg("Checking if all ${target_nodes} minions are reachable")
- def count = 0
- while(count < maxRetries) {
+ def cmd = "salt -t${cmdTimeout} -C '${targetNodes}' test.ping"
+ common.infoMsg("Checking if all ${targetNodes} minions are reachable")
+ def retriesCount = 0
+ while(retriesCount < maxRetries) {
Calendar timeout = Calendar.getInstance();
- timeout.add(Calendar.SECOND, wait);
- def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.shell', batch, [cmd], null, wait)
+ timeout.add(Calendar.SECOND, cmdTimeout);
+ def out = runSaltCommand(saltId, 'local', ['expression': target, 'type': 'compound'], 'cmd.shell', batch, [cmd], null, cmdTimeout)
Calendar current = Calendar.getInstance();
if (current.getTime().before(timeout.getTime())) {
- printSaltCommandResult(out)
- return out
+ common.infoMsg("Successful response received from all targeted nodes.")
+ printSaltCommandResult(out)
+ return out
}
- common.infoMsg("Not all of the targeted '${target_nodes}' minions returned yet. Waiting ...")
- count++
+ def outYaml = readYaml text: getReturnValues(out)
+ def successfulNodes = []
+ def failedNodes = []
+ for (node in outYaml.keySet()) {
+ if (outYaml[node] == true || outYaml[node].toString().toLowerCase() == 'true') {
+ successfulNodes.add(node)
+ } else {
+ failedNodes.add(node)
+ }
+ }
+ common.infoMsg("Not all of the targeted minions returned yet. Successful response from ${successfulNodes}. Still waiting for ${failedNodes}.")
+ retriesCount++
sleep(time: 500, unit: 'MILLISECONDS')
}
}
+
/**
* You can call this function when need to check that all minions are available, free and ready for command execution
* @param config LinkedHashMap config parameter, which contains next:
@@ -518,6 +531,42 @@
}
/**
+ * Restart and wait for salt-minions on target nodes.
+ * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method)
+ * @param target unique identification of a minion or group of salt minions
+ * @param wait timeout for the salt command if minions do not return (default 5)
+ * @param maxRetries finite number of iterations to check status of a command (default 10)
+ * @return output of salt command
+ */
+def restartSaltMinion(saltId, target, wait = 5, maxRetries = 10) {
+ def common = new com.mirantis.mk.Common()
+ common.infoMsg("Restarting salt-minion on ${target} and waiting for they are reachable.")
+ runSaltProcessStep(saltId, target, 'cmd.shell', ['salt-call service.restart salt-minion'], null, true, 60)
+ checkTargetMinionsReady(['saltId': saltId, 'target_reachable': target, timeout: wait, retries: maxRetries])
+ common.infoMsg("All ${target} minions are alive...")
+}
+
+/**
+ * Upgrade package and restart salt minion.
+ * @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method)
+ * @param target unique identification of a minion or group of salt minions
+ * @param the name of pkg_name to upgrade
+ * @param wait timeout for the salt command if minions do not return (default 5)
+ * @param maxRetries finite number of iterations to check status of a command (default 10)
+ * @return output of salt command
+ */
+def upgradePackageAndRestartSaltMinion(saltId, target, pkg_name, wait = 5, maxRetries = 10) {
+ def common = new com.mirantis.mk.Common()
+ def latest_version = getReturnValues(runSaltProcessStep(saltId, target, 'pkg.latest_version', [pkg_name, 'show_installed=True'])).split('\n')[0]
+ def current_version = getReturnValues(runSaltProcessStep(saltId, target, 'pkg.version', [pkg_name])).split('\n')[0]
+ if (current_version && latest_version != current_version) {
+ common.infoMsg("Upgrading current ${pkg_name}: ${current_version} to ${latest_version}")
+ runSaltProcessStep(saltId, target, 'pkg.install', [pkg_name], 'only_upgrade=True')
+ restartSaltMinion(saltId, target, wait, maxRetries)
+ }
+}
+
+/**
* Run command on salt minion (salt cmd.run wrapper)
* @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method)
* @param target Get pillar target