diff --git a/deployment-manager/.gitignore b/deployment-manager/.gitignore
new file mode 100644
index 0000000..e90878c
--- /dev/null
+++ b/deployment-manager/.gitignore
@@ -0,0 +1,7 @@
+*.classpath
+*.project
+*.iml
+*.settings
+.idea/
+work/
+target/
diff --git a/deployment-manager/CONTRIBUTING.md b/deployment-manager/CONTRIBUTING.md
new file mode 100644
index 0000000..e69de29
diff --git a/deployment-manager/LICENSE b/deployment-manager/LICENSE
new file mode 100644
index 0000000..ad410e1
--- /dev/null
+++ b/deployment-manager/LICENSE
@@ -0,0 +1,201 @@
+Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/deployment-manager/README.md b/deployment-manager/README.md
new file mode 100644
index 0000000..5fa4c9c
--- /dev/null
+++ b/deployment-manager/README.md
@@ -0,0 +1,29 @@
+# Openstack Murano Deployment Manager Plugin
+
+This plugin manages Openstack Murano environments
+
+# Build
+ mvn clean verify
+Creates the plugin HPI package for use with Jenkins.
+
+# Run by Maven
+ mvn hpi:run
+Runs Jenkins with the plugin installed
+
+# License
+
+ (The Apache v2 License)
+
+ Copyright 2016 Google Inc. All Rights Reserved.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/deployment-manager/pom.xml b/deployment-manager/pom.xml
new file mode 100644
index 0000000..43c7d37
--- /dev/null
+++ b/deployment-manager/pom.xml
@@ -0,0 +1,139 @@
+
+ 4.0.0
+
+
+ org.jenkins-ci.plugins
+ plugin
+ 2.15
+
+
+ Murano Deployment Manager Jenkins Plugin
+
+
+ This plugin provides integration with Openstack Murano
+
+
+ https://wiki.jenkins-ci.org/display/JENKINS/Murano+Deployment+Manager+Plugin
+ murano-deployment-manager
+ hpi
+ 0.1-SNAPSHOT
+
+
+
+ The Apache V2 License
+ http://www.apache.org/licenses/LICENSE-2.0
+ repo
+
+
+
+
+
+ Alexey Khivin
+ akhivin@gmail.com
+
+
+
+
+ 8
+ 2.23
+ UTF-8
+ 3.0.2
+
+
+
+
+ repo.jenkins-ci.org
+ Jenkins Repository
+ http://repo.jenkins-ci.org/public/
+
+
+ jgit-repository
+ Eclipse JGit Repository
+ http://download.eclipse.org/jgit/maven
+
+
+
+
+
+ repo.jenkins-ci.org
+ http://repo.jenkins-ci.org/public/
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ 2.8.2
+
+
+ org.apache.maven.wagon
+ wagon-http
+ 1.0-beta-6
+ jar
+
+
+
+
+
+
+
+ scm:git:git://github.com/jenkinsci/${project.artifactId}-plugin.git
+ scm:git:git@github.com:jenkinsci/${project.artifactId}-plugin.git
+ https://github.com/jenkinsci/${project.artifactId}-plugin
+ HEAD
+
+
+
+
+ org.mockito
+ mockito-all
+ RELEASE
+ test
+
+
+
+ org.pacesys
+ openstack4j-core
+ ${openstack4jversion}
+
+
+
+ org.pacesys.openstack4j.connectors
+ openstack4j-httpclient
+ ${openstack4jversion}
+
+
+
+ com.googlecode.json-simple
+ json-simple
+ 1.1.1
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.1
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.8.3
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-yaml
+ 2.7.4
+
+
+
+ org.jenkins-ci.plugins
+ credentials
+ LATEST
+
+
+
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/AbstractMuranoDeploymentDescriptor.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/AbstractMuranoDeploymentDescriptor.java
new file mode 100644
index 0000000..9147772
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/AbstractMuranoDeploymentDescriptor.java
@@ -0,0 +1,28 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+import hudson.model.Descriptor;
+import net.sf.json.JSONObject;
+import org.kohsuke.stapler.StaplerRequest;
+
+import static java.util.Objects.requireNonNull;
+
+public abstract class AbstractMuranoDeploymentDescriptor
+ extends Descriptor {
+
+ protected AbstractMuranoDeploymentDescriptor(Class extends MuranoDeployment> clazz) {
+ super(requireNonNull(clazz));
+ load();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean configure(StaplerRequest req, JSONObject json) throws FormException {
+ save();
+ return true;
+ }
+
+ public abstract boolean isApplicable(Descriptor descriptor);
+ public abstract String getDisplayName();
+}
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/BuildStepDetailsProvider.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/BuildStepDetailsProvider.java
new file mode 100644
index 0000000..dce30d0
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/BuildStepDetailsProvider.java
@@ -0,0 +1,36 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+import hudson.Extension;
+import hudson.ExtensionPoint;
+import hudson.model.Describable;
+import hudson.tasks.BuildStep;
+import hudson.tasks.Shell;
+
+
+public abstract class BuildStepDetailsProvider implements ExtensionPoint {
+
+ protected static String defaultName(BuildStep bs) {
+ return bs instanceof Describable> ? ((Describable>) bs).getDescriptor().getDisplayName()
+ : null;
+ }
+
+ /**
+ * @param bs A given {@link BuildStep}.
+ * @return the details of the build step.
+ */
+ public abstract String getDetails(T bs);
+
+ /**
+ * {@link BuildStepDetailsProvider} for {@link Shell}.
+ */
+ @Extension
+ public static class ShellBuildStepDetailsProvider extends BuildStepDetailsProvider {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails(Shell shell) {
+ return shell.getCommand();
+ }
+ }
+}
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoDeployment.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoDeployment.java
new file mode 100644
index 0000000..2182796
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoDeployment.java
@@ -0,0 +1,77 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+import hudson.DescriptorExtensionList;
+import hudson.ExtensionPoint;
+import hudson.model.Describable;
+import hudson.model.Descriptor;
+import jenkins.model.Jenkins;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import static java.util.Objects.requireNonNull;
+
+public abstract class MuranoDeployment
+ implements Describable, ExtensionPoint {
+ /**
+ * Json data that describes Murano Environment applications
+ */
+ private String objectModel;
+
+
+ public MuranoDeployment() {
+ super();
+ }
+
+ /**
+ * Contains data that describes Environment within Openstack Cloud
+ * and connection credentials.
+ *
+ * @param objectModel description of environment to be deployed
+ */
+ public MuranoDeployment(String objectModel) {
+
+ this.objectModel = requireNonNull(objectModel, "Object Model should not be Null");
+ }
+
+ /**
+ * Boilerplate, see:
+ * https://wiki.jenkins-ci.org/display/JENKINS/Defining+a+new+extension+point
+ *
+ * @return all registered {@link MuranoDeployment}s
+ */
+ public static DescriptorExtensionList all() {
+ return Jenkins.getInstance().getDescriptorList(MuranoDeployment.class);
+ }
+
+ public static List getCompatibleDeployments(Descriptor descriptor) {
+ LinkedList cloudDeployments =
+ new LinkedList<>();
+
+ for (AbstractMuranoDeploymentDescriptor deployment : all()) {
+ if (!deployment.isApplicable(descriptor)) {
+ continue;
+ }
+ cloudDeployments.add(deployment);
+ }
+
+ return cloudDeployments;
+ }
+
+ public String getObjectModel() {
+ return objectModel;
+ }
+
+ public void setObjectModel(String objectModel) {
+ this.objectModel = objectModel;
+ }
+
+ /**
+ * Boilerplate, see: https://wiki.jenkins-ci.org/display/JENKINS/Defining+a+new+extension+point
+ */
+ @Override
+ public Descriptor getDescriptor() {
+ return (Descriptor) Jenkins.getInstance().getDescriptor(getClass());
+ }
+
+}
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoHelper.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoHelper.java
new file mode 100644
index 0000000..ed8ca58
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoHelper.java
@@ -0,0 +1,363 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.json.simple.parser.JSONParser;
+import org.json.simple.parser.ParseException;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.openstack4j.api.OSClient;
+import org.openstack4j.api.exceptions.AuthenticationException;
+import org.openstack4j.connectors.httpclient.HttpCommand;
+import org.openstack4j.core.transport.HttpMethod;
+import org.openstack4j.core.transport.HttpRequest;
+import org.openstack4j.openstack.OSFactory;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.time.Instant;
+import java.util.concurrent.TimeoutException;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+public class MuranoHelper {
+ public static final int MURANO_DEFAULT_PORT = 8082;
+
+ private final static Logger LOG = Logger.getLogger(MuranoHelper.class.getName());
+
+ private OSClient.OSClientV2 os = null;
+
+ /**
+ * Suppose this is keystone Url
+ */
+ private String serverUrl;
+ private String username;
+ private String password;
+ private String tenantName;
+ /**
+ * Default timeout for waiting deployment success
+ */
+ private int timeout = 3600*1000;
+
+ @DataBoundConstructor
+ public MuranoHelper(String serverUrl,
+ String username,
+ String password,
+ String tenantName) {
+ this.serverUrl = serverUrl;
+ this.username = username;
+ this.password = password;
+ this.tenantName = tenantName;
+ }
+
+ public String getServerUrl() {
+ return serverUrl;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public String getTenantName() {
+ return tenantName;
+ }
+
+
+ /**
+ *
+ * @param name New environment name
+ * @param objectModel object model describing new environment
+ * @return new environment id
+ * @throws AuthenticationException in case credentials
+ */
+ public String deployNewFromObjectModel(String name, String objectModel)
+ throws AuthenticationException {
+
+ this.authenticate();
+
+ String token = getOSClient().getAccess().getToken().getId();
+
+ String envId = checkIfDeploymentExists(token, name);
+ if (envId == null) {
+ // Create Env
+ envId = this.createEnvironment(token, name);
+
+ // Create Session
+ String sessionId = this.createEnvironmentSession(token, envId);
+
+ // Add App to Environment
+ addApplicationToEnvironment(token, envId, sessionId, objectModel);
+
+ // Deploy
+ deployEnvironment(token, envId, sessionId);
+ }
+
+ return envId;
+ }
+
+
+ /**
+ * Loop around to see if the deployment is a success. This waits for about 10 secs 300 times hoping that
+ * it finishes. This all depends on teh number of nodes and the speed of the boxes. But seems sufficient.
+ *
+ * @param envId Environemnt Id
+ * @return whether the deployment is a success
+ * @throws TimeoutException if deployment process still in progress after deadline
+ */
+ public boolean waitDeploymentResult(String envId) throws TimeoutException {
+ String token = getOSClient().getAccess().getToken().getId();
+
+ boolean status = false;
+ Instant deadline = Instant.now().plusMillis(timeout);
+
+ while(true) {
+ try {
+ Thread.sleep(10000);
+ String payload = getResponseForJson(
+ getMuranoEnpoint(),
+ MURANO_DEFAULT_PORT,
+ "/v1/environments/" + envId + "/deployments",
+ HttpMethod.GET,
+ token,
+ null,
+ null);
+ JSONParser parser = new JSONParser();
+ try {
+ JSONObject deployments = (JSONObject) parser.parse(payload);
+ JSONArray deploymentList = (JSONArray) deployments.get("deployments");
+ JSONObject thisDeployment = (JSONObject) deploymentList.get(0);
+ if ("success".equals(thisDeployment.get("state"))) {
+ status = true;
+ break;
+ }
+ } catch (ParseException pe) {
+ }
+ } catch (Exception ex) {
+ status = false;
+ break;
+ }
+ if (Instant.now().isAfter(deadline)){
+ throw new TimeoutException("Environment was not ready in time.");
+ }
+
+ }
+ return status;
+ }
+
+ /**
+ * Return the Environment id if it exists
+ *
+ * @param token
+ * @param name
+ * @return
+ */
+ private String checkIfDeploymentExists(String token, String name) {
+ // TODO: remove string manipulation
+ String payload = getResponseForJson(
+ getMuranoEnpoint(),
+ MURANO_DEFAULT_PORT,
+ "/v1/environments",
+ HttpMethod.GET,
+ token,
+ null,
+ null);
+ String envId = null;
+ JSONParser parser = new JSONParser();
+ try{
+ Object obj = parser.parse(payload);
+ JSONObject response = (JSONObject)obj;
+ JSONArray environmentArray = (JSONArray) response.get("environments");
+ for (Object env: environmentArray) {
+ JSONObject thisEnv = (JSONObject) env;
+ String envName = (String) thisEnv.get("name");
+ if (envName.equals(name)) {
+ envId = (String) thisEnv.get("id");
+ break;
+ }
+ }
+ }catch(ParseException pe){
+ LogRecord logRecord = new LogRecord(Level.WARNING, "Parse exception: position: " + pe.getPosition());
+ logRecord.setThrown(pe);
+ LOG.log(logRecord);
+ }
+ return envId;
+ }
+
+ /**
+ * Deploy the environment given the environment id and Session Token
+ */
+ private void deployEnvironment(String token, String envId, String sessionId) {
+ String response = getResponseForJson(
+ getMuranoEnpoint(),
+ MURANO_DEFAULT_PORT,
+ "/v1/environments/" + envId + "/sessions/" + sessionId + "/deploy",
+ HttpMethod.POST,
+ token,
+ null,
+ null);
+ }
+
+
+ public String getMuranoEnpoint() {
+ /*
+ * TODO: This is temporary decision. Murano URL should be obtained from Keystone
+ */
+ String string[] = this.serverUrl.split(":");
+
+ return string[0] + ":" + string[1];
+ }
+
+ /**
+ * Add the app(K8S) to the environment
+ * @param token
+ * @param envId
+ * @param sessionId
+ * @param jsonReq
+ */
+ private void addApplicationToEnvironment(String token, String envId, String sessionId, String jsonReq) {
+ String response = getResponseForJson(this.getMuranoEnpoint(),
+ MURANO_DEFAULT_PORT,
+ "/v1/environments/" + envId + "/services",
+ HttpMethod.POST,
+ token,
+ jsonReq,
+ sessionId);
+ }
+
+ private String createEnvironmentSession(String token, String envId) {
+ String payload = getResponseForJson(this.getMuranoEnpoint(),
+ MURANO_DEFAULT_PORT,
+ "/v1/environments/" + envId + "/configure",
+ HttpMethod.POST,
+ token,
+ null,
+ null);
+
+ String sessionId = "";
+ JSONParser parser = new JSONParser();
+ try{
+ Object obj = parser.parse(payload);
+ JSONObject response = (JSONObject)obj;
+ sessionId = (String)response.get("id");
+ }catch(ParseException pe){
+ System.out.println("position: " + pe.getPosition());
+ System.out.println(pe);
+ }
+ return sessionId;
+ }
+
+ private String createEnvironment(String token, String envname) {
+ String reqPayload = "{\"name\":\"" + envname + "\"}";
+ String payload = getResponseForJson(
+ getMuranoEnpoint(),
+ MURANO_DEFAULT_PORT,
+ "/v1/environments",
+ HttpMethod.POST, token, reqPayload, null);
+
+ String envId = "";
+ JSONParser parser = new JSONParser();
+ try{
+ Object obj = parser.parse(payload);
+ JSONObject response = (JSONObject)obj;
+ envId = (String)response.get("id");
+ }catch(ParseException pe){
+ System.out.println("position: " + pe.getPosition());
+ System.out.println(pe);
+ }
+
+ return envId;
+ }
+
+ /**
+ * Main helper method to call the Murano API and return the response. Accepts both GET and POST.
+ *
+ * @param url Base URL to connect
+ * @param port Which port is murano listening on
+ * @param requestPath Path on Murano URL
+ * @param method GET or POST
+ * @param token Auth Token
+ * @param jsonPayload Payload for the message
+ * @param muranoSessionId Optional Session Id
+ * @return Response from the Call
+ */
+ private String getResponseForJson(String url,
+ int port,
+ String requestPath,
+ HttpMethod method,
+ String token,
+ String jsonPayload,
+ String muranoSessionId) {
+ HttpRequest request = HttpRequest.builder().method(method)
+ .endpoint(url + ":" + port)
+ .path(requestPath)
+ .header("X-Auth-Token", token)
+ .json(jsonPayload)
+ .build();
+ if (muranoSessionId != null) {
+ request.getHeaders().put("X-Configuration-Session", muranoSessionId);
+ }
+ if (jsonPayload != null) {
+ request = request.toBuilder().json(jsonPayload).build();
+ }
+
+ HttpCommand command = HttpCommand.create(request);
+ CloseableHttpResponse response = null;
+ try {
+ response = command.execute();
+ } catch(Exception ex) {
+ ex.printStackTrace();
+ return null;
+ }
+
+ StringBuffer jsonString = new StringBuffer();
+ try {
+ BufferedReader br = new BufferedReader(new InputStreamReader(
+ response.getEntity().getContent()));
+
+ //Print the raw output of murano api from the server
+ String output;
+
+ while ((output = br.readLine()) != null) {
+ jsonString.append(output + "\n");
+ }
+ } catch(Exception ex) {
+ return null;
+ }
+
+ return jsonString.toString();
+ }
+
+
+ /**
+ * Authenticate to the Openstack instance given the credentials in constructor
+ *
+ * @throws AuthenticationException in case of authentication failure.
+ */
+ public void authenticate() throws AuthenticationException {
+ this.os = OSFactory.builderV2()
+ .endpoint(this.serverUrl)
+ .credentials(this.username, this.password)
+ .tenantName(this.tenantName)
+ .authenticate();
+ }
+
+
+ /**
+ * Helper object to return the OSClient
+ * @return OSClient V2
+ */
+ public OSClient.OSClientV2 getOSClient() {
+ return this.os;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+}
+
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper.java
new file mode 100644
index 0000000..ff625cf
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper.java
@@ -0,0 +1,211 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+import com.cloudbees.plugins.credentials.CredentialsMatchers;
+import com.cloudbees.plugins.credentials.CredentialsProvider;
+import com.cloudbees.plugins.credentials.common.StandardCredentials;
+import com.cloudbees.plugins.credentials.common.StandardListBoxModel;
+import com.cloudbees.plugins.credentials.domains.DomainRequirement;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
+import hudson.EnvVars;
+import hudson.Extension;
+import hudson.FilePath;
+import hudson.Launcher;
+import hudson.model.*;
+import hudson.security.ACL;
+import hudson.tasks.BuildWrapper;
+import hudson.tasks.BuildWrapperDescriptor;
+import hudson.util.ListBoxModel;
+import jenkins.model.Jenkins;
+import org.apache.http.impl.client.SystemDefaultCredentialsProvider;
+import org.kohsuke.stapler.AncestorInPath;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+import org.kohsuke.stapler.export.Exported;
+import org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials.OpenstackCredentials;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.net.URL;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static java.util.Objects.requireNonNull;
+
+
+public class MuranoManagerBuildWrapper extends BuildWrapper implements Serializable {
+ private static String MURANO_ENV_NAME = "MuranoCI-";
+
+
+ private final MuranoDeployment deployment;
+ private String credentialsId;
+
+ @DataBoundConstructor
+ public MuranoManagerBuildWrapper(MuranoDeployment deployment,
+ String credentialsId) {
+
+ this.deployment = requireNonNull(deployment);
+ this.credentialsId = requireNonNull(credentialsId);
+ }
+
+ public String getCredentialsId() {
+ return credentialsId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BuildWrapper.Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener)
+ throws IOException, InterruptedException {
+ EnvVars env = build.getEnvironment(listener);
+ OpenstackCredentials credentials = getOpenstackCredentials(getCredentialsId());
+
+ try {
+ MuranoHelper helper = new MuranoHelper(
+ credentials.getIdentityServiceEndpoint(),
+ credentials.getUsername(),
+ credentials.getPassword().getPlainText(),
+ credentials.getTenant()
+ );
+ if (env.containsKey("BUILD_ENVIRONMENT_TIMEOUT")) {
+ int timeout = Integer.parseInt(env.get("BUILD_ENVIRONMENT_TIMEOUT"));
+ helper.setTimeout(timeout);
+ }
+
+ //TODO: Remove
+ try {
+ ((RepositoryTemplatedDeployment) deployment).readObjectModel(build.getWorkspace());
+ } catch (Exception io) {
+ }
+
+ String name = generateEnvName();
+
+ String envId = helper.deployNewFromObjectModel(
+ name, deployment.getObjectModel());
+
+ boolean result = helper.waitDeploymentResult(envId);
+ if (!result) {
+ build.setResult(Result.FAILURE);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ build.setResult(Result.FAILURE);
+ }
+
+ return new JenkinsEnvironmentImpl(env);
+ }
+
+ private String generateEnvName() {
+ return MURANO_ENV_NAME + new BigInteger(
+ 32,
+ new SecureRandom())
+ .toString(16);
+ }
+
+ private OpenstackCredentials getOpenstackCredentials(String credentialsId) {
+ List openstackCredentialsList =
+ CredentialsProvider.lookupCredentials(
+ OpenstackCredentials.class,
+ Jenkins.getInstance(),
+ ACL.SYSTEM);
+ OpenstackCredentials openstackCredentials = CredentialsMatchers.firstOrNull(
+ openstackCredentialsList,
+ CredentialsMatchers.allOf(
+ CredentialsMatchers.withId(credentialsId)));
+
+ return openstackCredentials;
+ }
+
+ @Exported
+ public MuranoDeployment getDeployment() {
+ return deployment;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DescriptorImpl getDescriptor() {
+ return (DescriptorImpl) super.getDescriptor();
+ }
+
+ /**
+ * The descriptor for our {@code MuranoManagerBuildWrapper} plugin.
+ */
+ @Extension
+ public static final class DescriptorImpl extends BuildWrapperDescriptor {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isApplicable(AbstractProject, ?> project) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDisplayName() {
+ return Messages.MuranoManagerBuildWrapper_DisplayName();
+ }
+
+
+ @SuppressWarnings("unused") // used by stapler
+ public ListBoxModel doFillCredentialsIdItems(@AncestorInPath Jenkins context,
+ @QueryParameter String remoteBase) {
+ if (context == null || !context.hasPermission(Item.CONFIGURE)) {
+ return new StandardListBoxModel();
+ }
+
+ List domainRequirements = newArrayList();
+ return new StandardListBoxModel()
+ .withEmptySelection()
+ .withMatching(
+ CredentialsMatchers.anyOf(
+ CredentialsMatchers.instanceOf(OpenstackCredentials.class)),
+ CredentialsProvider.lookupCredentials(
+ StandardCredentials.class,
+ context,
+ ACL.SYSTEM,
+ domainRequirements));
+ }
+
+ }
+
+ private final class JenkinsEnvironmentImpl extends Environment {
+ private final EnvVars envVars;
+
+ /**
+ * Construct the instance with a snapshot of the environment within which it was created in case
+ * values that were used to configure it at the start of the build change before the end.
+ *
+ * @param envVars The set of environment variables used to spin up the ephemeral deployment, so
+ * we can tear it down with the same.
+ */
+ public JenkinsEnvironmentImpl(EnvVars envVars) {
+ this.envVars = requireNonNull(envVars);
+ }
+
+ @Override
+ public void buildEnvVars(Map env) {
+ super.buildEnvVars(env);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean tearDown(AbstractBuild build, BuildListener listener)
+ throws IOException, InterruptedException {
+ return true;
+ }
+ }
+}
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer.java
new file mode 100644
index 0000000..2ba9ec4
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer.java
@@ -0,0 +1,92 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+import hudson.EnvVars;
+import hudson.Extension;
+import hudson.Launcher;
+import hudson.model.AbstractBuild;
+import hudson.model.AbstractProject;
+import hudson.model.BuildListener;
+import hudson.model.Result;
+import hudson.tasks.BuildStepDescriptor;
+import hudson.tasks.BuildStepMonitor;
+import hudson.tasks.Publisher;
+import hudson.tasks.Recorder;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+import java.io.IOException;
+
+/**
+ * After a successful build, this plugin deploys to Murano Environment via the
+ * Deployment Manager API.
+ */
+public class MuranoManagerDeployer extends Recorder {
+
+ @DataBoundConstructor
+ public MuranoManagerDeployer() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener)
+ throws IOException, InterruptedException {
+ if (build.getResult() != Result.SUCCESS) {
+ return true;
+ }
+
+ try {
+ build.getEnvironment(listener);
+ } catch (IOException e) {
+ e.printStackTrace(listener.error(Messages.MuranoManagerDeployer_EnvironmentException()));
+ build.setResult(Result.FAILURE);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BuildStepMonitor getRequiredMonitorService() {
+ return BuildStepMonitor.NONE;
+ }
+
+
+ @Extension
+ public static class DescriptorImpl extends BuildStepDescriptor {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isApplicable(Class extends AbstractProject> aClass) {
+ // Indicates that this builder can be used with all kinds of project types
+ return true;
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDisplayName() {
+ return Messages.MuranoManagerDeployer_DisplayName();
+ }
+ }
+
+ /**
+ * {@link BuildStepDetailsProvider} for the Cloud Manager Deployer.
+ */
+ @Extension
+ public static class DetailsProvider extends BuildStepDetailsProvider {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDetails(MuranoManagerDeployer deployer) {
+ return "MuranoManagerDeployer";
+ }
+ }
+}
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment.java
new file mode 100644
index 0000000..7111111
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment.java
@@ -0,0 +1,101 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.dataformat.yaml.YAMLParser;
+import hudson.Extension;
+import hudson.FilePath;
+import hudson.model.Descriptor;
+import org.kohsuke.stapler.DataBoundConstructor;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RepositoryTemplatedDeployment extends MuranoDeployment {
+ /**
+ * The file in the repository that contains muranoci configuration.
+ */
+ public static final String CI_CONFG_FILENAME = ".murano.yml";
+
+ /**
+ *
+ */
+ private final String environment;
+
+ /**
+ * The specific Implemenation of MuranoDeployment that
+ * gets object model from the file within the repo.
+ *
+ * @param environment The name of the environment within the .murano.yml config
+ */
+ @DataBoundConstructor
+ public RepositoryTemplatedDeployment(
+ String environment) {
+
+ this.environment = environment;
+ }
+
+ public String getEnvironment() {
+ return environment;
+ }
+
+ /**
+ * Denotes that this is a cloud deployment plugin.
+ */
+ @Extension
+ public static class DescriptorImpl extends AbstractMuranoDeploymentDescriptor {
+ public DescriptorImpl() {
+ this(RepositoryTemplatedDeployment.class);
+ }
+
+ public DescriptorImpl(Class extends RepositoryTemplatedDeployment> clazz) {
+ super(clazz);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDisplayName() {
+ return Messages.RepositoryTemplatedMuranoDeployment_DisplayName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isApplicable(Descriptor descriptor) {
+ return true;
+ }
+ }
+
+
+ public void readObjectModel(FilePath workspace) throws IOException {
+ String config = null;
+ try {
+ config = new FilePath(workspace, ".murano.yml").readToString();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ YAMLFactory factory = new YAMLFactory();
+ ObjectMapper mapper = new ObjectMapper(factory);
+ HashMap map = mapper.readValue(config, HashMap.class);
+ Object model = ((Map)((Map)map).get("environments")).get(this.environment);
+
+ JsonFactory jsonFactory = new JsonFactory();
+ ObjectMapper mapperModel = new ObjectMapper(jsonFactory);
+ String string = mapperModel.writeValueAsString(model);
+ System.out.println(string);
+ this.setObjectModel(string);
+ }
+}
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment.java
new file mode 100644
index 0000000..cb6c40d
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment.java
@@ -0,0 +1,60 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy;
+
+import hudson.Extension;
+import hudson.model.Descriptor;
+
+import hudson.util.FormValidation;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+import org.openstack4j.api.exceptions.AuthenticationException;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+import static java.util.Objects.requireNonNull;
+
+public class TemplatedDeployment extends MuranoDeployment {
+
+ /**
+ * The specific Implemenation of MuranoDeployment that
+ * gets object model from the contructor parameter
+ * (the direct textarea on Jenkins form)
+ *
+ * @param objectModel Object model for Murano environment to be deployed
+ */
+ @DataBoundConstructor
+ public TemplatedDeployment(
+ String objectModel) {
+ super(requireNonNull(objectModel));
+ }
+
+ /**
+ * Denotes that this is a cloud deployment plugin.
+ */
+ @Extension
+ public static class DescriptorImpl extends AbstractMuranoDeploymentDescriptor {
+ public DescriptorImpl() {
+ this(TemplatedDeployment.class);
+ }
+
+ public DescriptorImpl(Class extends TemplatedDeployment> clazz) {
+ super(clazz);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDisplayName() {
+ return Messages.TemplatedMuranoDeployment_DisplayName();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isApplicable(Descriptor descriptor) {
+ return true;
+ }
+ }
+}
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentials.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentials.java
new file mode 100644
index 0000000..e9fa747
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentials.java
@@ -0,0 +1,14 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials;
+
+
+import com.cloudbees.plugins.credentials.Credentials;
+import hudson.util.Secret;
+
+public interface OpenstackCredentials extends Credentials {
+ String getName();
+ String getDescription();
+ String getUsername();
+ Secret getPassword();
+ String getTenant();
+ String getIdentityServiceEndpoint();
+}
\ No newline at end of file
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsImpl.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsImpl.java
new file mode 100644
index 0000000..ab6150a
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsImpl.java
@@ -0,0 +1,109 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials;
+
+
+import com.cloudbees.plugins.credentials.CredentialsDescriptor;
+import com.cloudbees.plugins.credentials.NameWith;
+import com.cloudbees.plugins.credentials.impl.BaseStandardCredentials;
+import com.cloudbees.plugins.credentials.impl.Messages;
+import hudson.Extension;
+import hudson.util.FormValidation;
+import hudson.util.Secret;
+import org.kohsuke.stapler.DataBoundConstructor;
+import org.kohsuke.stapler.QueryParameter;
+import org.openstack.murano.jenkins_plugins.muranoci.deploy.MuranoHelper;
+import org.openstack4j.api.exceptions.AuthenticationException;
+
+import javax.servlet.ServletException;
+import java.io.IOException;
+
+import static java.util.Objects.requireNonNull;
+
+@NameWith(value = OpenstackCredentialsNameProvider.class, priority = 50)
+public class OpenstackCredentialsImpl extends BaseStandardCredentials implements OpenstackCredentials {
+ private final String name;
+
+ private final String identityServiceEndpoint;
+ private final String username;
+ private final Secret password;
+ private final String tenant;
+
+ @DataBoundConstructor
+ public OpenstackCredentialsImpl(
+ String id,
+ String name,
+ String description,
+ String identityServiceEndpoint,
+ String tenant,
+ String username,
+ String password) {
+ super(id, description);
+
+ this.name = name;
+ this.identityServiceEndpoint = identityServiceEndpoint;
+ this.username = username;
+ this.password = Secret.fromString(requireNonNull(password));
+ this.tenant = tenant;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ @Override
+ public String getUsername() {
+ return this.username;
+ }
+
+ @Override
+ public Secret getPassword() {
+ return this.password;
+ }
+
+ @Override
+ public String getTenant() {
+ return this.tenant;
+ }
+
+ @Override
+ public String getIdentityServiceEndpoint() {
+ return this.identityServiceEndpoint;
+ }
+
+ @Extension
+ public static class Descriptor
+ extends CredentialsDescriptor {
+
+ public FormValidation doTestConnection(@QueryParameter("identityServiceEndpoint") final String identityServiceEndpoint,
+ @QueryParameter("tenant") final String tenant,
+ @QueryParameter("username") final String username,
+ @QueryParameter("password") final String password)
+ throws IOException, ServletException {
+
+ MuranoHelper client = new MuranoHelper(
+ identityServiceEndpoint,
+ username,
+ password,
+ tenant);
+
+ try {
+ client.authenticate();
+ } catch (AuthenticationException ae) {
+ return FormValidation.error(
+ "Unable to connect to server. Please check credentials");
+ } catch (Exception e) {
+ return FormValidation.error("Error: " + e.getMessage());
+ }
+
+ return FormValidation.ok("Success");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getDisplayName() {
+// return Messages.CertificateCredentialsImpl_DisplayName();
+ return "Openstack Cloud";
+ }
+ }
+}
\ No newline at end of file
diff --git a/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsNameProvider.java b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsNameProvider.java
new file mode 100644
index 0000000..d15bd78
--- /dev/null
+++ b/deployment-manager/src/main/java/org/openstack/murano/jenkins_plugins/muranoci/deploy/credentials/OpenstackCredentialsNameProvider.java
@@ -0,0 +1,12 @@
+package org.openstack.murano.jenkins_plugins.muranoci.deploy.credentials;
+
+
+import com.cloudbees.plugins.credentials.CredentialsNameProvider;
+
+public class OpenstackCredentialsNameProvider extends CredentialsNameProvider {
+
+ @Override
+ public String getName(OpenstackCredentialsImpl openstackCredentials) {
+ return openstackCredentials.getName();
+ }
+}
\ No newline at end of file
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/Messages.properties b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/Messages.properties
new file mode 100644
index 0000000..6c73396
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/Messages.properties
@@ -0,0 +1,26 @@
+MuranoDeployment.InvalidDeploySpec=Invalid deployment specification.
+MuranoDeployment.DeployComplete=Deployment complete!
+MuranoDeployment.InsertRollback=Failure deploying, attempting rollback.
+MuranoDeployment.RollbackSuccess=Rollback successful.
+MuranoDeployment.DeleteComplete=Deletion complete!
+MuranoDeployment.CreatedDeploy=Created new deployment: {0}
+MuranoDeployment.CreatedDeployVerbose=Created new deployment: {0} with spec: {1}
+MuranoDeployment.DeletedDeploy=Deleted deployment: {0}
+MuranoDeployment.CreateDeployException=Exception creating deployment
+MuranoDeployment.GetDeployException=Exception getting deployment
+MuranoDeployment.DeleteDeployException=Exception deleting deployment
+MuranoDeployment.WaitDeploy=Waiting for deployment...
+MuranoDeployment.WaitDelete=Waiting for deletion...
+MuranoDeployment.WaitTimeoutDeploy=Timed out waiting for deployment.
+MuranoDeployment.WaitTimeoutDelete=Timed out waiting for deletion.
+MuranoDeployment.DeployFailed=Deployment failed for: {0}
+MuranoDeployment.ExecutorExceptionWhileDeploying=Bad state: RPC execution exception while deploying.
+MuranoDeployment.IOExceptionWhileDeploying=IO exception while deploying.
+MuranoDeployment.LogInsert=Inserting deployment: {0}
+MuranoDeployment.LogDelete=Deleting deployment: {0}
+MuranoDeploymentDescriptor.BadComponent=Components must match: {0}
+MuranoDeploymentDescriptor.NotEmpty=Components cannot be empty
+MuranoDeploymentDescriptor.SampleResolution={0}\nSample variable resolution: {1}
+MuranoDeploymentModule.AppName=Jenkins Cloud Deployer
+MuranoDeploymentModule.ConnectionError=Exception connecting to Deployment Manager
+MuranoEnvironmentDeleter_DisplayName=Deployment Turndown
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/Messages.properties b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/Messages.properties
new file mode 100644
index 0000000..776faa6
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/Messages.properties
@@ -0,0 +1,6 @@
+MuranoManagerDeployer.DisplayName=Openstack Murano Deployer
+MuranoManagerDeployer.EnvironmentException=Exception accessing build environment
+MuranoManagerBuildWrapper.DisplayName=Murano Deployer
+BuildStepDetailsProvider.MavenName=Maven
+TemplatedMuranoDeployment.DisplayName=Deploy using ready-made template
+RepositoryTemplatedMuranoDeployment.DisplayName=Deploy using configuration from project repository
\ No newline at end of file
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper/config.jelly
new file mode 100644
index 0000000..36f33d5
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerBuildWrapper/config.jelly
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer/config.jelly
new file mode 100644
index 0000000..4fb4c90
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/MuranoManagerDeployer/config.jelly
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment/config.jelly
new file mode 100644
index 0000000..c7d7252
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/RepositoryTemplatedDeployment/config.jelly
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/config.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/config.jelly
new file mode 100644
index 0000000..f6b2489
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/config.jelly
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/global.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/global.jelly
new file mode 100644
index 0000000..3c242ff
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/deploy/TemplatedDeployment/global.jelly
@@ -0,0 +1,12 @@
+
+
+
+
+
+
diff --git a/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/index.jelly b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/index.jelly
new file mode 100644
index 0000000..2c684f6
--- /dev/null
+++ b/deployment-manager/src/main/resources/org/openstack/murano/jenkins_plugins/muranoci/index.jelly
@@ -0,0 +1,4 @@
+
+
+ This plugin integrates Openstack Murano to Jenkins.
+