Update license headers
[vid.git] / vid-app-common / src / main / java / org / onap / vid / aai / AaiClient.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * VID
4  * ================================================================================
5  * Copyright (C) 2017 - 2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.vid.aai;
22
23 import com.fasterxml.jackson.databind.JsonNode;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.http.HttpStatus;
27 import org.apache.http.client.utils.URIBuilder;
28 import org.jetbrains.annotations.NotNull;
29 import org.json.simple.JSONArray;
30 import org.json.simple.JSONObject;
31 import org.json.simple.parser.JSONParser;
32 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
33 import org.onap.vid.aai.exceptions.InvalidAAIResponseException;
34 import org.onap.vid.aai.model.AaiGetAicZone.AicZones;
35 import org.onap.vid.aai.model.*;
36 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.*;
37 import org.onap.vid.aai.model.AaiGetOperationalEnvironments.OperationalEnvironmentList;
38 import org.onap.vid.aai.model.AaiGetPnfs.Pnf;
39 import org.onap.vid.aai.model.AaiGetServicesRequestModel.GetServicesAAIRespone;
40 import org.onap.vid.aai.model.AaiGetTenatns.GetTenantsResponse;
41 import org.onap.vid.aai.util.AAIRestInterface;
42 import org.onap.vid.aai.util.CacheProvider;
43 import org.onap.vid.aai.util.VidObjectMapperType;
44 import org.onap.vid.exceptions.GenericUncheckedException;
45 import org.onap.vid.model.SubscriberList;
46 import org.onap.vid.model.probes.ErrorMetadata;
47 import org.onap.vid.model.probes.ExternalComponentStatus;
48 import org.onap.vid.model.probes.HttpRequestMetadata;
49 import org.onap.vid.utils.Logging;
50 import org.onap.vid.utils.Unchecked;
51 import org.springframework.http.HttpMethod;
52 import org.springframework.web.util.UriUtils;
53
54 import javax.inject.Inject;
55 import javax.ws.rs.WebApplicationException;
56 import javax.ws.rs.core.Response;
57 import java.io.IOException;
58 import java.io.UnsupportedEncodingException;
59 import java.net.URI;
60 import java.net.URLEncoder;
61 import java.util.LinkedHashMap;
62 import java.util.List;
63 import java.util.Map;
64 import java.util.UUID;
65 import java.util.function.Function;
66
67 import static java.util.Collections.emptyList;
68 import static java.util.stream.Collectors.toMap;
69 import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
70
71 /**
72
73  * Created by Oren on 7/4/17.
74  */
75 public class AaiClient implements AaiClientInterface {
76
77
78     private static final String QUERY_FORMAT_RESOURCE = "query?format=resource";
79     private static final String SERVICE_SUBSCRIPTIONS_PATH = "/service-subscriptions/service-subscription/";
80     private static final String MODEL_INVARIANT_ID = "&model-invariant-id=";
81     private static final String QUERY_FORMAT_SIMPLE = "query?format=simple";
82     private static final String BUSINESS_CUSTOMER = "/business/customers/customer/";
83     private static final String SERVICE_INSTANCE = "/service-instances/service-instance/";
84     private static final String BUSINESS_CUSTOMERS_CUSTOMER = "business/customers/customer/";
85
86     protected String fromAppId = "VidAaiController";
87
88     private PortDetailsTranslator portDetailsTranslator;
89
90     private final AAIRestInterface restController;
91
92     private final CacheProvider cacheProvider;
93
94     ObjectMapper objectMapper = new ObjectMapper();
95
96     /**
97      * The logger
98      */
99     EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AaiClient.class);
100
101     private static final String GET_SERVICE_MODELS_RESPONSE_BODY = "{\"start\" : \"service-design-and-creation/models/\", \"query\" : \"query/serviceModels-byDistributionStatus?distributionStatus=DISTRIBUTION_COMPLETE_OK\"}";
102
103     @Inject
104     public AaiClient(AAIRestInterface restController, PortDetailsTranslator portDetailsTranslator, CacheProvider cacheProvider) {
105         this.restController = restController;
106         this.portDetailsTranslator = portDetailsTranslator;
107         this.cacheProvider = cacheProvider;
108     }
109
110
111     private static String checkForNull(String local) {
112         if (local != null)
113             return local;
114         else
115             return "";
116
117     }
118
119     @Override
120     public AaiResponse getServicesByOwningEntityId(List<String> owningEntityIds){
121         Response resp = doAaiGet(getUrlFromLIst("business/owning-entities?", "owning-entity-id=", owningEntityIds), false);
122         return processAaiResponse(resp, OwningEntityResponse.class, null);
123     }
124
125     @Override
126     public AaiResponse getServicesByProjectNames(List<String> projectNames){
127         Response resp = doAaiGet(getUrlFromLIst("business/projects?", "project-name=",  projectNames), false);
128         return processAaiResponse(resp, ProjectResponse.class, null);
129     }
130
131     @Override
132     public AaiResponse getServiceModelsByDistributionStatus() {
133         return getFromCache("getServiceModelsByDistributionStatus", this::getServiceModelsByDistributionStatusNonCached,
134                 true, "Failed to get service models by distribution status");
135     }
136
137     private AaiResponse getServiceModelsByDistributionStatusNonCached(boolean propagateExceptions) {
138         GetServiceModelsByDistributionStatusResponse response = typedAaiRest(QUERY_FORMAT_RESOURCE, GetServiceModelsByDistributionStatusResponse.class,
139                 GET_SERVICE_MODELS_RESPONSE_BODY, HttpMethod.PUT, propagateExceptions);
140         return new AaiResponse(response, "", HttpStatus.SC_OK);
141     }
142
143     @Override
144     public AaiResponse getNetworkCollectionDetails(String serviceInstanceId) {
145         Response resp = doAaiPut(QUERY_FORMAT_RESOURCE, "{\"start\": [\"nodes/service-instances/service-instance/" + serviceInstanceId + "\"],\"query\": \"query/network-collection-ByServiceInstance\"}\n", false);
146         AaiResponse<AaiGetNetworkCollectionDetailsHelper> aaiResponse = processAaiResponse(resp, AaiGetNetworkCollectionDetailsHelper.class, null, VidObjectMapperType.FASTERXML);
147         return getNetworkCollectionDetailsResponse(aaiResponse);
148     }
149
150     @Override
151     public AaiResponse getInstanceGroupsByCloudRegion(String cloudOwner, String cloudRegionId, String networkFunction) {
152         Response resp = doAaiPut(QUERY_FORMAT_RESOURCE,
153                 "{\"start\": [\"cloud-infrastructure/cloud-regions/cloud-region/" + encodePathSegment(cloudOwner) + "/" + encodePathSegment(cloudRegionId) + "\"]," +
154                         "\"query\": \"query/instance-groups-byCloudRegion?type=L3-NETWORK&role=SUB-INTERFACE&function=" + encodePathSegment(networkFunction) + "\"}\n", false);
155         return processAaiResponse(resp, AaiGetInstanceGroupsByCloudRegion.class, null, VidObjectMapperType.FASTERXML);
156     }
157
158     private AaiResponse getNetworkCollectionDetailsResponse(AaiResponse<AaiGetNetworkCollectionDetailsHelper> aaiResponse){
159         if(aaiResponse.getHttpCode() == 200) {
160             ObjectMapper om = objectMapper;
161             AaiGetNetworkCollectionDetails aaiGetNetworkCollectionDetails = new AaiGetNetworkCollectionDetails();
162             try {
163                 for (int i = 0; i < aaiResponse.getT().getResults().size(); i++) {
164                     LinkedHashMap<String, Object> temp = ((LinkedHashMap) aaiResponse.getT().getResults().get(i));
165                     if (temp.get("service-instance") != null)
166                         aaiGetNetworkCollectionDetails.getResults().setServiceInstance(om.readValue(om.writeValueAsString(temp.get("service-instance")), org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.ServiceInstance.class));
167                     else if (temp.get("collection") != null)
168                         aaiGetNetworkCollectionDetails.getResults().setCollection(om.readValue(om.writeValueAsString(temp.get("collection")), org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Collection.class));
169                     else if (temp.get("instance-group") != null)
170                         aaiGetNetworkCollectionDetails.getResults().setInstanceGroup(om.readValue(om.writeValueAsString(temp.get("instance-group")), InstanceGroup.class));
171                     else if (temp.get("l3-network") != null)
172                         aaiGetNetworkCollectionDetails.getResults().getNetworks().add(om.readValue(om.writeValueAsString(temp.get("l3-network")), Network.class));
173                 }
174                 return new AaiResponse(aaiGetNetworkCollectionDetails, null, HttpStatus.SC_OK);
175             }
176             catch (com.fasterxml.jackson.databind.JsonMappingException e) {
177                 logger.error(EELFLoggerDelegate.errorLogger, "AAI response parsing Error",  e);
178                 return new AaiResponse(e.getCause(), "AAI response parsing Error" , HttpStatus.SC_INTERNAL_SERVER_ERROR);
179             }
180             catch (Exception e) {
181                 return new AaiResponse(e.getCause(), "Got " + aaiResponse.getHttpCode() + " from a&ai" , aaiResponse.getHttpCode());
182             }
183         }
184         return aaiResponse;
185     }
186
187     @Override
188     public AaiResponse getPNFData(String globalCustomerId, String serviceType, String modelVersionId, String modelInvariantId, String cloudRegion, String equipVendor, String equipModel) {
189         String siQuery = BUSINESS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + "/service-instances?model-version-id=" + modelVersionId + MODEL_INVARIANT_ID + modelInvariantId;
190         String pnfQuery = "query/pnf-fromModel-byRegion?cloudRegionId=" + encodePathSegment(cloudRegion) + "&equipVendor=" + encodePathSegment(equipVendor) + "&equipModel=" + encodePathSegment(equipModel);
191         String payload = "{\"start\":\"" + siQuery + "\",\"query\":\"" + pnfQuery + "\"}";
192         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
193         return processAaiResponse(resp, AaiGetPnfResponse.class, null);
194     }
195
196
197     @Override
198     public AaiResponse<Pnf> getSpecificPnf(String pnfId) {
199         Response resp = doAaiGet("network/pnfs/pnf/"+pnfId, false);
200         return processAaiResponse(resp, Pnf.class, null);
201     }
202
203
204     public AaiResponse getInstanceGroupsByVnfInstanceId(String vnfInstanceId){
205         Response resp = doAaiGet("network/generic-vnfs/generic-vnf/" + vnfInstanceId + "?depth=0", false);
206         return processAaiResponse(resp, AaiGetRelatedInstanceGroupsByVnfId.class , null, null);
207     }
208
209
210     @Override
211     public List<PortDetailsTranslator.PortDetails> getPortMirroringSourcePorts(String configurationID) {
212         String payload = "{\"start\":\"/network/configurations/configuration/" + configurationID + "\",\"query\":\"query/pserver-fromConfiguration\"}";
213         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
214         resp.bufferEntity(); // avoid later "Entity input stream has already been closed" problems
215         String rawPayload = resp.readEntity(String.class);
216         AaiResponse<CustomQuerySimpleResult> aaiResponse = processAaiResponse(resp, CustomQuerySimpleResult.class, rawPayload);
217         return portDetailsTranslator.extractPortDetails(aaiResponse, rawPayload);
218     }
219
220
221
222     public AaiResponse getServiceInstance(String globalCustomerId, String serviceType, String serviceInstanceId) {
223         String getServiceInstancePath = BUSINESS_CUSTOMERS_CUSTOMER + globalCustomerId+ SERVICE_SUBSCRIPTIONS_PATH +serviceType+ SERVICE_INSTANCE +serviceInstanceId;
224         Response resp = doAaiGet(getServiceInstancePath , false);
225         return processAaiResponse(resp, ServiceRelationships.class, null);
226     }
227
228     @Override
229     public AaiResponse getLogicalLink(String link) {
230         Response resp = doAaiGet("network/logical-links/logical-link/" + link , false);
231         return processAaiResponse(resp, LogicalLinkResponse.class, null);
232     }
233
234     @Override
235     public boolean isNodeTypeExistsByName(String name, ResourceType type) {
236         if (StringUtils.isEmpty(name)) {
237             throw new GenericUncheckedException("Empty resource-name provided to searchNodeTypeByName; request is rejected as this will cause full resources listing");
238         }
239
240         URI path = Unchecked.toURI(String.format( // e.g. GET /aai/v$/nodes/vf-modules?vf-module-name={vf-module-name}
241                 "nodes/%s?%s=%s",
242                 type.getAaiFormat(),
243                 type.getNameFilter(),
244                 encodePathSegment(name)
245         ));
246         final ResponseWithRequestInfo responseWithRequestInfo = restController.RestGet(fromAppId, UUID.randomUUID().toString(), path, false, true);
247
248         return isResourceExistByStatusCode(responseWithRequestInfo);
249     }
250
251     public Map<String, Properties> getCloudRegionAndTenantByVnfId(String vnfId) {
252         String start = "/network/generic-vnfs/generic-vnf/" + vnfId;
253         String query = "/query/cloud-region-fromVnf";
254
255         String payload = "{\"start\":[\"" + start + "\"],\"query\":\"" + query + "\"}";
256         CustomQuerySimpleResult result = typedAaiRest(QUERY_FORMAT_SIMPLE, CustomQuerySimpleResult.class, payload, HttpMethod.PUT, false);
257
258         return result.getResults().stream()
259                 .filter(res -> StringUtils.equals(res.getNodeType(), "tenant") ||
260                         StringUtils.equals(res.getNodeType(), "cloud-region"))
261                 .collect(toMap(SimpleResult::getNodeType, SimpleResult::getProperties));
262     }
263
264     private boolean isResourceExistByStatusCode(ResponseWithRequestInfo responseWithRequestInfo) {
265         // 200 - is found
266         // 404 - resource not found
267         Response.Status statusInfo = responseWithRequestInfo.getResponse().getStatusInfo().toEnum();
268         switch (statusInfo) {
269             case OK:
270                 return true;
271             case NOT_FOUND:
272                 return false;
273             default:
274                 throw new GenericUncheckedException("Unexpected response-code (only OK and NOT_FOUND are expected): " +
275                         responseWithRequestInfo.getResponse().getStatusInfo());
276         }
277     }
278
279     @Override
280     public <T> T typedAaiGet(URI uri, Class<T> clz) {
281         return typedAaiRest(uri, clz, null, HttpMethod.GET, false);
282     }
283
284     public <T> T typedAaiRest(String path, Class<T> clz, String payload, HttpMethod method, boolean propagateExceptions) {
285         return typedAaiRest(Unchecked.toURI(path), clz, payload, method, propagateExceptions);
286     }
287
288
289     public <T> T typedAaiRest(URI path, Class<T> clz, String payload, HttpMethod method, boolean propagateExceptions) {
290         ResponseWithRequestInfo responseWithRequestInfo;
291         try {
292             responseWithRequestInfo = restController.doRest(fromAppId, UUID.randomUUID().toString(), path, payload, method, false, propagateExceptions);
293         } catch (Exception e) {
294             responseWithRequestInfo = handleExceptionFromRestCall(propagateExceptions, "doAai"+method.name(), e);
295         }
296
297         final AaiResponseWithRequestInfo<T> aaiResponse = processAaiResponse(responseWithRequestInfo, clz, VidObjectMapperType.FASTERXML, true);
298
299         if (aaiResponse.getAaiResponse().getHttpCode() > 399 || aaiResponse.getAaiResponse().getT() == null) {
300             throw new ExceptionWithRequestInfo(aaiResponse.getHttpMethod(),
301                     aaiResponse.getRequestedUrl(),
302                     aaiResponse.getRawData(),
303                     responseWithRequestInfo.getResponse().getStatus(),
304                     new InvalidAAIResponseException(aaiResponse.getAaiResponse()));
305         }
306
307         return aaiResponse.getAaiResponse().getT();
308     }
309
310
311     private String getUrlFromLIst(String url, String paramKey, List<String> params){
312         int i = 0;
313         for(String param: params){
314             i ++;
315             url = url.concat(paramKey);
316             String encodedParam= param;
317             try {
318                 encodedParam= URLEncoder.encode(param, "UTF-8");
319             } catch (UnsupportedEncodingException e) {
320                 String methodName = "getUrlFromList";
321                 logger.error(EELFLoggerDelegate.errorLogger, methodName + e.toString());
322                 logger.debug(EELFLoggerDelegate.debugLogger, methodName + e.toString());
323             }
324             url = url.concat(encodedParam);
325             if(i != params.size()){
326                 url = url.concat("&");
327             }
328         }
329         return url;
330     }
331
332
333
334     @Override
335     public AaiResponse<SubscriberList> getAllSubscribers() {
336         return getFromCache("getAllSubscribers", this::getAllSubscribersNonCached, true, "Failed to get all subscribers");
337     }
338
339     private <K> AaiResponse getFromCache(String cacheName, Function<K, AaiResponse> function, K argument, String errorMessage) {
340         try {
341             return cacheProvider
342                     .aaiClientCacheFor(cacheName, function)
343                     .get(argument);
344         } catch (ExceptionWithRequestInfo exception) {
345             logger.error(errorMessage, exception);
346             return new AaiResponse(null, exception.getRawData(), exception.getHttpCode());
347         }
348         catch (Exception exception) {
349             logger.error(errorMessage, exception);
350             return new AaiResponse(null, exception.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR);
351         }
352     }
353
354     private AaiResponse<SubscriberList> getAllSubscribersNonCached(boolean propagateExceptions) {
355         AaiResponse<SubscriberList> aaiResponse = getAllSubscribers(propagateExceptions).getAaiResponse();
356         if (propagateExceptions && (aaiResponse.getT() == null || aaiResponse.getT().customer == null || aaiResponse.getT().customer.isEmpty())) {
357             throw new GenericUncheckedException("Failed to get Subscribers data. The data is null or empty.");
358         } else {
359             return aaiResponse;
360         }
361     }
362
363     AaiResponseWithRequestInfo<SubscriberList> getAllSubscribers(boolean propagateExceptions){
364         String depth = "0";
365         ResponseWithRequestInfo aaiGetResult = doAaiGet("business/customers?subscriber-type=INFRA&depth=" + depth, false, propagateExceptions);
366         AaiResponseWithRequestInfo<SubscriberList> responseWithRequestInfo = processAaiResponse(aaiGetResult, SubscriberList.class, propagateExceptions);
367         responseWithRequestInfo.setRequestedUrl(aaiGetResult.getRequestUrl());
368         responseWithRequestInfo.setHttpMethod(aaiGetResult.getRequestHttpMethod());
369         return responseWithRequestInfo;
370     }
371
372
373     @Override
374     public AaiResponse getAllAicZones() {
375         Response resp = doAaiGet("network/zones", false);
376         return processAaiResponse(resp, AicZones.class, null);
377     }
378
379     @Override
380     public AaiResponse getVNFData(String globalSubscriberId, String serviceType) {
381         String payload = "{\"start\": [\"business/customers/customer/" + globalSubscriberId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) +"/service-instances\"]," +
382                 "\"query\": \"query/vnf-topology-fromServiceInstance\"}";
383         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
384         return processAaiResponse(resp, AaiGetVnfResponse.class, null);
385     }
386
387     @Override
388     public AaiResponse getVNFData(String globalSubscriberId, String serviceType, String serviceInstanceId) {
389         String payload = "{\"start\": [\"/business/customers/customer/" + globalSubscriberId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + SERVICE_INSTANCE + serviceInstanceId + "\"],       \"query\": \"query/vnf-topology-fromServiceInstance\"}";
390         Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
391         return processAaiResponse(resp, AaiGetVnfResponse.class, null);
392     }
393
394     @Override
395     public Response getVersionByInvariantId(List<String> modelInvariantId) {
396         if (modelInvariantId.isEmpty()) {
397             throw new GenericUncheckedException("Zero invariant-ids provided to getVersionByInvariantId; request is rejected as this will cause full models listing");
398         }
399
400         StringBuilder sb = new StringBuilder();
401         for (String id : modelInvariantId){
402             sb.append(MODEL_INVARIANT_ID);
403             sb.append(id);
404
405         }
406         return doAaiGet("service-design-and-creation/models?depth=2"+ sb.toString(), false);
407     }
408
409     @Override
410     public AaiResponse getSubscriberData(String subscriberId) {
411         String depth = "2";
412         AaiResponse subscriberDataResponse;
413         Response resp = doAaiGet(BUSINESS_CUSTOMERS_CUSTOMER + subscriberId + "?depth=" + depth, false);
414         subscriberDataResponse = processAaiResponse(resp, Services.class, null);
415         return subscriberDataResponse;
416     }
417
418     @Override
419     public AaiResponse getServices() {
420         Response resp = doAaiGet("service-design-and-creation/services", false);
421         return processAaiResponse(resp, GetServicesAAIRespone.class, null);
422     }
423
424     @Override
425     public AaiResponse getOperationalEnvironments(String operationalEnvironmentType, String operationalEnvironmentStatus) {
426         String url = "cloud-infrastructure/operational-environments";
427         URIBuilder urlBuilder  = new URIBuilder();
428         if (operationalEnvironmentType != null)
429             urlBuilder.addParameter("operational-environment-type", operationalEnvironmentType);
430         if (operationalEnvironmentStatus != null)
431             urlBuilder.addParameter("operational-environment-status", operationalEnvironmentStatus);
432         url += urlBuilder.toString();
433         Response resp = doAaiGet(url, false);
434         return processAaiResponse(resp, OperationalEnvironmentList.class, null);
435     }
436
437     @Override
438     public AaiResponse getTenants(String globalCustomerId, String serviceType) {
439         if ((globalCustomerId == null || globalCustomerId.isEmpty()) || ((serviceType == null) || (serviceType.isEmpty())) ){
440             return buildAaiResponseForGetTenantsFailure(" Failed to retrieve LCP Region & Tenants from A&AI, Subscriber ID or Service Type is missing.");
441         }
442         try {
443             return cacheProvider
444                     .aaiClientCacheFor("getTenants", this::getTenantsByKey)
445                     .get(CacheProvider.compileKey(globalCustomerId, serviceType));
446         }
447         catch (ParsingGetTenantsResponseFailure exception) {
448             logger.error("Failed to get tenants ", exception);
449             return buildAaiResponseForGetTenantsFailure(exception.getMessage());
450         }
451     }
452
453     @Override
454     public AaiResponse getNodeTemplateInstances(String globalCustomerId, String serviceType, String modelVersionId, String modelInvariantId, String cloudRegion) {
455
456         String siQuery = BUSINESS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + "/service-instances?model-version-id=" + modelVersionId + MODEL_INVARIANT_ID + modelInvariantId;
457         String vnfQuery = "query/queryvnfFromModelbyRegion?cloudRegionId=" + encodePathSegment(cloudRegion);
458         String payload1 = "{\"start\":\"" + siQuery + "\",\"query\":\"" + vnfQuery + "\"}";
459
460         Response resp1 = doAaiPut(QUERY_FORMAT_SIMPLE, payload1, false);
461         AaiResponse aaiResponse1 = processAaiResponse(resp1, AaiGetVnfResponse.class, null);
462         logger.debug(EELFLoggerDelegate.debugLogger, "getNodeTemplateInstances AAI's response: {}", aaiResponse1);
463         return aaiResponse1;
464     }
465
466     @Override
467     public AaiResponse<JsonNode> getCloudRegionAndSourceByPortMirroringConfigurationId(String configurationId) {
468         final String start = "[\"network/configurations/configuration/" + configurationId + "\"]";
469         final String query = "\"query/cloud-region-and-source-FromConfiguration\"";
470         String payload = "{\"start\":" + start + ",\"query\":" + query + "}";
471
472         Response response = doAaiPut("query?format=simple&nodesOnly=true", payload, false);
473         AaiResponse<JsonNode> aaiResponse = processAaiResponse(response, JsonNode.class, null);
474
475         logger.debug(EELFLoggerDelegate.debugLogger, "getNodeTemplateInstances AAI's response: {}", aaiResponse);
476         return aaiResponse;
477     }
478
479     private <T> AaiResponseWithRequestInfo<T> processAaiResponse(ResponseWithRequestInfo responseWithRequestInfo, Class<? extends T> classType, boolean propagateExceptions) {
480         return processAaiResponse(responseWithRequestInfo, classType, VidObjectMapperType.CODEHAUS, propagateExceptions);
481     }
482
483     private <T> AaiResponseWithRequestInfo<T> processAaiResponse(ResponseWithRequestInfo responseWithRequestInfo, Class<? extends T> classType, VidObjectMapperType omType, boolean propagateExceptions) {
484         String responseBody = null;
485         Integer responseHttpCode = null;
486         try {
487             Response response = responseWithRequestInfo.getResponse();
488             responseHttpCode = (response != null) ? response.getStatus() : null;
489             responseBody = (response != null) ? response.readEntity(String.class) : null;
490             AaiResponse<T> processedAaiResponse = processAaiResponse(response, classType, responseBody, omType, propagateExceptions);
491             return new AaiResponseWithRequestInfo<>(responseWithRequestInfo.getRequestHttpMethod(), responseWithRequestInfo.getRequestUrl(), processedAaiResponse,
492                     responseBody);
493         } catch (Exception e) {
494             throw new ExceptionWithRequestInfo(responseWithRequestInfo.getRequestHttpMethod(),
495                     responseWithRequestInfo.getRequestUrl(), responseBody, responseHttpCode, e);
496         }
497     }
498
499     private AaiResponse processAaiResponse(Response resp, Class classType, String responseBody) {
500         return processAaiResponse(resp, classType, responseBody, VidObjectMapperType.CODEHAUS);
501     }
502
503     private <T> AaiResponse<T> processAaiResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType) {
504         return processAaiResponse(resp, classType, responseBody, omType, false);
505     }
506
507     private  <T> AaiResponse<T> processAaiResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType, boolean propagateExceptions) {
508         AaiResponse<T> subscriberDataResponse;
509         if (resp == null) {
510             subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR);
511             logger.debug(EELFLoggerDelegate.debugLogger, "Invalid response from AAI");
512         } else {
513             logger.debug(EELFLoggerDelegate.debugLogger, "getSubscribers() resp=" + resp.getStatusInfo().toString());
514             if (resp.getStatus() != HttpStatus.SC_OK) {
515                 subscriberDataResponse = processFailureResponse(resp,responseBody);
516             } else {
517                 subscriberDataResponse = processOkResponse(resp, classType, responseBody, omType, propagateExceptions);
518             }
519         }
520         return subscriberDataResponse;
521     }
522
523     private AaiResponse processFailureResponse(Response resp, String responseBody) {
524         logger.debug(EELFLoggerDelegate.debugLogger, "Invalid response from AAI");
525         String rawData;
526         if (responseBody != null) {
527             rawData = responseBody;
528         } else {
529             rawData = resp.readEntity(String.class);
530         }
531         return new AaiResponse<>(null, rawData, resp.getStatus());
532     }
533
534     private <T> AaiResponse<T> processOkResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType, boolean propagateExceptions) {
535         AaiResponse<T> subscriberDataResponse;
536         String finalResponse = null;
537         try {
538             if (responseBody != null) {
539                 finalResponse = responseBody;
540             } else {
541                 finalResponse = resp.readEntity(String.class);
542             }
543
544             if(omType == VidObjectMapperType.CODEHAUS)
545                 subscriberDataResponse = parseCodeHausObject(classType, finalResponse);
546             else
547                 subscriberDataResponse = parseFasterXmlObject(classType, finalResponse);
548
549         } catch(Exception e){
550             if (propagateExceptions) {
551                 throw new GenericUncheckedException(e);
552             } else {
553                 subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR);
554                 logger.error("Failed to parse aai response: \"{}\" to class {}", finalResponse, classType, e);
555             }
556         }
557         return subscriberDataResponse;
558     }
559
560     private <T> AaiResponse<T> parseFasterXmlObject(Class<? extends T> classType, String finalResponse) throws IOException {
561         return new AaiResponse<>((objectMapper.readValue(finalResponse, classType)), null, HttpStatus.SC_OK);
562     }
563
564     private <T> AaiResponse<T> parseCodeHausObject(Class<? extends T> classType, String finalResponse) throws IOException {
565         return new AaiResponse<>((objectMapper.readValue(finalResponse, classType)), null, HttpStatus.SC_OK);
566     }
567
568     @Override
569     public Response doAaiGet(String uri, boolean xml) {
570         return doAaiGet(uri, xml, false).getResponse();
571     }
572
573
574     public ResponseWithRequestInfo doAaiGet(String uri, boolean xml, boolean propagateExceptions) {
575         return doAaiGet(Unchecked.toURI(uri), xml, propagateExceptions);
576     }
577
578     public ResponseWithRequestInfo doAaiGet(URI uri, boolean xml, boolean propagateExceptions) {
579         String methodName = "doAaiGet";
580         logger.debug(EELFLoggerDelegate.debugLogger, methodName + " start");
581
582         ResponseWithRequestInfo resp;
583         try {
584             resp = restController.RestGet(fromAppId, UUID.randomUUID().toString(), uri, xml, propagateExceptions);
585
586         } catch (Exception e) {
587             resp = handleExceptionFromRestCall(propagateExceptions, methodName, e);
588         }
589         return resp;
590     }
591
592     @NotNull
593     protected ResponseWithRequestInfo handleExceptionFromRestCall(boolean propagateExceptions, String methodName, Exception e) {
594         ResponseWithRequestInfo resp;
595         if (propagateExceptions) {
596             throw (e instanceof RuntimeException) ? (RuntimeException)e : new GenericUncheckedException(e);
597         } else {
598             final Exception actual =
599                     e instanceof ExceptionWithRequestInfo ? (Exception) e.getCause() : e;
600
601             final String message =
602                     actual instanceof WebApplicationException ? ((WebApplicationException) actual).getResponse().readEntity(String.class) : e.toString();
603
604             //ToDo: change parameter of requestUrl to real url from doRest function
605             resp = new ResponseWithRequestInfo(null, null, org.springframework.http.HttpMethod.GET);
606             logger.info(EELFLoggerDelegate.errorLogger, methodName + message);
607             logger.debug(EELFLoggerDelegate.debugLogger, methodName + message);
608         }
609         return resp;
610     }
611
612     private String parseForTenantsByServiceSubscription(String relatedToKey, String resp) {
613         String tenantList = "";
614
615         try {
616             JSONParser jsonParser = new JSONParser();
617
618             JSONObject jsonObject = (JSONObject) jsonParser.parse(resp);
619
620             return parseServiceSubscriptionObjectForTenants(relatedToKey, jsonObject);
621         } catch (Exception ex) {
622             logger.debug(EELFLoggerDelegate.debugLogger, "parseForTenantsByServiceSubscription error while parsing tenants by service subscription", ex);
623         }
624         return tenantList;
625     }
626
627     protected Response doAaiPut(String uri, String payload, boolean xml) {
628         return doAaiPut(uri, payload, xml, false).getResponse();
629     }
630
631     protected ResponseWithRequestInfo doAaiPut(String uri, String payload, boolean xml, boolean propagateExceptions) {
632         String methodName = "doAaiPut";
633         logger.debug(EELFLoggerDelegate.debugLogger, methodName + " start");
634
635         ResponseWithRequestInfo resp;
636         try {
637
638             resp = restController.RestPut(fromAppId, uri, payload, xml, propagateExceptions);
639
640         } catch (Exception e) {
641             resp = handleExceptionFromRestCall(propagateExceptions, methodName, e);
642         }
643         return resp;
644     }
645
646
647     private String parseServiceSubscriptionObjectForTenants(String relatedToKey, JSONObject jsonObject) {
648         JSONArray tenantArray = new JSONArray();
649         boolean bconvert = false;
650         try {
651             JSONObject relationShipListsObj = (JSONObject) jsonObject.get("relationship-list");
652             if (relationShipListsObj != null) {
653                 JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship");
654                 for (Object innerObj : defaultIfNull(rShipArray, emptyList())) {
655                     if (innerObj != null) {
656                         bconvert = parseTenant(relatedToKey, tenantArray, bconvert, (JSONObject) innerObj);
657                     }
658                 }
659             }
660         } catch (NullPointerException ex) {
661             logger.debug(EELFLoggerDelegate.debugLogger, "parseServiceSubscriptionObjectForTenants. error while parsing service subscription object for tenants", ex);
662         }
663
664         if (bconvert)
665             return tenantArray.toJSONString();
666         else
667             return "";
668
669     }
670
671     private static boolean parseTenant(String relatedToKey, JSONArray tenantArray, boolean bconvert, JSONObject inner1Obj) {
672         String relatedTo = checkForNull((String) inner1Obj.get("related-to"));
673         if (relatedTo.equalsIgnoreCase(relatedToKey)) {
674             JSONObject tenantNewObj = new JSONObject();
675
676             String relatedLink = checkForNull((String) inner1Obj.get("related-link"));
677             tenantNewObj.put("link", relatedLink);
678
679             JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data");
680             for (Object innerObj : defaultIfNull(rDataArray, emptyList())) {
681                 parseRelationShip(tenantNewObj, (JSONObject) innerObj);
682             }
683
684             JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property");
685             for (Object innerObj : defaultIfNull(relatedTPropArray, emptyList())) {
686                 parseRelatedTProp(tenantNewObj, (JSONObject) innerObj);
687             }
688             bconvert = true;
689             tenantArray.add(tenantNewObj);
690         }
691         return bconvert;
692     }
693
694     private static void parseRelatedTProp(JSONObject tenantNewObj, JSONObject innerObj) {
695         if (innerObj == null)
696             return;
697
698         String propKey = checkForNull((String) innerObj.get("property-key"));
699         String propVal = checkForNull((String) innerObj.get("property-value"));
700         if (propKey.equalsIgnoreCase("tenant.tenant-name")) {
701             tenantNewObj.put("tenantName", propVal);
702         }
703     }
704
705     private static void parseRelationShip(JSONObject tenantNewObj, JSONObject inner2Obj) {
706         if (inner2Obj == null)
707             return;
708
709         String rShipKey = checkForNull((String) inner2Obj.get("relationship-key"));
710         String rShipVal = checkForNull((String) inner2Obj.get("relationship-value"));
711         if (rShipKey.equalsIgnoreCase("cloud-region.cloud-owner")) {
712             tenantNewObj.put("cloudOwner", rShipVal);
713         } else if (rShipKey.equalsIgnoreCase("cloud-region.cloud-region-id")) {
714             tenantNewObj.put("cloudRegionID", rShipVal);
715         } else if (rShipKey.equalsIgnoreCase("tenant.tenant-id")) {
716             tenantNewObj.put("tenantID", rShipVal);
717         }
718     }
719
720     private static String encodePathSegment(String segmentToEncode) {
721         try {
722             return UriUtils.encodePathSegment(segmentToEncode, "UTF-8");
723         } catch (UnsupportedEncodingException e) {
724             throw new GenericUncheckedException("URI encoding failed unexpectedly", e);
725         }
726     }
727
728     @Override
729     public ExternalComponentStatus probeAaiGetAllSubscribers(){
730         long startTime = System.currentTimeMillis();
731         try {
732             AaiResponseWithRequestInfo<SubscriberList> responseWithRequestInfo = getAllSubscribers(true);
733             AaiResponse<SubscriberList> aaiResponse = responseWithRequestInfo.getAaiResponse();
734             long duration = System.currentTimeMillis() - startTime;
735
736             SubscriberList subscribersList = (aaiResponse != null) ? aaiResponse.getT() : null;
737             boolean isAvailable = subscribersList != null && subscribersList.customer != null && !subscribersList.customer.isEmpty();
738
739             HttpRequestMetadata metadata = new HttpRequestMetadata(
740                     responseWithRequestInfo.getHttpMethod(),
741                     (aaiResponse != null) ? aaiResponse.getHttpCode() : 0,
742                     responseWithRequestInfo.getRequestedUrl(),
743                     responseWithRequestInfo.getRawData(),
744                     isAvailable ? "OK" : "No subscriber received",
745                     duration
746             );
747             return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, isAvailable, metadata);
748
749         } catch (ExceptionWithRequestInfo e) {
750             long duration = System.currentTimeMillis() - startTime;
751             return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, false,
752                     new HttpRequestMetadata(e, duration));
753         } catch (Exception e) {
754             long duration = System.currentTimeMillis() - startTime;
755             return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, false,
756                     new ErrorMetadata(Logging.exceptionToDescription(e), duration));
757         }
758     }
759
760     @Override
761     public String getCloudOwnerByCloudRegionId(String cloudRegionId) {
762         return cacheProvider
763                 .aaiClientCacheFor("getCloudOwnerByCloudRegionId", this::getCloudOwnerByCloudRegionIdNonCached)
764                 .get(cloudRegionId);
765     }
766
767
768     @Override
769     public GetTenantsResponse getHomingDataByVfModule(String vnfInstanceId, String vfModuleId) {
770
771         if (StringUtils.isEmpty(vnfInstanceId)||StringUtils.isEmpty(vfModuleId)){
772             throw new GenericUncheckedException("Failed to retrieve homing data associated to vfModule from A&AI, VNF InstanceId or VF Module Id is missing.");
773         }
774         Response resp = doAaiGet("network/generic-vnfs/generic-vnf/" + vnfInstanceId +"/vf-modules/vf-module/"+ vfModuleId, false);
775         String responseAsString = parseForTenantsByServiceSubscription("vserver",resp.readEntity(String.class));
776         if (responseAsString.equals("")){
777             throw new GenericUncheckedException( String.format("A&AI has no homing data associated to vfModule '%s' of vnf '%s'", vfModuleId, vnfInstanceId));
778         }
779         else {
780             AaiResponse aaiResponse = processAaiResponse(resp, GetTenantsResponse[].class, responseAsString);
781             return ((GetTenantsResponse[])aaiResponse.getT())[0];
782         }
783     }
784
785     @Override
786     public void resetCache(String cacheName) {
787         cacheProvider.resetCache(cacheName);
788     }
789
790     String getCloudOwnerByCloudRegionIdNonCached(String cloudRegionId) {
791         String uri = "cloud-infrastructure/cloud-regions?cloud-region-id=" + encodePathSegment(cloudRegionId);
792
793         final CloudRegion.Collection cloudRegionCollection =
794                 typedAaiGet(Unchecked.toURI(uri), CloudRegion.Collection.class);
795
796         return cloudRegionCollection
797                 .getCloudRegions().stream()
798                 .map(CloudRegion::getCloudOwner)
799                 // from here we assure that the cloud owner is given, and not null
800                 // and non-empty, and that if more than one cloud-owner is given -
801                 // it is only a single value.
802                 // exception is thrown if none or more than a single values are
803                 // given.
804                 .filter(StringUtils::isNotEmpty)
805                 .distinct()
806                 .reduce((a, b) -> {
807                     // will be invoked only if distinct() leaves more than a single element
808                     throw new GenericUncheckedException("Conflicting cloud-owner found for " + cloudRegionId + ": '" + a + "' / '" + b + "'");
809                 })
810                 .orElseThrow(() -> new GenericUncheckedException("No cloud-owner found for " + cloudRegionId));
811     }
812
813     private AaiResponse getTenantsByKey(String key) {
814         String[] args = CacheProvider.decompileKey(key);
815         String globalCustomerId = safeGetFromArray(args, 0);
816         String serviceType = safeGetFromArray(args, 1);
817         return getTenantsNonCached(globalCustomerId, serviceType);
818     }
819
820     AaiResponse getTenantsNonCached(String globalCustomerId, String serviceType) {
821         String url = BUSINESS_CUSTOMERS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + serviceType;
822
823         Response resp = doAaiGet(url, false);
824         String responseAsString = parseForTenantsByServiceSubscription("tenant",resp.readEntity(String.class));
825         if (StringUtils.isEmpty(responseAsString)){
826            throw new ParsingGetTenantsResponseFailure(String.format("A&AI has no LCP Region & Tenants associated to subscriber '%s' and service type '%s'", globalCustomerId, serviceType));
827         }
828         else {
829             return processAaiResponse(resp, GetTenantsResponse[].class, responseAsString);
830         }
831     }
832
833     public static class ParsingGetTenantsResponseFailure extends GenericUncheckedException {
834
835         public ParsingGetTenantsResponseFailure(String message) {
836             super(message);
837         }
838     }
839
840     @NotNull
841     private AaiResponse<String> buildAaiResponseForGetTenantsFailure(String errorText) {
842         return new AaiResponse<>(null, String.format("{\"statusText\":\"%s\"}", errorText), HttpStatus.SC_INTERNAL_SERVER_ERROR);
843     }
844
845     private static String safeGetFromArray(String[] array, int i) {
846         if (i < 0 || i >= array.length) {
847             return null;
848         } else {
849             return array[i];
850         }
851     }
852 }