Rework the deploy/undeploy method

Create the camle box for depoy/undeploy closed loop with the new loop
data model

Issue-ID: CLAMP-334
Change-Id: Ica77163877c30b31efc37ab489b3810fe1251012
Signed-off-by: xg353y <xg353y@intl.att.com>
diff --git a/src/main/java/org/onap/clamp/clds/util/LoggingUtils.java b/src/main/java/org/onap/clamp/clds/util/LoggingUtils.java
index 300d606..47b4f9a 100644
--- a/src/main/java/org/onap/clamp/clds/util/LoggingUtils.java
+++ b/src/main/java/org/onap/clamp/clds/util/LoggingUtils.java
@@ -38,6 +38,7 @@
 import java.util.TimeZone;

 import java.util.UUID;

 

+import javax.net.ssl.HttpsURLConnection;

 import javax.servlet.http.HttpServletRequest;

 import javax.validation.constraints.NotNull;

 

@@ -259,6 +260,36 @@
         this.mLogger.info(ONAPLogConstants.Markers.INVOKE_SYNC + "{"+ invocationID +"}");

         return con;

     }

+

+    /**

+     * Report pending invocation with <tt>INVOKE</tt> marker,

+     * setting standard ONAP logging headers automatically.

+     *

+     * @param builder request builder, for setting headers.

+     * @param sync whether synchronous, nullable.

+     * @return invocation ID to be passed with invocation.

+     */

+    public HttpsURLConnection invokeHttps(final HttpsURLConnection con, String targetEntity, String targetServiceName) {

+        final String invocationID = UUID.randomUUID().toString();

+

+        // Set standard HTTP headers on (southbound request) builder.

+        con.setRequestProperty(ONAPLogConstants.Headers.REQUEST_ID,

+            defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.REQUEST_ID)));

+        con.setRequestProperty(ONAPLogConstants.Headers.INVOCATION_ID,

+            invocationID);

+        con.setRequestProperty(ONAPLogConstants.Headers.PARTNER_NAME,

+            defaultToEmpty(MDC.get(ONAPLogConstants.MDCs.PARTNER_NAME)));

+

+        invokeContext(targetEntity, targetServiceName, invocationID);

+

+        // Log INVOKE*, with the invocationID as the message body.

+        // (We didn't really want this kind of behavior in the standard,

+        // but is it worse than new, single-message MDC?)

+        this.mLogger.info(ONAPLogConstants.Markers.INVOKE);

+        this.mLogger.info(ONAPLogConstants.Markers.INVOKE_SYNC + "{"+ invocationID +"}");

+        return con;

+    }

+

     public void invokeReturn() {

         // Add the Invoke-return marker and clear the needed MDC

         this.mLogger.info(ONAPLogConstants.Markers.INVOKE_RETURN);

diff --git a/src/main/java/org/onap/clamp/exception/OperationException.java b/src/main/java/org/onap/clamp/exception/OperationException.java
new file mode 100644
index 0000000..0233b09
--- /dev/null
+++ b/src/main/java/org/onap/clamp/exception/OperationException.java
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. 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.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ * 
+ */
+
+package org.onap.clamp.exception;
+
+/**
+ * New exception to CldsUser errors.
+ *
+ */
+public class OperationException extends RuntimeException {
+
+    /**
+	 * The serial version ID.
+	 */
+	private static final long serialVersionUID = 2788967876393519620L;
+
+	/**
+     * This constructor can be used to create a new CldsUsersException.
+     * 
+     * @param message
+     *            A string message detailing the problem
+     * @param e
+     *            The exception sent by the code
+     */
+    public OperationException(String message, Throwable e) {
+        super(message, e);
+    }
+
+    /**
+     * This constructor can be used to create a new CldsUsersException. Use this
+     * constructor only if you are creating a new exception stack, not if an
+     * exception was already raised by another code.
+     *
+     * @param message
+     *            A string message detailing the problem
+     */
+    public OperationException(String message) {
+        super(message);
+    }
+
+}
diff --git a/src/main/java/org/onap/clamp/loop/Loop.java b/src/main/java/org/onap/clamp/loop/Loop.java
index 64e0108..6dc73e9 100644
--- a/src/main/java/org/onap/clamp/loop/Loop.java
+++ b/src/main/java/org/onap/clamp/loop/Loop.java
@@ -139,7 +139,7 @@
         return dcaeDeploymentId;
     }
 
-    void setDcaeDeploymentId(String dcaeDeploymentId) {
+    public void setDcaeDeploymentId(String dcaeDeploymentId) {
         this.dcaeDeploymentId = dcaeDeploymentId;
     }
 
@@ -147,7 +147,7 @@
         return dcaeDeploymentStatusUrl;
     }
 
-    void setDcaeDeploymentStatusUrl(String dcaeDeploymentStatusUrl) {
+    public void setDcaeDeploymentStatusUrl(String dcaeDeploymentStatusUrl) {
         this.dcaeDeploymentStatusUrl = dcaeDeploymentStatusUrl;
     }
 
@@ -167,11 +167,11 @@
         this.blueprint = blueprint;
     }
 
-    LoopState getLastComputedState() {
+    public LoopState getLastComputedState() {
         return lastComputedState;
     }
 
-    void setLastComputedState(LoopState lastComputedState) {
+    public void setLastComputedState(LoopState lastComputedState) {
         this.lastComputedState = lastComputedState;
     }
 
@@ -222,7 +222,7 @@
         log.setLoop(this);
     }
 
-    String getDcaeBlueprintId() {
+    public String getDcaeBlueprintId() {
         return dcaeBlueprintId;
     }
 
@@ -230,7 +230,7 @@
         this.dcaeBlueprintId = dcaeBlueprintId;
     }
 
-    JsonObject getModelPropertiesJson() {
+    public JsonObject getModelPropertiesJson() {
         return modelPropertiesJson;
     }
 
diff --git a/src/main/java/org/onap/clamp/loop/LoopService.java b/src/main/java/org/onap/clamp/loop/LoopService.java
index b499573..34e1b4b 100644
--- a/src/main/java/org/onap/clamp/loop/LoopService.java
+++ b/src/main/java/org/onap/clamp/loop/LoopService.java
@@ -54,7 +54,7 @@
         this.operationalPolicyService = operationalPolicyService;
     }
 
-    Loop saveOrUpdateLoop(Loop loop) {
+    public Loop saveOrUpdateLoop(Loop loop) {
         return loopsRepository.save(loop);
     }
 
@@ -62,7 +62,7 @@
         return loopsRepository.getAllLoopNames();
     }
 
-    Loop getLoop(String loopName) {
+    public Loop getLoop(String loopName) {
         return loopsRepository
             .findById(loopName)
             .orElse(null);
diff --git a/src/main/java/org/onap/clamp/operation/LoopOperation.java b/src/main/java/org/onap/clamp/operation/LoopOperation.java
new file mode 100644
index 0000000..af615af
--- /dev/null
+++ b/src/main/java/org/onap/clamp/operation/LoopOperation.java
@@ -0,0 +1,212 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. 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.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+package org.onap.clamp.operation;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.camel.Exchange;
+import org.onap.clamp.clds.client.DcaeDispatcherServices;
+import org.onap.clamp.clds.util.LoggingUtils;
+import org.onap.clamp.clds.util.ONAPLogConstants;
+import org.onap.clamp.exception.OperationException;
+import org.onap.clamp.loop.Loop;
+import org.onap.clamp.loop.LoopService;
+import org.onap.clamp.loop.LoopState;
+import org.slf4j.event.Level;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.yaml.snakeyaml.Yaml;
+
+/**
+ * Closed loop operations
+ */
+@Component
+public class LoopOperation {
+
+    protected static final EELFLogger logger          = EELFManager.getInstance().getLogger(LoopOperation.class);
+    protected static final EELFLogger auditLogger     = EELFManager.getInstance().getMetricsLogger();
+    private final DcaeDispatcherServices dcaeDispatcherServices;
+    private final LoopService loopService;
+    private LoggingUtils util = new LoggingUtils(logger);
+
+    @Autowired
+    private HttpServletRequest request;
+
+    @Autowired
+    public LoopOperation(LoopService loopService, DcaeDispatcherServices dcaeDispatcherServices) {
+        this.loopService = loopService;
+        this.dcaeDispatcherServices = dcaeDispatcherServices;
+    }
+
+    /**
+     * Deploy the closed loop.
+     *
+     * @param loopName the loop name
+     * @return the updated loop
+     * @throws Exceptions during the operation
+     */
+    public Loop deployLoop(Exchange camelExchange, String loopName) throws OperationException {
+        util.entering(request, "CldsService: Deploy model");
+        Date startTime = new Date();
+        Loop loop = loopService.getLoop(loopName);
+
+        if (loop == null) {
+            String msg = "Deploy loop exception: Not able to find closed loop:" + loopName;
+            util.exiting(HttpStatus.INTERNAL_SERVER_ERROR.toString(), msg, Level.INFO,
+                ONAPLogConstants.ResponseStatus.ERROR);
+            throw new OperationException(msg);
+        }
+
+        // verify the current closed loop state
+        if (loop.getLastComputedState() != LoopState.SUBMITTED) {
+            String msg = "Deploy loop exception: This closed loop is in state:" + loop.getLastComputedState() 
+                + ". It could be deployed only when it is in SUBMITTED state.";
+            util.exiting(HttpStatus.CONFLICT.toString(), msg, Level.INFO,
+                ONAPLogConstants.ResponseStatus.ERROR);
+            throw new OperationException(msg);
+        }
+
+        // Set the deploymentId if not present yet
+        String deploymentId = "";
+        // If model is already deployed then pass same deployment id
+        if (loop.getDcaeDeploymentId() != null && !loop.getDcaeDeploymentId().isEmpty()) {
+            deploymentId = loop.getDcaeDeploymentId();
+        } else {
+            loop.setDcaeDeploymentId(deploymentId = "closedLoop_" + loopName + "_deploymentId");
+        }
+
+        Yaml yaml = new Yaml();
+        Map<String, Object> yamlMap = yaml.load(loop.getBlueprint());
+        JsonObject bluePrint = wrapSnakeObject(yamlMap).getAsJsonObject();
+
+        loop.setDcaeDeploymentStatusUrl(dcaeDispatcherServices.createNewDeployment(deploymentId, loop.getDcaeBlueprintId(), bluePrint));
+        loop.setLastComputedState(LoopState.DEPLOYED);
+        // save the updated loop
+        loopService.saveOrUpdateLoop (loop);
+
+        // audit log
+        LoggingUtils.setTimeContext(startTime, new Date());
+        auditLogger.info("Deploy model completed");
+        util.exiting(HttpStatus.OK.toString(), "Successful", Level.INFO, ONAPLogConstants.ResponseStatus.COMPLETED);
+        return  loop;
+    }
+
+    /**
+     * Un deploy closed loop.
+     *
+     * @param loopName the loop name
+     * @return the updated loop
+     */
+    public Loop unDeployLoop(String loopName)  throws OperationException {
+        util.entering(request, "LoopOperation: Undeploy the closed loop");
+        Date startTime = new Date();
+        Loop loop = loopService.getLoop(loopName);
+
+        if (loop == null) {
+            String msg = "Undeploy loop exception: Not able to find closed loop:" + loopName;
+            util.exiting(HttpStatus.INTERNAL_SERVER_ERROR.toString(), msg, Level.INFO,
+                ONAPLogConstants.ResponseStatus.ERROR);
+            throw new OperationException(msg);
+        } 
+
+        // verify the current closed loop state
+        if (loop.getLastComputedState() != LoopState.DEPLOYED) {
+            String msg = "Unploy loop exception: This closed loop is in state:" + loop.getLastComputedState() 
+                + ". It could be undeployed only when it is in DEPLOYED state.";
+            util.exiting(HttpStatus.CONFLICT.toString(), msg, Level.INFO,
+                ONAPLogConstants.ResponseStatus.ERROR);
+            throw new OperationException(msg);
+        }
+
+        loop.setDcaeDeploymentStatusUrl(
+            dcaeDispatcherServices.deleteExistingDeployment(loop.getDcaeDeploymentId(), loop.getDcaeBlueprintId()));
+
+        // clean the deployment ID
+        loop.setDcaeDeploymentId(null);
+        loop.setLastComputedState(LoopState.SUBMITTED);
+
+        // save the updated loop
+        loopService.saveOrUpdateLoop (loop);
+
+        // audit log
+        LoggingUtils.setTimeContext(startTime, new Date());
+        auditLogger.info("Undeploy model completed");
+        util.exiting(HttpStatus.OK.toString(), "Successful", Level.INFO, ONAPLogConstants.ResponseStatus.COMPLETED);
+        return loop;
+    }
+
+    private JsonElement wrapSnakeObject(Object o) {
+        //NULL => JsonNull
+        if (o == null)
+            return JsonNull.INSTANCE;
+
+        // Collection => JsonArray
+        if (o instanceof Collection) {
+            JsonArray array = new JsonArray();
+            for (Object childObj : (Collection<?>)o)
+                array.add(wrapSnakeObject(childObj));
+            return array;
+        }
+
+        // Array => JsonArray
+        if (o.getClass().isArray()) {
+            JsonArray array = new JsonArray();
+
+            int length = Array.getLength(array);
+            for (int i=0; i<length; i++)
+                array.add(wrapSnakeObject(Array.get(array, i)));
+            return array;
+        }
+
+        // Map => JsonObject
+        if (o instanceof Map) {
+            Map<?, ?> map = (Map<?, ?>)o;
+
+            JsonObject jsonObject = new JsonObject();
+            for (final Map.Entry<?, ?> entry : map.entrySet()) {
+                final String name = String.valueOf(entry.getKey());
+                final Object value = entry.getValue();
+                jsonObject.add(name, wrapSnakeObject(value));
+            }
+            return jsonObject;
+        }
+
+        // otherwise take it as a string
+        return new JsonPrimitive(String.valueOf(o));
+    }
+}
diff --git a/src/main/resources/clds/camel/rest/clamp-api-v2.xml b/src/main/resources/clds/camel/rest/clamp-api-v2.xml
index d4872ef..c5828b2 100644
--- a/src/main/resources/clds/camel/rest/clamp-api-v2.xml
+++ b/src/main/resources/clds/camel/rest/clamp-api-v2.xml
@@ -10,7 +10,6 @@
         </get>
         <get uri="/v2/loop/{loopName}"
             outType="org.onap.clamp.loop.Loop"
-
             produces="application/json">
             <route>
                 <to uri="bean:org.onap.clamp.authorization.AuthorizationController?method=authorize(*,'cl','','read')" />
@@ -19,19 +18,22 @@
         </get>
         <get uri="/v2/loop/svgRepresentation/{loopName}"
             outType="java.lang.String"
-
             produces="application/xml">
-            <to
-                uri="bean:org.onap.clamp.loop.LoopController?method=getSVGRepresentation(${header.loopName})" />
+            <route>
+                <to uri="bean:org.onap.clamp.authorization.AuthorizationController?method=authorize(*,'cl','','read')" />
+                <to uri="bean:org.onap.clamp.loop.LoopController?method=getSVGRepresentation(${header.loopName})" />
+            </route>
         </get>
 
         <post uri="/v2/loop/updateGlobalProperties/{loopName}"
-              type="com.google.gson.JsonObject"
-              consumes="application/json"
-              outType="org.onap.clamp.loop.Loop"
-              produces="application/json">
-            <to
-                    uri="bean:org.onap.clamp.loop.LoopController?method=updateGlobalPropertiesJson(${header.loopName},${body})" />
+            type="com.google.gson.JsonObject"
+            consumes="application/json"
+            outType="org.onap.clamp.loop.Loop"
+            produces="application/json">
+            <route>
+                <to uri="bean:org.onap.clamp.authorization.AuthorizationController?method=authorize(*,'cl','','update')" />
+                <to uri="bean:org.onap.clamp.loop.LoopController?method=updateGlobalPropertiesJson(${header.loopName},${body})" />
+            </route>
         </post>
         <post uri="/v2/loop/updateOperationalPolicies/{loopName}"
             type="com.google.gson.JsonArray"
@@ -53,6 +55,22 @@
                 <to uri="bean:org.onap.clamp.loop.LoopController?method=updateMicroservicePolicy(${header.loopName},${body})" />
             </route>
         </post>
+        <put uri="/v2/loop/deployLoop/{loopName}"
+            outType="org.onap.clamp.loop.Loop"
+            produces="application/json">
+            <route>
+                <to uri="bean:org.onap.clamp.authorization.AuthorizationController?method=authorize(*,'cl','','update')" />
+                <to uri="bean:org.onap.clamp.operation.LoopOperation?method=deployLoop(*,${header.loopName})" />
+            </route>
+        </put>
+        <put uri="/v2/loop/undeployLoop/{loopName}"
+            outType="org.onap.clamp.loop.Loop"
+            produces="application/json">
+            <route>
+                <to uri="bean:org.onap.clamp.authorization.AuthorizationController?method=authorize(*,'cl','','update')" />
+                <to uri="bean:org.onap.clamp.operation.LoopOperation?method=unDeployLoop(${header.loopName})" />
+            </route>
+        </put>
     </rest>
 </rests>