ffd7bf8952959c4b1b9dc328fafd773c2857568b
[sdnc/apps.git] /
1 /*
2  * ============LICENSE_START===================================================
3  * Copyright (c) 2018 Amdocs
4  * ============================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  * ============LICENSE_END=====================================================
17  */
18 package org.onap.sdnc.apps.pomba.servicedecomposition.util;
19
20 import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.FETCH_RESOURCE_FAILED;
21 import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.INVALID_URL;
22 import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.RELATIONSHIP_LINK_PARSE_ERROR;
23 import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.SERVICE_INSTANCE_NOT_FOUND;
24 import static org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException.Error.SERVICE_RELATIONSHIP_PARSE_ERROR;
25
26 import com.sun.jersey.core.util.MultivaluedMapImpl;
27 import java.text.MessageFormat;
28 import java.util.ArrayList;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 import javax.ws.rs.core.MediaType;
34 import javax.ws.rs.core.MultivaluedMap;
35 import javax.ws.rs.core.Response.Status;
36 import org.json.JSONArray;
37 import org.json.JSONObject;
38 import org.onap.aai.restclient.client.OperationResult;
39 import org.onap.aai.restclient.client.RestClient;
40 import org.onap.logging.ref.slf4j.ONAPLogAdapter;
41 import org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45
46
47
48 public class RestUtil {
49     // Parameters for Query AAI Model Data API
50     // HTTP headers
51     private static final String TRANSACTION_ID = "X-TransactionId";
52     private static final String FROM_APP_ID = "X-FromAppId";
53     private static final String APP_NAME = "aaiCtxBuilder";
54     private static final String AUTHORIZATION = "Authorization";
55
56     private static final Resource GENERIC_VNF = new Resource("generic-vnf");
57
58     public static class Resource {
59         private String resourceName;
60         private String collectionName;
61
62         private Resource(String resource) {
63             this.resourceName = resource;
64             this.collectionName = resource + "s";
65         }
66
67         private String getResourceName() {
68             return this.resourceName;
69         }
70
71         private String getCollectionName() {
72             return this.collectionName;
73         }
74     }
75
76     private static final String JSON_ATT_RELATED_TO = "related-to";
77     private static final String JSON_ATT_RELATED_LINK = "related-link";
78
79     private static final String EMPTY_JSON_STRING = "{}";
80
81     private static final String DEPTH = "?depth=2";
82     private static Logger logger = LoggerFactory.getLogger(RestUtil.class);
83
84     /**
85      * Validates the URL parameter.
86      *
87      * @throws DiscoveryException if there is missing parameter
88      */
89     public static void validateURL(String serviceInstanceId) throws DiscoveryException {
90
91         if (serviceInstanceId == null || serviceInstanceId.isEmpty()) {
92             throw new DiscoveryException(INVALID_URL, Status.BAD_REQUEST);
93         }
94     }
95
96     private static String generateServiceInstanceURL(String siPath, String serviceInstanceId) {
97         return MessageFormat.format(siPath, serviceInstanceId);
98     }
99
100
101     /*
102      * Trigger external API call to AAI to retrieve Service Instance data (i.e. genericVNF and VNFC)
103      */
104
105     /**
106      * @param aaiClient
107      * @param baseURL
108      * @param aaiBasicAuthorization
109      * @param aaiServiceInstancePath
110      * @param aaiResourceList
111      * @param transactionId
112      * @param serviceInstanceId
113      * @param adapter
114      * @return
115      * @throws DiscoveryException
116      */
117     public static JSONObject retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiBasicAuthorization, String aaiServiceInstancePath, String aaiResourceList,
118             String transactionId, String serviceInstanceId, ONAPLogAdapter adapter) throws DiscoveryException {
119
120         // Follow two variables for transform purpose
121         String url = baseURL + generateServiceInstanceURL(aaiServiceInstancePath, serviceInstanceId);
122         // Response from service instance API call
123         JSONObject serviceInstancePayload = new JSONObject(
124                 getResource(aaiClient, url, aaiBasicAuthorization, transactionId));
125         // Handle the case if the service instance is not found in AAI
126         if (serviceInstancePayload == null || serviceInstancePayload.length() == 0) {
127             logger.info("Service Instance " + serviceInstanceId + " is not found from AAI");
128             // Only return the empty Json on the root level. i.e service instance
129             throw new DiscoveryException(SERVICE_INSTANCE_NOT_FOUND, Status.NOT_FOUND);
130         }
131
132         HashMap<String, List<String>> relationMap = extractServiceRelationShips(serviceInstancePayload);
133         logger.info("The number of the relationships for service instance id {} is: {}", serviceInstanceId,
134                 relationMap.size());
135
136         JSONObject response = processVNFRelationMap(aaiClient, aaiResourceList, baseURL, aaiBasicAuthorization, transactionId, relationMap, serviceInstancePayload);
137         return response;
138     }
139
140
141     /**
142      * @param aaiClient
143      * @param baseURL
144      * @param transactionId
145      * @param relationMap
146      * @throws DiscoveryException
147      */
148     private static JSONObject processVNFRelationMap(RestClient aaiClient, String aaiResourceList, String baseURL, String aaiBasicAuthorization, String transactionId,
149             HashMap<String, List<String>> relationMap, JSONObject serviceInstancePayload) throws DiscoveryException {
150         List<JSONObject> vnfLst = new ArrayList<JSONObject>(); // List of the VNF JSON along with related resources
151
152         JSONObject response = serviceInstancePayload;
153
154         List<Resource> resourceTypes = getResourceTypes(aaiResourceList);
155
156         if (relationMap.get(GENERIC_VNF.getResourceName()) != null) {
157             List<JSONObject> vnfList = processResourceList(aaiClient, baseURL, aaiBasicAuthorization, transactionId, GENERIC_VNF.getResourceName(),
158                     relationMap.get(GENERIC_VNF.getResourceName()));
159             // Logic to Create the Generic VNF JSON and extract further relationships
160             for (JSONObject vnfPayload : vnfList) {
161                 for (Resource resourceType : resourceTypes) {
162                     List<String> vnfcLinkLst = extractRelatedLink(vnfPayload, resourceType.getResourceName());
163                     if (vnfcLinkLst != null && !vnfcLinkLst.isEmpty()) {
164                         logger.info("The number of the API call for vnfc is:" + vnfcLinkLst.size());
165                         List<JSONObject> vnfcList = processResourceList(aaiClient, baseURL, aaiBasicAuthorization, transactionId,
166                                 resourceType.getResourceName(), vnfcLinkLst);
167                         if (vnfcList != null) {
168                             vnfPayload.put(resourceType.getCollectionName(), vnfcList);
169                         }
170                     } else {
171                         logger.info("No " + resourceType.getResourceName() + " found for vnf-id:" + vnfPayload.getString("vnf-id"));
172                     }
173                 }
174             // Add final vnf payload to list
175             vnfLst.add(vnfPayload);
176             }
177         } else {
178             logger.info("No " + GENERIC_VNF.getResourceName() +  " found for :" + serviceInstancePayload.getString("service-instance-id"));
179         }
180
181         // Add generic vnf with related resource payload to response
182         if (vnfLst != null && !vnfLst.isEmpty()) {
183             response.put(GENERIC_VNF.getCollectionName(), vnfLst);
184         }
185         return response;
186
187     }
188
189
190     /**
191      * @param aaiClient
192      * @param aaiBaseURL
193      * @param transactionId
194      * @param resourceType
195      * @param resourceList
196      * @return
197      * @throws DiscoveryException
198      */
199     private static List<JSONObject> processResourceList(RestClient aaiClient, String aaiBaseURL, String aaiBasicAuthorization, String transactionId,
200             String resourceType, List<String> resourceList) throws DiscoveryException {
201         List<JSONObject> resourcePayloadList = new ArrayList<JSONObject>();
202         for (String resourceLink : resourceList) {
203             String resourceURL = aaiBaseURL + resourceLink;
204             // With latest AAI development, in order to retrieve the both generic VNF + vf_module, we can use
205             // one API call but with depth=2
206             if (resourceType.equals(GENERIC_VNF.getResourceName())) {
207                 resourceURL += DEPTH;
208             }
209
210             // Response from generic VNF API call
211             JSONObject resourcePayload = new JSONObject(
212                     getResource(aaiClient, resourceURL, aaiBasicAuthorization, transactionId));
213             if (resourcePayload == null || resourcePayload.length() == 0) {
214                 logger.info("Resource with url " + resourceLink + " is not found from AAI");
215             } else {
216                 resourcePayloadList.add(resourcePayload);
217             }
218         }
219         return resourcePayloadList;
220     }
221
222
223     /**
224      * @param serviceInstancePayload
225      * @param relationMap
226      * @return
227      * @throws DiscoveryException
228      */
229     private static HashMap<String, List<String>> extractServiceRelationShips(JSONObject payload)
230             throws DiscoveryException {
231
232         JSONArray relationships = null;
233         HashMap<String, List<String>> relationMap = new HashMap<String, List<String>>();
234         logger.info("Fetching Service Instance Relationships");
235         try {
236             JSONObject relationshipList = payload.getJSONObject("relationship-list");
237             if (relationshipList != null) {
238                 relationships = relationshipList.getJSONArray("relationship");
239             }
240         } catch (Exception e) {
241             logger.error(e.getMessage());
242             throw new DiscoveryException(SERVICE_RELATIONSHIP_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR,
243                     e.getMessage());
244         }
245
246         if (relationships != null && relationships.length() > 0) {
247             for (int i = 0; i < relationships.length(); i++) {
248                 JSONObject obj = relationships.optJSONObject(i);
249                 String relatedToObj = obj.getString(JSON_ATT_RELATED_TO);
250                 String relatedLinkObj = obj.getString(JSON_ATT_RELATED_LINK);
251
252                 if (relatedToObj == null) {
253                     logger.info("Related-To Object found null");
254                     continue;
255                 }
256                 List<String> relatedLinkList = relationMap.get(relatedToObj);
257                 if (relatedLinkList == null) {
258                     relatedLinkList = new ArrayList<>();
259                     relationMap.put(relatedToObj, relatedLinkList);
260                 }
261                 relatedLinkList.add(relatedLinkObj);
262             }
263         }
264         return relationMap;
265     }
266
267     /**
268      * @param client
269      * @param url
270      * @param aaiBasicAuthorization
271      * @param transId
272      * @return
273      * @throws DiscoveryException
274      */
275     private static String getResource(RestClient client, String url, String aaiBasicAuthorization, String transId)
276             throws DiscoveryException {
277         OperationResult result = client.get(url, buildHeaders(aaiBasicAuthorization, transId), MediaType.valueOf(MediaType.APPLICATION_JSON));
278
279         if (result.getResultCode() == 200) {
280             String jsonString = result.getResult();
281             return jsonString;
282         } else if (result.getResultCode() == 404) {
283             // Resource not found, generate empty JSON format
284             logger.info("Resource for " + url + " is not found " + "return empty Json format");
285             return EMPTY_JSON_STRING;
286         } else {
287             throw new DiscoveryException(FETCH_RESOURCE_FAILED, Status.INTERNAL_SERVER_ERROR, result.getFailureCause());
288         }
289     }
290
291     /**
292      * Extract the related-Link from Json payload. For example
293      * {
294      *    "related-to": "vnfc",
295      *    "related-link": "/aai/v11/network/vnfcs/vnfc/zrdm5aepdg01vmg003",
296      *    "relationship-data": [
297      *       {
298      *          "relationship-key": "vnfc.vnfc-name",
299      *          "relationship-value": "zrdm5aepdg01vmg003"
300      *       }
301      *    ]
302      * }
303      */
304     private static List<String> extractRelatedLink(JSONObject payload, String catalog) throws DiscoveryException {
305         List<String> relatedLinkList = new ArrayList<String>();
306         JSONArray relationships = null;
307         logger.info("Fetching relationships for resource type: " + catalog);
308         try {
309             JSONObject relationshipLst = payload.getJSONObject("relationship-list");
310             if (relationshipLst != null) {
311                 relationships = relationshipLst.getJSONArray("relationship");
312             }
313
314         } catch (Exception e) {
315             logger.error(e.getMessage());
316             throw new DiscoveryException(RELATIONSHIP_LINK_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR, e.getMessage());
317         }
318
319         if (relationships != null && relationships.length() > 0) {
320             for (int i = 0; i < relationships.length(); i++) {
321                 Object relatedToObj = null;
322                 Object relatedLinkObj = null;
323
324                 JSONObject obj = relationships.optJSONObject(i);
325                 relatedToObj = obj.get(JSON_ATT_RELATED_TO);
326
327                 if (relatedToObj.toString().equals(catalog)) {
328                     relatedLinkObj = obj.get(JSON_ATT_RELATED_LINK);
329                     if (relatedLinkObj != null) {
330                         relatedLinkList.add(relatedLinkObj.toString());
331                     }
332                 }
333
334             }
335         }
336         if (relatedLinkList != null) {
337             logger.info(
338                     "Number of relationships found for resource type: " + catalog + " are: " + relatedLinkList.size());
339         }
340         return relatedLinkList;
341     }
342
343
344
345     private static Map<String, List<String>> buildHeaders(String aaiBasicAuthorization, String transactionId) {
346         MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
347         headers.put(TRANSACTION_ID, Collections.singletonList(transactionId));
348         headers.put(FROM_APP_ID, Collections.singletonList(APP_NAME));
349         headers.put(AUTHORIZATION, Collections.singletonList(aaiBasicAuthorization));
350         return headers;
351     }
352
353     private static List<Resource> getResourceTypes(String aaiResourceList) {
354         List<Resource> resources = new ArrayList<Resource>();
355         String noSpaceAaiResourceList = aaiResourceList.replaceAll("\\s", "");
356         String[] resourceList = noSpaceAaiResourceList.split(",");
357         for (int i = 0; i < resourceList.length; i++) {
358                 resources.add(new Resource(resourceList[i]));
359         }
360         return resources;
361     }
362
363 }