@Grab('org.codehaus.groovy:groovy-xmlrpc:0.8') import groovy.net.xmlrpc.* import groovy.util.XmlSlurper import groovy.util.slurpersupport.GPathResult import groovy.util.slurpersupport.NodeChild import groovy.xml.XmlUtil import org.jenkinsci.plugins.workflow.steps.FlowInterruptedException class Client { String oneAuth String url Script script // Voir http://groovy-lang.org/processing-xml.html pour plus d'informations // sur le traitement du XML en Groovy // OpenNebula RPC API @NonCPS XMLRPCServerProxy createProxy() { return new XMLRPCServerProxy(url, true) } // Voir https://docs.opennebula.org/5.6/integration/system_interfaces/api.html#one-templatepool-info @NonCPS GPathResult templatepoolInfo(int filter = -2, int start = -1, int end = -1) { def proxy = createProxy() def results = proxy."one.templatepool.info"(oneAuth, filter, start, end) assert results[0] : results[1] return new XmlSlurper().parseText(results[1]) } // Voir https://docs.opennebula.org/5.6/integration/system_interfaces/api.html#one-vm-allocate @NonCPS Integer vmAllocate(String template, Boolean hold = false) { def proxy = createProxy() def results = proxy."one.vm.allocate"(oneAuth, template, hold) assert results[0] : results[1] return results[1] } // Voir https://docs.opennebula.org/5.6/integration/system_interfaces/api.html#one-vm-action @NonCPS Integer vmAction(String actionName, Integer objectId) { def proxy = createProxy() def results = proxy."one.vm.action"(oneAuth, actionName, objectId) assert results[0] : results[1] return results[1] } // Voir https://docs.opennebula.org/5.6/integration/system_interfaces/api.html#one-vm-info @NonCPS GPathResult vmInfo(Integer objectId) { def proxy = createProxy() def results = proxy."one.vm.info"(oneAuth, objectId) assert results[0] : results[1] return new XmlSlurper().parseText(results[1]) } // Utilitaires de haut niveau pour les pipelines Jenkins // Retrouve un template de VM via son nom @NonCPS NodeChild findVMTemplate(String name) { def templates = templatepoolInfo() def result = null templates.'*'.each { if (it.NAME.text().trim() == name) { result = it } } return result } // Démarre une VM, exécute les étapes passées en paramètres // puis supprime la VM @NonCPS void withNewVM(String templateName, Boolean terminateOnExit = true, Closure body) { def tmpl = findVMTemplate(templateName) def id = vmAllocate(XmlUtil.serialize(tmpl.TEMPLATE)) tmpl = null script.println("Démarrage de la VM '${id}'...") def info = vmInfo(id) script.println("En attente du démarrage de la VM...") while(info.STATE.toInteger() != 3 || info.LCM_STATE.toInteger() != 3) { sleep(5000) info = vmInfo(id) } script.println("VM démarrée.") def ip = info.TEMPLATE.NIC.IP.text() info = null runThenTerminate(id, ip, terminateOnExit, body) } void runThenTerminate(Integer id, String ip, Boolean terminateOnExit, Closure body) { try { body(ip) } finally { if (terminateOnExit) { script.println("Suppression de la VM '${id}'...") vmAction("terminate", id) } } } } @NonCPS def init(String url, String username, String password, Closure body) { def oneAuth = "${username}:${password}" def client = new Client(oneAuth: oneAuth, url: url, script: this) proxy = null body.call(client) } def initWithCredentials(String urlCredentialsId, String userCredentialsId, Closure body) { withCredentials([ string(credentialsId: urlCredentialsId, variable: 'NEBULA_URL'), usernamePassword(credentialsId: userCredentialsId, usernameVariable: 'NEBULA_USERNAME', passwordVariable: 'NEBULA_PASSWORD') ]) { init(env.NEBULA_URL, env.NEBULA_USERNAME, env.NEBULA_PASSWORD, body) } } def runScriptInNewVM(Map args) { def script = args.get("script", "") runInNewVM(args) { shell -> shell(script) } } def runInNewVM(Map args, Closure body) { def urlCredentialsId = args.get('urlCredentialsId', 'opennebula-dev-url') def userCredentialsId = args.get('userCredentialsId', 'kipp-credentials') def sshCredentialsId = args.get('sshCredentialsId', 'kipp-opennebula-dev-ssh-keypair') def vmTemplate = args.get('vmTemplate', '') def terminateOnExit = args.get('terminateOnExit', true) def shell = args.get("shell", "/bin/sh") def connectionTimeout = args.get('connectionTimeout', 10) // On récupère les identifiants de connexion SSH pour la VM withCredentials([ sshUserPrivateKey(credentialsId: sshCredentialsId, keyFileVariable: 'VM_SSH_KEY') ]) { initWithCredentials(urlCredentialsId, userCredentialsId) { client -> client.withNewVM(vmTemplate, terminateOnExit) { host -> def sshArgs = "-i '${VM_SSH_KEY}' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" // On attend que la connexion SSH soit disponible println "En attente de l'accès SSH sur la machine ${host}..." timeout(connectionTimeout) { while(true) { def status = sh script: "nc -zv ${host} 22", returnStatus: true if (status == 0) { break; } sleep(5) } } def remoteShell = { script -> // On créait un script temporaire à exécuter sur la machine distante def now = System.currentTimeMillis() def tempScriptFile = "script_${env.BUILD_ID}_${now}.sh" writeFile(file: tempScriptFile, text: """ #!${shell} ${script.stripIndent()} """) // On transfère le script sur la machine distante et on l'exécute sh """ scp ${sshArgs} '${tempScriptFile}' 'root@${host}:/tmp/${tempScriptFile}' ssh ${sshArgs} root@${host} 'chmod +x /tmp/${tempScriptFile}; /tmp/${tempScriptFile}' """ } body(remoteShell) } } } }