af2f7bedbbda23e3d1f6496933efcd5ceec83217
[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.SERVICE_INSTANCE_NOT_FOUND;
23
24 import java.text.MessageFormat;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.ws.rs.core.MediaType;
32 import javax.ws.rs.core.MultivaluedHashMap;
33 import javax.ws.rs.core.MultivaluedMap;
34 import javax.ws.rs.core.Response.Status;
35
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.sdnc.apps.pomba.servicedecomposition.exception.DiscoveryException;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44
45
46
47 public class RestUtil {
48     
49     
50     private static final String RELATIONSHIP_KEY = "relationship";
51     private static final String RELATIONSHIP_LIST_KEY = "relationship-list";
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     private static final String AUTHORIZATION = "Authorization";
58
59     private static final Resource GENERIC_VNF = new Resource("generic-vnf");
60     private static final Resource VF_MODULE = new Resource("vf-module");
61     private static final Resource L3_NETWORK = new Resource("l3-network");
62
63     public static class Resource {
64         private String resourceName;
65         private String collectionName;
66
67         private Resource(String resource) {
68             this.resourceName = resource;
69             this.collectionName = resource + "s";
70         }
71
72         private String getResourceName() {
73             return this.resourceName;
74         }
75
76         private String getCollectionName() {
77             return this.collectionName;
78         }
79
80         @Override
81         public String toString() {
82             return "Resource [resourceName=" + resourceName + ", collectionName=" + collectionName + "]";
83         }
84     }
85
86     private static final String JSON_ATT_RELATED_TO = "related-to";
87     private static final String JSON_ATT_RELATED_LINK = "related-link";
88
89     private static final String EMPTY_JSON_STRING = "{}";
90
91     private static final String DEPTH = "?depth=2";
92     private static Logger logger = LoggerFactory.getLogger(RestUtil.class);
93
94     private RestUtil() {
95         throw new IllegalStateException("Utility class");
96     }
97
98     /**
99      * Validates the URL parameter.
100      *
101      * @throws DiscoveryException if there is missing parameter
102      */
103     public static void validateURL(String serviceInstanceId) throws DiscoveryException {
104
105         if (serviceInstanceId == null || serviceInstanceId.isEmpty()) {
106             throw new DiscoveryException(INVALID_URL, Status.BAD_REQUEST);
107         }
108     }
109
110     private static String generateServiceInstanceURL(String siPath, String serviceInstanceId) {
111         return MessageFormat.format(siPath, serviceInstanceId);
112     }
113
114     /**
115      * @param aaiClient
116      * @param baseURL
117      * @param aaiBasicAuthorization
118      * @param aaiServiceInstancePath
119      * @param aaiResourceList
120      * @param transactionId
121      * @param serviceInstanceId
122      * @param adapter
123      * @return
124      * @throws DiscoveryException
125      */
126     public static JSONObject retrieveAAIModelData(RestClient aaiClient, String baseURL, String aaiBasicAuthorization, String aaiServiceInstancePath, String aaiResourceList,
127             String transactionId, String serviceInstanceId) throws DiscoveryException {
128
129         // Follow two variables for transform purpose
130         String url = baseURL + generateServiceInstanceURL(aaiServiceInstancePath, serviceInstanceId);
131         // Response from service instance API call
132         JSONObject serviceInstancePayload = new JSONObject(
133                 getResource(aaiClient, url, aaiBasicAuthorization, transactionId));
134         // Handle the case if the service instance is not found in AAI
135         if (serviceInstancePayload.length() == 0) {
136             logger.info("Service Instance {} is not found from AAI", serviceInstanceId);
137             // Only return the empty Json on the root level. i.e service instance
138             throw new DiscoveryException(SERVICE_INSTANCE_NOT_FOUND, Status.NOT_FOUND);
139         }
140
141         Map<String, List<String>> relationMap = extractRelationships(serviceInstancePayload);
142         logger.info("The number of the relationship types for service instance id {} is: {}", serviceInstanceId,
143                 relationMap.size());
144
145         JSONObject response = processVNFRelationMap(aaiClient, aaiResourceList, baseURL, aaiBasicAuthorization, transactionId, relationMap, serviceInstancePayload);
146                
147         if (relationMap.containsKey(L3_NETWORK.getResourceName())) {
148             List<String> l3NetworkRelatedLinks = relationMap.get(L3_NETWORK.getResourceName());           
149             List<JSONObject> l3networkPayload = processResourceList(aaiClient, baseURL, aaiBasicAuthorization, transactionId,
150                     L3_NETWORK.getResourceName(), l3NetworkRelatedLinks);
151             
152             response.put(L3_NETWORK.getCollectionName(), l3networkPayload);
153         }
154
155         return response;
156
157     }
158
159     /**
160      * @param aaiClient
161      * @param baseURL
162      * @param transactionId
163      * @param relationMap
164      * @throws DiscoveryException
165      */
166     private static JSONObject processVNFRelationMap(RestClient aaiClient, String aaiResourceList, String baseURL, String aaiBasicAuthorization, String transactionId,
167             Map<String, List<String>> relationMap, JSONObject serviceInstancePayload) throws DiscoveryException {
168         List<JSONObject> vnfLst = new ArrayList<>(); // List of the VNF JSON along with related resources
169
170         JSONObject response = serviceInstancePayload;
171
172         List<Resource> resourceTypes = getResourceTypes(aaiResourceList);
173
174         if (relationMap.get(GENERIC_VNF.getResourceName()) != null) {
175             List<JSONObject> vnfList = processResourceList(aaiClient, baseURL, aaiBasicAuthorization, transactionId,
176                     GENERIC_VNF.getResourceName(), relationMap.get(GENERIC_VNF.getResourceName()));
177             // Logic to Create the Generic VNF JSON and extract further relationships
178             for (JSONObject vnfPayload : vnfList) {
179                 Map<String, List<String>> vnfRelationMap = extractRelationships(vnfPayload);
180                 String vnfId = vnfPayload.optString("vnf-id");
181
182                 for (Resource resourceType : resourceTypes) {
183                     List<String> vnfcLinkLst = vnfRelationMap.get(resourceType.getResourceName());
184                     if (vnfcLinkLst == null || vnfcLinkLst.isEmpty()) {
185                         logger.info("No relationships found for generic-vnf '{}', resource type '{}'", vnfId,
186                                 resourceType.getResourceName());
187                         continue;
188                     }
189
190                     logger.info("Number of relationships found for generic-vnf '{}', resource type '{}' are: {}", vnfId,
191                             resourceType.getResourceName(), vnfcLinkLst.size());
192                     List<JSONObject> vnfcList = processResourceList(aaiClient, baseURL, aaiBasicAuthorization,
193                             transactionId, resourceType.getResourceName(), vnfcLinkLst);
194                     vnfPayload.put(resourceType.getCollectionName(), vnfcList);
195                 }
196
197                 // Process vf-module looking for l3-network:
198                 processVfModuleList(aaiClient, baseURL, aaiBasicAuthorization, transactionId, vnfPayload);
199                 // Add final vnf payload to list
200                 vnfLst.add(vnfPayload);
201             }
202         } else {
203             logger.info("No {} found for service-instance-id: {}", GENERIC_VNF.getResourceName(),
204                     serviceInstancePayload.optString("service-instance-id"));
205         }
206
207         // Add generic vnf with related resource payload to response
208         if (!vnfLst.isEmpty()) {
209             response.put(GENERIC_VNF.getCollectionName(), vnfLst);
210         }
211         return response;
212
213     }
214
215     private static void processVfModuleList(RestClient aaiClient, String baseURL, String aaiBasicAuthorization, String transactionId,
216             JSONObject vnfPayload) throws DiscoveryException {
217
218        if (!vnfPayload.has(VF_MODULE.getCollectionName())) {
219            return;
220        }
221
222        JSONObject vfmoduleCollection = vnfPayload.getJSONObject(VF_MODULE.getCollectionName());
223
224        if (!vfmoduleCollection.has(VF_MODULE.getResourceName())) {
225            return;
226        }
227        
228        JSONArray vfModuleList = vfmoduleCollection.getJSONArray(VF_MODULE.getResourceName());
229
230         for (int i = 0; i < vfModuleList.length(); i++) {
231             JSONObject vfModulePayload = vfModuleList.optJSONObject(i);
232             if (vfModulePayload == null) {
233                 logger.error("VF Module not found for vnf-id {}", vnfPayload.opt("vnf-id"));
234                 continue;
235             }
236             processVfModule(aaiClient, baseURL, aaiBasicAuthorization, transactionId, vfModulePayload);
237         }     
238    }
239
240     private static void processVfModule(RestClient aaiClient, String baseURL, String aaiBasicAuthorization,
241             String transactionId, JSONObject vfModulePayload) throws DiscoveryException {
242
243         Map<String, List<String>> relationMap = extractRelationships(vfModulePayload);
244         Object vfModuleId = vfModulePayload.opt("vf-module-id");
245
246         List<String> l3NetworkRelatedLinks = relationMap.get(L3_NETWORK.getResourceName());
247         if (l3NetworkRelatedLinks == null) {
248             logger.info("No relationships found for vf-module '{}', resource type '{}'", vfModuleId, L3_NETWORK.getResourceName());
249             // No L3-network relationships exist.
250             return;
251         }
252
253         logger.info("Number of relationships found for vf-module '{}', resource type '{}' are: {}", vfModuleId, L3_NETWORK.getResourceName(), l3NetworkRelatedLinks.size());
254
255         List<JSONObject> l3NetworkObjects = processResourceList(aaiClient, baseURL, aaiBasicAuthorization,
256                 transactionId, L3_NETWORK.getResourceName(), l3NetworkRelatedLinks);
257
258         // Add l3-network with related resource payload to the vfModulePayload:
259         vfModulePayload.put(L3_NETWORK.getCollectionName(), l3NetworkObjects);
260     }
261
262     /**
263      * @param aaiClient
264      * @param aaiBaseURL
265      * @param transactionId
266      * @param resourceType
267      * @param resourceList
268      * @return
269      * @throws DiscoveryException
270      */
271     private static List<JSONObject> processResourceList(RestClient aaiClient, String aaiBaseURL, String aaiBasicAuthorization, String transactionId,
272             String resourceType, List<String> resourceList) throws DiscoveryException {
273         List<JSONObject> resourcePayloadList = new ArrayList<>();
274         for (String resourceLink : resourceList) {
275             String resourceURL = aaiBaseURL + resourceLink;
276             // With latest AAI development, in order to retrieve the both generic VNF + vf_module, we can use
277             // one API call but with depth=2
278             if (resourceType.equals(GENERIC_VNF.getResourceName())) {
279                 resourceURL += DEPTH;
280             }
281
282             // Response from generic VNF API call
283             JSONObject resourcePayload = new JSONObject(
284                     getResource(aaiClient, resourceURL, aaiBasicAuthorization, transactionId));
285             if (resourcePayload.length() == 0) {
286                 logger.info("Resource with url {} is not found from AAI", resourceLink);
287             } else {
288                 resourcePayloadList.add(resourcePayload);
289             }
290         }
291         return resourcePayloadList;
292     }
293
294     /**
295      * Extract the related-Link from Json payload. For example
296      * <pre>
297      * {
298      *    "related-to": "vnfc",
299      *    "related-link": "/aai/v11/network/vnfcs/vnfc/zrdm5aepdg01vmg003",
300      *    "relationship-data": [
301      *       {
302      *          "relationship-key": "vnfc.vnfc-name",
303      *          "relationship-value": "zrdm5aepdg01vmg003"
304      *       }
305      *    ]
306      * }
307      * </pre>
308      * @param payload input pay load json.
309      * @return Map of "related-to" to list of "related-link" strings.
310      */
311     private static Map<String, List<String>> extractRelationships(JSONObject payload) {
312
313         Map<String, List<String>> relationMap = new HashMap<>();
314
315         if (!payload.has(RELATIONSHIP_LIST_KEY)) {
316             return relationMap;
317         }
318
319         JSONObject relationshipList = payload.getJSONObject(RELATIONSHIP_LIST_KEY);
320
321         if (!relationshipList.has(RELATIONSHIP_KEY)) {
322             return relationMap;
323         }
324
325         JSONArray relationships = relationshipList.getJSONArray(RELATIONSHIP_KEY);
326
327         for (int i = 0; i < relationships.length(); i++) {
328             JSONObject obj = relationships.optJSONObject(i);
329             String relatedToObj = obj.optString(JSON_ATT_RELATED_TO);
330
331             if (relatedToObj == null) {
332                 logger.error("Related-To Object is null");
333                 continue;
334             }
335             List<String> relatedLinkList = relationMap.get(relatedToObj);
336             if (relatedLinkList == null) {
337                 relatedLinkList = new ArrayList<>();
338                 relationMap.put(relatedToObj, relatedLinkList);
339             }
340             String relatedLinkObj = obj.getString(JSON_ATT_RELATED_LINK);
341             relatedLinkList.add(relatedLinkObj);
342         }
343
344         return relationMap;
345     }
346
347     /**
348      * @param client
349      * @param url
350      * @param aaiBasicAuthorization
351      * @param transId
352      * @return
353      * @throws DiscoveryException
354      */
355     private static String getResource(RestClient client, String url, String aaiBasicAuthorization, String transId)
356             throws DiscoveryException {
357         OperationResult result = client.get(url, buildHeaders(aaiBasicAuthorization, transId), MediaType.valueOf(MediaType.APPLICATION_JSON));
358
359         if (result.wasSuccessful()) {
360             return result.getResult();
361         } else if (result.getResultCode() == 404) {
362             // Resource not found, generate empty JSON format
363             logger.info("Resource for {} is not found, return empty Json format", url);
364             return EMPTY_JSON_STRING;
365         } else {
366             throw new DiscoveryException(FETCH_RESOURCE_FAILED, Status.INTERNAL_SERVER_ERROR, result.getFailureCause());
367         }
368     }
369
370     private static Map<String, List<String>> buildHeaders(String aaiBasicAuthorization, String transactionId) {
371         MultivaluedMap<String, String> headers = new MultivaluedHashMap<>();
372         headers.put(TRANSACTION_ID, Collections.singletonList(transactionId));
373         headers.put(FROM_APP_ID, Collections.singletonList(APP_NAME));
374         headers.put(AUTHORIZATION, Collections.singletonList(aaiBasicAuthorization));
375         return headers;
376     }
377
378     private static List<Resource> getResourceTypes(String aaiResourceList) {
379         List<Resource> resources = new ArrayList<>();
380         String noSpaceAaiResourceList = aaiResourceList.replaceAll("\\s", "");
381         String[] resourceList = noSpaceAaiResourceList.split(",");
382         for (int i = 0; i < resourceList.length; i++) {
383                 resources.add(new Resource(resourceList[i]));
384         }
385         return resources;
386     }
387
388 }