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