From 3173bf5db39bf974f665aa812f2ebb7bedf2e48d Mon Sep 17 00:00:00 2001 From: William Petit Date: Mon, 18 Feb 2019 16:05:33 +0100 Subject: [PATCH] OpenNebula XML-RPC API client --- vars/nebula.groovy | 181 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 vars/nebula.groovy diff --git a/vars/nebula.groovy b/vars/nebula.groovy new file mode 100644 index 0000000..f85a131 --- /dev/null +++ b/vars/nebula.groovy @@ -0,0 +1,181 @@ +@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 + +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 runInNewVM(Map args) { + + 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 script = args.get('script', '') + + // 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..." + waitUntil { + try { + sh "ssh -q ${sshArgs} -o ConnectTimeout=1 root@${host} exit" + return true + } catch (e) { + return false + } + } + + // 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}' + """ + } + } + } +}