PNF support changes in policy/models

AAI Enrichment method for PNF target-type and unit tests.
Addition of a hashmap field to ControlLoopEvent class to hold event specific parameters.
Update CDS actor to include additional event parameters

Issue-ID: POLICY-1187
Signed-off-by: Rashmi Pujar <rashmi.pujar@bell.ca>
Change-Id: Ie0ceb320943531de6e6bc8675844b29a358dfb7e
diff --git a/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/CdsActorServiceProvider.java b/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/CdsActorServiceProvider.java
index 65cc039..df13ba3 100644
--- a/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/CdsActorServiceProvider.java
+++ b/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/CdsActorServiceProvider.java
@@ -130,6 +130,11 @@
         // E.g. For vFW usecase El-Alto inject service-instance-id, generic-vnf-id as needed by CDS.
         request.setAaiProperties(aaiParams);
 
+        // Inject any additional event parameters that may be present in the onset event
+        if (onset.getAdditionalEventParams() != null) {
+            request.setAdditionalEventParams(onset.getAdditionalEventParams());
+        }
+
         Builder struct = Struct.newBuilder();
         try {
             String requestStr = request.generateCdsPayload();
diff --git a/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequest.java b/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequest.java
index 38ab2bd..4193db5 100644
--- a/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequest.java
+++ b/models-interactions/model-actors/actor.cds/src/main/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequest.java
@@ -38,16 +38,18 @@
     private String resolutionKey;
     private Map<String, String> aaiProperties;
     private Map<String, String> policyPayload;
+    private Map<String, String> additionalEventParams;
 
     /**
      * Generate the CDS gRPC request payload from the action-name (aka operational policy recipe).
      * The CDS gRPC request payload generation follows the below pattern:
      *  {
      *    "{@link CdsActionRequest#getActionName()}-request": {
-     *      "resolution-key": "{@link CdsActionRequest#getResolutionKey()} ()}",
+     *      "resolution-key": "{@link CdsActionRequest#getResolutionKey()}",
      *      "{@link CdsActionRequest#getActionName()}-properties": {
-     *        "{@link CdsActionRequest#getAaiProperties()} ()}",
-     *        "{@link CdsActionRequest#getPolicyPayload()} ()}"
+     *        "{@link CdsActionRequest#getAaiProperties()}",
+     *        "{@link CdsActionRequest#getPolicyPayload()}",
+     *        "{@link CdsActionRequest#getAdditionalEventParams()}"
      *      }
      *    }
      *  }
@@ -59,6 +61,9 @@
         Map<String, String> cdsActionPropsMap = new LinkedHashMap<>();
         cdsActionPropsMap.putAll(aaiProperties);
         cdsActionPropsMap.putAll(policyPayload);
+        if (additionalEventParams != null) {
+            cdsActionPropsMap.putAll(additionalEventParams);
+        }
 
         // 2. Build the enclosing CDS action request properties object to contain (1) and the resolution-key
         Map<String, Object> cdsActionRequestMap = new LinkedHashMap<>();
diff --git a/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequestTest.java b/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequestTest.java
index 3f28bad..e34fa33 100644
--- a/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequestTest.java
+++ b/models-interactions/model-actors/actor.cds/src/test/java/org/onap/policy/controlloop/actor/cds/request/CdsActionRequestTest.java
@@ -43,6 +43,10 @@
                 ImmutableMap.of("service-instance.service-instance-id", "1234", "generic-vnf.vnf-id", "5678");
         req.setAaiProperties(aaiParams);
 
+        Map<String, String> eventParams =
+                ImmutableMap.of("event-param-1", "1234", "event-param-2", "5678");
+        req.setAdditionalEventParams(eventParams);
+
         // Act
         String result = req.generateCdsPayload();
 
diff --git a/models-interactions/model-impl/aai/src/main/java/org/onap/policy/aai/AaiManager.java b/models-interactions/model-impl/aai/src/main/java/org/onap/policy/aai/AaiManager.java
index 20998ae..d95068b 100644
--- a/models-interactions/model-impl/aai/src/main/java/org/onap/policy/aai/AaiManager.java
+++ b/models-interactions/model-impl/aai/src/main/java/org/onap/policy/aai/AaiManager.java
@@ -23,15 +23,21 @@
 package org.onap.policy.aai;
 
 import com.google.gson.JsonSyntaxException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
+import java.util.stream.Collectors;
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.onap.policy.aai.util.Serialization;
 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
 import org.onap.policy.common.endpoints.utils.NetLoggerUtil;
 import org.onap.policy.common.endpoints.utils.NetLoggerUtil.EventType;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
 import org.onap.policy.rest.RestManager;
 import org.onap.policy.rest.RestManager.Pair;
 import org.slf4j.Logger;
@@ -47,16 +53,19 @@
 
     private static final String APPLICATION_JSON = "application/json";
 
+    private static final StandardCoder CODER = new StandardCoder();
+
     /** The rest manager. */
     // The REST manager used for processing REST calls for this AAI manager
     private final RestManager restManager;
 
-    /** custom query URLs. */
+    /** custom query and other AAI resource URLs. */
     private static final String CQ_URL = "/aai/v16/query?format=resource";
     private static final String TENANT_URL =
             "/aai/v16/search/nodes-query?search-node-type=vserver&filter=vserver-name:EQUALS:";
     private static final String PREFIX = "/aai/v16";
-
+    private static final String PNF_URL = PREFIX + "/network/pnfs/pnf/";
+    private static final String AAI_DEPTH_SUFFIX = "?depth=0";
 
     /**
      * Constructor, create the AAI manager with the specified REST manager.
@@ -96,7 +105,6 @@
         }
     }
 
-
     /**
      * This method is used to get the information for custom query.
      *
@@ -116,8 +124,6 @@
         return createCustomQueryPayload(getResponse);
     }
 
-
-
     /**
      * Calls Aai and returns a custom query response for a vserver.
      *
@@ -161,8 +167,6 @@
         return null;
     }
 
-
-
     /**
      * Returns the string response of a get query.
      *
@@ -213,7 +217,6 @@
         return null;
     }
 
-
     /**
      * Post a query to A&AI.
      *
@@ -303,12 +306,12 @@
      * Perform a GET query for a particular entity towards A&AI.
      *
      * @param <T> the generic type for the response
-     * @param urlGet the A&AI URL
+     * @param url the A&AI URL
      * @param username the user name for authentication
      * @param password the password for authentication
      * @param requestId the UUID of the request
      * @param key the name of the VNF
-     * @param classOfT the class of the response to return
+     * @param classOfResponse the class of the response to return
      * @return the response for the virtual server from A&AI
      */
     private <T> T getQuery(final String url, final String username, final String password, final UUID requestId,
@@ -367,7 +370,6 @@
         return headers;
     }
 
-
     /**
      * This method uses Google's GSON to create a response object from a JSON string.
      *
@@ -389,4 +391,39 @@
             return null;
         }
     }
+
+    /**
+     * Perform a GET request for a particular PNF by PNF ID towards A&AI.
+     *
+     * @param url the A&AI URL
+     * @param username the user name for authentication
+     * @param password the password for authentication
+     * @param requestId the UUID of the request
+     * @param pnfName the AAI unique identifier for PNF object
+     * @return HashMap of PNF properties
+     */
+    public Map<String, String> getPnf(String url, String username, String password, UUID requestId, String pnfName) {
+        String urlGet;
+        try {
+            urlGet = url + PNF_URL;
+            pnfName = URLEncoder.encode(pnfName, StandardCharsets.UTF_8.toString()) + AAI_DEPTH_SUFFIX;
+        } catch (UnsupportedEncodingException e) {
+            logger.error("Failed to encode the pnfName: {} using UTF-8 encoding. {}", pnfName, e);
+            return null;
+        }
+        String responseGet = getStringQuery(urlGet, username, password, requestId, pnfName);
+        if (responseGet == null) {
+            logger.error("Null response from AAI for the url: {}.", urlGet);
+            return null;
+        }
+        try {
+            Map<String, String> pnfParams = CODER.decode(responseGet, HashMap.class);
+            // Map to AAI node.attribute notation
+            return pnfParams.entrySet().stream()
+                           .collect(Collectors.toMap(e -> "pnf." + e.getKey(), Map.Entry::getValue));
+        } catch (CoderException e) {
+            logger.error("Failed to fetch PNF from AAI");
+            return null;
+        }
+    }
 }
diff --git a/models-interactions/model-impl/aai/src/test/java/org/onap/policy/aai/AaiManagerTest.java b/models-interactions/model-impl/aai/src/test/java/org/onap/policy/aai/AaiManagerTest.java
index 9a8d7d2..ff86577 100644
--- a/models-interactions/model-impl/aai/src/test/java/org/onap/policy/aai/AaiManagerTest.java
+++ b/models-interactions/model-impl/aai/src/test/java/org/onap/policy/aai/AaiManagerTest.java
@@ -21,10 +21,12 @@
 
 package org.onap.policy.aai;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyMap;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.contains;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.ArgumentMatchers.startsWith;
@@ -34,7 +36,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
-import java.util.HashMap;
 import java.util.Map;
 import java.util.UUID;
 import org.junit.Before;
@@ -50,19 +51,17 @@
     private static final String DOROTHY = "Dorothy";
     private static final String SOME_URL = "http://somewhere.over.the.rainbow";
     private static final String ANOTHER_URL = "http://somewhere.under.the.rainbow";
-    RestManager restManagerMock;
-    UUID aaiNqRequestUuid = UUID.randomUUID();
-    Pair<Integer, String> httpResponseOk;
-    Pair<Integer, String> httpResponseErr0;
-    Pair<Integer, String> httpResponseErr1;
-    Pair<Integer, String> httpResponseWait;
-    Pair<Integer, String> httpTenantResponseOk;
-    Pair<Integer, String> httpCqResponseOk;
-
     private static final String TENANT_RESPONSE_SAMPLE =
             "src/test/resources/org/onap/policy/aai/AaiTenantResponse.json";
 
-
+    private RestManager restManagerMock;
+    private UUID aaiNqRequestUuid = UUID.randomUUID();
+    private Pair<Integer, String> httpResponseOk;
+    private Pair<Integer, String> httpResponseErr0;
+    private Pair<Integer, String> httpResponseErr1;
+    private Pair<Integer, String> httpResponseWait;
+    private Pair<Integer, String> httpTenantResponseOk;
+    private Pair<Integer, String> httpCqResponseOk;
 
     /**
      * Set up test cases.
@@ -73,11 +72,6 @@
     public void beforeTestAaiManager() throws Exception {
         restManagerMock = mock(RestManager.class);
 
-        Map<String, String> expectedHeaders = new HashMap<>();
-        expectedHeaders.put("X-FromAppId", "POLICY");
-        expectedHeaders.put("X-TransactionId", aaiNqRequestUuid.toString());
-        expectedHeaders.put("Accept", "application/json");
-
         String aaiCqResponse = new AaiCqResponseTest().getAaiCqResponse();
         String tenantResponse = this.getTenantQueryResponse();
         httpCqResponseOk = restManagerMock.new Pair<>(200, aaiCqResponse);
@@ -235,4 +229,21 @@
                 "Gale", vserverNameRequestId, "vnfName");
         assertNotNull(vnfResponse);
     }
+
+    @Test
+    public void testAaiManagerGetPnf() {
+        AaiManager aaiManager = new AaiManager(restManagerMock);
+        assertNotNull(aaiManager);
+        String pnfName = "test-pnf";
+        String pnfResponse = "{\"pnf-name\":" + pnfName
+                                     + ",\"pnf-id\":\"123456\",\"in-maint\":false,\"ipaddress-v4-oam\":\"1.1.1.1\"}";
+
+        Pair<Integer, String> pnfHttpResponse = restManagerMock.new Pair<>(200, pnfResponse);
+        when(restManagerMock.get(contains(pnfName), eq(DOROTHY), eq("Gale"), anyMap()))
+                .thenReturn(pnfHttpResponse);
+
+        Map<String, String> pnfParams = aaiManager.getPnf(SOME_URL, DOROTHY, "Gale", UUID.randomUUID(), pnfName);
+        assertNotNull(pnfParams);
+        assertEquals(pnfName, pnfParams.get("pnf.pnf-name"));
+    }
 }
diff --git a/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopEvent.java b/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopEvent.java
index 534e843..3ff7aa2 100644
--- a/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopEvent.java
+++ b/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopEvent.java
@@ -23,6 +23,7 @@
 
 import com.google.gson.annotations.SerializedName;
 import java.io.Serializable;
+import java.util.Map;
 import java.util.UUID;
 import lombok.Getter;
 import lombok.Setter;
@@ -33,45 +34,27 @@
 
     private static final long serialVersionUID = 2391252138583119195L;
 
-    @SerializedName("closedLoopControlName")
-    private String closedLoopControlName;
-
-    @SerializedName("version")
-    private String version = "1.0.2";
-
     @SerializedName("requestID")
     private UUID requestId;
-
-    @SerializedName("closedLoopEventClient")
-    private String closedLoopEventClient;
-
     @SerializedName("target_type")
     private String targetType;
-
-    @SerializedName("target")
+    private String closedLoopControlName;
+    private String version = "1.0.2";
+    private String closedLoopEventClient;
     private String target;
-
-    @SerializedName("from")
     private String from;
-
-    @SerializedName("policyScope")
     private String policyScope;
-
-    @SerializedName("policyName")
     private String policyName;
-
-    @SerializedName("policyVersion")
     private String policyVersion;
-
-    @SerializedName("closedLoopEventStatus")
     private ControlLoopEventStatus closedLoopEventStatus;
+    private Map<String, String> additionalEventParams;
 
     public ControlLoopEvent() {
 
     }
 
     /**
-     * Construct an instace from an existing instance.
+     * Construct an instance from an existing instance.
      *
      * @param event the existing instance
      */
@@ -79,6 +62,7 @@
         if (event == null) {
             return;
         }
+        this.version =  event.version;
         this.closedLoopControlName = event.closedLoopControlName;
         this.requestId = event.requestId;
         this.closedLoopEventClient = event.closedLoopEventClient;
@@ -89,6 +73,7 @@
         this.policyName = event.policyName;
         this.policyVersion = event.policyVersion;
         this.closedLoopEventStatus = event.closedLoopEventStatus;
+        this.additionalEventParams = event.additionalEventParams;
     }
 
     public boolean isEventStatusValid() {
diff --git a/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopTargetType.java b/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopTargetType.java
index 1ca1825..b79140a 100644
--- a/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopTargetType.java
+++ b/models-interactions/model-impl/events/src/main/java/org/onap/policy/controlloop/ControlLoopTargetType.java
@@ -26,4 +26,5 @@
     public static final String VF = "VF";
     public static final String VFC = "VFC";
     public static final String VNF = "VNF";
+    public static final String PNF = "PNF";
 }
diff --git a/models-interactions/model-impl/events/src/test/java/org/onap/policy/controlloop/ControlLoopTargetTypeTest.java b/models-interactions/model-impl/events/src/test/java/org/onap/policy/controlloop/ControlLoopTargetTypeTest.java
index daf6bb8..ac700c8 100644
--- a/models-interactions/model-impl/events/src/test/java/org/onap/policy/controlloop/ControlLoopTargetTypeTest.java
+++ b/models-interactions/model-impl/events/src/test/java/org/onap/policy/controlloop/ControlLoopTargetTypeTest.java
@@ -33,5 +33,6 @@
         assertEquals("VF", ControlLoopTargetType.VF);
         assertEquals("VFC", ControlLoopTargetType.VFC);
         assertEquals("VNF", ControlLoopTargetType.VNF);
+        assertEquals("PNF", ControlLoopTargetType.PNF);
     }
 }