Merge "Separating salt ca minions from highstate run to protect from race condition"
diff --git a/src/com/mirantis/mk/Aptly.groovy b/src/com/mirantis/mk/Aptly.groovy
index 4771084..94f085f 100644
--- a/src/com/mirantis/mk/Aptly.groovy
+++ b/src/com/mirantis/mk/Aptly.groovy
@@ -144,7 +144,8 @@
if (findFiles(glob: "${prefix}*")) {
archiveArtifacts artifacts: "${prefix}*"
} else {
- throw new Exception("Aptly dump publishes for a prefix ${prefix}* failed. No dump files found!")
+ def common = new com.mirantis.mk.Common()
+ common.warningMsg("Aptly dump publishes for a prefix ${prefix}* failed. No dump files found! This can be OK in case of your creating new publish")
}
}
@@ -205,6 +206,32 @@
}
/**
+ * Returns list of the packages from specified Aptly repo by REST API
+ *
+ * @param server URI of the server insluding port and protocol
+ * @param repo Local repo name
+ **/
+def listPackagesFromRepoByAPI(server, repo){
+ http = new com.mirantis.mk.Http()
+ def packageList = http.restGet(server, "/api/repos/${repo}/packages")
+ return packageList
+}
+
+/**
+ * Deletes packages from specified Aptly repo by REST API
+ *
+ * @param server URI of the server insluding port and protocol
+ * @param repo Local repo name
+ * @param packageRefs Package list specified by packageRefs
+ **/
+def deletePackagesFromRepoByAPI(server, repo, packageRefs){
+ http = new com.mirantis.mk.Http()
+ def data = [:]
+ data['PackageRefs'] = packageRefs
+ http.restDelete(server, "/api/repos/${repo}/packages", data)
+}
+
+/**
* Returns list of the packages matched to pattern and
* belonged to particular snapshot by REST API
*
@@ -247,7 +274,7 @@
data['PackageRefs'] = packageRefs
http.restPost(server, '/api/snapshots', data)
} else {
- http.restPost(server + "/api/repos/${repo}/snapshots", data)
+ http.restPost(server, "/api/repos/${repo}/snapshots", data)
}
}
diff --git a/src/com/mirantis/mk/Debian.groovy b/src/com/mirantis/mk/Debian.groovy
index ff05ce3..7b778e8 100644
--- a/src/com/mirantis/mk/Debian.groovy
+++ b/src/com/mirantis/mk/Debian.groovy
@@ -38,6 +38,7 @@
export DEBUG="${debug}" &&
export LD_LIBRARY_PATH=\${LD_LIBRARY_PATH:+"\$LD_LIBRARY_PATH:"}/usr/lib/libeatmydata &&
export LD_PRELOAD=\${LD_PRELOAD:+"\$LD_PRELOAD "}libeatmydata.so &&
+ export DEB_BUILD_OPTIONS=nocheck &&
[[ -z "${extraRepoUrl}" && "${extraRepoUrl}" != "null" ]] || echo "${extraRepoUrl}" | tr ";" "\\\\n" >/etc/apt/sources.list.d/extra.list &&
[[ -z "${extraRepoKeyUrl}" && "${extraRepoKeyUrl}" != "null" ]] || (
which curl || (apt-get update && apt-get install -y curl) &&
diff --git a/src/com/mirantis/mk/Http.groovy b/src/com/mirantis/mk/Http.groovy
index e14a523..987a998 100644
--- a/src/com/mirantis/mk/Http.groovy
+++ b/src/com/mirantis/mk/Http.groovy
@@ -220,3 +220,138 @@
System.getProperties().remove("http.nonProxyHosts")
System.getProperties().remove("https.nonProxyHosts")
}
+
+/**
+ * Make HTTP request to the specified URL
+ *
+ * @param url URL to do HTTP request to
+ * @param method HTTP method to execute (`GET` by default)
+ * @param credsId Jenkins credentials ID to use for authorization
+ * @param pushData Data to send
+ * @param pushType MIME-type of pushData
+ * @param params Custom HTTP-headers
+ *
+ * @return Array containing return code and text
+ *
+ * @exception org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException
+ */
+def methodCall(String url, String method = 'GET', String credsId = '',
+ String pushData = '', String pushType = '', Map params = [:]) {
+
+ // Connection object
+ def httpReq = new URI(url).normalize().toURL().openConnection()
+ httpReq.setRequestMethod(method)
+
+ // Timeouts
+ httpReq.setConnectTimeout(10*1000) // milliseconds
+ httpReq.setReadTimeout(600*1000) // milliseconds
+
+ // Add authentication data
+ if (credsId) {
+ String authHeader = ''
+ def cred = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
+ com.cloudbees.plugins.credentials.impl.BaseStandardCredentials.class,
+ jenkins.model.Jenkins.instance).findAll {it.id == credsId}[0]
+ if (cred.class == com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl.class) {
+ authHeader = 'Basic ' + "${cred.getUsername()}:${cred.getPassword()}".getBytes('UTF-8').encodeBase64().toString()
+ } else if (cred.class == org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl.class) {
+ authHeader = 'Bearer ' + cred.getSecret()
+ //params << ['X-JFrog-Art-Api': cred.getSecret()]
+ }
+ params << ['Authorization': authHeader]
+ }
+
+ // Add custom headers if any
+ for (param in params) {
+ httpReq.setRequestProperty(param.key, param.value.toString())
+ }
+
+ // Do request
+ try {
+ if (pushData) {
+ httpReq.setRequestProperty('Content-Type', pushType ?: 'application/x-www-form-urlencoded')
+ if (method == 'GET') { // override incorrect method if pushData is passed
+ httpReq.setRequestMethod('POST')
+ }
+ httpReq.setDoOutput(true)
+ httpReq.getOutputStream().write(pushData.getBytes('UTF-8'))
+ } else {
+ httpReq.connect()
+ }
+ } catch (org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException e) {
+ throw e // show sandbox errors
+ } catch (Exception e) {
+ echo "Cauhgt '${e.class.name}' error: ${e.getMessage()}"
+ return [ -1, e.getMessage() ]
+ }
+
+ // Handle return data
+ int respCode = httpReq.getResponseCode()
+ String respText = ''
+ if (respCode >= 400) {
+ respText = httpReq.getErrorStream().getText() ?: httpReq.getResponseMessage()
+ } else {
+ respText = httpReq.getInputStream().getText()
+ }
+
+ // Return result as a tuple of response code and text
+ return [respCode, respText]
+}
+
+/**
+ * Make HTTP GET request to the specified URL
+ *
+ * @param url URL to do HTTP request to
+ * @param credsId Jenkins credentials ID to use for authorization
+ * @param params Custom HTTP-headers
+ *
+ * @return Array containing return code and text
+ */
+def doGet(String url, String credsId = '', Map params = [:]) {
+ return methodCall(url, 'GET', credsId, null, null, params)
+}
+
+/**
+ * Make HTTP POST request to the specified URL
+ *
+ * @param url URL to do HTTP request to
+ * @param credsId Jenkins credentials ID to use for authorization
+ * @param pushData Data to send
+ * @param pushType MIME-type of pushData
+ * @param params Custom HTTP-headers
+ *
+ * @return Array containing return code and text
+ */
+def doPost(String url, String credsId = '',
+ String pushData = '', String pushType = '', Map params = [:]) {
+ return methodCall(url, 'POST', credsId, pushData, pushType, params)
+}
+
+/**
+ * Make HTTP PUT request to the specified URL
+ *
+ * @param url URL to do HTTP request to
+ * @param credsId Jenkins credentials ID to use for authorization
+ * @param pushData Data to send
+ * @param pushType MIME-type of pushData
+ * @param params Custom HTTP-headers
+ *
+ * @return Array containing return code and text
+ */
+def doPut(String url, String credsId = '',
+ String pushData = '', String pushType = '', Map params = [:]) {
+ return methodCall(url, 'PUT', credsId, pushData, pushType, params)
+}
+
+/**
+ * Make HTTP DELETE request to the specified URL
+ *
+ * @param url URL to do HTTP request to
+ * @param credsId Jenkins credentials ID to use for authorization
+ * @param params Custom HTTP-headers
+ *
+ * @return Array containing return code and text
+ */
+def doDelete(String url, String credsId = '', Map params = [:]) {
+ return methodCall(url, 'DELETE', credsId, null, null, params)
+}
diff --git a/src/com/mirantis/mk/Orchestrate.groovy b/src/com/mirantis/mk/Orchestrate.groovy
index 62a75c7..eb3352b 100644
--- a/src/com/mirantis/mk/Orchestrate.groovy
+++ b/src/com/mirantis/mk/Orchestrate.groovy
@@ -16,6 +16,7 @@
def installFoundationInfra(master, staticMgmtNet=false) {
def salt = new com.mirantis.mk.Salt()
+ def common = new com.mirantis.mk.Common()
// NOTE(vsaienko) Apply reclass first, it may update cluster model
// apply linux and salt.master salt.minion states afterwards to make sure
@@ -29,30 +30,50 @@
salt.enforceState(master, 'I@salt:master', ['salt.minion'], true, false, null, false, 60, 2)
salt.enforceState(master, 'I@salt:master', ['salt.minion'])
salt.fullRefresh(master, "*")
-
+ salt.enforceState(master, '*', ['linux.network.proxy'], true, false, null, false, 60, 2)
+ try {
+ salt.enforceState(master, '*', ['salt.minion.base'], true, false, null, false, 60, 2)
+ sleep(5)
+ } catch (Throwable e) {
+ common.warningMsg('Salt state salt.minion.base is not present in the Salt-formula yet.')
+ }
salt.enforceState(master, '*', ['linux.system'])
if (staticMgmtNet) {
salt.runSaltProcessStep(master, '*', 'cmd.shell', ["salt-call state.sls linux.network; salt-call service.restart salt-minion"], null, true, 60)
}
+ salt.enforceState(master, 'I@linux:network:interface', ['linux.network.interface'])
salt.enforceState(master, 'I@linux:system', ['linux', 'openssh', 'ntp', 'rsyslog'])
salt.enforceState(master, '*', ['salt.minion'], true, false, null, false, 60, 2)
sleep(5)
salt.runSaltProcessStep(master, '*', 'mine.update', [], null, true)
salt.enforceState(master, '*', ['linux.network.host'])
+
+ // Install and configure iptables
+ if (salt.testTarget(master, 'I@iptables:service')) {
+ salt.enforceState(master, 'I@iptables:service', 'iptables')
+ }
}
def installFoundationInfraOnTarget(master, target, staticMgmtNet=false) {
def salt = new com.mirantis.mk.Salt()
+ def common = new com.mirantis.mk.Common()
salt.enforceState(master, 'I@salt:master', ['reclass'], true, false, null, false, 120, 2)
salt.fullRefresh(master, target)
+ salt.enforceState(master, target, ['linux.network.proxy'], true, false, null, false, 60, 2)
+ try {
+ salt.enforceState(master, target, ['salt.minion.base'], true, false, null, false, 60, 2)
+ sleep(5)
+ } catch (Throwable e) {
+ common.warningMsg('Salt state salt.minion.base is not present in the Salt-formula yet.')
+ }
salt.enforceState(master, target, ['linux.system'])
if (staticMgmtNet) {
salt.runSaltProcessStep(master, target, 'cmd.shell', ["salt-call state.sls linux.network; salt-call service.restart salt-minion"], null, true, 60)
}
salt.enforceState(master, target, ['salt.minion'], true, false, null, false, 60, 2)
salt.enforceState(master, target, ['salt.minion'])
-
+ salt.enforceState(master, target, ['linux.network.interface'])
salt.enforceState(master, target, ['linux', 'openssh', 'ntp', 'rsyslog'])
sleep(5)
salt.runSaltProcessStep(master, target, 'mine.update', [], null, true)
@@ -265,11 +286,6 @@
}
}
- // Create neutron resources
- if (salt.testTarget(master, 'I@neutron:client')) {
- salt.enforceState(master, 'I@neutron:client', 'neutron.client')
- }
-
// Install heat service
if (salt.testTarget(master, 'I@heat:server')) {
// run on first node first
@@ -356,16 +372,8 @@
// Install ceilometer server
if (salt.testTarget(master, 'I@ceilometer:server')) {
- common.retry(3,5){
- salt.enforceState(master, 'I@ceilometer:server and *01*', 'ceilometer')
- salt.runSaltProcessStep(master, 'I@ceilometer:server and *01*', 'service.restart', ['apache2'])
- sleep(30)
- }
- common.retry(3,5){
- salt.enforceState(master, 'I@ceilometer:server and not *01*', 'ceilometer')
- salt.runSaltProcessStep(master, 'I@ceilometer:server and not *01*', 'service.restart', ['apache2'])
- sleep(30)
- }
+ salt.enforceState(master, 'I@ceilometer:server and *01*', 'ceilometer')
+ salt.enforceState(master, 'I@ceilometer:server', 'ceilometer')
}
// Install aodh server
@@ -417,6 +425,20 @@
def installOpenstackNetwork(master, physical = "false") {
def salt = new com.mirantis.mk.Salt()
+ //run full neutron state on neutron.gateway - this will install
+ //neutron agents in addition to neutron server. Once neutron agents
+ //are up neutron resources can be created without hitting the situation when neutron resources are created
+ //prior to neutron agents which results in creating ports in non-usable state
+ if (salt.testTarget(master, 'I@neutron:gateway')) {
+ salt.enforceState(master, 'I@neutron:gateway', 'neutron')
+ }
+
+ // Create neutron resources - this step was moved here to ensure that
+ //neutron resources are created after neutron agens are up. In this case neutron ports will be in
+ //usable state. More information: https://bugs.launchpad.net/neutron/+bug/1399249
+ if (salt.testTarget(master, 'I@neutron:client')) {
+ salt.enforceState(master, 'I@neutron:client', 'neutron.client')
+ }
salt.enforceHighstate(master, 'I@neutron:gateway')
diff --git a/src/com/mirantis/mk/Salt.groovy b/src/com/mirantis/mk/Salt.groovy
index 5973184..a1781cf 100644
--- a/src/com/mirantis/mk/Salt.groovy
+++ b/src/com/mirantis/mk/Salt.groovy
@@ -575,12 +575,22 @@
* @param name Name of the VM (for ex. ctl01)
* @return Salt minion ID of KVM node hosting 'name' VM
*/
-def getNodeProvider(saltId, name) {
- def kvm = getKvmMinionId(saltId)
- return getReturnValues(getPillar(saltId, "${kvm}", "salt:control:cluster:internal:node:${name}:provider"))
+def getNodeProvider(saltId, nodeName) {
+ def salt = new com.mirantis.mk.Salt()
+ def common = new com.mirantis.mk.Common()
+ def kvms = salt.getMinions(saltId, 'I@salt:control')
+ for (kvm in kvms) {
+ try {
+ vms = salt.getReturnValues(salt.runSaltProcessStep(saltId, kvm, 'virt.list_domains', [], null, true))
+ if (vms.toString().contains(nodeName)) {
+ return kvm
+ }
+ } catch (Exception er) {
+ common.infoMsg("${nodeName} not present on ${kvm}")
+ }
+ }
}
-
/**
* Test if there are any minions to target
* @param saltId Salt Connection object or pepperEnv (the command will be sent using the selected method)
diff --git a/src/com/mirantis/mk/Virsh.groovy b/src/com/mirantis/mk/Virsh.groovy
index a866fa3..d972d91 100644
--- a/src/com/mirantis/mk/Virsh.groovy
+++ b/src/com/mirantis/mk/Virsh.groovy
@@ -54,17 +54,17 @@
try {
salt.cmdRun(master, "${nodeProvider}*", "virsh snapshot-delete ${target}.${domain} --metadata ${snapshotName}")
} catch (Exception e) {
- common.warningMsg('Snapshot ${snapshotName} for ${target}.${domain} does not exist or failed to be removed')
+ common.warningMsg("Snapshot ${snapshotName} for ${target}.${domain} does not exist or failed to be removed")
}
try {
salt.runSaltProcessStep(master, "${nodeProvider}*", 'file.remove', ["${path}/${target}.${domain}.${snapshotName}.qcow2"], null, true)
} catch (Exception e) {
- common.warningMsg('Snapshot ${snapshotName} qcow2 file for ${target}.${domain} does not exist or failed to be removed')
+ common.warningMsg("Snapshot ${snapshotName} qcow2 file for ${target}.${domain} does not exist or failed to be removed")
}
try {
salt.runSaltProcessStep(master, "${nodeProvider}*", 'file.remove', ["${path}/${target}.${domain}.xml"], null, true)
} catch (Exception e) {
- common.warningMsg('Dumpxml file for ${target}.${domain} does not exist or failed to be removed')
+ common.warningMsg("Dumpxml file for ${target}.${domain} does not exist or failed to be removed")
}
}
@@ -87,7 +87,7 @@
liveSnapshotAbsent(master, nodeProvider, target, snapshotName, path)
salt.runSaltProcessStep(master, "${nodeProvider}*", 'virt.start', ["${target}.${domain}"], null, true)
} catch (Exception er) {
- common.infoMsg('No rollback for ${target}.${domain} was executed. Dumpxml file not present.')
+ common.infoMsg("No rollback for ${target}.${domain} was executed. Dumpxml file not present.")
}
}
@@ -109,17 +109,17 @@
try {
salt.cmdRun(master, "${nodeProvider}*", "virsh snapshot-delete ${target}.${domain} --metadata ${snapshotName}")
} catch (Exception e) {
- common.warningMsg('Snapshot ${snapshotName} for ${target}.${domain} does not exist or failed to be removed')
+ common.warningMsg("Snapshot ${snapshotName} for ${target}.${domain} does not exist or failed to be removed")
}
try {
salt.runSaltProcessStep(master, "${nodeProvider}*", 'file.remove', ["${path}/${target}.${domain}.${snapshotName}.qcow2"], null, true)
} catch (Exception e) {
- common.warningMsg('Snapshot ${snapshotName} qcow2 file for ${target}.${domain} does not exist or failed to be removed')
+ common.warningMsg("Snapshot ${snapshotName} qcow2 file for ${target}.${domain} does not exist or failed to be removed")
}
try {
salt.runSaltProcessStep(master, "${nodeProvider}*", 'file.remove', ["${path}/${target}.${domain}.xml"], null, true)
} catch (Exception e) {
- common.warningMsg('Dumpxml file for ${target}.${domain} does not exist or failed to be removed')
+ common.warningMsg("Dumpxml file for ${target}.${domain} does not exist or failed to be removed")
}
} catch (Exception e) {
common.errorMsg("The live snapshoted VM ${target}.${domain} failed to be merged, trying to fix it")