3043269e7be7f823f89357f2cae8fa54adaca043
[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
28 import java.text.MessageFormat;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34
35 import javax.ws.rs.core.MediaType;
36 import javax.ws.rs.core.MultivaluedMap;
37 import javax.ws.rs.core.Response.Status;
38
39 import org.json.JSONArray;
40 import org.json.JSONObject;
41 import org.onap.aai.restclient.client.OperationResult;
42 import org.onap.aai.restclient.client.RestClient;
43 import org.onap.logging.ref.slf4j.ONAPLogAdapter;
44 import org.onap.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
47
48
49
50
51 public class RestUtil {
52     // Parameters for Query AAI Model Data API
53     // HTTP headers
54     private static final String TRANSACTION_ID = "X-TransactionId";
55     private static final String FROM_APP_ID = "X-FromAppId";
56     private static final String APP_NAME = "aaiCtxBuilder";
57
58     // Service Catalog
59     public enum Catalog {
60
61         VNF("generic-vnf"), VNFC("vnfc"), VSERVER("vserver"), L3NETWORK("l3-network");
62
63         private final String resourceName;
64         private final String collectionName;
65
66         private Catalog(String objName) {
67             this.resourceName = objName;
68             this.collectionName = objName + "s"; // make plural
69         }
70
71         public String objName() {
72             return resourceName;
73         }
74     };
75
76
77     private static final String JSON_ATT_RELATED_TO = "related-to";
78     private static final String JSON_ATT_RELATED_LINK = "related-link";
79
80     private static final String EMPTY_JSON_STRING = "{}";
81
82     private static final String DEPTH = "?depth=2";
83     private static Logger logger = LoggerFactory.getLogger(RestUtil.class);
84
85     /**
86      * Validates the URL parameter.
87      *
88      * @throws DiscoveryException if there is missing parameter
89      */
90     public static void validateURL(String serviceInstanceId) throws DiscoveryException {
91
92         if (serviceInstanceId == null || serviceInstanceId.isEmpty()) {
93             throw new DiscoveryException(INVALID_URL, Status.BAD_REQUEST);
94         }
95     }
96
97     private static String generateServiceInstanceURL(String siPath, String serviceInstanceId) {
98         return MessageFormat.format(siPath, serviceInstanceId);
99     }
100
101
102     /*
103      * Trigger external API call to AAI to retrieve Service Instance data (i.e. genericVNF and VNFC)
104      */
105
106     /**
107      * @param aaiClient
108      * @param baseURL
109      * @param aaiServiceInstancePath
110      * @param transactionId
111      * @param serviceInstanceId
112      * @param modelVersionId
113      * @param modelInvariantId
114      * @return
115      * @throws DiscoveryException
116      */
117     public static JSONObject retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiServiceInstancePath,
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, 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, baseURL, 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 baseURL, 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
155         if (relationMap.get(Catalog.VNF.resourceName) != null) {
156             List<JSONObject> vnfList = processResourceList(aaiClient, baseURL, transactionId, Catalog.VNF.resourceName,
157                     relationMap.get(Catalog.VNF.resourceName));
158             // Logic to Create the Generic VNF JSON and extract further relationships
159             for (JSONObject vnfPayload : vnfList) {
160                 List<String> vnfcLinkLst = extractRelatedLink(vnfPayload, Catalog.VNFC.resourceName);
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                             Catalog.VNFC.resourceName, vnfcLinkLst);
165                     if (vnfcList != null) {
166                         vnfPayload.put(Catalog.VNFC.collectionName, vnfcList);
167                     }
168                 } else {
169                     logger.info("No vnfc found for vnf-id:" + vnfPayload.getString("vnf-id"));
170                 }
171
172                 List<String> networkLinkLst = extractRelatedLink(vnfPayload, Catalog.L3NETWORK.resourceName);
173                 if (networkLinkLst != null && !networkLinkLst.isEmpty()) {
174                     logger.info("The number of the API call for l3-network is:" + networkLinkLst.size());
175                     List<JSONObject> networkList = processResourceList(aaiClient, baseURL, transactionId,
176                             Catalog.L3NETWORK.resourceName, networkLinkLst);
177                     if (networkList != null) {
178                         vnfPayload.put(Catalog.L3NETWORK.collectionName, networkList);
179                     }
180                 } else {
181                     logger.info("No l3-network found for vnf-id:" + vnfPayload.getString("vnf-id"));
182                 }
183                 List<String> vserverLinkLst = extractRelatedLink(vnfPayload, Catalog.VSERVER.resourceName);
184                 if (vserverLinkLst != null && !vserverLinkLst.isEmpty()) {
185                     logger.info("The number of the API call for vserver is:" + vserverLinkLst.size());
186                     List<JSONObject> vserverList = processResourceList(aaiClient, baseURL, transactionId,
187                             Catalog.VSERVER.resourceName, vserverLinkLst);
188                     if (vserverList != null) {
189                         vnfPayload.put(Catalog.VSERVER.collectionName, vserverList);
190                     }
191                 } else {
192                     logger.info("No vserver found for vnf-id:" + vnfPayload.getString("vnf-id"));
193                 }
194
195                 // Add final vnf payload to list
196                 vnfLst.add(vnfPayload);
197             }
198         } else {
199             logger.info("No generic vnf found for :" + serviceInstancePayload.getString("service-instance-id"));
200         }
201
202         // Add generic vnf with related resource payload to response
203         if (vnfLst != null && !vnfLst.isEmpty()) {
204             response.put(Catalog.VNF.collectionName, vnfLst);
205         }
206
207         return response;
208
209     }
210
211
212     /**
213      * @param aaiClient
214      * @param aaiBaseURL
215      * @param transactionId
216      * @param resourceType
217      * @param resourceList
218      * @return
219      * @throws DiscoveryException
220      */
221     private static List<JSONObject> processResourceList(RestClient aaiClient, String aaiBaseURL, String transactionId,
222             String resourceType, List<String> resourceList) throws DiscoveryException {
223         List<JSONObject> resourcePayloadList = new ArrayList<JSONObject>();
224         for (String resourceLink : resourceList) {
225             String resourceURL = aaiBaseURL + resourceLink;
226             // With latest AAI development, in order to retrieve the both generic VNF + vf_module, we can use
227             // one API call but with depth=2
228             if (resourceType.equals(Catalog.VNF.resourceName)) {
229                 resourceURL += DEPTH;
230             }
231
232             // Response from generic VNF API call
233             JSONObject resourcePayload = new JSONObject(
234                     getResource(aaiClient, resourceURL, transactionId));
235             if (resourcePayload == null || resourcePayload.length() == 0) {
236                 logger.info("Resource with url " + resourceLink + " is not found from AAI");
237             } else {
238                 resourcePayloadList.add(resourcePayload);
239             }
240         }
241         return resourcePayloadList;
242     }
243
244
245     /**
246      * @param serviceInstancePayload
247      * @param relationMap
248      * @return
249      * @throws DiscoveryException
250      */
251     private static HashMap<String, List<String>> extractServiceRelationShips(JSONObject payload)
252             throws DiscoveryException {
253
254         JSONArray relationships = null;
255         HashMap<String, List<String>> relationMap = new HashMap<String, List<String>>();
256         logger.info("Fetching Service Instance Relationships");
257         try {
258             JSONObject relationshipList = payload.getJSONObject("relationship-list");
259             if (relationshipList != null) {
260                 relationships = relationshipList.getJSONArray("relationship");
261             }
262         } catch (Exception e) {
263             logger.error(e.getMessage());
264             throw new DiscoveryException(SERVICE_RELATIONSHIP_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR,
265                     e.getMessage());
266         }
267
268         if (relationships != null && relationships.length() > 0) {
269             for (int i = 0; i < relationships.length(); i++) {
270                 JSONObject obj = relationships.optJSONObject(i);
271                 String relatedToObj = obj.getString(JSON_ATT_RELATED_TO);
272                 String relatedLinkObj = obj.getString(JSON_ATT_RELATED_LINK);
273
274                 if (relatedToObj == null) {
275                     logger.info("Related-To Object found null");
276                     continue;
277                 }
278                 List<String> relatedLinkList = relationMap.get(relatedToObj);
279                 if (relatedLinkList == null) {
280                     relatedLinkList = new ArrayList<>();
281                     relationMap.put(relatedToObj, relatedLinkList);
282                 }
283                 relatedLinkList.add(relatedLinkObj);
284             }
285         }
286         return relationMap;
287     }
288
289     /**
290      * @param client
291      * @param url
292      * @param transId
293      * @param mediaType
294      * @return
295      * @throws DiscoveryException
296      */
297     private static String getResource(RestClient client, String url, String transId)
298             throws DiscoveryException {
299         OperationResult result = client.get(url, buildHeaders(transId), MediaType.valueOf(MediaType.APPLICATION_JSON));
300
301         if (result.getResultCode() == 200) {
302             String jsonString = result.getResult();
303             return jsonString;
304         } else if (result.getResultCode() == 404) {
305             // Resource not found, generate empty JSON format
306             logger.info("Resource for " + url + " is not found " + "return empty Json format");
307             return EMPTY_JSON_STRING;
308         } else {
309             throw new DiscoveryException(FETCH_RESOURCE_FAILED, Status.INTERNAL_SERVER_ERROR, result.getFailureCause());
310         }
311     }
312
313     /**
314      * Extract the related-Link from Json payload. For example
315      * {
316      *    "related-to": "vnfc",
317      *    "related-link": "/aai/v11/network/vnfcs/vnfc/zrdm5aepdg01vmg003",
318      *    "relationship-data": [
319      *       {
320      *          "relationship-key": "vnfc.vnfc-name",
321      *          "relationship-value": "zrdm5aepdg01vmg003"
322      *       }
323      *    ]
324      * }
325      */
326     private static List<String> extractRelatedLink(JSONObject payload, String catalog) throws DiscoveryException {
327         List<String> relatedLinkList = new ArrayList<String>();
328         JSONArray relationships = null;
329         logger.info("Fetching relationships for resource type: " + catalog);
330         try {
331             JSONObject relationshipLst = payload.getJSONObject("relationship-list");
332             if (relationshipLst != null) {
333                 relationships = relationshipLst.getJSONArray("relationship");
334             }
335
336         } catch (Exception e) {
337             logger.error(e.getMessage());
338             throw new DiscoveryException(RELATIONSHIP_LINK_PARSE_ERROR, Status.INTERNAL_SERVER_ERROR, e.getMessage());
339         }
340
341         if (relationships != null && relationships.length() > 0) {
342             for (int i = 0; i < relationships.length(); i++) {
343                 Object relatedToObj = null;
344                 Object relatedLinkObj = null;
345
346                 JSONObject obj = relationships.optJSONObject(i);
347                 relatedToObj = obj.get(JSON_ATT_RELATED_TO);
348
349                 if (relatedToObj.toString().equals(catalog)) {
350                     relatedLinkObj = obj.get(JSON_ATT_RELATED_LINK);
351                     if (relatedLinkObj != null) {
352                         relatedLinkList.add(relatedLinkObj.toString());
353                     }
354                 }
355
356             }
357         }
358         if (relatedLinkList != null) {
359             logger.info(
360                     "Number of relationships found for resource type: " + catalog + " are: " + relatedLinkList.size());
361         }
362         return relatedLinkList;
363     }
364
365
366
367     private static Map<String, List<String>> buildHeaders(String transactionId) {
368         MultivaluedMap<String, String> headers = new MultivaluedMapImpl();
369         headers.put(TRANSACTION_ID, Collections.singletonList(transactionId));
370         headers.put(FROM_APP_ID, Collections.singletonList(APP_NAME));
371         return headers;
372     }
373
374 }