Browse Source

Merge "[Ansible] Ansible Playbook support demo"

Jenkins 2 years ago
parent
commit
843fde43d9

+ 77
- 0
tomcat/pom.xml View File

@@ -0,0 +1,77 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
4
+  <modelVersion>4.0.0</modelVersion>
5
+
6
+  <parent>
7
+    <groupId>org.jenkins-ci.plugins</groupId>
8
+    <artifactId>plugin</artifactId>
9
+    <version>2.11</version>
10
+    <relativePath/>
11
+  </parent>
12
+
13
+  <groupId>com.mirantis.plugins</groupId>
14
+  <artifactId>murano-tomcat</artifactId>
15
+  <version>1.0-SNAPSHOT</version>
16
+  <packaging>hpi</packaging>
17
+
18
+  <properties>
19
+    <jenkins.version>1.625.3</jenkins.version>
20
+    <java.level>7</java.level>
21
+    <jenkins-test-harness.version>2.13</jenkins-test-harness.version>
22
+  </properties>
23
+
24
+  <name>Murano Tomcat Plugin</name>
25
+  <description>Murano plugin with Tomcat to deploy Jenkins builds</description>
26
+  <url>https://wiki.jenkins-ci.org/display/JENKINS/TODO+Plugin</url>
27
+
28
+  <licenses>
29
+    <license>
30
+      <name>MIT License</name>
31
+      <url>http://opensource.org/licenses/MIT</url>
32
+    </license>
33
+  </licenses>
34
+
35
+  <repositories>
36
+    <repository>
37
+      <id>repo.jenkins-ci.org</id>
38
+      <url>https://repo.jenkins-ci.org/public/</url>
39
+    </repository>
40
+  </repositories>
41
+  <pluginRepositories>
42
+    <pluginRepository>
43
+      <id>repo.jenkins-ci.org</id>
44
+      <url>https://repo.jenkins-ci.org/public/</url>
45
+    </pluginRepository>
46
+  </pluginRepositories>
47
+
48
+  <dependencies>
49
+    <dependency>
50
+      <groupId>org.pacesys</groupId>
51
+      <artifactId>openstack4j-core</artifactId>
52
+      <version>3.0.1</version>
53
+    </dependency>
54
+    <dependency>
55
+      <groupId>org.pacesys.openstack4j.connectors</groupId>
56
+      <artifactId>openstack4j-httpclient</artifactId>
57
+      <version>3.0.1</version>
58
+    </dependency>
59
+    <dependency>
60
+      <groupId>com.googlecode.json-simple</groupId>
61
+      <artifactId>json-simple</artifactId>
62
+      <version>1.1.1</version>
63
+    </dependency>
64
+    <dependency>
65
+      <groupId>org.jenkins-ci.plugins</groupId>
66
+      <artifactId>credentials</artifactId>
67
+      <version>1.16.1</version>
68
+    </dependency>
69
+    <dependency>
70
+      <groupId>org.jenkins-ci.plugins</groupId>
71
+      <artifactId>ssh-credentials</artifactId>
72
+      <version>1.10</version>
73
+    </dependency>
74
+
75
+  </dependencies>
76
+
77
+</project>

+ 30
- 0
tomcat/src/main/java/com/mirantis/plugins/murano/tomcat/ConfigurationSection.java View File

@@ -0,0 +1,30 @@
1
+package com.mirantis.plugins.murano.tomcat;
2
+
3
+import java.util.HashMap;
4
+import java.util.List;
5
+import java.util.Map;
6
+import java.util.Set;
7
+
8
+/**
9
+ * Configuration Helper to understand the Mirantis UI definition
10
+ */
11
+public class ConfigurationSection {
12
+    Map<String, List<Map>> conf;
13
+
14
+    public ConfigurationSection() {
15
+        conf = new HashMap();
16
+    }
17
+
18
+    public void addSection(String sectionName, List fields) {
19
+        conf.put(sectionName, fields);
20
+    }
21
+
22
+    public List<Map> getSection(String sectionName){
23
+        return conf.get(sectionName);
24
+    }
25
+
26
+    public Set getSections(){
27
+        return conf.keySet();
28
+    }
29
+}
30
+

+ 696
- 0
tomcat/src/main/java/com/mirantis/plugins/murano/tomcat/MuranoTomcatBuilder.java View File

@@ -0,0 +1,696 @@
1
+package com.mirantis.plugins.murano.tomcat;
2
+
3
+import com.cloudbees.jenkins.plugins.sshcredentials.SSHUser;
4
+import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey;
5
+import com.cloudbees.plugins.credentials.CredentialsProvider;
6
+import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
7
+import com.mirantis.plugins.murano.tomcat.client.OpenstackClient;
8
+import hudson.Extension;
9
+import hudson.FilePath;
10
+import hudson.Launcher;
11
+import hudson.Proc;
12
+import hudson.model.AbstractBuild;
13
+import hudson.model.AbstractProject;
14
+import hudson.model.BuildListener;
15
+import hudson.tasks.BuildStepDescriptor;
16
+import hudson.tasks.BuildStepMonitor;
17
+import hudson.tasks.Notifier;
18
+import hudson.tasks.Publisher;
19
+import hudson.util.FormValidation;
20
+import hudson.util.ListBoxModel;
21
+import jenkins.model.Jenkins;
22
+import org.apache.commons.io.output.TeeOutputStream;
23
+import org.apache.http.client.methods.CloseableHttpResponse;
24
+import org.json.simple.JSONArray;
25
+import org.json.simple.JSONObject;
26
+import org.json.simple.parser.JSONParser;
27
+import org.json.simple.parser.ParseException;
28
+import org.kohsuke.stapler.DataBoundConstructor;
29
+import org.kohsuke.stapler.QueryParameter;
30
+import org.openstack4j.connectors.httpclient.HttpCommand;
31
+import org.openstack4j.core.transport.HttpMethod;
32
+import org.openstack4j.core.transport.HttpRequest;
33
+import org.yaml.snakeyaml.Yaml;
34
+
35
+import javax.servlet.ServletException;
36
+import java.io.*;
37
+import java.security.SecureRandom;
38
+import java.util.ArrayList;
39
+import java.util.HashMap;
40
+import java.util.List;
41
+import java.util.Map;
42
+
43
+/**
44
+ * Created by raja on 20/09/16.
45
+ */
46
+public class MuranoTomcatBuilder extends Notifier {
47
+    private static String privateKeyIdentifier = "muranodemo";
48
+    private String serverUrl;
49
+    private String username;
50
+    private String password;
51
+    private String tenantName;
52
+    private String flavor;
53
+    private String keypairs;
54
+    private String images;
55
+    private String clusterName;
56
+    private String playbook;
57
+    private OpenstackClient client;
58
+
59
+    @DataBoundConstructor
60
+    public MuranoTomcatBuilder(String serverUrl,
61
+                         String username,
62
+                         String password,
63
+                         String tenantName,
64
+                         String clusterName,
65
+                         String flavor,
66
+                         String keypairs,
67
+                         String images,
68
+                         String playbook) {
69
+        this.serverUrl = serverUrl;
70
+        this.username = username;
71
+        this.password = password;
72
+        this.tenantName = tenantName;
73
+        this.flavor = flavor;
74
+        this.keypairs = keypairs;
75
+        this.images = images;
76
+        this.clusterName = clusterName;
77
+        this.playbook = playbook;
78
+    }
79
+
80
+    public String getServerUrl() {
81
+        return serverUrl;
82
+    }
83
+
84
+    public String getUsername() {
85
+        return username;
86
+    }
87
+
88
+    public String getPassword() {
89
+        return password;
90
+    }
91
+
92
+    public String getTenantName() {
93
+        return tenantName;
94
+    }
95
+
96
+    public String getFlavor() {
97
+        return flavor;
98
+    }
99
+
100
+    public String getImages() {
101
+        return this.images;
102
+    }
103
+
104
+    public String getKeypairs() {
105
+        return this.keypairs;
106
+    }
107
+
108
+    public String getPlaybook() {
109
+        return this.playbook;
110
+    }
111
+
112
+    public String getClusterName() {
113
+        return this.clusterName;
114
+    }
115
+
116
+
117
+    @Override
118
+    public boolean perform(AbstractBuild<?, ?> build, Launcher launcher,
119
+                           BuildListener listener) throws IOException, InterruptedException {
120
+
121
+        client = new OpenstackClient(serverUrl,
122
+                username,
123
+                password,
124
+                tenantName);
125
+        if (!client.authenticate()) {
126
+            listener.getLogger().println("Cannot connect to Openstack server. Pls check configuration");
127
+            return false;
128
+        }
129
+
130
+        // Get the images to cache them
131
+        client.getImages();
132
+
133
+        FilePath workspace = build.getWorkspace();
134
+        FilePath privKeyFile = null;
135
+        StandardUsernameCredentials c = CredentialsProvider.findCredentialById(privateKeyIdentifier, StandardUsernameCredentials.class, build);
136
+        if (c instanceof SSHUserPrivateKey) {
137
+            SSHUserPrivateKey privateKey = (SSHUserPrivateKey) c;
138
+            List<String> keys = privateKey.getPrivateKeys();
139
+            for (String s: keys) {
140
+                privKeyFile = workspace.createTextTempFile("ssh", ".key", s, false);
141
+                privKeyFile.chmod(0400);
142
+            }
143
+        }
144
+
145
+        // Get the UI Definition for Tomcat. This is hardcoded based on the Tomcat Definition in Murano
146
+        // TODO: See if this can be changed dynamically
147
+        String token = client.getOSClient().getAccess().getToken().getId();
148
+        HttpRequest request = HttpRequest.builder().method(HttpMethod.GET)
149
+                .endpoint(serverUrl + ":9292")
150
+                .path("/v3/artifacts/murano/v1/725cb06b-c69b-46c0-8768-e2f5cdb14e30/ui_definition/download") // Tomcat id
151
+                .header("X-Auth-Token", token)
152
+                .build();
153
+        HttpCommand command = HttpCommand.create(request);
154
+        CloseableHttpResponse response = null;
155
+        try {
156
+            response = command.execute();
157
+        } catch(Exception ex) {
158
+            ex.printStackTrace();;
159
+            return false;
160
+        }
161
+        if (response == null) {
162
+            listener.getLogger().println("Error getting response for UI definition");
163
+            return false;
164
+        }
165
+
166
+        StringBuffer jsonString = new StringBuffer();
167
+        try {
168
+            BufferedReader br = new BufferedReader(new InputStreamReader(
169
+                    response.getEntity().getContent()));
170
+
171
+            //Print the raw output of murano api from the server
172
+            String output;
173
+
174
+            while ((output = br.readLine()) != null) {
175
+                jsonString.append(output + "\n");
176
+            }
177
+        } catch(Exception ex) {
178
+            listener.getLogger().println("Error getting output for UI definition");
179
+            return false;
180
+        }
181
+
182
+        // Fill in the templates according to the jenkins job
183
+        // TODO: Could be done only if the image needs to be created. Otehrwise its a waste of resouces.
184
+        Yaml yaml = new Yaml();
185
+        String appTemplate = "";
186
+        ConfigurationSection sections = new ConfigurationSection();
187
+        Map templates = new HashMap();
188
+        Map<String, Map<String, Object>> values = (Map<String, Map<String, Object>>) yaml.load(jsonString.toString());
189
+        for (String key : values.keySet()) {
190
+            System.out.println(key);
191
+            if (key.equals("Forms")) {
192
+                List<Map> formElements = (ArrayList<Map>)values.get(key);
193
+
194
+                for (Map type: formElements) {
195
+                    String typeName = (String)type.keySet().toArray()[0]; // gets the form types like : appConfiguration, instanceConfiguration etc
196
+
197
+                    Map<String, Map<String, String>> fields = (HashMap<String, Map<String,String>>) type.get(typeName);
198
+                    List<Map> fieldArr = (ArrayList<Map>)fields.get("fields");
199
+
200
+                    sections.addSection(typeName, fieldArr);
201
+
202
+                }
203
+            } else if (key.equals("Application")) {
204
+                System.out.println("Application section : ");
205
+                Map<String, Object> applicationTemplate = (Map<String, Object>)values.get(key);
206
+                appTemplate = "{" + getApplicationJsonTemplate(applicationTemplate) + "}";
207
+
208
+            } else if (key.equals("Templates")) {
209
+                System.out.println("Templates");
210
+                Map<String, Object> templateMap = values.get(key);
211
+                for(String templateName: templateMap.keySet()) {
212
+                    Object templateValue = templateMap.get(templateName);
213
+                    if (templateValue instanceof Map) {
214
+                        Map<String, Object> internalTemplateHash = (Map<String, Object>) templateValue;
215
+                        //String templateExpanded = "{" + getApplicationJsonTemplate(internalTemplateHash) + "}";
216
+                        templates.put(templateName, internalTemplateHash);
217
+                    }
218
+                }
219
+            }
220
+        }
221
+
222
+        String envId = checkIfDeploymentExists(token, this.clusterName);
223
+        if (envId == null) {
224
+            listener.getLogger().println("Creating new enviroment");
225
+            // No Environment, create the cluster
226
+            String filledTemplate = fillTemplate(appTemplate, build.getId());
227
+
228
+            // Create Env
229
+            envId = this.createEnvironment(token, this.clusterName);
230
+
231
+            // Create Session
232
+            String sessionId = this.createEnvironmentSession(token, envId);
233
+
234
+            // Add App to Environment
235
+            addApplicationToEnvironment(token, envId, sessionId, filledTemplate);
236
+
237
+            // Deploy
238
+            deployEnvironment(token, envId, sessionId);
239
+            listener.getLogger().println("Waiting for Deployment to finish...");
240
+            if (!isDeploymentSuccess(token, envId)) {
241
+                listener.getLogger().println("Taking longer to deploy, please check the murano console");
242
+                return false;
243
+            }
244
+            listener.getLogger().println("Deployed Tomcat");
245
+        }
246
+
247
+
248
+        //  Get the Floating IP to deploy the image
249
+        String floatingIp = null;
250
+        try {
251
+            floatingIp = getFloatingIp(token, envId);
252
+        } catch(Exception ex) {
253
+            listener.getLogger().println("Error getting Floating ip");
254
+            return false;
255
+        }
256
+
257
+        // Call Ansible Playbook to execute
258
+        return callAnsiblePlaybook(launcher, floatingIp, privKeyFile, workspace, listener.getLogger());
259
+
260
+    }
261
+
262
+    private List<String> generateCommandArgs(FilePath privateKeyPath,
263
+                                             FilePath workspace,
264
+                                             String floatingIp) {
265
+        List<String> args = new ArrayList<String>();
266
+
267
+        // Build command line
268
+
269
+        // TODO: See if ansible-playbook is available somewhere else
270
+        String ansiblePlaybook = "ansible-playbook";
271
+
272
+        args.add(ansiblePlaybook);
273
+
274
+        // --private-key
275
+        if (privateKeyPath != null) {
276
+            args.add("--private-key=" + privateKeyPath.toString());
277
+        }
278
+
279
+        // Add inventory information
280
+        args.add("-i");
281
+        args.add(floatingIp + ",");
282
+
283
+        args.add("-e");
284
+        args.add("localFile=" + new FilePath(workspace, "./target/petclinic.war"));
285
+        args.add(new FilePath(workspace, this.playbook).toString());
286
+
287
+        return args;
288
+    }
289
+
290
+    private boolean callAnsiblePlaybook(Launcher launcher,
291
+                                        String floatingIp,
292
+                                        FilePath privKeyFile,
293
+                                        FilePath workspace,
294
+                                        PrintStream jenkinsLogger) throws IOException, InterruptedException {
295
+        ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream();
296
+        ByteArrayOutputStream stderrStream = new ByteArrayOutputStream();
297
+        TeeOutputStream stdoutTee = new TeeOutputStream(stdoutStream, jenkinsLogger);
298
+        TeeOutputStream stderrTee = new TeeOutputStream(stderrStream, jenkinsLogger);
299
+
300
+        // Build a launcher
301
+        Launcher.ProcStarter settings = launcher.launch();
302
+        settings.cmds(this.generateCommandArgs(privKeyFile, workspace, floatingIp));
303
+        settings.stdout(stdoutTee);
304
+        settings.stderr(stderrTee);
305
+        // settings.pwd(containingFolder);
306
+        // settings.envs(joinedEnvVars);
307
+
308
+        // Launch
309
+        Proc proc = settings.start();
310
+
311
+        // Wait for exit and capture outputs
312
+        int exitCode = proc.join();
313
+        String stdout = stdoutStream.toString();
314
+        String stderr = stderrStream.toString();
315
+
316
+        return exitCode == 0;
317
+    }
318
+
319
+    private boolean isDeploymentSuccess(String token, String envId) {
320
+        boolean status = false;
321
+        for (int i=0; i<300; i++) {
322
+
323
+            try {
324
+                Thread.sleep(10000);
325
+                String payload = getResponseForJsonPost(serverUrl,
326
+                        8082,
327
+                        "/v1/environments/" + envId + "/deployments",
328
+                        HttpMethod.GET,
329
+                        token,
330
+                        null,
331
+                        null);
332
+                JSONParser parser = new JSONParser();
333
+                try {
334
+                    JSONObject deployments = (JSONObject) parser.parse(payload);
335
+                    JSONArray deploymentList = (JSONArray) deployments.get("deployments");
336
+                    JSONObject thisDeployment = (JSONObject) deploymentList.get(0);
337
+                    if ("success".equals((String) thisDeployment.get("state"))) {
338
+                        status = true;
339
+                        break;
340
+                    }
341
+                } catch (ParseException pe) {
342
+                    System.out.println("position: " + pe.getPosition());
343
+                    System.out.println(pe);
344
+                }
345
+            } catch (Exception ex) {
346
+                status = false;
347
+                break;
348
+            }
349
+        }
350
+        return status;
351
+    }
352
+
353
+    private String getFloatingIp(String token, String envId) throws Exception {
354
+        String payload = getResponseForJsonPost(serverUrl,
355
+                8082,
356
+                "/v1/environments/" + envId + "/services",
357
+                HttpMethod.GET,
358
+                token,
359
+                null,
360
+                null);
361
+        String floatingIp = null;
362
+        if (payload != null) {
363
+            JSONParser parser = new JSONParser();
364
+            JSONArray array = (JSONArray) parser.parse(payload);
365
+            floatingIp =   (String) ((JSONObject) ((JSONObject) array.get(0)).get("instance")).get("floatingIpAddress");
366
+        }
367
+
368
+        return floatingIp;
369
+    }
370
+    /**
371
+     * Return the Environment id if it exists
372
+     *
373
+     * @param token
374
+     * @param name
375
+     * @return
376
+     */
377
+    private String checkIfDeploymentExists(String token, String name) {
378
+        String payload = getResponseForJsonPost(serverUrl,
379
+                8082,
380
+                "/v1/environments",
381
+                HttpMethod.GET,
382
+                token,
383
+                null,
384
+                null);
385
+        String envId = null;
386
+        JSONParser parser = new JSONParser();
387
+        try{
388
+            Object obj = parser.parse(payload);
389
+            JSONObject response = (JSONObject)obj;
390
+            JSONArray environmentArray =  (JSONArray) response.get("environments");
391
+            for (Object env: environmentArray) {
392
+                JSONObject thisEnv = (JSONObject) env;
393
+                String envName = (String) thisEnv.get("name");
394
+                if (envName.equals(name)) {
395
+                    envId = (String) thisEnv.get("id");
396
+                    break;
397
+                }
398
+            }
399
+        }catch(ParseException pe){
400
+            System.out.println("position: " + pe.getPosition());
401
+            System.out.println(pe);
402
+        }
403
+        return envId;
404
+    }
405
+
406
+    /**
407
+     * Deploy the environment given the environment id and Session Token
408
+     */
409
+    private void deployEnvironment(String token, String envId, String sessionId) {
410
+        String response = getResponseForJsonPost(serverUrl,
411
+                8082,
412
+                "/v1/environments/" + envId + "/sessions/" + sessionId + "/deploy",
413
+                HttpMethod.POST,
414
+                token,
415
+                null,
416
+                null);
417
+    }
418
+
419
+    /**
420
+     * Add the app(K8S) to the environment
421
+     * @param token
422
+     * @param envId
423
+     * @param sessionId
424
+     * @param jsonReq
425
+     */
426
+    private void addApplicationToEnvironment(String token, String envId, String sessionId, String jsonReq) {
427
+        String response = getResponseForJsonPost(serverUrl,
428
+                8082,
429
+                "/v1/environments/" + envId + "/services",
430
+                HttpMethod.POST,
431
+                token,
432
+                jsonReq,
433
+                sessionId);
434
+        System.out.println("add application response  : " + response);
435
+        //return sessionId;
436
+    }
437
+
438
+    private String createEnvironmentSession(String token, String envId) {
439
+        String payload = getResponseForJsonPost(serverUrl,
440
+                8082,
441
+                "/v1/environments/" + envId + "/configure",
442
+                HttpMethod.POST,
443
+                token,
444
+                null,
445
+                null);
446
+
447
+        String sessionId = "";
448
+        JSONParser parser = new JSONParser();
449
+        try{
450
+            Object obj = parser.parse(payload);
451
+            JSONObject response = (JSONObject)obj;
452
+            sessionId = (String)response.get("id");
453
+        }catch(ParseException pe){
454
+            System.out.println("position: " + pe.getPosition());
455
+            System.out.println(pe);
456
+        }
457
+        System.out.println("Session Id : " + sessionId);
458
+        return sessionId;
459
+    }
460
+
461
+    private String createEnvironment(String token, String envname) {
462
+        String reqPayload = "{\"name\":\"" + envname + "\"}";
463
+        String payload = getResponseForJsonPost(serverUrl, 8082, "/v1/environments", HttpMethod.POST, token, reqPayload, null);
464
+
465
+        String envId = "";
466
+        JSONParser parser = new JSONParser();
467
+        try{
468
+            Object obj = parser.parse(payload);
469
+            JSONObject response = (JSONObject)obj;
470
+            envId = (String)response.get("id");
471
+        }catch(ParseException pe){
472
+            System.out.println("position: " + pe.getPosition());
473
+            System.out.println(pe);
474
+        }
475
+        System.out.println("Envid : " + envId);
476
+        return envId;
477
+    }
478
+
479
+    /**
480
+     * Main helper method to call the Murano API and return the response. Accepts both GET and POST.
481
+     *
482
+     * @param url Base URL to connect
483
+     * @param port Which port is murano listening on
484
+     * @param requestPath Path on Murano URL
485
+     * @param method GET or POST
486
+     * @param token Auth Token
487
+     * @param jsonPayload Payload for the message
488
+     * @param muranoSessionId Optional Session Id
489
+     * @return Response from the Call
490
+     */
491
+    private  String getResponseForJsonPost(String url,
492
+                                           int port,
493
+                                           String requestPath,
494
+                                           HttpMethod method,
495
+                                           String token,
496
+                                           String jsonPayload,
497
+                                           String muranoSessionId) {
498
+        HttpRequest request = HttpRequest.builder().method(method)
499
+                .endpoint(url + ":" + port)
500
+                .path(requestPath) // K8S cluster id
501
+                .header("X-Auth-Token", token)
502
+                .json(jsonPayload)
503
+                .build();
504
+        if (muranoSessionId != null) {
505
+            request.getHeaders().put("X-Configuration-Session", muranoSessionId);
506
+        }
507
+        if (jsonPayload != null) {
508
+            request = request.toBuilder().json(jsonPayload).build();
509
+        }
510
+
511
+        HttpCommand command = HttpCommand.create(request);
512
+        CloseableHttpResponse response = null;
513
+        try {
514
+            response = command.execute();
515
+        } catch(Exception ex) {
516
+            ex.printStackTrace();;
517
+            return null;
518
+        }
519
+
520
+        StringBuffer jsonString = new StringBuffer();
521
+        try {
522
+            BufferedReader br = new BufferedReader(new InputStreamReader(
523
+                    response.getEntity().getContent()));
524
+
525
+            //Print the raw output of murano api from the server
526
+            String output;
527
+
528
+            while ((output = br.readLine()) != null) {
529
+                jsonString.append(output + "\n");
530
+            }
531
+        } catch(Exception ex) {
532
+            return null;
533
+        }
534
+
535
+        return jsonString.toString();
536
+    }
537
+
538
+    private String generateUUID() {
539
+        SecureRandom ng = new SecureRandom();
540
+        long MSB = 0x8000000000000000L;
541
+
542
+        String str = Long.toHexString(MSB | ng.nextLong()) + Long.toHexString(MSB | ng.nextLong());
543
+        return str;
544
+
545
+    }
546
+
547
+    private String fillTemplate(String template, String buildId) {
548
+        String appTemplate = template.replace("$.appConfiguration.name", "Tomcat_" + buildId);
549
+
550
+        appTemplate = appTemplate.replace("$.instanceConfiguration.osImage", client.getImageId(this.images));
551
+        appTemplate = appTemplate.replace("$.instanceConfiguration.flavor", flavor);
552
+        appTemplate = appTemplate.replace("$.appConfiguration.assignFloatingIP", "True");
553
+        appTemplate = appTemplate.replace("$.instanceConfiguration.keyPair", this.keypairs);
554
+        appTemplate = appTemplate.replace("$.instanceConfiguration.availabilityZone", "nova");
555
+
556
+        return appTemplate;
557
+    }
558
+
559
+    private String getApplicationJsonTemplate(Map<String,Object> template) {
560
+        String templateStr = "";
561
+        boolean firstStr = true;
562
+        for (String k : template.keySet()) {
563
+            if (!firstStr) {
564
+                templateStr += ",";
565
+            }
566
+            firstStr = false;
567
+            Object v = template.get(k);
568
+            if (v instanceof Map) {
569
+                templateStr += "\"" + k + "\": {" + this.getApplicationJsonTemplate((Map<String, Object>)v) ;
570
+                templateStr += "}";
571
+            } else if (v instanceof String) {
572
+                String uuid = generateUUID();
573
+                if (k.equals("type")) {
574
+                    templateStr += "\"" + k + "\":\"" + v + "\"";
575
+                    templateStr += ",\"id\":\"" + uuid + "\"";
576
+                }else if (k.equals("name") && (((String)v).contains("generateHostname"))) {
577
+                    templateStr += "\"name\":\"$.instanceConfiguration.unitNamingPattern-" + uuid + ".com\"";
578
+                }else {
579
+                    templateStr += "\"" + k + "\":\"" + v + "\"";
580
+                }
581
+            }
582
+        }
583
+        return templateStr;
584
+    }
585
+
586
+    @Override
587
+    public BuildStepMonitor getRequiredMonitorService() {
588
+        return BuildStepMonitor.BUILD;
589
+    }
590
+
591
+    @Extension
592
+    public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {
593
+
594
+        public DescriptorImpl() {
595
+            load();
596
+        }
597
+
598
+        @Override
599
+        public boolean isApplicable(Class<? extends AbstractProject> aClass) {
600
+            return true;
601
+        }
602
+
603
+
604
+        public FormValidation doCheckServerUrl(@QueryParameter String serverUrl) {
605
+            if (serverUrl.length() == 0)
606
+                return FormValidation.error("Please enter a server url");
607
+
608
+            if (serverUrl.indexOf("://") == -1)
609
+                return FormValidation.error("Enter a url of the format http(s)://<server>");
610
+
611
+            return FormValidation.ok();
612
+        }
613
+
614
+        public FormValidation doTestConnection(@QueryParameter("serverUrl") final String serverUrl,
615
+                                               @QueryParameter("username") final String username,
616
+                                               @QueryParameter("password") final String password,
617
+                                               @QueryParameter("tenantName") final String tenantName)
618
+                throws IOException, ServletException {
619
+            try {
620
+                OpenstackClient client = new OpenstackClient(serverUrl,
621
+                        username,
622
+                        password,
623
+                        tenantName);
624
+                if (client.authenticate()) {
625
+                    return FormValidation.ok("Success");
626
+                } else {
627
+                    return FormValidation.error("Unable to connect to server. Please check credentials");
628
+                }
629
+            } catch (Exception e) {
630
+                return FormValidation.error("Error: "+e.getMessage());
631
+            }
632
+        }
633
+
634
+        public ListBoxModel doFillFlavorItems(@QueryParameter("serverUrl") final String serverUrl,
635
+                                              @QueryParameter("username") final String username,
636
+                                              @QueryParameter("password") final String password,
637
+                                              @QueryParameter("tenantName") final String tenantName) {
638
+            ListBoxModel flavor = new ListBoxModel();
639
+            OpenstackClient client = new OpenstackClient(serverUrl,
640
+                    username,
641
+                    password,
642
+                    tenantName);
643
+            client.authenticate();      // Authenticate to Openstack
644
+            if (client.getOSClient() != null) {
645
+                for (String fl: client.getFlavors()) {
646
+                    flavor.add(fl);
647
+                }
648
+            }
649
+            return flavor;
650
+        }
651
+
652
+        public ListBoxModel doFillKeypairsItems(@QueryParameter("serverUrl") final String serverUrl,
653
+                                                @QueryParameter("username") final String username,
654
+                                                @QueryParameter("password") final String password,
655
+                                                @QueryParameter("tenantName") final String tenantName) {
656
+            ListBoxModel keypairs = new ListBoxModel();
657
+            OpenstackClient client = new OpenstackClient(serverUrl,
658
+                    username,
659
+                    password,
660
+                    tenantName);
661
+            client.authenticate();      // Authenticate to Openstack
662
+            if (client.getOSClient() != null) {
663
+                for (String fl: client.getKeypairs()) {
664
+                    keypairs.add(fl);
665
+                }
666
+            }
667
+            return keypairs;
668
+        }
669
+
670
+        public ListBoxModel doFillImagesItems(@QueryParameter("serverUrl") final String serverUrl,
671
+                                                @QueryParameter("username") final String username,
672
+                                                @QueryParameter("password") final String password,
673
+                                                @QueryParameter("tenantName") final String tenantName) {
674
+            ListBoxModel images = new ListBoxModel();
675
+            OpenstackClient client = new OpenstackClient(serverUrl,
676
+                    username,
677
+                    password,
678
+                    tenantName);
679
+            client.authenticate();      // Authenticate to Openstack
680
+            if (client.getOSClient() != null) {
681
+                for (String image: client.getImages()) {
682
+                    images.add(image);
683
+                }
684
+            }
685
+            return images;
686
+        }
687
+
688
+        /**
689
+         * This human readable name is used in the configuration screen.
690
+         */
691
+        public String getDisplayName() {
692
+            return "Murano Tomcat Plugin";
693
+        }
694
+    }
695
+}
696
+

+ 111
- 0
tomcat/src/main/java/com/mirantis/plugins/murano/tomcat/client/OpenstackClient.java View File

@@ -0,0 +1,111 @@
1
+package com.mirantis.plugins.murano.tomcat.client;
2
+
3
+import org.openstack4j.api.OSClient;
4
+import org.openstack4j.model.compute.Flavor;
5
+import org.openstack4j.model.compute.Image;
6
+import org.openstack4j.model.compute.Keypair;
7
+import org.openstack4j.openstack.OSFactory;
8
+
9
+import java.util.*;
10
+import java.util.logging.Level;
11
+import java.util.logging.Logger;
12
+
13
+/**
14
+ * Client Class to talk to Openstack/Murano APIs
15
+ */
16
+public class OpenstackClient {
17
+
18
+    private final static Logger LOG = Logger.getLogger(OpenstackClient.class.getName());
19
+    private String serverUrl;
20
+    private String username;
21
+    private String password;
22
+    private String tenantName;
23
+    private Map<String, String> imagesMap = new HashMap<String, String>();
24
+
25
+    private OSClient.OSClientV2 os = null;
26
+
27
+    public OpenstackClient(String serverUrl,
28
+                           String username,
29
+                           String password,
30
+                           String tenantName) {
31
+        this.serverUrl = serverUrl;
32
+        this.username = username;
33
+        this.password = password;
34
+        this.tenantName = tenantName;
35
+    }
36
+
37
+    /**
38
+     * Authenticate to the Openstack instance given the credentials in constructor
39
+     * @return whether the auth was successful
40
+     */
41
+    public boolean authenticate() {
42
+        boolean success = false;
43
+        try {
44
+            this.os = OSFactory.builderV2()
45
+                    .endpoint(this.serverUrl + ":5000/v2.0")
46
+                    .credentials(this.username, this.password)
47
+                    .tenantName(this.tenantName)
48
+                    .authenticate();
49
+            success = true;
50
+        } catch(Exception ex) {
51
+            LOG.log(Level.SEVERE, "Error connecting to Client", ex);
52
+            success = false;
53
+        }
54
+        return success;
55
+    }
56
+
57
+    /**
58
+     * Get all the Flavors the user has access to
59
+     * @return A List of Flavor objects
60
+     */
61
+    public ArrayList<String> getFlavors() {
62
+        ArrayList<String> flavors = new ArrayList<String>();
63
+        if (this.os != null) {
64
+            List<? extends Flavor> flavorsList = this.os.compute().flavors().list();
65
+            for (Flavor f : flavorsList) {
66
+                flavors.add(f.getName());
67
+            }
68
+        }
69
+        return flavors;
70
+
71
+    }
72
+
73
+    /**
74
+     * Get all the Keypairs the user has access to
75
+     * @return A List of Keypair object
76
+     */
77
+    public ArrayList<String> getKeypairs() {
78
+        ArrayList<String> keypairs = new ArrayList<String>();
79
+        if (this.os != null) {
80
+            List<? extends Keypair> kpList = this.os.compute().keypairs().list();
81
+            for (Keypair k : kpList) {
82
+                keypairs.add(k.getName());
83
+            }
84
+        }
85
+        return keypairs;
86
+    }
87
+
88
+    public Set<String> getImages() {
89
+        imagesMap.clear();
90
+        ArrayList<String> images = new ArrayList<String>();
91
+        if (this.os != null) {
92
+            List<? extends Image> iList = this.os.compute().images().list();
93
+            for (Image i : iList) {
94
+                imagesMap.put(i.getName(), i.getId());
95
+            }
96
+        }
97
+        return imagesMap.keySet();
98
+    }
99
+
100
+    public String getImageId(String name) {
101
+        return imagesMap.get(name);
102
+    }
103
+
104
+    /**
105
+     * Helper object to return the OSClient
106
+     * @return OSClient V2
107
+     */
108
+    public OSClient.OSClientV2 getOSClient() {
109
+        return this.os;
110
+    }
111
+}

+ 47
- 0
tomcat/src/main/resources/com/mirantis/plugins/murano/tomcat/MuranoTomcatBuilder/config.jelly View File

@@ -0,0 +1,47 @@
1
+<?jelly escape-by-default='true'?>
2
+<j:jelly xmlns:j="jelly:core"
3
+         xmlns:st="jelly:stapler"
4
+         xmlns:d="jelly:define"
5
+         xmlns:l="/lib/layout"
6
+         xmlns:t="/lib/hudson"
7
+         xmlns:f="/lib/form">
8
+
9
+  <f:section title="Server Credentials">
10
+      <f:entry title="Server URL" field="serverUrl">
11
+        <f:textbox />
12
+      </f:entry>
13
+      <f:entry title="Username" field="username">
14
+        <f:textbox />
15
+      </f:entry>
16
+      <f:entry title="Password" field="password">
17
+        <f:password />
18
+      </f:entry>
19
+      <f:entry title="Tenant Name" field="tenantName">
20
+        <f:textbox />
21
+      </f:entry>
22
+      <f:validateButton
23
+         title="Test Connection" progress="Connecting..."
24
+         method="testConnection" with="serverUrl,username,password,tenantName" />
25
+  </f:section>
26
+
27
+  <f:section title="Node Details">
28
+    <f:entry title="Tomcat Cluster Name" field="clusterName">
29
+        <f:textbox />
30
+    </f:entry>
31
+    <f:entry title="Flavor List" field="flavor">
32
+        <f:select />
33
+    </f:entry>
34
+    <f:entry title="Keypair" field="keypairs">
35
+        <f:select />
36
+    </f:entry>
37
+    <f:entry title="Image" field="images">
38
+      <f:select />
39
+    </f:entry>
40
+  </f:section>
41
+
42
+  <f:section title="Ansible Playbook details">
43
+     <f:entry title="Playbook Name" field="playbook">
44
+        <f:textbox />
45
+      </f:entry>
46
+   </f:section>
47
+</j:jelly>

Loading…
Cancel
Save