transaction tracking improvements with telemetry 20/106520/3
authorjhh <jorge.hernandez-herrero@att.com>
Thu, 23 Apr 2020 18:18:48 +0000 (13:18 -0500)
committerjhh <jorge.hernandez-herrero@att.com>
Thu, 23 Apr 2020 20:48:55 +0000 (15:48 -0500)
Issue-ID: POLICY-2456
Signed-off-by: jhh <jorge.hernandez-herrero@att.com>
Change-Id: I75b7e8e787d567cf120d7ae7d65d1bff12e8e24e
Signed-off-by: jhh <jorge.hernandez-herrero@att.com>
controlloop/common/feature-controlloop-trans/src/main/java/org/onap/policy/drools/apps/controlloop/feature/trans/CacheBasedControlLoopMetricsManager.java
controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/apps/controlloop/feature/trans/ControlLoopMetricsFeatureTest.java
controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/server/restful/RestTransactionTracker.java [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/server/restful/RestTransactionTrackerTest.java [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/logback.xml [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-active.json [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-final-success.json [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-operation.json [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-permit.json [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-restart-success.json [new file with mode: 0644]
controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-restart.json [new file with mode: 0644]

index f0d8eb0..75e0f1f 100644 (file)
@@ -31,6 +31,7 @@ import java.util.List;
 import java.util.Properties;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
+import org.apache.commons.collections4.CollectionUtils;
 import org.onap.policy.controlloop.ControlLoopOperation;
 import org.onap.policy.controlloop.VirtualControlLoopNotification;
 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
@@ -88,11 +89,10 @@ class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
         this.cacheSize = cacheSize;
         this.transactionTimeout = transactionTimeout;
 
-        CacheLoader<UUID, VirtualControlLoopNotification> loader =
-                new CacheLoader<UUID, VirtualControlLoopNotification>() {
+        CacheLoader<UUID, VirtualControlLoopNotification> loader = new CacheLoader<>() {
 
             @Override
-            public VirtualControlLoopNotification load(UUID key) throws Exception {
+            public VirtualControlLoopNotification load(UUID key) {
                 return null;
             }
         };
@@ -176,7 +176,8 @@ class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
             notification.setNotificationTime(ZonedDateTime.now());
         }
 
-        notification.setFrom(notification.getFrom() + ":" + controller.getName());
+        notification.setFrom(notification.getFrom() + ":" + controller.getName()
+            + ":" + controller.getDrools().getCanonicalSessionNames());
     }
 
     @Override
@@ -218,7 +219,7 @@ class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
 
         this.transaction(notification, startTime);
         if (startNotification != null) {
-            cache.invalidate(startNotification);
+            removeTransaction(startNotification.getRequestId());
         }
     }
 
@@ -256,28 +257,20 @@ class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
     }
 
     protected void metric(VirtualControlLoopNotification notification) {
-        MdcTransaction trans = MdcTransaction
-                .newTransaction(notification.getRequestId().toString(), notification.getFrom())
-                .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget());
-
+        MdcTransaction trans = getMdcTransaction(notification);
         List<ControlLoopOperation> operations = notification.getHistory();
         switch (notification.getNotification()) {
             case ACTIVE:
-                trans.setStatusCode(true);
-                trans.metric().resetTransaction();
+                trans.setStatusCode(true).metric().resetTransaction();
                 break;
             case OPERATION:
-                metricOperation(trans, operations);
+                operation(trans.setStatusCode(true), operations).metric().resetTransaction();
                 break;
             case OPERATION_SUCCESS:
-                trans.setStatusCode(true);
-                operation(trans, operations);
-                trans.transaction().resetTransaction();
+                operation(trans.setStatusCode(true), operations).metric().transaction().resetTransaction();
                 break;
             case OPERATION_FAILURE:
-                trans.setStatusCode(false);
-                operation(trans, operations);
-                trans.transaction().resetTransaction();
+                operation(trans.setStatusCode(false), operations).metric().transaction().resetTransaction();
                 break;
             default:
                 /* unexpected */
@@ -287,65 +280,66 @@ class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
         }
     }
 
-    private void metricOperation(MdcTransaction trans, List<ControlLoopOperation> operations) {
-        trans.setStatusCode(true);
-        if (!operations.isEmpty()) {
-            ControlLoopOperation operation = operations.get(operations.size() - 1);
-            trans.setTargetEntity(operation.getTarget());
-            trans.setTargetServiceName(operation.getActor());
-        }
-        trans.metric().resetTransaction();
+    private MdcTransaction getMdcTransaction(VirtualControlLoopNotification notification) {
+        return MdcTransaction
+                .newTransaction(notification.getRequestId().toString(), notification.getFrom())
+                .setServiceName(notification.getClosedLoopControlName())
+                .setServiceInstanceId(notification.getPolicyScope()
+                    + ":" + notification.getPolicyName() + ":" + notification.getPolicyVersion())
+                .setProcessKey("" + notification.getAai())
+                .setTargetEntity(notification.getTargetType() + "." + notification.getTarget())
+                .setResponseCode((notification.getNotification() != null) ? notification.getNotification().name() : "-")
+                .setResponseDescription(notification.getMessage())
+                .setClientIpAddress(notification.getClosedLoopEventClient());
     }
 
-    protected void operation(MdcTransaction trans, List<ControlLoopOperation> operations) {
-        if (!operations.isEmpty()) {
-            ControlLoopOperation operation = operations.get(operations.size() - 1);
+    protected MdcTransaction operation(MdcTransaction trans, List<ControlLoopOperation> operations) {
+        if (CollectionUtils.isEmpty(operations)) {
+            return trans;
+        }
 
-            if (operation.getTarget() != null) {
-                trans.setTargetEntity(operation.getTarget());
-            }
+        ControlLoopOperation operation = operations.get(operations.size() - 1);
 
-            if (operation.getActor() != null) {
-                trans.setTargetServiceName(operation.getActor());
-            }
+        if (operation.getActor() != null) {
+            trans.setTargetServiceName(operation.getActor() + "." + operation.getOperation());
+        }
 
-            if (operation.getMessage() != null) {
-                trans.setResponseDescription(operation.getMessage());
-            }
+        if (operation.getTarget() != null) {
+            trans.setTargetVirtualEntity(operation.getTarget());
+        }
 
+        if (operation.getSubRequestId() != null) {
             trans.setInvocationId(operation.getSubRequestId());
+        }
 
-            if (operation.getOutcome() != null) {
-                trans.setResponseCode(operation.getOutcome());
-            }
+        if (operation.getOutcome() != null) {
+            trans.setResponseDescription(operation.getOutcome() + ":" + operation.getMessage());
+        }
 
-            if (operation.getStart() != null) {
-                trans.setStartTime(operation.getStart());
-            }
+        if (operation.getStart() != null) {
+            trans.setStartTime(operation.getStart());
+        }
 
-            if (operation.getEnd() != null) {
-                trans.setEndTime(operation.getEnd());
-            }
+        if (operation.getEnd() != null) {
+            trans.setEndTime(operation.getEnd());
         }
+
+        return trans;
     }
 
     protected void transaction(VirtualControlLoopNotification notification, ZonedDateTime startTime) {
-        MdcTransaction trans = MdcTransaction
-                .newTransaction(notification.getRequestId().toString(), notification.getFrom())
-                .setServiceName(notification.getClosedLoopControlName()).setTargetEntity(notification.getTarget())
-                .setStartTime(startTime.toInstant()).setEndTime(notification.getNotificationTime().toInstant())
-                .setResponseDescription(notification.getMessage());
+        MdcTransaction trans = getMdcTransaction(notification)
+                .setStartTime(startTime.toInstant())
+                .setEndTime(notification.getNotificationTime().toInstant());
 
         switch (notification.getNotification()) {
             case FINAL_OPENLOOP:
-                trans.setStatusCode(true);
-                break;
+                /* fall through */
             case FINAL_SUCCESS:
                 trans.setStatusCode(true);
                 break;
             case FINAL_FAILURE:
-                trans.setStatusCode(false);
-                break;
+                /* fall through */
             case REJECTED:
                 trans.setStatusCode(false);
                 break;
@@ -361,11 +355,11 @@ class CacheBasedControlLoopMetricsManager implements ControlLoopMetrics {
 
     @Override
     public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append("CacheBasedControlLoopMetricsManager{");
-        sb.append("cacheSize=").append(cacheSize);
-        sb.append(", transactionTimeout=").append(transactionTimeout);
-        sb.append('}');
-        return sb.toString();
+        return "CacheBasedControlLoopMetricsManager{" + "cacheSize=" + cacheSize
+                       + ",transactionTimeout="
+                       + transactionTimeout
+                       + ",cacheOccupancy="
+                       + getCacheOccupancy()
+                       + "}";
     }
 }
index b713e8e..eeb382a 100644 (file)
@@ -23,6 +23,7 @@ package org.onap.policy.drools.apps.controlloop.feature.trans;
 import static org.awaitility.Awaitility.await;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -34,8 +35,10 @@ import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.utils.resources.ResourceUtils;
 import org.onap.policy.controlloop.ControlLoopNotificationType;
 import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.controlloop.util.Serialization;
 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
 import org.onap.policy.drools.system.PolicyController;
 import org.onap.policy.drools.system.PolicyEngineConstants;
@@ -87,7 +90,7 @@ public class ControlLoopMetricsFeatureTest {
     }
 
     @Test
-    public void testValidActiveNotification() throws InterruptedException {
+    public void testValidActiveNotification() {
         ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
         VirtualControlLoopNotification notification = new VirtualControlLoopNotification();
         UUID requestId = UUID.randomUUID();
@@ -99,7 +102,7 @@ public class ControlLoopMetricsFeatureTest {
         assertTrue(ControlLoopMetricsManager.getManager().getTransaction(requestId).getFrom()
                         .contains(testController.getName()));
         assertNotNull(ControlLoopMetricsManager.getManager().getTransaction(requestId).getNotificationTime());
-        assertTrue(ControlLoopMetricsManager.getManager().getCacheOccupancy() == 1);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getCacheOccupancy());
 
         /* wait for the entries to expire */
         await().atMost(ControlLoopMetricsManager.getManager().getTransactionTimeout() + 1, TimeUnit.SECONDS)
@@ -135,7 +138,7 @@ public class ControlLoopMetricsFeatureTest {
     }
 
     @Test
-    public void testEviction() throws InterruptedException {
+    public void testEviction() {
         ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
         for (int i = 0; i < ControlLoopMetricsManager.getManager().getCacheSize(); i++) {
             VirtualControlLoopNotification notification = generateNotification();
@@ -151,10 +154,10 @@ public class ControlLoopMetricsFeatureTest {
         assertEquals(ControlLoopMetricsManager.getManager().getCacheOccupancy(),
                         ControlLoopMetricsManager.getManager().getCacheOccupancy());
         assertNotNull(ControlLoopMetricsManager.getManager().getTransaction(overflowNotification.getRequestId()));
-        assertTrue(ControlLoopMetricsManager.getManager().getTransactionIds().size() == ControlLoopMetricsManager
-                        .getManager().getCacheSize());
-        assertTrue(ControlLoopMetricsManager.getManager().getCacheOccupancy() == ControlLoopMetricsManager.getManager()
-                        .getCacheSize());
+        assertEquals(ControlLoopMetricsManager.getManager().getTransactionIds().size(),
+                ControlLoopMetricsManager.getManager().getCacheSize());
+        assertEquals(ControlLoopMetricsManager.getManager().getCacheOccupancy(),
+                ControlLoopMetricsManager.getManager().getCacheSize());
         assertFalse(ControlLoopMetricsManager.getManager().getTransactionIds().isEmpty());
         assertFalse(ControlLoopMetricsManager.getManager().getTransactions().isEmpty());
 
@@ -163,10 +166,10 @@ public class ControlLoopMetricsFeatureTest {
                         .until(() -> ControlLoopMetricsManager.getManager().getTransactions().isEmpty());
 
         ControlLoopMetricsManager.getManager().refresh();
-        assertTrue(ControlLoopMetricsManager.getManager().getTransactionIds().size() == ControlLoopMetricsManager
-                        .getManager().getCacheOccupancy());
-        assertFalse(ControlLoopMetricsManager.getManager().getCacheOccupancy() == ControlLoopMetricsManager.getManager()
-                        .getCacheSize());
+        assertEquals(ControlLoopMetricsManager.getManager().getTransactionIds().size(),
+                ControlLoopMetricsManager.getManager().getCacheOccupancy());
+        assertNotEquals(ControlLoopMetricsManager.getManager().getCacheOccupancy(),
+                ControlLoopMetricsManager.getManager().getCacheSize());
         assertTrue(ControlLoopMetricsManager.getManager().getTransactionIds().isEmpty());
         assertTrue(ControlLoopMetricsManager.getManager().getTransactions().isEmpty());
 
@@ -184,6 +187,72 @@ public class ControlLoopMetricsFeatureTest {
     @Test
     public void getSequenceNumber() {
         ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
-        assertTrue(feature.getSequenceNumber() == ControlLoopMetricsFeature.FEATURE_SEQUENCE_PRIORITY);
+        assertEquals(feature.getSequenceNumber(), ControlLoopMetricsFeature.FEATURE_SEQUENCE_PRIORITY);
+    }
+
+    @Test
+    public void testSuccessControlLoop() {
+        ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
+
+        String activeNotification = ResourceUtils.getResourceAsString("policy-cl-mgt-active.json");
+        VirtualControlLoopNotification active =
+                Serialization.gsonPretty.fromJson(activeNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, active);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        String opStartNotification = ResourceUtils.getResourceAsString("policy-cl-mgt-operation.json");
+        VirtualControlLoopNotification opStart =
+                Serialization.gsonPretty.fromJson(opStartNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, opStart);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        String permitNotification = ResourceUtils.getResourceAsString("policy-cl-mgt-permit.json");
+        VirtualControlLoopNotification permit =
+                Serialization.gsonPretty.fromJson(permitNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, permit);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        String restartNotification = ResourceUtils.getResourceAsString("policy-cl-mgt-restart.json");
+        VirtualControlLoopNotification restart =
+                Serialization.gsonPretty.fromJson(restartNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, restart);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        String restartSuccessNotification =
+                ResourceUtils.getResourceAsString("policy-cl-mgt-restart-success.json");
+        VirtualControlLoopNotification restartSuccess =
+                Serialization.gsonPretty.fromJson(restartSuccessNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, restartSuccess);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        String finalSuccessNotification =
+                ResourceUtils.getResourceAsString("policy-cl-mgt-final-success.json");
+        VirtualControlLoopNotification finalSuccess =
+                Serialization.gsonPretty.fromJson(finalSuccessNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, finalSuccess);
+        assertEquals(0, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+    }
+
+    @Test
+    public void testUntrackedNotifications() throws InterruptedException {
+        ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
+
+        String finalSuccessNotification =
+                ResourceUtils.getResourceAsString("policy-cl-mgt-final-success.json");
+        VirtualControlLoopNotification finalSuccess =
+                Serialization.gsonPretty.fromJson(finalSuccessNotification, VirtualControlLoopNotification.class);
+        finalSuccess.setRequestId(UUID.randomUUID());
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, finalSuccess);
+        assertEquals(0, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        String opStartNotification =
+                ResourceUtils.getResourceAsString("policy-cl-mgt-operation.json");
+        VirtualControlLoopNotification opStart =
+                Serialization.gsonPretty.fromJson(opStartNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, CommInfrastructure.DMAAP, POLICY_CL_MGT, opStart);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        Thread.sleep((ControlLoopMetricsManager.getManager().getTransactionTimeout() + 1) * 1000L);  // NOSONAR
+        assertEquals(0, ControlLoopMetricsManager.getManager().getTransactionIds().size());
     }
 }
diff --git a/controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/server/restful/RestTransactionTracker.java b/controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/server/restful/RestTransactionTracker.java
new file mode 100644 (file)
index 0000000..b954167
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.drools.server.restful;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import java.util.UUID;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.drools.apps.controlloop.feature.trans.ControlLoopMetricsManager;
+
+/**
+ * REST Transaction Tracker.
+ */
+
+@Path("/policy/pdp/engine/controllers")
+@Produces({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
+@Consumes({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
+@Api
+public class RestTransactionTracker {
+
+    /**
+     * GET transactions.
+     */
+
+    @GET
+    @Path("transactions")
+    @ApiOperation(value = "Retrieve in-progress transactions", responseContainer = "List")
+    public Response transactions() {
+        return Response.status(Response.Status.OK)
+                       .entity(ControlLoopMetricsManager.getManager().getTransactions()).build();
+    }
+
+    /**
+     * GET one transaction.
+     */
+
+    @GET
+    @Path("transactions/{transactionId}")
+    @ApiOperation(value = "Retrieve an in-progress transaction", response = VirtualControlLoopNotification.class)
+    public Response transactionId(
+          @ApiParam(value = "UUID", required = true) @PathParam("transactionId") String transactionId) {
+        VirtualControlLoopNotification notification =
+                ControlLoopMetricsManager.getManager().getTransaction(UUID.fromString(transactionId));
+        return Response.status((notification != null) ? Response.Status.OK : Response.Status.NOT_FOUND)
+                       .entity(notification).build();
+    }
+
+    /**
+     * Resets the cache with a new cache size.
+     */
+
+    @PUT
+    @Path("transactions/cacheSize/{cacheSize}")
+    @ApiOperation(value = "Sets the cache size", response = Integer.class)
+    public Response cacheSize(
+            @ApiParam(value = "cache size", required = true) @PathParam("cacheSize") int cacheSize) {
+        ControlLoopMetricsManager.getManager().resetCache(cacheSize,
+                ControlLoopMetricsManager.getManager().getTransactionTimeout());
+        return Response.status(Response.Status.OK)
+                       .entity(ControlLoopMetricsManager.getManager().getCacheSize()).build();
+    }
+
+    /**
+     * GET the cache size.
+     */
+
+    @GET
+    @Path("transactions/cacheSize")
+    @ApiOperation(value = "Gets the cache size", response = Integer.class)
+    public Response cacheSize() {
+        return Response.status(Response.Status.OK)
+                       .entity(ControlLoopMetricsManager.getManager().getCacheSize()).build();
+    }
+
+    /**
+     * Resets the cache with a new transaction timeout in seconds.
+     */
+
+    @PUT
+    @Path("transactions/timeout/{timeoutSecs}")
+    @ApiOperation(value = "Sets the timeout in seconds", response = Integer.class)
+    public Response timeout(
+            @ApiParam(value = "timeout", required = true) @PathParam("timeoutSecs") long timeoutSecs) {
+        ControlLoopMetricsManager.getManager().resetCache(
+                ControlLoopMetricsManager.getManager().getCacheSize(), timeoutSecs);
+        return Response.status(Response.Status.OK)
+                       .entity(ControlLoopMetricsManager.getManager().getTransactionTimeout()).build();
+    }
+
+    /**
+     * GET the cache timeout.
+     */
+
+    @GET
+    @Path("transactions/timeout")
+    @ApiOperation(value = "Gets the cache timeout", response = Long.class)
+    public Response timeout() {
+        return Response.status(Response.Status.OK)
+                       .entity(ControlLoopMetricsManager.getManager().getTransactionTimeout()).build();
+    }
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/server/restful/RestTransactionTrackerTest.java b/controlloop/common/feature-controlloop-trans/src/test/java/org/onap/policy/drools/server/restful/RestTransactionTrackerTest.java
new file mode 100644 (file)
index 0000000..2fa4ed5
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2020 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.policy.drools.server.restful;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.List;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.Response;
+import org.jetbrains.annotations.NotNull;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.common.endpoints.event.comm.Topic;
+import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
+import org.onap.policy.common.endpoints.http.client.HttpClient;
+import org.onap.policy.common.endpoints.http.client.HttpClientFactoryInstance;
+import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.http.server.HttpServletServerFactoryInstance;
+import org.onap.policy.common.utils.network.NetworkUtil;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.controlloop.VirtualControlLoopNotification;
+import org.onap.policy.controlloop.util.Serialization;
+import org.onap.policy.drools.apps.controlloop.feature.trans.ControlLoopMetricsFeature;
+import org.onap.policy.drools.apps.controlloop.feature.trans.ControlLoopMetricsManager;
+import org.onap.policy.drools.persistence.SystemPersistenceConstants;
+import org.onap.policy.drools.system.PolicyController;
+import org.onap.policy.drools.system.PolicyEngineConstants;
+
+public class RestTransactionTrackerTest {
+
+    private static PolicyController testController;
+    private static HttpClient client;
+
+    @BeforeClass
+    public static void testBeforeClass() throws Exception {
+        SystemPersistenceConstants.getManager().setConfigurationDir("target/test-classes");
+
+        HttpServletServerFactoryInstance.getServerFactory().destroy();
+        HttpClientFactoryInstance.getClientFactory().destroy();
+
+        HttpClientFactoryInstance.getClientFactory().build(
+                BusTopicParams.builder()
+                        .clientName("trans")
+                        .hostname("localhost")
+                        .port(8769)
+                        .basePath("policy/pdp/engine/controllers/transactions")
+                        .managed(true)
+                        .build());
+
+        HttpServletServer server =
+                HttpServletServerFactoryInstance
+                        .getServerFactory()
+                        .build("trans", "localhost", 8769, "/", true, true);
+        server.addServletClass("/*", RestTransactionTracker.class.getName());
+        server.waitedStart(5000L);
+        assertTrue(NetworkUtil.isTcpPortOpen("localhost", 8769, 5, 10000L));
+
+        testController = PolicyEngineConstants.getManager().createPolicyController("metrics",
+                SystemPersistenceConstants.getManager().getControllerProperties("metrics"));
+
+        client = HttpClientFactoryInstance.getClientFactory().get("trans");
+    }
+
+    @AfterClass
+    public static void testAfterClass() {
+        HttpClientFactoryInstance.getClientFactory().destroy();
+        HttpServletServerFactoryInstance.getServerFactory().destroy();
+
+        SystemPersistenceConstants.getManager().setConfigurationDir(null);
+    }
+
+    @Test
+    public void testConfiguration() {
+        equals(get("cacheSize", Response.Status.OK.getStatusCode()), Integer.class, 3);
+        equals(get("timeout", Response.Status.OK.getStatusCode()), Integer.class, 2);
+
+        put("cacheSize/10", "", Response.Status.OK.getStatusCode());
+        put("timeout/20", "", Response.Status.OK.getStatusCode());
+
+        equals(get("cacheSize", Response.Status.OK.getStatusCode()), Integer.class, 10);
+        equals(get("timeout", Response.Status.OK.getStatusCode()), Integer.class, 20);
+
+        put("cacheSize/3", "", Response.Status.OK.getStatusCode());
+        put("timeout/2", "", Response.Status.OK.getStatusCode());
+
+        equals(get("cacheSize", Response.Status.OK.getStatusCode()), Integer.class, 3);
+        equals(get("timeout", Response.Status.OK.getStatusCode()), Integer.class, 2);
+    }
+
+    @Test
+    public void testTransactions() {
+        equals(get("/", Response.Status.OK.getStatusCode()), List.class, Collections.emptyList());
+
+        ControlLoopMetricsFeature feature = new ControlLoopMetricsFeature();
+
+        assertTrue(HttpClient.getBody(get("/", Response.Status.OK.getStatusCode()), List.class).isEmpty());
+        get("/664be3d2-6c12-4f4b-a3e7-c349acced200", Response.Status.NOT_FOUND.getStatusCode());
+
+        String activeNotification = ResourceUtils.getResourceAsString("policy-cl-mgt-active.json");
+        VirtualControlLoopNotification active =
+                Serialization.gsonPretty.fromJson(activeNotification, VirtualControlLoopNotification.class);
+        feature.beforeDeliver(testController, Topic.CommInfrastructure.DMAAP, "POLICY-CL-MGT", active);
+        assertEquals(1, ControlLoopMetricsManager.getManager().getTransactionIds().size());
+
+        assertFalse(HttpClient.getBody(get("/", Response.Status.OK.getStatusCode()), List.class).isEmpty());
+        notNull(get("/664be3d2-6c12-4f4b-a3e7-c349acced200", Response.Status.OK.getStatusCode()), String.class);
+    }
+
+    private Response get(String contextPath, int statusCode) {
+        Response response = client.get(contextPath);
+        return checkResponse(statusCode, response);
+    }
+
+    private Response put(String contextPath, String body, int statusCode) {
+        Response response = client.put(contextPath, Entity.json(body), Collections.emptyMap());
+        return checkResponse(statusCode, response);
+    }
+
+    private <T, Y> void equals(Response response, Class<T> clazz, Y expected) {
+        assertEquals(expected, HttpClient.getBody(response, clazz));
+    }
+
+    private <T> void notNull(Response response, Class<T> clazz) {
+        assertNotNull(HttpClient.getBody(response, clazz));
+    }
+
+    private <T> void empty(Response response, Class<T> clazz) {
+        assertNull(HttpClient.getBody(response, clazz));
+    }
+
+    @NotNull
+    private Response checkResponse(int statusCode, Response response) {
+        assertEquals(statusCode, response.getStatus());
+        return response;
+    }
+}
\ No newline at end of file
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/logback.xml b/controlloop/common/feature-controlloop-trans/src/test/resources/logback.xml
new file mode 100644 (file)
index 0000000..6a25769
--- /dev/null
@@ -0,0 +1,148 @@
+<!--
+  ============LICENSE_START=======================================================
+  policy-management
+  ================================================================================
+  Copyright (C) 2017-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=========================================================
+  -->
+
+<configuration scan="true" scanPeriod="30 seconds" debug="false">
+
+    <property name="logDir" value="target/test-classes" />
+
+    <property name="errorLog" value="error" />
+    <property name="debugLog" value="debug" />
+    <property name="networkLog" value="network" />
+
+    <property name="metricLog" value="metric" />
+    <property name="transactionLog" value="audit" />
+
+    <property name="debugPattern"
+        value="[%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00, UTC}|%level|%logger{0}|%thread] %msg%n" />
+    <property name="errorPattern" value="${debugPattern}" />
+    <property name="networkPattern" value="[%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00, UTC}|%t]%m%n" />
+    <property name="abstractNetworkPattern"
+        value="[%d{yyyy-MM-dd'T'HH:mm:ss.SSS+00:00, UTC}] [%X{networkEventType:-NULL}|%X{networkProtocol:-NULL}|%X{networkTopic:-NULL}|%X{requestID:-NULL}]%n" />
+
+    <property name="metricPattern"
+        value="%X{RequestID}|%X{InvocationID}|%X{ServiceName}|%X{PartnerName}|%X{BeginTimestamp}|%X{EndTimestamp}|%X{ElapsedTime}|%X{ServiceInstanceID}|%X{VirtualServerName}|%X{StatusCode}|%X{ResponseCode}|%X{ResponseDescription}|%X{InstanceUUID}|%X{Severity}|%X{TargetEntity}|%X{TargetServiceName}|%X{Server}|%X{ServerIPAddress}|%X{ServerFQDN}|%X{ClientIPAddress}|%X{ProcessKey}|%X{RemoteHost}||%X{TargetVirtualEntity}|%level|%thread| %msg%n" />
+    <property name="transactionPattern" value="${metricPattern}" />
+
+    <appender name="ErrorOut" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logDir}/${errorLog}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${logDir}/${errorLog}.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
+            <maxFileSize>50MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>WARN</level>
+        </filter>
+        <encoder>
+            <pattern>${errorPattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="AsyncErrorOut" class="ch.qos.logback.classic.AsyncAppender">
+        <appender-ref ref="ErrorOut" />
+    </appender>
+
+    <appender name="DebugOut" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logDir}/${debugLog}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${logDir}/${debugLog}.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
+            <maxFileSize>50MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${debugPattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="AsyncDebugOut" class="ch.qos.logback.classic.AsyncAppender">
+        <appender-ref ref="DebugOut" />
+    </appender>
+
+    <appender name="NetworkOut" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logDir}/${networkLog}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${logDir}/${networkLog}.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
+            <maxFileSize>50MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+        </rollingPolicy>
+        <encoder>
+            <pattern>${networkPattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="AsyncNetworkOut" class="ch.qos.logback.classic.AsyncAppender">
+        <appender-ref ref="NetworkOut" />
+    </appender>
+
+    <appender name="MetricOut" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logDir}/${metricLog}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${logDir}/${metricLog}.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
+            <maxFileSize>50MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+        </rollingPolicy>
+        <filter class="org.onap.policy.drools.utils.logging.LoggerMarkerFilter$MetricLoggerMarkerFilter" />
+        <encoder>
+            <pattern>${metricPattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="AsyncMetricOut" class="ch.qos.logback.classic.AsyncAppender">
+        <appender-ref ref="MetricOut" />
+    </appender>
+
+    <appender name="TransactionOut" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${logDir}/${transactionLog}.log</file>
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${logDir}/${transactionLog}.%d{yyyy-MM-dd}.%i.log.zip</fileNamePattern>
+            <maxFileSize>50MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+        </rollingPolicy>
+        <filter class="org.onap.policy.drools.utils.logging.LoggerMarkerFilter$TransactionLoggerMarkerFilter" />
+        <encoder>
+            <pattern>${transactionPattern}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="AsyncTransactionOut" class="ch.qos.logback.classic.AsyncAppender">
+        <appender-ref ref="TransactionOut" />
+    </appender>
+
+    <logger name="network" level="INFO" additivity="false">
+        <appender-ref ref="AsyncNetworkOut" />
+    </logger>
+
+    <logger name="org.eclipse.jetty.server.RequestLog" level="info" additivity="false">
+        <appender-ref ref="AsyncNetworkOut" />
+    </logger>
+
+    <root level="INFO">
+        <appender-ref ref="AsyncDebugOut" />
+        <appender-ref ref="AsyncErrorOut" />
+        <appender-ref ref="AsyncMetricOut" />
+        <appender-ref ref="AsyncTransactionOut" />
+    </root>
+
+</configuration>
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-active.json b/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-active.json
new file mode 100644 (file)
index 0000000..9430327
--- /dev/null
@@ -0,0 +1,21 @@
+{
+    "AAI": {
+        "vserver.prov-status": "ACTIVE",
+        "vserver.is-closed-loop-disabled": "false",
+        "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+    },
+    "closedLoopAlarmStart": 1463679805324,
+    "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+    "version": "1.0.2",
+    "requestId": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+    "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+    "targetType": "VNF",
+    "target": "generic-vnf.vnf-id",
+    "from": "policy",
+    "policyScope": "onap.policies.controlloop.operational.common.Drools:1.0.0",
+    "policyName": "operational.restart.EVENT",
+    "policyVersion": "1.0.0",
+    "notification": "ACTIVE",
+    "notificationTime": "2020-04-20 19:12:17.223175+00:00",
+    "history": []
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-final-success.json b/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-final-success.json
new file mode 100644 (file)
index 0000000..e1f98de
--- /dev/null
@@ -0,0 +1,32 @@
+{
+    "AAI": {
+        "vserver.prov-status": "ACTIVE",
+        "vserver.is-closed-loop-disabled": "false",
+        "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+    },
+    "closedLoopAlarmStart": 1463679805324,
+    "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+    "version": "1.0.2",
+    "requestId": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+    "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+    "targetType": "VNF",
+    "target": "generic-vnf.vnf-id",
+    "from": "policy",
+    "policyScope": "onap.policies.controlloop.operational.common.Drools:1.0.0",
+    "policyName": "operational.restart.EVENT.MANAGER.FINAL",
+    "policyVersion": "1.0.0",
+    "notification": "FINAL: SUCCESS",
+    "notificationTime": "2020-04-20 19:12:17.684718+00:00",
+    "history": [
+        {
+            "actor": "APPC",
+            "operation": "Restart",
+            "target": "Target [type=VM, resourceId=null]",
+            "start": 1587409937614,
+            "end": 1587409937679,
+            "subRequestId": "38ce5fcb-f16a-4cfc-abc6-8d8b78e2f959",
+            "outcome": "Success",
+            "message": "Restart Successful"
+        }
+    ]
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-operation.json b/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-operation.json
new file mode 100644 (file)
index 0000000..567af48
--- /dev/null
@@ -0,0 +1,22 @@
+{
+    "AAI": {
+        "vserver.prov-status": "ACTIVE",
+        "vserver.is-closed-loop-disabled": "false",
+        "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+    },
+    "closedLoopAlarmStart": 1463679805324,
+    "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+    "version": "1.0.2",
+    "requestId": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+    "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+    "targetType": "VNF",
+    "target": "generic-vnf.vnf-id",
+    "from": "policy",
+    "policyScope": "onap.policies.controlloop.operational.common.Drools:1.0.0",
+    "policyName": "operational.restart.EVENT.MANAGER.PROCESSING",
+    "policyVersion": "1.0.0",
+    "notification": "OPERATION",
+    "message": "Sending guard query for APPC Restart",
+    "notificationTime": "2020-04-20 19:12:17.244277+00:00",
+    "history": []
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-permit.json b/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-permit.json
new file mode 100644 (file)
index 0000000..d8c28be
--- /dev/null
@@ -0,0 +1,22 @@
+{
+    "AAI": {
+        "vserver.prov-status": "ACTIVE",
+        "vserver.is-closed-loop-disabled": "false",
+        "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+    },
+    "closedLoopAlarmStart": 1463679805324,
+    "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+    "version": "1.0.2",
+    "requestId": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+    "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+    "targetType": "VNF",
+    "target": "generic-vnf.vnf-id",
+    "from": "policy",
+    "policyScope": "onap.policies.controlloop.operational.common.Drools:1.0.0",
+    "policyName": "operational.restart.EVENT.MANAGER.PROCESSING",
+    "policyVersion": "1.0.0",
+    "notification": "OPERATION",
+    "message": "Guard result for APPC Restart is Permit",
+    "notificationTime": "2020-04-20 19:12:17.608770+00:00",
+    "history": []
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-restart-success.json b/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-restart-success.json
new file mode 100644 (file)
index 0000000..8068645
--- /dev/null
@@ -0,0 +1,33 @@
+{
+    "AAI": {
+        "vserver.prov-status": "ACTIVE",
+        "vserver.is-closed-loop-disabled": "false",
+        "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+    },
+    "closedLoopAlarmStart": 1463679805324,
+    "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+    "version": "1.0.2",
+    "requestId": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+    "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+    "targetType": "VNF",
+    "target": "generic-vnf.vnf-id",
+    "from": "policy",
+    "policyScope": "onap.policies.controlloop.operational.common.Drools:1.0.0",
+    "policyName": "operational.restart.EVENT.MANAGER.PROCESSING",
+    "policyVersion": "1.0.0",
+    "notification": "OPERATION: SUCCESS",
+    "message": "actor=APPC,operation=Restart,target=Target [type=VM, resourceId=null],start=2020-04-20T19:12:17.614106Z,end=2020-04-20T19:12:17.679500Z,subRequestId=38ce5fcb-f16a-4cfc-abc6-8d8b78e2f959,outcome=Success,message=Restart Successful",
+    "notificationTime": "2020-04-20 19:12:17.680716+00:00",
+    "history": [
+        {
+            "actor": "APPC",
+            "operation": "Restart",
+            "target": "Target [type=VM, resourceId=null]",
+            "start": 1587409937614,
+            "end": 1587409937679,
+            "subRequestId": "38ce5fcb-f16a-4cfc-abc6-8d8b78e2f959",
+            "outcome": "Success",
+            "message": "Restart Successful"
+        }
+    ]
+}
diff --git a/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-restart.json b/controlloop/common/feature-controlloop-trans/src/test/resources/policy-cl-mgt-restart.json
new file mode 100644 (file)
index 0000000..1825a02
--- /dev/null
@@ -0,0 +1,22 @@
+{
+    "AAI": {
+        "vserver.prov-status": "ACTIVE",
+        "vserver.is-closed-loop-disabled": "false",
+        "generic-vnf.vnf-id": "vCPE_Infrastructure_vGMUX_demo_app"
+    },
+    "closedLoopAlarmStart": 1463679805324,
+    "closedLoopControlName": "ControlLoop-vCPE-48f0c2c3-a172-4192-9ae3-052274181b6e",
+    "version": "1.0.2",
+    "requestId": "664be3d2-6c12-4f4b-a3e7-c349acced200",
+    "closedLoopEventClient": "DCAE_INSTANCE_ID.dcae-tca",
+    "targetType": "VNF",
+    "target": "generic-vnf.vnf-id",
+    "from": "policy",
+    "policyScope": "onap.policies.controlloop.operational.common.Drools:1.0.0",
+    "policyName": "operational.restart.EVENT.MANAGER.PROCESSING",
+    "policyVersion": "1.0.0",
+    "notification": "OPERATION",
+    "message": "actor=APPC,operation=Restart,target=Target [type=VM, resourceId=null],subRequestId=38ce5fcb-f16a-4cfc-abc6-8d8b78e2f959",
+    "notificationTime": "2020-04-20 19:12:17.614625+00:00",
+    "history": []
+}