[Ansible] Ansible Playbook support demo
A demo of deploying Tomcat using Ansible Playbook integrated with Murano Change-Id: I7c94c43577c16c8e7c677a0424acb70f5cec4583
This commit is contained in:
parent
d7be94d354
commit
506ce8648f
77
tomcat/pom.xml
Normal file
77
tomcat/pom.xml
Normal file
@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.jenkins-ci.plugins</groupId>
|
||||
<artifactId>plugin</artifactId>
|
||||
<version>2.11</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<groupId>com.mirantis.plugins</groupId>
|
||||
<artifactId>murano-tomcat</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>hpi</packaging>
|
||||
|
||||
<properties>
|
||||
<jenkins.version>1.625.3</jenkins.version>
|
||||
<java.level>7</java.level>
|
||||
<jenkins-test-harness.version>2.13</jenkins-test-harness.version>
|
||||
</properties>
|
||||
|
||||
<name>Murano Tomcat Plugin</name>
|
||||
<description>Murano plugin with Tomcat to deploy Jenkins builds</description>
|
||||
<url>https://wiki.jenkins-ci.org/display/JENKINS/TODO+Plugin</url>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT License</name>
|
||||
<url>http://opensource.org/licenses/MIT</url>
|
||||
</license>
|
||||
</licenses>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>repo.jenkins-ci.org</id>
|
||||
<url>https://repo.jenkins-ci.org/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>repo.jenkins-ci.org</id>
|
||||
<url>https://repo.jenkins-ci.org/public/</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.pacesys</groupId>
|
||||
<artifactId>openstack4j-core</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.pacesys.openstack4j.connectors</groupId>
|
||||
<artifactId>openstack4j-httpclient</artifactId>
|
||||
<version>3.0.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.json-simple</groupId>
|
||||
<artifactId>json-simple</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jenkins-ci.plugins</groupId>
|
||||
<artifactId>credentials</artifactId>
|
||||
<version>1.16.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jenkins-ci.plugins</groupId>
|
||||
<artifactId>ssh-credentials</artifactId>
|
||||
<version>1.10</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,30 @@
|
||||
package com.mirantis.plugins.murano.tomcat;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Configuration Helper to understand the Mirantis UI definition
|
||||
*/
|
||||
public class ConfigurationSection {
|
||||
Map<String, List<Map>> conf;
|
||||
|
||||
public ConfigurationSection() {
|
||||
conf = new HashMap();
|
||||
}
|
||||
|
||||
public void addSection(String sectionName, List fields) {
|
||||
conf.put(sectionName, fields);
|
||||
}
|
||||
|
||||
public List<Map> getSection(String sectionName){
|
||||
return conf.get(sectionName);
|
||||
}
|
||||
|
||||
public Set getSections(){
|
||||
return conf.keySet();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,696 @@
|
||||
package com.mirantis.plugins.murano.tomcat;
|
||||
|
||||
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUser;
|
||||
import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
|
||||
import com.cloudbees.plugins.credentials.CredentialsProvider;
|
||||
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
|
||||
import com.mirantis.plugins.murano.tomcat.client.OpenstackClient;
|
||||
import hudson.Extension;
|
||||
import hudson.FilePath;
|
||||
import hudson.Launcher;
|
||||
import hudson.Proc;
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.BuildListener;
|
||||
import hudson.tasks.BuildStepDescriptor;
|
||||
import hudson.tasks.BuildStepMonitor;
|
||||
import hudson.tasks.Notifier;
|
||||
import hudson.tasks.Publisher;
|
||||
import hudson.util.FormValidation;
|
||||
import hudson.util.ListBoxModel;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.apache.commons.io.output.TeeOutputStream;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.kohsuke.stapler.DataBoundConstructor;
|
||||
import org.kohsuke.stapler.QueryParameter;
|
||||
import org.openstack4j.connectors.httpclient.HttpCommand;
|
||||
import org.openstack4j.core.transport.HttpMethod;
|
||||
import org.openstack4j.core.transport.HttpRequest;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.io.*;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by raja on 20/09/16.
|
||||
*/
|
||||
public class MuranoTomcatBuilder extends Notifier {
|
||||
private static String privateKeyIdentifier = "muranodemo";
|
||||
private String serverUrl;
|
||||
private String username;
|
||||
private String password;
|
||||
private String tenantName;
|
||||
private String flavor;
|
||||
private String keypairs;
|
||||
private String images;
|
||||
private String clusterName;
|
||||
private String playbook;
|
||||
private OpenstackClient client;
|
||||
|
||||
@DataBoundConstructor
|
||||
public MuranoTomcatBuilder(String serverUrl,
|
||||
String username,
|
||||
String password,
|
||||
String tenantName,
|
||||
String clusterName,
|
||||
String flavor,
|
||||
String keypairs,
|
||||
String images,
|
||||
String playbook) {
|
||||
this.serverUrl = serverUrl;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.tenantName = tenantName;
|
||||
this.flavor = flavor;
|
||||
this.keypairs = keypairs;
|
||||
this.images = images;
|
||||
this.clusterName = clusterName;
|
||||
this.playbook = playbook;
|
||||
}
|
||||
|
||||
public String getServerUrl() {
|
||||
return serverUrl;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getTenantName() {
|
||||
return tenantName;
|
||||
}
|
||||
|
||||
public String getFlavor() {
|
||||
return flavor;
|
||||
}
|
||||
|
||||
public String getImages() {
|
||||
return this.images;
|
||||
}
|
||||
|
||||
public String getKeypairs() {
|
||||
return this.keypairs;
|
||||
}
|
||||
|
||||
public String getPlaybook() {
|
||||
return this.playbook;
|
||||
}
|
||||
|
||||
public String getClusterName() {
|
||||
return this.clusterName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
|
||||
BuildListener listener) throws IOException, InterruptedException {
|
||||
|
||||
client = new OpenstackClient(serverUrl,
|
||||
username,
|
||||
password,
|
||||
tenantName);
|
||||
if (!client.authenticate()) {
|
||||
listener.getLogger().println("Cannot connect to Openstack server. Pls check configuration");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the images to cache them
|
||||
client.getImages();
|
||||
|
||||
FilePath workspace = build.getWorkspace();
|
||||
FilePath privKeyFile = null;
|
||||
StandardUsernameCredentials c = CredentialsProvider.findCredentialById(privateKeyIdentifier, StandardUsernameCredentials.class, build);
|
||||
if (c instanceof SSHUserPrivateKey) {
|
||||
SSHUserPrivateKey privateKey = (SSHUserPrivateKey) c;
|
||||
List<String> keys = privateKey.getPrivateKeys();
|
||||
for (String s: keys) {
|
||||
privKeyFile = workspace.createTextTempFile("ssh", ".key", s, false);
|
||||
privKeyFile.chmod(0400);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the UI Definition for Tomcat. This is hardcoded based on the Tomcat Definition in Murano
|
||||
// TODO: See if this can be changed dynamically
|
||||
String token = client.getOSClient().getAccess().getToken().getId();
|
||||
HttpRequest request = HttpRequest.builder().method(HttpMethod.GET)
|
||||
.endpoint(serverUrl + ":9292")
|
||||
.path("/v3/artifacts/murano/v1/725cb06b-c69b-46c0-8768-e2f5cdb14e30/ui_definition/download") // Tomcat id
|
||||
.header("X-Auth-Token", token)
|
||||
.build();
|
||||
HttpCommand command = HttpCommand.create(request);
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
response = command.execute();
|
||||
} catch(Exception ex) {
|
||||
ex.printStackTrace();;
|
||||
return false;
|
||||
}
|
||||
if (response == null) {
|
||||
listener.getLogger().println("Error getting response for UI definition");
|
||||
return false;
|
||||
}
|
||||
|
||||
StringBuffer jsonString = new StringBuffer();
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(
|
||||
response.getEntity().getContent()));
|
||||
|
||||
//Print the raw output of murano api from the server
|
||||
String output;
|
||||
|
||||
while ((output = br.readLine()) != null) {
|
||||
jsonString.append(output + "\n");
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
listener.getLogger().println("Error getting output for UI definition");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Fill in the templates according to the jenkins job
|
||||
// TODO: Could be done only if the image needs to be created. Otehrwise its a waste of resouces.
|
||||
Yaml yaml = new Yaml();
|
||||
String appTemplate = "";
|
||||
ConfigurationSection sections = new ConfigurationSection();
|
||||
Map templates = new HashMap();
|
||||
Map<String, Map<String, Object>> values = (Map<String, Map<String, Object>>) yaml.load(jsonString.toString());
|
||||
for (String key : values.keySet()) {
|
||||
System.out.println(key);
|
||||
if (key.equals("Forms")) {
|
||||
List<Map> formElements = (ArrayList<Map>)values.get(key);
|
||||
|
||||
for (Map type: formElements) {
|
||||
String typeName = (String)type.keySet().toArray()[0]; // gets the form types like : appConfiguration, instanceConfiguration etc
|
||||
|
||||
Map<String, Map<String, String>> fields = (HashMap<String, Map<String,String>>) type.get(typeName);
|
||||
List<Map> fieldArr = (ArrayList<Map>)fields.get("fields");
|
||||
|
||||
sections.addSection(typeName, fieldArr);
|
||||
|
||||
}
|
||||
} else if (key.equals("Application")) {
|
||||
System.out.println("Application section : ");
|
||||
Map<String, Object> applicationTemplate = (Map<String, Object>)values.get(key);
|
||||
appTemplate = "{" + getApplicationJsonTemplate(applicationTemplate) + "}";
|
||||
|
||||
} else if (key.equals("Templates")) {
|
||||
System.out.println("Templates");
|
||||
Map<String, Object> templateMap = values.get(key);
|
||||
for(String templateName: templateMap.keySet()) {
|
||||
Object templateValue = templateMap.get(templateName);
|
||||
if (templateValue instanceof Map) {
|
||||
Map<String, Object> internalTemplateHash = (Map<String, Object>) templateValue;
|
||||
//String templateExpanded = "{" + getApplicationJsonTemplate(internalTemplateHash) + "}";
|
||||
templates.put(templateName, internalTemplateHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String envId = checkIfDeploymentExists(token, this.clusterName);
|
||||
if (envId == null) {
|
||||
listener.getLogger().println("Creating new enviroment");
|
||||
// No Environment, create the cluster
|
||||
String filledTemplate = fillTemplate(appTemplate, build.getId());
|
||||
|
||||
// Create Env
|
||||
envId = this.createEnvironment(token, this.clusterName);
|
||||
|
||||
// Create Session
|
||||
String sessionId = this.createEnvironmentSession(token, envId);
|
||||
|
||||
// Add App to Environment
|
||||
addApplicationToEnvironment(token, envId, sessionId, filledTemplate);
|
||||
|
||||
// Deploy
|
||||
deployEnvironment(token, envId, sessionId);
|
||||
listener.getLogger().println("Waiting for Deployment to finish...");
|
||||
if (!isDeploymentSuccess(token, envId)) {
|
||||
listener.getLogger().println("Taking longer to deploy, please check the murano console");
|
||||
return false;
|
||||
}
|
||||
listener.getLogger().println("Deployed Tomcat");
|
||||
}
|
||||
|
||||
|
||||
// Get the Floating IP to deploy the image
|
||||
String floatingIp = null;
|
||||
try {
|
||||
floatingIp = getFloatingIp(token, envId);
|
||||
} catch(Exception ex) {
|
||||
listener.getLogger().println("Error getting Floating ip");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call Ansible Playbook to execute
|
||||
return callAnsiblePlaybook(launcher, floatingIp, privKeyFile, workspace, listener.getLogger());
|
||||
|
||||
}
|
||||
|
||||
private List<String> generateCommandArgs(FilePath privateKeyPath,
|
||||
FilePath workspace,
|
||||
String floatingIp) {
|
||||
List<String> args = new ArrayList<String>();
|
||||
|
||||
// Build command line
|
||||
|
||||
// TODO: See if ansible-playbook is available somewhere else
|
||||
String ansiblePlaybook = "ansible-playbook";
|
||||
|
||||
args.add(ansiblePlaybook);
|
||||
|
||||
// --private-key
|
||||
if (privateKeyPath != null) {
|
||||
args.add("--private-key=" + privateKeyPath.toString());
|
||||
}
|
||||
|
||||
// Add inventory information
|
||||
args.add("-i");
|
||||
args.add(floatingIp + ",");
|
||||
|
||||
args.add("-e");
|
||||
args.add("localFile=" + new FilePath(workspace, "./target/petclinic.war"));
|
||||
args.add(new FilePath(workspace, this.playbook).toString());
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
private boolean callAnsiblePlaybook(Launcher launcher,
|
||||
String floatingIp,
|
||||
FilePath privKeyFile,
|
||||
FilePath workspace,
|
||||
PrintStream jenkinsLogger) throws IOException, InterruptedException {
|
||||
ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream();
|
||||
ByteArrayOutputStream stderrStream = new ByteArrayOutputStream();
|
||||
TeeOutputStream stdoutTee = new TeeOutputStream(stdoutStream, jenkinsLogger);
|
||||
TeeOutputStream stderrTee = new TeeOutputStream(stderrStream, jenkinsLogger);
|
||||
|
||||
// Build a launcher
|
||||
Launcher.ProcStarter settings = launcher.launch();
|
||||
settings.cmds(this.generateCommandArgs(privKeyFile, workspace, floatingIp));
|
||||
settings.stdout(stdoutTee);
|
||||
settings.stderr(stderrTee);
|
||||
// settings.pwd(containingFolder);
|
||||
// settings.envs(joinedEnvVars);
|
||||
|
||||
// Launch
|
||||
Proc proc = settings.start();
|
||||
|
||||
// Wait for exit and capture outputs
|
||||
int exitCode = proc.join();
|
||||
String stdout = stdoutStream.toString();
|
||||
String stderr = stderrStream.toString();
|
||||
|
||||
return exitCode == 0;
|
||||
}
|
||||
|
||||
private boolean isDeploymentSuccess(String token, String envId) {
|
||||
boolean status = false;
|
||||
for (int i=0; i<300; i++) {
|
||||
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
String payload = getResponseForJsonPost(serverUrl,
|
||||
8082,
|
||||
"/v1/environments/" + envId + "/deployments",
|
||||
HttpMethod.GET,
|
||||
token,
|
||||
null,
|
||||
null);
|
||||
JSONParser parser = new JSONParser();
|
||||
try {
|
||||
JSONObject deployments = (JSONObject) parser.parse(payload);
|
||||
JSONArray deploymentList = (JSONArray) deployments.get("deployments");
|
||||
JSONObject thisDeployment = (JSONObject) deploymentList.get(0);
|
||||
if ("success".equals((String) thisDeployment.get("state"))) {
|
||||
status = true;
|
||||
break;
|
||||
}
|
||||
} catch (ParseException pe) {
|
||||
System.out.println("position: " + pe.getPosition());
|
||||
System.out.println(pe);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private String getFloatingIp(String token, String envId) throws Exception {
|
||||
String payload = getResponseForJsonPost(serverUrl,
|
||||
8082,
|
||||
"/v1/environments/" + envId + "/services",
|
||||
HttpMethod.GET,
|
||||
token,
|
||||
null,
|
||||
null);
|
||||
String floatingIp = null;
|
||||
if (payload != null) {
|
||||
JSONParser parser = new JSONParser();
|
||||
JSONArray array = (JSONArray) parser.parse(payload);
|
||||
floatingIp = (String) ((JSONObject) ((JSONObject) array.get(0)).get("instance")).get("floatingIpAddress");
|
||||
}
|
||||
|
||||
return floatingIp;
|
||||
}
|
||||
/**
|
||||
* Return the Environment id if it exists
|
||||
*
|
||||
* @param token
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
private String checkIfDeploymentExists(String token, String name) {
|
||||
String payload = getResponseForJsonPost(serverUrl,
|
||||
8082,
|
||||
"/v1/environments",
|
||||
HttpMethod.GET,
|
||||
token,
|
||||
null,
|
||||
null);
|
||||
String envId = null;
|
||||
JSONParser parser = new JSONParser();
|
||||
try{
|
||||
Object obj = parser.parse(payload);
|
||||
JSONObject response = (JSONObject)obj;
|
||||
JSONArray environmentArray = (JSONArray) response.get("environments");
|
||||
for (Object env: environmentArray) {
|
||||
JSONObject thisEnv = (JSONObject) env;
|
||||
String envName = (String) thisEnv.get("name");
|
||||
if (envName.equals(name)) {
|
||||
envId = (String) thisEnv.get("id");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}catch(ParseException pe){
|
||||
System.out.println("position: " + pe.getPosition());
|
||||
System.out.println(pe);
|
||||
}
|
||||
return envId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy the environment given the environment id and Session Token
|
||||
*/
|
||||
private void deployEnvironment(String token, String envId, String sessionId) {
|
||||
String response = getResponseForJsonPost(serverUrl,
|
||||
8082,
|
||||
"/v1/environments/" + envId + "/sessions/" + sessionId + "/deploy",
|
||||
HttpMethod.POST,
|
||||
token,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the app(K8S) to the environment
|
||||
* @param token
|
||||
* @param envId
|
||||
* @param sessionId
|
||||
* @param jsonReq
|
||||
*/
|
||||
private void addApplicationToEnvironment(String token, String envId, String sessionId, String jsonReq) {
|
||||
String response = getResponseForJsonPost(serverUrl,
|
||||
8082,
|
||||
"/v1/environments/" + envId + "/services",
|
||||
HttpMethod.POST,
|
||||
token,
|
||||
jsonReq,
|
||||
sessionId);
|
||||
System.out.println("add application response : " + response);
|
||||
//return sessionId;
|
||||
}
|
||||
|
||||
private String createEnvironmentSession(String token, String envId) {
|
||||
String payload = getResponseForJsonPost(serverUrl,
|
||||
8082,
|
||||
"/v1/environments/" + envId + "/configure",
|
||||
HttpMethod.POST,
|
||||
token,
|
||||
null,
|
||||
null);
|
||||
|
||||
String sessionId = "";
|
||||
JSONParser parser = new JSONParser();
|
||||
try{
|
||||
Object obj = parser.parse(payload);
|
||||
JSONObject response = (JSONObject)obj;
|
||||
sessionId = (String)response.get("id");
|
||||
}catch(ParseException pe){
|
||||
System.out.println("position: " + pe.getPosition());
|
||||
System.out.println(pe);
|
||||
}
|
||||
System.out.println("Session Id : " + sessionId);
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
private String createEnvironment(String token, String envname) {
|
||||
String reqPayload = "{\"name\":\"" + envname + "\"}";
|
||||
String payload = getResponseForJsonPost(serverUrl, 8082, "/v1/environments", HttpMethod.POST, token, reqPayload, null);
|
||||
|
||||
String envId = "";
|
||||
JSONParser parser = new JSONParser();
|
||||
try{
|
||||
Object obj = parser.parse(payload);
|
||||
JSONObject response = (JSONObject)obj;
|
||||
envId = (String)response.get("id");
|
||||
}catch(ParseException pe){
|
||||
System.out.println("position: " + pe.getPosition());
|
||||
System.out.println(pe);
|
||||
}
|
||||
System.out.println("Envid : " + envId);
|
||||
return envId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main helper method to call the Murano API and return the response. Accepts both GET and POST.
|
||||
*
|
||||
* @param url Base URL to connect
|
||||
* @param port Which port is murano listening on
|
||||
* @param requestPath Path on Murano URL
|
||||
* @param method GET or POST
|
||||
* @param token Auth Token
|
||||
* @param jsonPayload Payload for the message
|
||||
* @param muranoSessionId Optional Session Id
|
||||
* @return Response from the Call
|
||||
*/
|
||||
private String getResponseForJsonPost(String url,
|
||||
int port,
|
||||
String requestPath,
|
||||
HttpMethod method,
|
||||
String token,
|
||||
String jsonPayload,
|
||||
String muranoSessionId) {
|
||||
HttpRequest request = HttpRequest.builder().method(method)
|
||||
.endpoint(url + ":" + port)
|
||||
.path(requestPath) // K8S cluster id
|
||||
.header("X-Auth-Token", token)
|
||||
.json(jsonPayload)
|
||||
.build();
|
||||
if (muranoSessionId != null) {
|
||||
request.getHeaders().put("X-Configuration-Session", muranoSessionId);
|
||||
}
|
||||
if (jsonPayload != null) {
|
||||
request = request.toBuilder().json(jsonPayload).build();
|
||||
}
|
||||
|
||||
HttpCommand command = HttpCommand.create(request);
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
response = command.execute();
|
||||
} catch(Exception ex) {
|
||||
ex.printStackTrace();;
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuffer jsonString = new StringBuffer();
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(
|
||||
response.getEntity().getContent()));
|
||||
|
||||
//Print the raw output of murano api from the server
|
||||
String output;
|
||||
|
||||
while ((output = br.readLine()) != null) {
|
||||
jsonString.append(output + "\n");
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return jsonString.toString();
|
||||
}
|
||||
|
||||
private String generateUUID() {
|
||||
SecureRandom ng = new SecureRandom();
|
||||
long MSB = 0x8000000000000000L;
|
||||
|
||||
String str = Long.toHexString(MSB | ng.nextLong()) + Long.toHexString(MSB | ng.nextLong());
|
||||
return str;
|
||||
|
||||
}
|
||||
|
||||
private String fillTemplate(String template, String buildId) {
|
||||
String appTemplate = template.replace("$.appConfiguration.name", "Tomcat_" + buildId);
|
||||
|
||||
appTemplate = appTemplate.replace("$.instanceConfiguration.osImage", client.getImageId(this.images));
|
||||
appTemplate = appTemplate.replace("$.instanceConfiguration.flavor", flavor);
|
||||
appTemplate = appTemplate.replace("$.appConfiguration.assignFloatingIP", "True");
|
||||
appTemplate = appTemplate.replace("$.instanceConfiguration.keyPair", this.keypairs);
|
||||
appTemplate = appTemplate.replace("$.instanceConfiguration.availabilityZone", "nova");
|
||||
|
||||
return appTemplate;
|
||||
}
|
||||
|
||||
private String getApplicationJsonTemplate(Map<String,Object> template) {
|
||||
String templateStr = "";
|
||||
boolean firstStr = true;
|
||||
for (String k : template.keySet()) {
|
||||
if (!firstStr) {
|
||||
templateStr += ",";
|
||||
}
|
||||
firstStr = false;
|
||||
Object v = template.get(k);
|
||||
if (v instanceof Map) {
|
||||
templateStr += "\"" + k + "\": {" + this.getApplicationJsonTemplate((Map<String, Object>)v) ;
|
||||
templateStr += "}";
|
||||
} else if (v instanceof String) {
|
||||
String uuid = generateUUID();
|
||||
if (k.equals("type")) {
|
||||
templateStr += "\"" + k + "\":\"" + v + "\"";
|
||||
templateStr += ",\"id\":\"" + uuid + "\"";
|
||||
}else if (k.equals("name") && (((String)v).contains("generateHostname"))) {
|
||||
templateStr += "\"name\":\"$.instanceConfiguration.unitNamingPattern-" + uuid + ".com\"";
|
||||
}else {
|
||||
templateStr += "\"" + k + "\":\"" + v + "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
return templateStr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BuildStepMonitor getRequiredMonitorService() {
|
||||
return BuildStepMonitor.BUILD;
|
||||
}
|
||||
|
||||
@Extension
|
||||
public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {
|
||||
|
||||
public DescriptorImpl() {
|
||||
load();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public FormValidation doCheckServerUrl(@QueryParameter String serverUrl) {
|
||||
if (serverUrl.length() == 0)
|
||||
return FormValidation.error("Please enter a server url");
|
||||
|
||||
if (serverUrl.indexOf("://") == -1)
|
||||
return FormValidation.error("Enter a url of the format http(s)://<server>");
|
||||
|
||||
return FormValidation.ok();
|
||||
}
|
||||
|
||||
public FormValidation doTestConnection(@QueryParameter("serverUrl") final String serverUrl,
|
||||
@QueryParameter("username") final String username,
|
||||
@QueryParameter("password") final String password,
|
||||
@QueryParameter("tenantName") final String tenantName)
|
||||
throws IOException, ServletException {
|
||||
try {
|
||||
OpenstackClient client = new OpenstackClient(serverUrl,
|
||||
username,
|
||||
password,
|
||||
tenantName);
|
||||
if (client.authenticate()) {
|
||||
return FormValidation.ok("Success");
|
||||
} else {
|
||||
return FormValidation.error("Unable to connect to server. Please check credentials");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return FormValidation.error("Error: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public ListBoxModel doFillFlavorItems(@QueryParameter("serverUrl") final String serverUrl,
|
||||
@QueryParameter("username") final String username,
|
||||
@QueryParameter("password") final String password,
|
||||
@QueryParameter("tenantName") final String tenantName) {
|
||||
ListBoxModel flavor = new ListBoxModel();
|
||||
OpenstackClient client = new OpenstackClient(serverUrl,
|
||||
username,
|
||||
password,
|
||||
tenantName);
|
||||
client.authenticate(); // Authenticate to Openstack
|
||||
if (client.getOSClient() != null) {
|
||||
for (String fl: client.getFlavors()) {
|
||||
flavor.add(fl);
|
||||
}
|
||||
}
|
||||
return flavor;
|
||||
}
|
||||
|
||||
public ListBoxModel doFillKeypairsItems(@QueryParameter("serverUrl") final String serverUrl,
|
||||
@QueryParameter("username") final String username,
|
||||
@QueryParameter("password") final String password,
|
||||
@QueryParameter("tenantName") final String tenantName) {
|
||||
ListBoxModel keypairs = new ListBoxModel();
|
||||
OpenstackClient client = new OpenstackClient(serverUrl,
|
||||
username,
|
||||
password,
|
||||
tenantName);
|
||||
client.authenticate(); // Authenticate to Openstack
|
||||
if (client.getOSClient() != null) {
|
||||
for (String fl: client.getKeypairs()) {
|
||||
keypairs.add(fl);
|
||||
}
|
||||
}
|
||||
return keypairs;
|
||||
}
|
||||
|
||||
public ListBoxModel doFillImagesItems(@QueryParameter("serverUrl") final String serverUrl,
|
||||
@QueryParameter("username") final String username,
|
||||
@QueryParameter("password") final String password,
|
||||
@QueryParameter("tenantName") final String tenantName) {
|
||||
ListBoxModel images = new ListBoxModel();
|
||||
OpenstackClient client = new OpenstackClient(serverUrl,
|
||||
username,
|
||||
password,
|
||||
tenantName);
|
||||
client.authenticate(); // Authenticate to Openstack
|
||||
if (client.getOSClient() != null) {
|
||||
for (String image: client.getImages()) {
|
||||
images.add(image);
|
||||
}
|
||||
}
|
||||
return images;
|
||||
}
|
||||
|
||||
/**
|
||||
* This human readable name is used in the configuration screen.
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return "Murano Tomcat Plugin";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,111 @@
|
||||
package com.mirantis.plugins.murano.tomcat.client;
|
||||
|
||||
import org.openstack4j.api.OSClient;
|
||||
import org.openstack4j.model.compute.Flavor;
|
||||
import org.openstack4j.model.compute.Image;
|
||||
import org.openstack4j.model.compute.Keypair;
|
||||
import org.openstack4j.openstack.OSFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Client Class to talk to Openstack/Murano APIs
|
||||
*/
|
||||
public class OpenstackClient {
|
||||
|
||||
private final static Logger LOG = Logger.getLogger(OpenstackClient.class.getName());
|
||||
private String serverUrl;
|
||||
private String username;
|
||||
private String password;
|
||||
private String tenantName;
|
||||
private Map<String, String> imagesMap = new HashMap<String, String>();
|
||||
|
||||
private OSClient.OSClientV2 os = null;
|
||||
|
||||
public OpenstackClient(String serverUrl,
|
||||
String username,
|
||||
String password,
|
||||
String tenantName) {
|
||||
this.serverUrl = serverUrl;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.tenantName = tenantName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate to the Openstack instance given the credentials in constructor
|
||||
* @return whether the auth was successful
|
||||
*/
|
||||
public boolean authenticate() {
|
||||
boolean success = false;
|
||||
try {
|
||||
this.os = OSFactory.builderV2()
|
||||
.endpoint(this.serverUrl + ":5000/v2.0")
|
||||
.credentials(this.username, this.password)
|
||||
.tenantName(this.tenantName)
|
||||
.authenticate();
|
||||
success = true;
|
||||
} catch(Exception ex) {
|
||||
LOG.log(Level.SEVERE, "Error connecting to Client", ex);
|
||||
success = false;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the Flavors the user has access to
|
||||
* @return A List of Flavor objects
|
||||
*/
|
||||
public ArrayList<String> getFlavors() {
|
||||
ArrayList<String> flavors = new ArrayList<String>();
|
||||
if (this.os != null) {
|
||||
List<? extends Flavor> flavorsList = this.os.compute().flavors().list();
|
||||
for (Flavor f : flavorsList) {
|
||||
flavors.add(f.getName());
|
||||
}
|
||||
}
|
||||
return flavors;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the Keypairs the user has access to
|
||||
* @return A List of Keypair object
|
||||
*/
|
||||
public ArrayList<String> getKeypairs() {
|
||||
ArrayList<String> keypairs = new ArrayList<String>();
|
||||
if (this.os != null) {
|
||||
List<? extends Keypair> kpList = this.os.compute().keypairs().list();
|
||||
for (Keypair k : kpList) {
|
||||
keypairs.add(k.getName());
|
||||
}
|
||||
}
|
||||
return keypairs;
|
||||
}
|
||||
|
||||
public Set<String> getImages() {
|
||||
imagesMap.clear();
|
||||
ArrayList<String> images = new ArrayList<String>();
|
||||
if (this.os != null) {
|
||||
List<? extends Image> iList = this.os.compute().images().list();
|
||||
for (Image i : iList) {
|
||||
imagesMap.put(i.getName(), i.getId());
|
||||
}
|
||||
}
|
||||
return imagesMap.keySet();
|
||||
}
|
||||
|
||||
public String getImageId(String name) {
|
||||
return imagesMap.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper object to return the OSClient
|
||||
* @return OSClient V2
|
||||
*/
|
||||
public OSClient.OSClientV2 getOSClient() {
|
||||
return this.os;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core"
|
||||
xmlns:st="jelly:stapler"
|
||||
xmlns:d="jelly:define"
|
||||
xmlns:l="/lib/layout"
|
||||
xmlns:t="/lib/hudson"
|
||||
xmlns:f="/lib/form">
|
||||
|
||||
<f:section title="Server Credentials">
|
||||
<f:entry title="Server URL" field="serverUrl">
|
||||
<f:textbox />
|
||||
</f:entry>
|
||||
<f:entry title="Username" field="username">
|
||||
<f:textbox />
|
||||
</f:entry>
|
||||
<f:entry title="Password" field="password">
|
||||
<f:password />
|
||||
</f:entry>
|
||||
<f:entry title="Tenant Name" field="tenantName">
|
||||
<f:textbox />
|
||||
</f:entry>
|
||||
<f:validateButton
|
||||
title="Test Connection" progress="Connecting..."
|
||||
method="testConnection" with="serverUrl,username,password,tenantName" />
|
||||
</f:section>
|
||||
|
||||
<f:section title="Node Details">
|
||||
<f:entry title="Tomcat Cluster Name" field="clusterName">
|
||||
<f:textbox />
|
||||
</f:entry>
|
||||
<f:entry title="Flavor List" field="flavor">
|
||||
<f:select />
|
||||
</f:entry>
|
||||
<f:entry title="Keypair" field="keypairs">
|
||||
<f:select />
|
||||
</f:entry>
|
||||
<f:entry title="Image" field="images">
|
||||
<f:select />
|
||||
</f:entry>
|
||||
</f:section>
|
||||
|
||||
<f:section title="Ansible Playbook details">
|
||||
<f:entry title="Playbook Name" field="playbook">
|
||||
<f:textbox />
|
||||
</f:entry>
|
||||
</f:section>
|
||||
</j:jelly>
|
Loading…
Reference in New Issue
Block a user