c9e2c501005a6fa653512966a0f6aafae9b8bedb
[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
55     private static final Resource GENERIC_VNF = new Resource("generic-vnf");
56
57     public static class Resource {
58         private String resourceName;
59         private String collectionName;
60
61         private Resource(String resource) {
62             this.resourceName = resource;
63             this.collectionName = resource + "s";
64         }
65
66         private String getResourceName() {
67             return this.resourceName;
68         }
69
70         private String getCollectionName() {
71             return this.collectionName;
72         }
73     }
74
75     private static final String JSON_ATT_RELATED_TO = "related-to";
76     private static final String JSON_ATT_RELATED_LINK = "related-link";
77
78     private static final String EMPTY_JSON_STRING = "{}";
79
80     private static final String DEPTH = "?depth=2";
81     private static Logger logger = LoggerFactory.getLogger(RestUtil.class);
82
83     /**
84      * Validates the URL parameter.
85      *
86      * @throws DiscoveryException if there is missing parameter
87      */
88     public static void validateURL(String serviceInstanceId) throws DiscoveryException {
89
90         if (serviceInstanceId == null || serviceInstanceId.isEmpty()) {
91             throw new DiscoveryException(INVALID_URL, Status.BAD_REQUEST);
92         }
93     }
94
95     private static String generateServiceInstanceURL(String siPath, String serviceInstanceId) {
96         return MessageFormat.format(siPath, serviceInstanceId);
97     }
98
99
100     /*
101      * Trigger external API call to AAI to retrieve Service Instance data (i.e. genericVNF and VNFC)
102      */
103
104     /**
105      * @param aaiClient
106      * @param baseURL
107      * @param aaiServiceInstancePath
108      * @param transactionId
109      * @param serviceInstanceId
110      * @param modelVersionId
111      * @param modelInvariantId
112      * @return
113      * @throws DiscoveryException
114      */
115     public static JSONObject retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiServiceInstancePath, String aaiResourceList,
116             String transactionId, String serviceInstanceId, ONAPLogAdapter adapter) throws DiscoveryException {
117
118         // Follow two variables for transform purpose
119         String url = baseURL + generateServiceInstanceURL(aaiServiceInstancePath, serviceInstanceId);
120         // Response from service instance API call
121         JSONObject serviceInstancePayload = new JSONObject(
122                 getResource(aaiClient, url, transactionId));
123         // Handle the case if the service instance is not found in AAI
124         if (serviceInstancePayload == null || serviceInstancePayload.length() == 0) {
125             logger.info("Service Instance " + serviceInstanceId + " is not found from AAI");
126             // Only return the empty Json on the root level. i.e service instance
127             throw new DiscoveryException(SERVICE_INSTANCE_NOT_FOUND, Status.NOT_FOUND);
128         }
129
130         HashMap<String, List<String>> relationMap = extractServiceRelationShips(serviceInstancePayload);
131         logger.info("The number of the relationships for service instance id {} is: {}", serviceInstanceId,
132                 relationMap.size());
133
134         JSONObject response = processVNFRelationMap(aaiClient, aaiResourceList, baseURL, transactionId, relationMap, serviceInstancePayload);
135         return response;
136     }
137
138
139     /**
140      * @param aaiClient
141      * @param baseURL
142      * @param transactionId
143      * @param relationMap
144      * @throws DiscoveryException
145      */
146     private static JSONObject processVNFRelationMap(RestClient aaiClient, String aaiResourceList, String baseURL, String transactionId,
147             HashMap<String, List<String>> relationMap, JSONObject serviceInstancePayload) throws DiscoveryException {
148         List<JSONObject> vnfLst = new ArrayList<JSONObject>(); // List of the VNF JSON along with related resources
149
150         JSONObject response = serviceInstancePayload;
151
152         List<Resource> resourceTypes = getResourceTypes(aaiResourceList);
153
154         if (relationMap.get(GENERIC_VNF.getResourceName()) != null) {
155             List<JSONObject> vnfList = processResourceList(aaiClient, baseURL, transactionId, GENERIC_VNF.getResourceName(),
156                     relationMap.get(GENERIC_VNF.getResourceName()));
157             // Logic to Create the Generic VNF JSON and extract further relationships
158             for (JSONObject vnfPayload : vnfList) {
159                 for (Resource resourceType : resourceTypes) {
160                     List<String> vnfcLinkLst = extractRelatedLink(vnfPayload, resourceType.getResourceName());
161                     if (vnfcLinkLst != null && !vnfcLinkLst.isEmpty()) {
162                         logger.info("The number of the API call for vnfc is:" + vnfcLinkLst.size());
163                         List<JSONObject> vnfcList = processResourceList(aaiClient, baseURL, transactionId,
164                                 resourceType.getResourceName(), vnfcLinkLst);
165                         if (vnfcList != null) {
166                             vnfPayload.put(resourceType.getCollectionName(), vnfcList);
167                         }
168                     } else {
169                         logger.info("No " + resourceType.getResourceName() + " found for vnf-id:" + vnfPayload.getString("vnf-id"));
170                     }
171                 }
172             // Add final vnf payload to list
173             vnfLst.add(vnfPayload);
174             }
175         } else {
176             logger.info("No " + GENERIC_VNF.getResourceName() +  " found for :" + serviceInstancePayload.getString("service-instance-id"));
177         }
178
179         // Add generic vnf with related resource payload to response
180         if (vnfLst != null && !vnfLst.isEmpty()) {
181             response.put(GENERIC_VNF.getCollectionName(), vnfLst);
182         }
183         return response;
184
185     }
186
187
188     /**
189      * @param aaiClient
190      * @param aaiBaseURL
191      * @param transactionId
192      * @param resourceType
193      * @param resourceList
194      * @return
195      * @throws DiscoveryException
196      */
197     private static List<JSONObject> processResourceList(RestClient aaiClient, String aaiBaseURL, String transactionId,
198             String resourceType, List<String> resourceList) throws DiscoveryException {
199         List<JSONObject> resourcePayloadList = new ArrayList<JSONObject>();
200         for (String resourceLink : resourceList) {
201             String resourceURL = aaiBaseURL + resourceLink;
202             // With latest AAI development, in order to retrieve the both generic VNF + vf_module, we can use
203             // one API call but with depth=2
204             if (resourceType.equals(GENERIC_VNF.getResourceName())) {
205                 resourceURL += DEPTH;
206             }
207
208             // Response from generic VNF API call
209             JSONObject resourcePayload = new JSONObject(
210                     getResource(aaiClient, resourceURL, transactionId));
211             if (resourcePayload == null || resourcePayload.length() == 0) {
212                 logger.info("Resource with url " + resourceLink + " is not found from AAI");
213             } else {
214                 resourcePayloadList.add(resourcePayload);
215             }
216         }
217         return resourcePayloadList;
218     }
219
220
221     /**
222      * @param serviceInstancePayload
223      * @param relationMap
224      * @return
225      * @throws DiscoveryException
226      */
227     private static HashMap<String, List<String>> extractServiceRelationShips(JSONObject payload)
228             throws DiscoveryException {
229
230         JSONArray relationships = null;
231         HashMap<String, List<String>> relationMap = new HashMap<String, List<String>>();
232         logger.info("Fetching Service Instance Relationships");
233         try {
234             JSONObject relationshipList = payload.getJSONObject("relationship-list");
235             if (relationshipList != null) {
236                 relationships = relationshipList.getJSONArray("relationship");
237             }
238         } catch (Exception e) {
239             logger.error(e.getMessage());
240             throw new DiscoveryException(SERVICE_RELATIONSHIP_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR,
241                     e.getMessage());
242         }
243
244         if (relationships != null && relationships.length() > 0) {
245             for (int i = 0; i < relationships.length(); i++) {
246                 JSONObject obj = relationships.optJSONObject(i);
247                 String relatedToObj = obj.getString(JSON_ATT_RELATED_TO);
248                 String relatedLinkObj = obj.getString(JSON_ATT_RELATED_LINK);
249
250                 if (relatedToObj == null) {
251                     logger.info("Related-To Object found null");
252                     continue;
253                 }
254                 List<String> relatedLinkList = relationMap.get(relatedToObj);
255                 if (relatedLinkList == null) {
256                     relatedLinkList = new ArrayList<>();
257                     relationMap.put(relatedToObj, relatedLinkList);
258                 }
259                 relatedLinkList.add(relatedLinkObj);
260             }
261         }
262         return relationMap;
263     }
264
265     /**
266      * @param client
267      * @param url
268      * @param transId
269      * @param mediaType
270      * @return
271      * @throws DiscoveryException
272      */
273     private static String getResource(RestClient client, String url, String transId)
274             throws DiscoveryException {
275         OperationResult result = client.get(url, buildHeaders(transId), MediaType.valueOf(MediaType.APPLICATION_JSON));
276
277         if (result.getResultCode() == 200) {
278             String jsonString = result.getResult();
279             return jsonString;
280         } else if (result.getResultCode() == 404) {
281             // Resource not found, generate empty JSON format
282             logger.info("Resource for " + url + " is not found " + "return empty Json format");
283             return EMPTY_JSON_STRING;
284         } else {
285             throw new DiscoveryException(FETCH_RESOURCE_FAILED, Status.INTERNAL_SERVER_ERROR, result.getFailureCause());
286         }
287     }
288
289     /**
290      * Extract the related-Link from Json payload. For example
291      * {
292      *    "related-to": "vnfc",
293      *    "related-link": "/aai/v11/network/vnfcs/vnfc/zrdm5aepdg01vmg003",
294      *    "relationship-data": [
295      *       {
296      *          "relationship-key": "vnfc.vnfc-name",
297      *          "relationship-value": "zrdm5aepdg01vmg003"
298      *       }
299      *    ]
300      * }
301      */
302     private static List<String> extractRelatedLink(JSONObject payload, String catalog) throws DiscoveryException {
303         List<String> relatedLinkList = new ArrayList<String>();
304         JSONArray relationships = null;
305         logger.info("Fetching relationships for resource type: " + catalog);
306         try {
307             JSONObject relationshipLst = payload.getJSONObject("relationship-list");
308             if (relationshipLst != null) {
309                 relationships = relationshipLst.getJSONArray("relationship");
310             }
311
312         } catch (Exception e) {
313             logger.error(e.getMessage());
314             throw new DiscoveryException(RELATIONSHIP_LINK_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR, e.getMessage());
315         }
316
317         if (relationships != null && relationships.length() > 0) {
318             for (int i = 0; i < relationships.length(); i++) {
319                 Object relatedToObj = null;
320                 Object relatedLinkObj = null;
321
322                 JSONObject obj = relationships.optJSONObject(i);
323                 relatedToObj = obj.get(JSON_ATT_RELATED_TO);
324
325                 if (relatedToObj.toString().equals(catalog)) {
326                     relatedLinkObj = obj.get(JSON_ATT_RELATED_LINK);
327                     if (relatedLinkObj != null) {
328                         relatedLinkList.add(relatedLinkObj.toString());
329                     }
330                 }
331
332             }
333         }
334         if (relatedLinkList != null) {
335             logger.info(
336                     "Number of relationships found for resource type: " + catalog + " are: " + relatedLinkList.size());
337         }
338         return relatedLinkList;
339     }
340
341
342
343     private static Map<String, List<String>> buildHeaders(String transactionId) {
344         MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
345         headers.put(TRANSACTION_ID, Collections.singletonList(transactionId));
346         headers.put(FROM_APP_ID, Collections.singletonList(APP_NAME));
347         return headers;
348     }
349
350     private static List<Resource> getResourceTypes(String aaiResourceList) {
351         List<Resource> resources = new ArrayList<Resource>();
352         String[] resourceList = aaiResourceList.split(",");
353         for (int i = 0; i < resourceList.length; i++) {
354                 resources.add(new Resource(resourceList[i]));
355         }
356         return resources;
357     }
358
359 }