From 2c51ab5c61050e0a63be6ebe1d35aaf53aa6bf0f Mon Sep 17 00:00:00 2001 From: Suryadhulipudi Date: Fri, 28 Sep 2018 15:31:24 +0530 Subject: [PATCH] Initial commit added Nutanix Calm Jenkins Plugin code --- .gitignore | 1 + Nutanix-Calm.iml | 205 ++++++++++ README.md | 36 +- pom.xml | 126 ++++++ .../com/calm/CalmHelpers/Application.java | 375 ++++++++++++++++++ .../java/com/calm/CalmHelpers/Blueprint.java | 317 +++++++++++++++ .../java/com/calm/CalmHelpers/Project.java | 76 ++++ .../com/calm/Descriptors/BlueprintLaunch.java | 261 ++++++++++++ .../Descriptors/RunApplicationAction.java | 202 ++++++++++ .../java/com/calm/Executor/CalmExecutor.java | 142 +++++++ .../CalmGlobalConfiguration.java | 57 +++ src/main/java/com/calm/Interface/Rest.java | 135 +++++++ .../com/calm/Logger/NutanixCalmLogger.java | 27 ++ .../Descriptors/BlueprintLaunch/config.jelly | 209 ++++++++++ .../BlueprintLaunch/help-actionName.html | 4 + .../BlueprintLaunch/help-appProfileName.html | 3 + .../BlueprintLaunch/help-applicationName.html | 4 + .../help-blueprintDescription.html | 3 + .../BlueprintLaunch/help-blueprintName.html | 3 + .../BlueprintLaunch/help-calmIP.html | 4 + .../BlueprintLaunch/help-calmPwd.html | 4 + .../BlueprintLaunch/help-calmUser.html | 4 + .../BlueprintLaunch/help-projectName.html | 3 + .../help-runtimeVariables.html | 3 + .../RunApplicationAction/config.jelly | 206 ++++++++++ .../RunApplicationAction/help-actionName.html | 3 + .../help-applicationName.html | 4 + .../help-runtimeVariables.html | 3 + .../CalmGlobalConfiguration/config.jelly | 29 ++ .../help-password.html | 4 + .../help-prismCentralIp.html | 4 + .../help-userName.html | 4 + src/main/resources/index.jelly | 7 + 33 files changed, 2467 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 Nutanix-Calm.iml create mode 100644 pom.xml create mode 100644 src/main/java/com/calm/CalmHelpers/Application.java create mode 100644 src/main/java/com/calm/CalmHelpers/Blueprint.java create mode 100644 src/main/java/com/calm/CalmHelpers/Project.java create mode 100644 src/main/java/com/calm/Descriptors/BlueprintLaunch.java create mode 100644 src/main/java/com/calm/Descriptors/RunApplicationAction.java create mode 100644 src/main/java/com/calm/Executor/CalmExecutor.java create mode 100644 src/main/java/com/calm/GlobalConfiguration/CalmGlobalConfiguration.java create mode 100644 src/main/java/com/calm/Interface/Rest.java create mode 100644 src/main/java/com/calm/Logger/NutanixCalmLogger.java create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/config.jelly create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-actionName.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-appProfileName.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-applicationName.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintDescription.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintName.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmIP.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmPwd.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmUser.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-projectName.html create mode 100644 src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-runtimeVariables.html create mode 100644 src/main/resources/com/calm/Descriptors/RunApplicationAction/config.jelly create mode 100644 src/main/resources/com/calm/Descriptors/RunApplicationAction/help-actionName.html create mode 100644 src/main/resources/com/calm/Descriptors/RunApplicationAction/help-applicationName.html create mode 100644 src/main/resources/com/calm/Descriptors/RunApplicationAction/help-runtimeVariables.html create mode 100644 src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/config.jelly create mode 100644 src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-password.html create mode 100644 src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-prismCentralIp.html create mode 100644 src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-userName.html create mode 100644 src/main/resources/index.jelly diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd5106f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_STORE diff --git a/Nutanix-Calm.iml b/Nutanix-Calm.iml new file mode 100644 index 0000000..7b4c994 --- /dev/null +++ b/Nutanix-Calm.iml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index b591a91..6b8f0f5 100644 --- a/README.md +++ b/README.md @@ -1 +1,35 @@ -# nutanix-jenkins-calm-plugin \ No newline at end of file +# Calm_Jenkins_Plugin +Jenkins Nutanix Calm plugin allows you to launch Nutanix Calm blueprint, provision infrastructure and services in multi cloud environment and subsequently run actions/tasks on those applications. + +#### License: +* All source code is licensed under the MIT license. + +#### Supported Versions: +* Jenkins support versions : 2.107.2 and later +* Calm Tested versions : 5.7.1 and later +* Google browser Tested version : Version 69.0.3497.100 (Official Build) (64-bit) + +#### Plugin Installation: +* Navigate to Manage Jenkins-> Manage Plugin-> Available. Search for Nutanix Calm plugin. Click on install. + +#### Plugin Configuration: +* To configure the plugin first Navigate to Manage Jenkins -> Configure System -> Nutanix Calm Plugin Configuration. Provide the Prism Central IP, Username and Password. The username can be of any user authorized in the SSP. + +#### Jenkins Freestyle job Setup: +* Now that we have configured the plugin , we can go and launch the Nutanix Clam blueprint. First let us look at Adding the Calm specific build steps in the Freestyle projects, navigate to new item, select Free style project, Enter an item name, select OK. +Click Add Build step. Select Nutanix Calm Blueprint Launch. In the section, select the Calm project, Select the blueprint to launch, Select the application profile listed and modify the values for runtime variables available for that application profile. Provide an application name. BUILD_ID is appended by default to the application name to uniquely identify it in Calm. Select the option if you want Jenkins job to wait for blueprint launch to complete before proceeding to the next step. + +* We can also invoke actions on the blueprint launched in the previous step or invoke any actions for the existing applications that are running in the Nutanix calm instance. +Click on Add Build Step. Select Nutanix Calm Application Action Run. In the section select the application name. Select the application actions available. If necessary, modify the values for the runtime variables available. + +#### Jenkins Pipeline: +* To utilize this plugin in the Jenkins pipeline, navigate to new item, select Pipeline, Enter an item name, select OK. +To generate the pipeline syntax , click on the Pipeline Syntax at the bottom. +* In the Pipeline Syntax window , Select the General build Step in the dropdown. IN the build step dropdown select the Nutanix Calm blueprint launch. The section similar to the one in the Freestyle project shows up. Select the project, Blueprint, Application profile , Variables , App name . Click on Generate Pipeline Script. Copy and paste the text in the box below into the pipeline script box. +* NOTE: We can also use Jenkinsfile from the any Source control management. + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5189c35 --- /dev/null +++ b/pom.xml @@ -0,0 +1,126 @@ + + + 4.0.0 + + org.jenkins-ci.plugins + plugin + 2.33 + + + Nutanix-Calm + 1.0-SNAPSHOT + hpi + + + 2.7.3 + + 1.8 + + Nutanix Calm Plugin + This plugin allows users to launch blueprints in Nutanix Calm. Currently this plugin can be used in Jenkins freestyle projects + https://wiki.jenkins-ci.org/display/JENKINS/Nutanix+Calm+Plugin + + + + MIT License + http://opensource.org/licenses/MIT + + + + + org.jenkins-ci.plugins + structs + 1.7 + + + org.jenkins-ci.plugins.workflow + workflow-step-api + 2.12 + test + + + org.jenkins-ci.plugins.workflow + workflow-cps + 2.39 + test + + + org.jenkins-ci.plugins.workflow + workflow-job + 2.11.2 + test + + + org.jenkins-ci.plugins.workflow + workflow-basic-steps + 2.6 + test + + + org.jenkins-ci.plugins.workflow + workflow-durable-task-step + 2.13 + test + + + org.jenkins-ci.plugins.workflow + workflow-api + 2.20 + test + + + org.jenkins-ci.plugins.workflow + workflow-support + 2.14 + test + + + org.json + json + 20090211 + + + org.jglobus + jsse + 2.0.5 + + + com.googlecode.json-simple + json-simple + 1.1 + + + + + + + Nutanix + Nutanix Calm + nucalm-devops@nutanix.com + + + + + scm:git:git://github.com/jenkinsci/Nutanix-Calm-Plugin.git + scm:git:git@github.com:jenkinsci/Nutanix-Calm-Plugin.git + https://github.com/jenkinsci/Nutanix-Calm-Plugin + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + + + repo.jenkins-ci.org + https://repo.jenkins-ci.org/public/ + + + diff --git a/src/main/java/com/calm/CalmHelpers/Application.java b/src/main/java/com/calm/CalmHelpers/Application.java new file mode 100644 index 0000000..f5c4bb4 --- /dev/null +++ b/src/main/java/com/calm/CalmHelpers/Application.java @@ -0,0 +1,375 @@ +package com.calm.CalmHelpers; + +import com.calm.Interface.Rest; +import com.calm.Logger.NutanixCalmLogger; +import org.json.JSONArray; +import org.json.JSONObject; +import java.io.PrintStream; +import java.util.*; +import java.util.concurrent.TimeUnit; + +public class Application { + private final Rest rest; + private final long THRESHOLD = 300000l; + public static Map systemActions; + private Blueprint blueprintHelper; + static { + systemActions = new HashMap<>(); + systemActions.put("START", "action_start"); + systemActions.put("STOP", "action_stop"); + systemActions.put("RESTART", "action_restart"); + systemActions.put("DELETE", "action_delete"); + systemActions.put("SOFT_DELETE", "action_soft_delete"); + } + private static long lastUpdated; + private static List applicationNames; + private static Application applicationHelper; + public static Map applicationNameUuidPair; + private final static NutanixCalmLogger LOGGER = new NutanixCalmLogger(Application.class); + + private Application(Rest rest) throws Exception{ + this.rest = rest; + applicationNames = new ArrayList<>(); + applicationNameUuidPair = new HashMap<>(); + fetchApplications(); + blueprintHelper = Blueprint.getInstance(rest); + lastUpdated = System.currentTimeMillis(); + } + + public static Application getInstance(Rest rest) throws Exception{ +// if(applicationHelper != null) { +// long currentTime = System.currentTimeMillis(); +// if((currentTime - lastUpdated) > applicationHelper.THRESHOLD) { +// applicationHelper.fetchApplications(); +// lastUpdated = currentTime; +// } +// return applicationHelper; +// } + return new Application(rest); + } + + private void fetchApplications() { + JSONObject responseObject; + JSONArray entities; + try { + responseObject = rest.post("apps/list"); + entities = responseObject.getJSONArray("entities"); + } + catch (Exception e) { + applicationNames = null; + LOGGER.debug("Error occurred while fetching the applications :" + e.getMessage()); + return; + } + + for (int i = 0; i < entities.length(); i++) { + try { + JSONObject entity = entities.getJSONObject(i); + String state = entity.getJSONObject("status").getString("state"); + if ((state.equals("running")) || (state.equals("stopped"))) { + String applicationName = entity.getJSONObject("status").getString("name"); + applicationNames.add(applicationName); + applicationNameUuidPair.put(applicationName, entity.getJSONObject("metadata").getString("uuid")); + } + } + catch (Exception e){ + //Skip this entity and go ahead + LOGGER.debug("Error occurred while parsing the response"); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + } + } + } + + public List getApplicationNames(){ + return applicationNames; + } + + public String getApplicationUuidByRequestId(String blueprintUuid, String requestId) throws Exception{ + String appUuid = null; + do { + JSONObject applicationJSON = rest.get("blueprints/" + blueprintUuid + "/pending_launches/" + requestId); + appUuid = applicationJSON.getJSONObject("status").getString("application_uuid"); + TimeUnit.SECONDS.sleep(15); + } while (appUuid.equals("null")); + return appUuid; + } + + public JSONObject getApplicationDetails(String applicationUuid) throws Exception{ + return rest.get("apps/" + applicationUuid); + } + + public List getApplicationActions(String applicationName){ + List applicationActions = new ArrayList<>(); + String appUuid; + JSONObject applicationJson; + JSONArray entities; + try { + appUuid = applicationNameUuidPair.get(applicationName); + applicationJson = getApplicationDetails(appUuid); + entities = applicationJson.getJSONObject("status").getJSONObject("resources").getJSONArray("action_list"); + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while fetching actions for: " + applicationName ); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + return null; + } + + for (int i = 0; i < entities.length(); i++) { + try{ + JSONObject entity = entities.getJSONObject(i); + String actionName = entity.getString("name"); + if (!entity.getString("type").equals("system")) { + applicationActions.add(actionName); + } + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while parsing the entity: "); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + } + } + applicationActions.addAll(getSystemActionsList()); + return applicationActions; + } + + public String waitForApplicationToGoIntoSuccessState(String appName, String applicationUuid, String state_name, PrintStream logger) throws Exception{ + JSONObject applicationJson = getApplicationDetails(applicationUuid); + String applicationStatus = applicationJson.getJSONObject("status").getString("state"); + while (!applicationStatus.equals(state_name)) { + TimeUnit.SECONDS.sleep(45); + applicationJson = getApplicationDetails(applicationUuid); + applicationStatus = applicationJson.getJSONObject("status").getString("state"); + if (applicationStatus.equals(state_name) || applicationStatus.equalsIgnoreCase("error")) + break; + logger.println(" Application " + appName + " status is : " + applicationStatus); + } + return applicationStatus; + } + + public String fetchRuntimeProfileActionVariables(String applicationName, String actionName)throws Exception{ + String appUuid = applicationNameUuidPair.get(applicationName); + JSONObject applicationJson = getApplicationDetails(appUuid); + Map keyValuePair = new HashMap<>(); + JSONArray entities = applicationJson.getJSONObject("status").getJSONObject("resources").getJSONArray("action_list"); + for (int i = 0; i < entities.length(); i++) { + JSONObject entity = entities.getJSONObject(i); + if(entity.getString("name").equals(actionName)){ + JSONArray varlist; + try { + varlist = entity.getJSONObject("runbook").getJSONArray("variable_list"); + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while getting runtime variables for application " + applicationName + + " action " + actionName); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + return null; + } + for (int j = 0; j < varlist.length(); j++) { + JSONObject num = varlist.getJSONObject(j); + try { + if (num.has("editables")) { + JSONObject editables = num.getJSONObject("editables"); + if ((editables.has("value") && editables.getBoolean("value"))) { + if ((num.has("attrs")) && (num.getJSONObject("attrs").has("is_secret_modified"))) { + JSONObject attrs = num.getJSONObject("attrs"); + String name = (num.getString("name")); + String value = ""; + keyValuePair.put(name, value); + } else { + String name = (num.getString("name")); + String value = (num.getString("value")); + keyValuePair.put(name, value); + } + } + } + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while parsing runtime variable: "+ num.toString()); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + } + + } + break; + } + } + JSONObject appProfileActionVariables = new JSONObject(); + for(String key : keyValuePair.keySet()) + appProfileActionVariables.put(key, keyValuePair.get(key)); + return appProfileActionVariables.toString(); + } + + public JSONObject patchAppProfileActionVariables(JSONObject actionSpec, String applicationName, String actionName, + String runtimeVariablesStr, PrintStream logger)throws Exception{ + JSONArray args_list = new JSONArray(); + String appUuid = applicationNameUuidPair.get(applicationName); + JSONObject appResponse = getApplicationDetails(appUuid); + JSONObject appProfileActionVariables = new JSONObject(runtimeVariablesStr); + JSONArray actionList = appResponse.getJSONObject("status").getJSONObject("resources").getJSONArray("action_list"); + for (int i = 0; i < actionList.length(); i++) { + JSONObject action = actionList.getJSONObject(i); + if (action.getString("name").equals(actionName)){ + JSONArray variableList = action.getJSONObject("runbook").getJSONArray("variable_list"); + for(int j = 0;j < variableList.length();j++){ + JSONObject variable = variableList.getJSONObject(j); + JSONObject var = new JSONObject(); + String variableName = variable.getString("name"); + var.put("name", variableName); + if(appProfileActionVariables.has(variableName)) + var.put("value", appProfileActionVariables.get(variableName)); + else + var.put("value", variable.getString("value")); + args_list.put(var); + } + break; + } + } + actionSpec.getJSONObject("spec").put("args", args_list); + return actionSpec; + } + + public String getActionUuid(String applicationName, String actionName) throws Exception{ + String appUuid = applicationNameUuidPair.get(applicationName); + JSONObject applicationDetails = getApplicationDetails(appUuid); + JSONArray actionList = applicationDetails.getJSONObject("status").getJSONObject("resources").getJSONArray("action_list"); + for (int i = 0; i < actionList.length(); i++) { + JSONObject action = actionList.getJSONObject(i); + if(action.getString("name").equals(actionName)) + return action.getString("uuid"); + + } + return null; + } + + public JSONObject runAction(String applicationName, String actionName, JSONObject actionSpec) throws Exception{ + String applicationUUid = applicationNameUuidPair.get(applicationName); + actionName = systemActions.get(actionName) != null ? systemActions.get(actionName) : actionName; + switch (actionName){ + case "action_delete" : return rest.delete("apps/" + applicationUUid); + case "action_soft_delete" : return rest.delete("apps/" + applicationUUid + "?type=soft"); + default : String actionUuid = getActionUuid(applicationName, actionName); + JSONObject applicationJson = getApplicationDetails(applicationUUid); + String applicationUuid = applicationJson.getJSONObject("metadata").getString("uuid"); + return rest.post("apps/"+ applicationUuid + "/actions/" + + actionUuid+"/run", actionSpec.toString()); + } + } + + + public String waitForActionToComplete(String applicationName, String runlogUuid, PrintStream logger)throws Exception{ + String applicationUuid = applicationNameUuidPair.get(applicationName); + JSONObject actionStatusResponse = rest.get("apps/" + applicationUuid + "/app_runlogs/" + runlogUuid); + String actionStatus = actionStatusResponse.getJSONObject("status").getString("state"); + String state = "SUCCESS"; + String actionName = actionStatusResponse.getJSONObject("status").getJSONObject("action_reference").getString("name"); + + while (!actionStatus.equals(state)) { + TimeUnit.SECONDS.sleep(45); + actionStatusResponse = rest.get("apps/" + applicationUuid + "/app_runlogs/" + runlogUuid); + actionStatus = actionStatusResponse.getJSONObject("status").getString("state"); + if (actionStatus.equals(state) || actionStatus.equalsIgnoreCase("error")) + break; + logger.println(" Application Action " + actionName + " status is : " + actionStatus); + } + return actionStatus; + } + + public String getAppUUID(String appName)throws Exception { + return applicationNameUuidPair.get(appName); + } + public void taskOutput(String appUuid, String action, PrintStream logger)throws Exception { + String actionParentReference = null; + String actionData; + String spec = "{\"filter\": \"application_reference==" + appUuid + ";(type==action_runlog,type==audit_runlog)\"}"; + JSONObject response = rest.post("apps/" + appUuid + "/app_runlogs/list", spec); + JSONArray entities = response.getJSONArray("entities"); + for (int i = 0; i < entities.length(); i++) { + JSONObject eachAction = entities.getJSONObject(i); + String actionName = eachAction.getJSONObject("status").getJSONObject("action_reference").getString("name"); + if (actionName.equals(action)) { + actionParentReference = eachAction.getJSONObject("metadata").getString("uuid"); + actionData = "{\"filter\": \"root_reference==" + actionParentReference + "\" }"; + response = rest.post("apps/" + appUuid + "/app_runlogs/list", actionData); + entities = response.getJSONArray("entities"); + for (int j = 0; j < entities.length(); j++) { + eachAction = entities.getJSONObject(j); + if (eachAction.getJSONObject("status").has("task_reference")) { + String taskName = eachAction.getJSONObject("status").getJSONObject("task_reference").getString("name"); + String taskUuid = eachAction.getJSONObject("metadata").getString("uuid"); + response = rest.get("apps/" + appUuid + "/app_runlogs/" + taskUuid + "/output"); + JSONArray outputList = response.getJSONObject("status").getJSONArray("output_list"); + String output = outputList.getJSONObject(0).getString("output"); + logger.println("Task Name: " + taskName); + logger.println(taskName + " output is " + output); + } + } + } + break; + } + } + + public String getAppName(String appUUID) throws Exception { + JSONObject appResponse = getApplicationDetails(appUUID); + return appResponse.getJSONObject("status").getString("name"); + } + + public String getAppState(String appName)throws Exception{ + String appUUID = applicationNameUuidPair.get(appName); + JSONObject appResponse = getApplicationDetails(appUUID); + return appResponse.getJSONObject("status").getString("state"); + } + + public static List getSystemActionsList(){ + return new ArrayList<>(systemActions.keySet()); + } + + public List getProfileActionsFromBlueprint(String blueprintName, String appProfileName) throws Exception{ + String blueprintUuid = Blueprint.blueprintNameUuidPair.get(blueprintName); + JSONObject blueprint = blueprintHelper.getBlueprintDetails(blueprintUuid); + JSONArray appProfileList = blueprint.getJSONObject("spec").getJSONObject("resources"). + getJSONArray("app_profile_list"); + List profileActions = new ArrayList(); + for (int i = 0; i < appProfileList.length(); i++) { + JSONObject profilesList = appProfileList.getJSONObject(i); + if (profilesList.getString("name").equals(appProfileName)) { + JSONArray actionList = profilesList.getJSONArray("action_list"); + for (int j = 0; j < actionList.length(); j++) { + JSONObject eachAction = actionList.getJSONObject(j); + String actionName = eachAction.getString("name"); + profileActions.add(actionName); + } + break; + } + } + profileActions.addAll(Application.getSystemActionsList()); + return profileActions; + } + + + public String getProfileActionsVariablesFromBlueprint(String blueprintName, String appProfileName, String actionName) throws Exception{ + String blueprintUuid = Blueprint.blueprintNameUuidPair.get(blueprintName); + JSONObject blueprint = blueprintHelper.getBlueprintDetails(blueprintUuid); + JSONArray appProfileList = blueprint.getJSONObject("spec").getJSONObject("resources"). + getJSONArray("app_profile_list"); + HashMap profileActionsRuntimeMap = new HashMap(); + for (int i = 0; i < appProfileList.length(); i++) { + JSONObject appProfile = appProfileList.getJSONObject(i); + if(appProfile.getString("name").equals(appProfileName)) { + //fetch profile action runtime variables + JSONArray actionList = appProfile.getJSONArray("action_list"); + for (int j = 0; j < actionList.length(); j++) { + JSONObject action = actionList.getJSONObject(j); + if (action.getString("name").equals(actionName)) { + JSONArray variableList = action.getJSONObject("runbook").getJSONArray("variable_list"); + profileActionsRuntimeMap = blueprintHelper.getRuntimeVariables(variableList); + break; + } + } + break; + } + } + JSONObject appProfileActionVariables = new JSONObject(); + for(String key : profileActionsRuntimeMap.keySet()) + appProfileActionVariables.put(key, profileActionsRuntimeMap.get(key)); + return appProfileActionVariables.toString(); + } +} + + diff --git a/src/main/java/com/calm/CalmHelpers/Blueprint.java b/src/main/java/com/calm/CalmHelpers/Blueprint.java new file mode 100644 index 0000000..2cf8fde --- /dev/null +++ b/src/main/java/com/calm/CalmHelpers/Blueprint.java @@ -0,0 +1,317 @@ +package com.calm.CalmHelpers; + +import com.calm.Interface.Rest; +import com.calm.Logger.NutanixCalmLogger; +import net.sf.json.processors.JsonBeanProcessor; +import org.json.JSONArray; +import org.json.JSONObject; +import java.util.*; + + +public class Blueprint { + private final Rest rest; + private final long THRESHOLD = 300000l; + private static long lastUpdated; + public static Map blueprintNameUuidPair; + private static Map> projectBlueprintPairs; + private static Blueprint blueprintHelper; + private final static NutanixCalmLogger LOGGER = new NutanixCalmLogger(Blueprint.class); + + private Blueprint(Rest rest) throws Exception{ + blueprintNameUuidPair = new HashMap<>(); + projectBlueprintPairs = new HashMap<>(); + this.rest = rest; + fetchBlueprintList(); + lastUpdated = System.currentTimeMillis(); + } + + public static Blueprint getInstance(Rest rest)throws Exception{ +// if(blueprintHelper != null) { +// long currentTime = System.currentTimeMillis(); +// if((currentTime - lastUpdated) > blueprintHelper.THRESHOLD) { +// blueprintHelper.fetchBlueprintList(); +// lastUpdated = currentTime; +// } +// return blueprintHelper; +// } + return new Blueprint(rest); + } + + private void fetchBlueprintList(){ + /** + * This method fetches the blueprints from pc. It stores the whole bp objects as json in blueprintList, and + * project, bp name as pairs in projectBlueprintPairs + */ + JSONObject responseObject; + JSONArray blueprintEntities; + try { + responseObject = rest.post("blueprints/list"); + blueprintEntities = responseObject.getJSONArray("entities"); + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while fetching blueprints " +e.getMessage()); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + blueprintNameUuidPair = null; + projectBlueprintPairs = null; + return; + } + for (int i = 0; i < blueprintEntities.length(); i++) { + JSONObject blueprint = null; + try { + blueprint = blueprintEntities.getJSONObject(i); + String status = blueprint.getJSONObject("status").getString("state"); + if (status.equals("ACTIVE") && blueprint.getJSONObject("metadata").has("project_reference")) { + String project_name = blueprint.getJSONObject("metadata").getJSONObject("project_reference").getString("name"); + String blueprintName = blueprint.getJSONObject("status").getString("name"); + blueprintNameUuidPair.put(blueprintName, blueprint.getJSONObject("metadata").getString("uuid")); + List currentList = projectBlueprintPairs.get(project_name) != null ? + projectBlueprintPairs.get(project_name) : new ArrayList(); + currentList.add(blueprintName); + projectBlueprintPairs.put(project_name, currentList); + } + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while parsing the blueprint: " + blueprint.toString()); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + } + } + } + + public List getBlueprintsList(String projectName){ + /** + * This method returns the list of blueprint names for the given project. If the relevant project is not found + * returns null + */ + if(projectBlueprintPairs != null) { + List blueprints = projectBlueprintPairs.get(projectName); + if(blueprints != null) + return blueprints; + return new ArrayList<>(); + } + return null; + } + + public String fetchBlueprintDescription(String blueprintName){ + String blueprintUuid = blueprintNameUuidPair.get(blueprintName); + try{ + JSONObject blueprint = getBlueprintDetails(blueprintUuid); + return blueprint.getJSONObject("status").getString("description"); + } + catch(Exception e){ + LOGGER.debug("ERROR occurred while fetching blueprint details"); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + } + return ""; + } + + public JSONObject getBlueprintDetails(String blueprintUuid) throws Exception{ + return rest.get("blueprints/" + blueprintUuid); + } + public JSONObject getBlueprintSpec(String blueprintName, String appProfileName, String runtimeVariables, + String applicationName) throws Exception{ + String blueprintUuid = blueprintNameUuidPair.get(blueprintName); + JSONObject blueprintJson = getBlueprintDetails(blueprintUuid); + JSONObject runtimeVariablesJson = new JSONObject(runtimeVariables); + //JSONObject appProfileVariables = runtimeVariablesJson.getJSONObject("appProfileVariables"); + if(runtimeVariablesJson.length() > 0) + blueprintJson = patchAppProfileVariables(blueprintJson, runtimeVariablesJson, appProfileName); + String appname = applicationName; + blueprintJson.remove("status"); + blueprintJson.getJSONObject("spec").remove("name"); + blueprintJson.getJSONObject("spec").put("application_name", appname); + String appProfileUuid = null; + JSONArray appProfileList = blueprintJson.getJSONObject("spec").getJSONObject("resources").getJSONArray("app_profile_list"); + for (int i = 0; i < appProfileList.length(); i++) { + JSONObject appProfile = appProfileList.getJSONObject(i); + if (appProfile.getString("name").equals(appProfileName)) { + appProfileUuid = appProfile.getString("uuid"); + break; + } + } + if (appProfileUuid == null) { + throw new Exception("App profile with name " + appProfileName + " not found in list"); + } + JSONObject appProfileReference = new JSONObject(); + appProfileReference.put("kind", "app_profile"); + appProfileReference.put("uuid", appProfileUuid); + blueprintJson.getJSONObject("spec").put("app_profile_reference", appProfileReference); + return blueprintJson; + } + + + public List getAppProfiles(String blueprintName){ + /** + * This method returns the appProfile list for the given blueprint + */ + List appProfileList = new ArrayList<>(); + JSONArray appProfiles; + try { + String blueprintUuid = blueprintNameUuidPair.get(blueprintName); + JSONObject blueprint = getBlueprintDetails(blueprintUuid); + appProfiles = blueprint.getJSONObject("spec"). + getJSONObject("resources").getJSONArray("app_profile_list"); + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while fetching app profile for: " + blueprintName); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + return null; + } + for (int i = 0; i < appProfiles.length(); i++) { + JSONObject appProfile = null; + try { + appProfile = appProfiles.getJSONObject(i); + String profileName = appProfile.getString("name"); + appProfileList.add(profileName); + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while parsing: "+ appProfile.toString()); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + } + } + return appProfileList; + } + + public List getProfileActions(String blueprintName, String appProfileName) throws Exception{ + String blueprintUuid = blueprintNameUuidPair.get(blueprintName); + JSONObject blueprint = getBlueprintDetails(blueprintUuid); + JSONArray appProfileList = blueprint.getJSONObject("spec").getJSONObject("resources"). + getJSONArray("app_profile_list"); + List profileActions = new ArrayList(); + for (int i = 0; i < appProfileList.length(); i++) { + JSONObject profilesList = appProfileList.getJSONObject(i); + if (profilesList.getString("name").equals(appProfileName)) { + JSONArray actionList; + try { + actionList = profilesList.getJSONArray("action_list"); + for (int j = 0; j < actionList.length(); j++) { + JSONObject eachAction = actionList.getJSONObject(j); + String actionName = eachAction.getString("name"); + profileActions.add(actionName); + } + break; + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while fetching actions for"+ appProfileName); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + return null; + } + } + } + profileActions.addAll(Application.getSystemActionsList()); + return profileActions; + } + + public String fetchRunTimeProfileVariables(String blueprintName, String appProfileName) throws Exception{ + String blueprintUuid = blueprintNameUuidPair.get(blueprintName); + JSONObject blueprint = getBlueprintDetails(blueprintUuid); + JSONArray appProfileList = blueprint.getJSONObject("spec").getJSONObject("resources"). + getJSONArray("app_profile_list"); + HashMap appProfileRuntimeMap = new HashMap(); + for (int i = 0; i < appProfileList.length(); i++) { + JSONObject appProfile = appProfileList.getJSONObject(i); + if(appProfile.getString("name").equals(appProfileName)) { + try { + appProfileRuntimeMap = getRuntimeVariables(appProfile.getJSONArray("variable_list")); + break; + } + catch(Exception e){ + LOGGER.debug("ERROR occurred while fetching runtime variables"); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + return null; + } + + } + } + JSONObject appProfileVariables = new JSONObject(); + for(String key : appProfileRuntimeMap.keySet()) + appProfileVariables.put(key, appProfileRuntimeMap.get(key)); + return appProfileVariables.toString(); + } + + public String fetchRunTimeActionVariables(String blueprintName, String appProfileName, + String actionName) throws Exception{ + String blueprintUuid = blueprintNameUuidPair.get(blueprintName); + JSONObject blueprint = getBlueprintDetails(blueprintUuid); + JSONArray appProfileList = blueprint.getJSONObject("spec").getJSONObject("resources"). + getJSONArray("app_profile_list"); + HashMap profileActionsRuntimeMap = new HashMap(); + for (int i = 0; i < appProfileList.length(); i++) { + JSONObject appProfile = appProfileList.getJSONObject(i); + if(appProfile.getString("name").equals(appProfileName)) { + //fetch profile action runtime variables + JSONArray actionList = appProfile.getJSONArray("action_list"); + for (int j = 0; j < actionList.length(); j++) { + JSONObject action = actionList.getJSONObject(j); + if (action.getString("name").equals(actionName)) { + JSONArray variableList = action.getJSONObject("runbook").getJSONArray("variable_list"); + profileActionsRuntimeMap = getRuntimeVariables(variableList); + break; + } + } + break; + } + } + JSONObject appProfileActionVariables = new JSONObject(); + for(String key : profileActionsRuntimeMap.keySet()) + appProfileActionVariables.put(key, profileActionsRuntimeMap.get(key)); + return appProfileActionVariables.toString(); + } + + + public HashMap getRuntimeVariables(JSONArray variableList) throws Exception{ + HashMap keyValuePair = new HashMap<>(); + for (int i = 0; i < variableList.length(); i++) { + JSONObject variable = variableList.getJSONObject(i); + if (variable.has("editables")) { + JSONObject editables = variable.getJSONObject("editables"); + if ((editables.has("value") && editables.getBoolean("value"))) { + if ((variable.has("attrs")) && (variable.getJSONObject("attrs").has("is_secret_modified"))) { + JSONObject attrs = variable.getJSONObject("attrs"); + String name = (variable.getString("name")); + String value = ""; + keyValuePair.put(name, value); + } else { + String name = (variable.getString("name")); + String value = (variable.getString("value")); + keyValuePair.put(name, value); + } + } + } + } + return keyValuePair; + } + + private JSONObject patchAppProfileVariables(JSONObject blueprintJson, JSONObject profileVariables, + String appProfileName) throws Exception{ + JSONArray appProfileList = blueprintJson.getJSONObject("spec").getJSONObject("resources").getJSONArray("app_profile_list"); + for (int i = 0; i < appProfileList.length(); i++) { + JSONObject appProfile = appProfileList.getJSONObject(i); + if (appProfile.getString("name").equals(appProfileName)) { + Iterator iterator = profileVariables.keys(); + while (iterator.hasNext()) { + String key = iterator.next(); + String val = (String) profileVariables.get(key); + JSONArray varlist = appProfile.getJSONArray("variable_list"); + for (int k = 0; k < varlist.length(); k++) { + JSONObject variable = varlist.getJSONObject(k); + if (variable.getString("name").equals(key)) { + blueprintJson.getJSONObject("spec").getJSONObject("resources").getJSONArray("app_profile_list"). + getJSONObject(i).getJSONArray("variable_list").getJSONObject(k).put("value", val); + } + } + } + break; + } + + } + return blueprintJson; + } + + public JSONObject launchBlueprint(String blueprintUuid, JSONObject blueprintSpec) throws Exception{ + return rest.post("blueprints/" + blueprintUuid +"/launch", blueprintSpec.toString()); + } +} + + + + diff --git a/src/main/java/com/calm/CalmHelpers/Project.java b/src/main/java/com/calm/CalmHelpers/Project.java new file mode 100644 index 0000000..2e0b07a --- /dev/null +++ b/src/main/java/com/calm/CalmHelpers/Project.java @@ -0,0 +1,76 @@ +package com.calm.CalmHelpers; + +import com.calm.Interface.Rest; +import com.calm.Logger.NutanixCalmLogger; +import org.json.JSONArray; +import org.json.JSONObject; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + + +public class Project { + private final Rest rest; + private final long THRESHOLD = 300000l; + private static long lastUpdated; + private static HashMap projectNameUUID; + private static List projectNames; + private static Project projectHelper; + private final static NutanixCalmLogger LOGGER = new NutanixCalmLogger(Project.class); + + private Project(Rest rest) throws Exception{ + this.rest = rest; + projectNameUUID = new HashMap<>(); + projectNames = new ArrayList<>(); + fetchProjects(); + lastUpdated = System.currentTimeMillis(); + } + + public static Project getInstance(Rest rest) throws Exception{ +// if(projectHelper != null) { +// long currentTime = System.currentTimeMillis(); +// if((currentTime - lastUpdated) > projectHelper.THRESHOLD) { +// projectHelper.fetchProjects(); +// lastUpdated = currentTime; +// } +// return projectHelper; +// } + return new Project(rest); + } + private void fetchProjects(){ + JSONArray projectListObject; + try { + JSONObject response = rest.post("projects/list"); + projectListObject = response.getJSONArray("entities"); + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while fetching projects"); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + projectNames = null; + return; + } + for (int i = 0; i < projectListObject.length(); i++) { + try { + JSONObject project = projectListObject.getJSONObject(i); + String projectName = project.getJSONObject("status").getString("name"); + String projectUuid = project.getJSONObject("metadata").getString("uuid"); + projectNameUUID.put(projectName, projectUuid); + projectNames.add(projectName); + } + catch (Exception e){ + LOGGER.debug("ERROR occurred while paring a project object"); + LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); + } + } + } + + public List getProjectNames(){ + return projectNames; + } + + public String getProjectUUID(String projectName){ + + return projectNameUUID.get(projectName); + + } +} diff --git a/src/main/java/com/calm/Descriptors/BlueprintLaunch.java b/src/main/java/com/calm/Descriptors/BlueprintLaunch.java new file mode 100644 index 0000000..c361e7a --- /dev/null +++ b/src/main/java/com/calm/Descriptors/BlueprintLaunch.java @@ -0,0 +1,261 @@ +package com.calm.Descriptors; + + +import com.calm.CalmHelpers.Blueprint; +import com.calm.CalmHelpers.Project; +import com.calm.Executor.CalmExecutor; +import com.calm.GlobalConfiguration.CalmGlobalConfiguration; +import com.calm.Interface.Rest; +import com.calm.Logger.NutanixCalmLogger; +import hudson.util.ListBoxModel; +import org.kohsuke.stapler.bind.JavaScriptMethod; +import java.io.*; +import java.util.*; +import net.sf.json.*; +import hudson.*; +import hudson.model.*; +import hudson.tasks.*; +import org.kohsuke.stapler.*; +import jenkins.tasks.SimpleBuildStep; + + +public class BlueprintLaunch extends Builder implements SimpleBuildStep { + + + private final String projectName, blueprintName, applicationName, appProfileName, actionName, runtimeVariables; + private String blueprintDescription; + private final boolean waitForSuccessFulLaunch; + + + // Fields in config.jelly must match the parameter names in the "DataBoundConstructor" + @DataBoundConstructor + public BlueprintLaunch(String projectName, String blueprintName, String applicationName, String appProfileName, + String actionName, String runtimeVariables, boolean waitForSuccessFulLaunch, String blueprintDescription) { + this.projectName = projectName; + this.blueprintName = blueprintName; + this.applicationName = applicationName; + this.appProfileName = appProfileName; + this.actionName = actionName; + this.runtimeVariables = runtimeVariables; + this.waitForSuccessFulLaunch = waitForSuccessFulLaunch; + this.blueprintDescription = blueprintDescription; + } + + public String getProjectName() { + return projectName; + } + + public String getBlueprintName() { + return blueprintName; + } + + public String getApplicationName() { + return applicationName; + } + + public String getAppProfileName() { + return appProfileName; + } + + public String getActionName() { + return actionName; + } + + public String getRuntimeVariables() { + return runtimeVariables; + } + + public boolean getWaitForSuccessFulLaunch() { + return waitForSuccessFulLaunch; + } + + public String getBlueprintDescription(){ + return blueprintDescription; + } + + @Override + public void perform(Run build, FilePath workspace, Launcher launcher, TaskListener listener) + throws IOException, InterruptedException { + // This is where you 'build' the project. + EnvVars envVars = new EnvVars(); + final EnvVars env = build.getEnvironment(listener); + PrintStream log = listener.getLogger(); + //Expanding appname to include the env variables in it's name + String expandedApplicationName = env.expand(applicationName); + log.println("Executing Nutanix Calm Blueprint launch Build Step"); + BlueprintLaunchDescriptorImpl blueprintLaunchDescriptor = getDescriptor(); + String prismCentralIp = blueprintLaunchDescriptor.getPrismCentralIp(); + String userName = blueprintLaunchDescriptor.getUserName(); + String password = blueprintLaunchDescriptor.getPassword(); + CalmExecutor calmExecutor = new CalmExecutor(prismCentralIp, userName, password, projectName, blueprintName, + appProfileName, actionName, runtimeVariables, expandedApplicationName, waitForSuccessFulLaunch, log); + log.println("##Connecting to calm instance##"); + List globalError = new ArrayList(); + try{ + if (prismCentralIp == null || prismCentralIp.length() == 0) { + globalError.add("IP Address is mandatory parameter"); + } + + if (userName == null || userName.length() == 0) { + globalError.add("Username is mandatory parameter"); + } + + if (password == null || password.length() == 0) { + globalError.add("Password is mandatory parameter"); + } + + if (applicationName == null || applicationName.length() == 0) { + globalError.add("Application name is mandatory"); + } + + if (globalError.size() > 0){ + listener.error("Nutanix Calm Prism Central Details Required" + globalError); + build.setResult(Result.FAILURE); + } + calmExecutor.launchBlueprint(); + } + catch (Exception e){ + log.println(e.getMessage()); + build.setResult(Result.FAILURE); + + } + } + + // Overridden for better type safety. + // If your plugin doesn't really define any property on Descriptor, + // you don't have to do this. + @Override + public BlueprintLaunchDescriptorImpl getDescriptor() { + return (BlueprintLaunchDescriptorImpl) super.getDescriptor(); + } + + + @Extension // This indicates to Jenkins that this is an implementation of an extension point. + public static final class BlueprintLaunchDescriptorImpl extends BuildStepDescriptor { + + private String prismCentralIp; + private String userName; + private String password; + private Project projectHelper; + private Blueprint blueprintHelper; + private int lastEditorId = 0; + private Rest rest; + private final static NutanixCalmLogger LOGGER = new NutanixCalmLogger(BlueprintLaunchDescriptorImpl.class); + private String getPrismCentralIp() { + return prismCentralIp; + } + + private String getUserName() { + return userName; + } + + private String getPassword() { + return password; + } + + public BlueprintLaunchDescriptorImpl(){ + load(); + } + + public boolean isApplicable(Class aClass) { + // Indicates that this builder can be used with all kinds of project types + return true; + } + + + public String getDisplayName() { + return "Nutanix Calm Blueprint Launch"; + } + + + @Override + public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { + save(); + LOGGER.debug("Inside configure"); + return super.configure(req, formData); + + } + + @JavaScriptMethod + public synchronized String createEditorId() { + CalmGlobalConfiguration calmGlobalConfiguration = CalmGlobalConfiguration.get(); + prismCentralIp = calmGlobalConfiguration.getPrismCentralIp(); + userName = calmGlobalConfiguration.getUserName(); + password = calmGlobalConfiguration.getPassword(); + rest = new Rest(prismCentralIp, userName, password); +// try { +// projectHelper = Project.getInstance(rest); +// blueprintHelper = Blueprint.getInstance(rest); +// } +// catch (Exception e){ +// LOGGER.debug("ERROR occurred while initializing the project and blueprint helper"); +// LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); +// } + return String.valueOf(lastEditorId++); + } + + + @JavaScriptMethod + public List fetchProjects()throws Exception{ + /** + * This method gets called by the javascript in the config.jelly for listing projects in UI + */ + return Project.getInstance(rest).getProjectNames(); + } + + + @JavaScriptMethod + public List fetchBlueprints(String projectName)throws Exception{ + /** + * This method get called by the javascript in the config.jelly for listing blueprints in UI + */ + blueprintHelper = Blueprint.getInstance(rest); + return blueprintHelper.getBlueprintsList(projectName); + } + + @JavaScriptMethod + public List fetchAppProfiles(String blueprintName)throws Exception{ + /** + * This method get called by the javascript in the config.jelly for listing profiles in UI + */ + + return blueprintHelper.getAppProfiles(blueprintName); + } + + @JavaScriptMethod + public List fetchProfileActions(String blueprintName,String appProfileName)throws Exception{ + return blueprintHelper.getProfileActions(blueprintName, appProfileName); + } + + @JavaScriptMethod + public String fetchRuntimeProfileVariables(String blueprintName, String appProfileName)throws Exception{ + return blueprintHelper.fetchRunTimeProfileVariables(blueprintName, appProfileName); + } + + @JavaScriptMethod + public String fetchBlueprintDescription(String blueprintName){ + return blueprintHelper.fetchBlueprintDescription(blueprintName); + } + + public ListBoxModel doFillProjectNameItems(@QueryParameter("projectName") String projectName){ + return new ListBoxModel(new ListBoxModel.Option(projectName)); + } + + public ListBoxModel doFillBlueprintNameItems(@QueryParameter("blueprintName") String blueprintName){ + return new ListBoxModel(new ListBoxModel.Option(blueprintName)); + } + + public ListBoxModel doFillAppProfileNameItems(@QueryParameter("appProfileName") String appProfileName){ + return new ListBoxModel(new ListBoxModel.Option(appProfileName)); + } + + + public ListBoxModel doFillActionNameItems(@QueryParameter("actionName") String actionName){ + return new ListBoxModel(new ListBoxModel.Option(actionName)); + } + + + } + + +} diff --git a/src/main/java/com/calm/Descriptors/RunApplicationAction.java b/src/main/java/com/calm/Descriptors/RunApplicationAction.java new file mode 100644 index 0000000..18a1c78 --- /dev/null +++ b/src/main/java/com/calm/Descriptors/RunApplicationAction.java @@ -0,0 +1,202 @@ +package com.calm.Descriptors; + +import com.calm.CalmHelpers.Application; +import com.calm.CalmHelpers.Blueprint; +import com.calm.Executor.CalmExecutor; +import com.calm.GlobalConfiguration.CalmGlobalConfiguration; +import com.calm.Interface.Rest; +import com.calm.Logger.NutanixCalmLogger; +import hudson.util.ListBoxModel; +import net.sf.json.JSONObject; +import org.kohsuke.stapler.bind.JavaScriptMethod; +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import hudson.*; +import hudson.model.*; +import hudson.tasks.*; +import org.kohsuke.stapler.*; +import jenkins.tasks.SimpleBuildStep; + + +public class RunApplicationAction extends Builder implements SimpleBuildStep { + + private final String applicationName, actionName, runtimeVariables; + private String applicationUuid; + + @DataBoundConstructor + public RunApplicationAction(String applicationName, String actionName, String runtimeVariables){ + this.applicationName = applicationName; + this.actionName = actionName; + this.runtimeVariables = runtimeVariables; + } + + public String getApplicationName() { + return applicationName; + } + + + public String getActionName(){ + return actionName; + } + + public String getRuntimeVariables(){ + return runtimeVariables; + } + + + @Override + public void perform(Run build, FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { + PrintStream log = listener.getLogger(); + RunActionDescriptorImpl actionDescriptor = getDescriptor(); + String prismCentralIp = actionDescriptor.getPrismCentralIp(); + String userName = actionDescriptor.getUserName(); + String password = actionDescriptor.getPassword(); + Rest rest = actionDescriptor.getRest(); + EnvVars envVars = new EnvVars(); + final EnvVars env = build.getEnvironment(listener); + //Expanding appname to include the env variables in it's name + String expandedApplicationName = env.expand(applicationName); + + try { + applicationUuid = Application.getInstance(rest).getAppUUID(expandedApplicationName); + } + catch (Exception e) { + log.println(e.getMessage()); + } + log.println(" "); + log.println("Executing Nutanix Calm Application Action Run Build Step"); + CalmExecutor calmExecutor = new CalmExecutor(prismCentralIp, userName, password, expandedApplicationName, actionName, runtimeVariables, log); + log.println("##Connecting to calm instance##"); + List globalError = new ArrayList(); + try{ + if (prismCentralIp == null || prismCentralIp.length() == 0) { + globalError.add("IP Address is mandatory parameter"); + } + + if (userName == null || userName.length() == 0) { + globalError.add("Username is mandatory parameter"); + } + + if (password == null || password.length() == 0) { + globalError.add("Password is mandatory parameter"); + } + + if (globalError.size() > 0){ + listener.error("Nutanix Calm Prism Central Details Required" + globalError); + build.setResult(Result.FAILURE); + } + calmExecutor.runAppAction(); + String action = Application.systemActions.get(actionName) != null ? Application.systemActions.get(actionName) : actionName; + Application.getInstance(rest).taskOutput(applicationUuid, action, log); + } + catch (Exception e){ + log.println(e.getMessage()); + build.setResult(Result.FAILURE); + } + } + + + @Override + public RunActionDescriptorImpl getDescriptor() { + return (RunActionDescriptorImpl) super.getDescriptor(); + } + + @Extension // This indicates to Jenkins that this is an implementation of an extension point. + public static final class RunActionDescriptorImpl extends BuildStepDescriptor { + + private String prismCentralIp; + private String userName; + private String password; + private Rest rest; + private int lastEditorId = 0; + private Application applicationHelper; + private final static NutanixCalmLogger LOGGER = new NutanixCalmLogger(RunActionDescriptorImpl.class); + + private String getPrismCentralIp() { + return prismCentralIp; + } + + private String getUserName() { + return userName; + } + + private String getPassword() { + return password; + } + + private Rest getRest (){ return rest;} + + public RunActionDescriptorImpl(){ + load(); + } + + public boolean isApplicable(Class aClass) { + return true; + } + + public String getDisplayName() { + return "Nutanix Calm Application Action Run"; + } + + @Override + public boolean configure(StaplerRequest req, JSONObject formData) throws FormException { + save(); + return super.configure(req, formData); + } + + @JavaScriptMethod + public synchronized String createEditorId() { + CalmGlobalConfiguration calmGlobalConfiguration = CalmGlobalConfiguration.get(); + prismCentralIp = calmGlobalConfiguration.getPrismCentralIp(); + userName = calmGlobalConfiguration.getUserName(); + password = calmGlobalConfiguration.getPassword(); + rest = new Rest(prismCentralIp, userName, password); +// try{ +// applicationHelper = Application.getInstance(rest); +// } +// catch (Exception e){ +// LOGGER.debug("ERROR occurred while initializing the application helper"); +// LOGGER.debug(LOGGER.getStackTraceStr(e.getStackTrace())); +// } + return String.valueOf(lastEditorId++); + } + + + @JavaScriptMethod + public List fetchApplications()throws Exception{ + applicationHelper = Application.getInstance(rest); + return applicationHelper.getApplicationNames(); + } + + @JavaScriptMethod + public List fetchApplicationActions(String applicationName)throws Exception{ + return applicationHelper.getApplicationActions(applicationName); + } + + @JavaScriptMethod + public String fetchRuntimeProfileActionVariables(String applicationName, String actionName)throws Exception{ + return applicationHelper.fetchRuntimeProfileActionVariables(applicationName, actionName); + } + + @JavaScriptMethod + public List getProfileActionsFromBlueprint(String blueprintName, String appProfileName)throws Exception{ + return applicationHelper.getProfileActionsFromBlueprint(blueprintName, appProfileName); + } + + @JavaScriptMethod + public String getProfileActionsVariablesFromBlueprint(String blueprintName, String appProfileName, String actionName)throws Exception{ + return applicationHelper.getProfileActionsVariablesFromBlueprint(blueprintName, appProfileName, actionName); + } + + public ListBoxModel doFillApplicationNameItems(@QueryParameter("applicationName") String applicationName){ + return new ListBoxModel(new ListBoxModel.Option(applicationName)); + } + + public ListBoxModel doFillActionNameItems(@QueryParameter("actionName") String actionName){ + return new ListBoxModel(new ListBoxModel.Option(actionName)); + } + + } + +} diff --git a/src/main/java/com/calm/Executor/CalmExecutor.java b/src/main/java/com/calm/Executor/CalmExecutor.java new file mode 100644 index 0000000..f2a842a --- /dev/null +++ b/src/main/java/com/calm/Executor/CalmExecutor.java @@ -0,0 +1,142 @@ +package com.calm.Executor; + +import com.calm.CalmHelpers.Application; +import com.calm.CalmHelpers.Blueprint; +import com.calm.CalmHelpers.Project; +import com.calm.Interface.Rest; +import org.json.JSONObject; +import org.json.simple.parser.JSONParser; + +import java.io.File; +import java.io.FileReader; +import java.io.PrintStream; + +public class CalmExecutor { + private String prismCentralIp, userName, password, blueprintName, applicationName, applicationProfileName, actionName, + projectName, runTimeVariables; + private boolean waitForLaunchSuccess; + private final PrintStream logger; + + + public CalmExecutor(String prismCentralIp, String userName, String password, String projectName, String blueprintName, + String appProfileName, String actionName, String runtimeVariables, String applicationName, + boolean waitForLaunchSuccess, PrintStream logger) { + this.prismCentralIp = prismCentralIp; + this.userName = userName; + this.password = password; + this.projectName = projectName; + this.blueprintName = blueprintName; + this.applicationName = applicationName; + this.applicationProfileName = appProfileName; + this.actionName = actionName; + this.runTimeVariables = runtimeVariables; + this.waitForLaunchSuccess = waitForLaunchSuccess; + this.logger = logger; + } + + public CalmExecutor(String prismCentralIp, String userName, String password, String applicationName, + String actionName, String runTimeVariables, PrintStream logger){ + this.prismCentralIp = prismCentralIp; + this.userName = userName; + this.password = password; + this.applicationName = applicationName; + this.actionName = actionName; + this.runTimeVariables = runTimeVariables; + this.logger = logger; + + } + + public void launchBlueprint()throws Exception{ + Rest rest = new Rest(this.prismCentralIp, this.userName, this.password); + Blueprint blueprintHelper = Blueprint.getInstance(rest); + Application applicationHelper = Application.getInstance(rest); + String applicationUuid = null; + logger.println(" Selected Project: " + this.projectName); + logger.println(" Selected Blueprint: "+ this.blueprintName); + logger.println(" Selected Profile: " + this.applicationProfileName); + logger.println(" Application Name: " + this.applicationName); + + JSONObject blueprintJson = blueprintHelper.getBlueprintSpec(this.blueprintName, this.applicationProfileName, + this.runTimeVariables, this.applicationName); + String blueprintUuid = blueprintJson.getJSONObject("metadata").getString("uuid"); + JSONObject response; + try{ + response = blueprintHelper.launchBlueprint(blueprintUuid, blueprintJson); + } + catch (Exception e){ + throw new Exception("Blueprint launch failed with error: \n" + e.getMessage()); + } + + if(this.waitForLaunchSuccess){ + logger.println(" "); + logger.println("Waiting for application " + this.applicationName + " launch to complete : "); + } + else{ + logger.println(" "); + logger.println("Blueprint launched sucessfully"); + } + + if (this.waitForLaunchSuccess) { + String requestId = response.getJSONObject("status").getString("request_id"); + applicationUuid = applicationHelper.getApplicationUuidByRequestId(blueprintUuid, requestId); + Application.applicationNameUuidPair.put(applicationName, applicationUuid); + String applicationStatus = applicationHelper.waitForApplicationToGoIntoSuccessState(this.applicationName, applicationUuid, "running", logger); + if (!applicationStatus.equals("running")) { + applicationHelper.taskOutput(applicationUuid, "action_create", logger); + throw new Exception("Application " + this.applicationName + " has failed with an error, please have a look into this app in your PC"); + + } + else{ + applicationHelper.taskOutput(applicationUuid, "action_create", logger); + logger.println(" Application " + this.applicationName + " is " + applicationStatus); + } + } + } + + public void runAppAction()throws Exception{ + Rest rest = new Rest(prismCentralIp, userName, password); + Application applicationHelper = Application.getInstance(rest); + String appUuid = applicationHelper.getAppUUID(this.applicationName); + JSONObject appResponse = applicationHelper.getApplicationDetails(appUuid); + String projectName = appResponse.getJSONObject("metadata").getJSONObject("project_reference").getString("name"); + String projectUUID = appResponse.getJSONObject("metadata").getJSONObject("project_reference").getString("uuid"); + String spec = "{\n" + + " \"api_version\": \"3.0\",\n" + + " \"metadata\": {\n" + + " \"kind\": \"app\",\n" + + " \"spec_version\": 5,\n" + + " \"project_reference\": {\n" + + " \"kind\": \"project\",\n" + + " \"name\":\"" + projectName + "\",\n" + + " \"uuid\":\"" + projectUUID + "\"\n" + + " }\n" + + " },\n" + + " \"spec\": {\n" + + " \"target_uuid\":\"" + appUuid + "\",\n" + + " \"target_kind\": \"Application\",\n" + + " \"args\": []\n" + + " }\n" + + " }"; + JSONObject actionSpec = new JSONObject(spec); + actionSpec = applicationHelper.patchAppProfileActionVariables(actionSpec, this.applicationName, this.actionName, this.runTimeVariables, logger); + JSONObject actionRunResponse; + try{ + actionRunResponse = applicationHelper.runAction(this.applicationName, this.actionName, actionSpec); + } + catch (Exception e){ + logger.println("Action run failed with :\n" + e.getMessage()); + throw new Exception("Action run failed "+ e.getMessage()); + } + + String runlogUuid = actionRunResponse.getJSONObject("status").getString("runlog_uuid"); + String actionStatus = applicationHelper.waitForActionToComplete(this.applicationName, runlogUuid, this.logger); + if (!actionStatus.equals("SUCCESS")) { + //TODO : fetch failed task logs + throw new Exception("Application Action " + this.actionName + " has failed with an error, please have a look into this app in your PC"); + } + else{ + logger.println(" Application Action " + this.actionName + " is " + actionStatus); + } + } + +} diff --git a/src/main/java/com/calm/GlobalConfiguration/CalmGlobalConfiguration.java b/src/main/java/com/calm/GlobalConfiguration/CalmGlobalConfiguration.java new file mode 100644 index 0000000..d1c2386 --- /dev/null +++ b/src/main/java/com/calm/GlobalConfiguration/CalmGlobalConfiguration.java @@ -0,0 +1,57 @@ +package com.calm.GlobalConfiguration; + + +import hudson.Extension; +import jenkins.model.GlobalConfiguration; +import org.kohsuke.stapler.DataBoundSetter; + + + +@Extension +public class CalmGlobalConfiguration extends GlobalConfiguration { + + private String prismCentralIp; + private String userName; + private String password; + + public String getPrismCentralIp() { + return prismCentralIp; + } + + @DataBoundSetter + public void setPrismCentralIp(String prismCentralIp) { + this.prismCentralIp = prismCentralIp; + save(); + } + + public String getUserName() { + return userName; + } + + @DataBoundSetter + public void setUserName(String userName) { + this.userName = userName; + save(); + } + + public String getPassword() { + return password; + } + + @DataBoundSetter + public void setPassword(String password) { + this.password = password; + save(); + } + + public static CalmGlobalConfiguration get() { + return GlobalConfiguration.all().get(CalmGlobalConfiguration.class); + } + + public CalmGlobalConfiguration() { + // When Jenkins is restarted, load any saved configuration from disk. + load(); + } + + +} diff --git a/src/main/java/com/calm/Interface/Rest.java b/src/main/java/com/calm/Interface/Rest.java new file mode 100644 index 0000000..5de2ecf --- /dev/null +++ b/src/main/java/com/calm/Interface/Rest.java @@ -0,0 +1,135 @@ +package com.calm.Interface; +import org.json.JSONObject; + +import javax.net.ssl.*; +import javax.xml.bind.DatatypeConverter; +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.security.SecureRandom; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +public class Rest { + private String BASE_URL = "https://:9440/api/nutanix/v3/"; + private final static String DEFAULT_POST_PARAMS = "{\"length\":250}"; + private final static String GET = "GET"; + private final static String POST = "POST"; + private final static String DELETE = "DELETE"; + private final String PRISMCENTRALIP; + private final String USERNAME; + private final String PASSWORD; + + public Rest(String prismCentralIp, String userName, String password){ + PRISMCENTRALIP = prismCentralIp; + USERNAME = userName; + PASSWORD = password; + BASE_URL = BASE_URL.replace("", PRISMCENTRALIP); + } + + public JSONObject get(String relativeURL)throws Exception{ + return send_request(relativeURL, GET); + } + + public JSONObject post(String relativeURL, String... payload)throws Exception{ + + if(payload.length > 0) + return send_request(relativeURL, POST, payload[0]); + return send_request(relativeURL, POST); + } + + public JSONObject delete(String relativeURL) throws Exception{ + return send_request(relativeURL, DELETE); + } + + private JSONObject send_request(String relativeURL, String requestType, String... payload) throws Exception{ + + //add certificate + trustCertificates(); + + //Connect to url + URL url = new URL(BASE_URL + relativeURL); + HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); + try { + + String encoding = DatatypeConverter.printBase64Binary((USERNAME + ":" + PASSWORD).getBytes()); + httpURLConnection.setRequestMethod(requestType); + httpURLConnection.setDoOutput(true); + httpURLConnection.setRequestProperty("Authorization", "Basic " + encoding); + httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0"); + httpURLConnection.setRequestProperty("Content-Type", "application/json"); + + //send payload for post request + if(requestType.equals(POST)){ + httpURLConnection.setDoOutput(true); + OutputStream os = httpURLConnection.getOutputStream(); + if(payload.length > 0) + os.write(payload[0].getBytes()); + else + os.write(DEFAULT_POST_PARAMS.getBytes()); + os.flush(); + os.close(); + } + + //return the response on success + int responseCode = httpURLConnection.getResponseCode(); + switch (responseCode){ + case 200 : BufferedReader bufferedReader = + new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream())); + String inputLine; + StringBuffer response = new StringBuffer(); + while ((inputLine = bufferedReader.readLine()) != null) + response.append(inputLine); + return new JSONObject(response.toString()); + default : //TODO: Throw user defined exception + throw new Exception("URL: " + url.toString() + "\n" + + "Payload: " + (payload.length > 0?payload[0]:null) + "\n" + + "Response code: "+ responseCode + "\n" + + "Response: " + httpURLConnection.getResponseMessage()); + + } + } + catch (Exception e){ + System.out.println("Error occurred while sending request"); + throw e; + } + finally { + //close connection + httpURLConnection.disconnect(); + } + } + + private void trustCertificates() throws Exception{ + //Security.addProvider(new Provider()); + TrustManager[] trustAllCerts = new TrustManager[]{ + new X509TrustManager() { + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { + return; + } + + public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException { + return; + } + } + }; + + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, trustAllCerts, new SecureRandom()); + HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); + HostnameVerifier hv = new HostnameVerifier() { + public boolean verify(String urlHostName, SSLSession session) { + if (!urlHostName.equals(session.getPeerHost())) { + System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'."); + } + return true; + } + }; + HttpsURLConnection.setDefaultHostnameVerifier(hv); + } +} diff --git a/src/main/java/com/calm/Logger/NutanixCalmLogger.java b/src/main/java/com/calm/Logger/NutanixCalmLogger.java new file mode 100644 index 0000000..1dbbb1e --- /dev/null +++ b/src/main/java/com/calm/Logger/NutanixCalmLogger.java @@ -0,0 +1,27 @@ +package com.calm.Logger; + + +import java.util.logging.Level; +import java.util.logging.Logger; + +public class NutanixCalmLogger{ + private Logger logger; + public NutanixCalmLogger(Class clazz){ + this.logger = Logger.getLogger(clazz.getName()); + } + + public void debug(String message){ + logger.log(Level.SEVERE, message); + } + + public void info(String message){ + logger.info(message); + } + + public String getStackTraceStr(StackTraceElement[] stackTraceElements){ + String stackStr = ""; + for(StackTraceElement stackTraceElement : stackTraceElements) + stackStr += stackTraceElement.toString(); + return stackStr; + } +} diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/config.jelly b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/config.jelly new file mode 100644 index 0000000..2e3eef1 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/config.jelly @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-actionName.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-actionName.html new file mode 100644 index 0000000..91a3527 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-actionName.html @@ -0,0 +1,4 @@ +
+

The field is mandatory


+

Select the required action need to run after the application launch from the list of actions, else please select none.

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-appProfileName.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-appProfileName.html new file mode 100644 index 0000000..3b8a28a --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-appProfileName.html @@ -0,0 +1,3 @@ +
+

Application Profile selection is mandatory.

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-applicationName.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-applicationName.html new file mode 100644 index 0000000..5b4e846 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-applicationName.html @@ -0,0 +1,4 @@ +
+

Application Name is mandatory.

+

This is the Application name used for blueprint launch in Nutanix Calm. Appending the _${BUILD_ID} to the Application name is recommended for unique application names. Other Jenkins Environment Variables may also be used.

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintDescription.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintDescription.html new file mode 100644 index 0000000..6faedd7 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintDescription.html @@ -0,0 +1,3 @@ +
+

Description is fetched from the selected Calm blueprint

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintName.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintName.html new file mode 100644 index 0000000..d5acc76 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-blueprintName.html @@ -0,0 +1,3 @@ +
+

Blueprint selection is mandatory.

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmIP.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmIP.html new file mode 100644 index 0000000..b43711a --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmIP.html @@ -0,0 +1,4 @@ +
+

The field is mandatory


+

Enter the IP address of Nutanix Prism central where Calm is running. Please contact system administrator or Prism Central admin to obtain the IP address

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmPwd.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmPwd.html new file mode 100644 index 0000000..aea334b --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmPwd.html @@ -0,0 +1,4 @@ +
+

The field is mandatory


+

Enter the prism central password that is used to connect to Calm instance

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmUser.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmUser.html new file mode 100644 index 0000000..f0df1df --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-calmUser.html @@ -0,0 +1,4 @@ +
+

The field is mandatory


+

Enter the prism central username that is used to connect to Calm instance

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-projectName.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-projectName.html new file mode 100644 index 0000000..49aa25a --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-projectName.html @@ -0,0 +1,3 @@ +
+

Project selection is mandatory.

+
diff --git a/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-runtimeVariables.html b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-runtimeVariables.html new file mode 100644 index 0000000..4c514e7 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/BlueprintLaunch/help-runtimeVariables.html @@ -0,0 +1,3 @@ +
+

Click on Fetch Runtime Variables to fetch all editable variables for the selected Application Profile in JSON format. Modify the key values from the defaults as needed.

+
diff --git a/src/main/resources/com/calm/Descriptors/RunApplicationAction/config.jelly b/src/main/resources/com/calm/Descriptors/RunApplicationAction/config.jelly new file mode 100644 index 0000000..a3a003e --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/RunApplicationAction/config.jelly @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-actionName.html b/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-actionName.html new file mode 100644 index 0000000..2ea7315 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-actionName.html @@ -0,0 +1,3 @@ +
+

Application Action selection is mandatory

+
diff --git a/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-applicationName.html b/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-applicationName.html new file mode 100644 index 0000000..6cbc7e0 --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-applicationName.html @@ -0,0 +1,4 @@ +
+

Application selection is mandatory

+

Select an existing Application in Calm or the ones provisioned in Nutanix Calm Blueprint Launch Steps.

+
diff --git a/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-runtimeVariables.html b/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-runtimeVariables.html new file mode 100644 index 0000000..936188c --- /dev/null +++ b/src/main/resources/com/calm/Descriptors/RunApplicationAction/help-runtimeVariables.html @@ -0,0 +1,3 @@ +
+

Click on Fetch Runtime Variables to fetch all editable variables for the selected Action in JSON format. Modify the key values from the defaults as needed.

+
diff --git a/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/config.jelly b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/config.jelly new file mode 100644 index 0000000..f0d5a02 --- /dev/null +++ b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/config.jelly @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-password.html b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-password.html new file mode 100644 index 0000000..7cf6310 --- /dev/null +++ b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-password.html @@ -0,0 +1,4 @@ +
+

Password is mandatory.

+

Enter the password for the Prism Central user authorized to use Calm.

+
diff --git a/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-prismCentralIp.html b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-prismCentralIp.html new file mode 100644 index 0000000..4fd346a --- /dev/null +++ b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-prismCentralIp.html @@ -0,0 +1,4 @@ +
+

IP address is mandatory.

+

Enter the IP address of Nutanix Prism central where Calm is enabled. Please contact your Prism Central admin to obtain the IP address.

+
diff --git a/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-userName.html b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-userName.html new file mode 100644 index 0000000..2db1a69 --- /dev/null +++ b/src/main/resources/com/calm/GlobalConfiguration/CalmGlobalConfiguration/help-userName.html @@ -0,0 +1,4 @@ +
+

Username is mandatory.

+

Enter the Prism Central user authorized to use Calm.

+
diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly new file mode 100644 index 0000000..2a1d49a --- /dev/null +++ b/src/main/resources/index.jelly @@ -0,0 +1,7 @@ + + +
+ This plugin allows integration with Nutanix Calm for freestyle projects and pipeline. +