2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.vid.aai;
23 import static com.fasterxml.jackson.module.kotlin.ExtensionsKt.jacksonObjectMapper;
24 import static java.util.Collections.emptyList;
25 import static java.util.Comparator.comparing;
26 import static java.util.stream.Collectors.toMap;
27 import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
28 import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase;
29 import static org.apache.commons.lang3.StringUtils.isEmpty;
30 import static org.onap.vid.utils.KotlinUtilsKt.JACKSON_OBJECT_MAPPER;
32 import com.fasterxml.jackson.core.JsonProcessingException;
33 import com.fasterxml.jackson.databind.JsonNode;
34 import com.fasterxml.jackson.databind.ObjectMapper;
35 import com.google.common.collect.ImmutableList;
36 import com.google.common.collect.ImmutableMap;
37 import java.io.IOException;
38 import java.io.Serializable;
39 import java.io.UnsupportedEncodingException;
41 import java.net.URLEncoder;
42 import java.util.LinkedHashMap;
43 import java.util.List;
45 import java.util.UUID;
46 import java.util.function.Function;
47 import java.util.stream.Collectors;
48 import java.util.stream.Stream;
49 import javax.inject.Inject;
50 import javax.ws.rs.WebApplicationException;
51 import javax.ws.rs.core.Response;
52 import org.apache.commons.lang3.StringUtils;
53 import org.apache.commons.lang3.exception.ExceptionUtils;
54 import org.apache.http.HttpStatus;
55 import org.apache.http.client.utils.URIBuilder;
56 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
57 import org.jetbrains.annotations.NotNull;
58 import org.jetbrains.annotations.Nullable;
59 import org.json.simple.JSONArray;
60 import org.json.simple.JSONObject;
61 import org.json.simple.parser.JSONParser;
62 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
63 import org.onap.vid.aai.exceptions.InvalidAAIResponseException;
64 import org.onap.vid.aai.model.AaiGetAicZone.AicZones;
65 import org.onap.vid.aai.model.AaiGetInstanceGroupsByCloudRegion;
66 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.AaiGetNetworkCollectionDetails;
67 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.AaiGetNetworkCollectionDetailsHelper;
68 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.AaiGetRelatedInstanceGroupsByVnfId;
69 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.CloudRegion;
70 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.InstanceGroup;
71 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Network;
72 import org.onap.vid.aai.model.AaiGetOperationalEnvironments.OperationalEnvironmentList;
73 import org.onap.vid.aai.model.AaiGetPnfResponse;
74 import org.onap.vid.aai.model.AaiGetPnfs.Pnf;
75 import org.onap.vid.aai.model.AaiGetServicesRequestModel.GetServicesAAIRespone;
76 import org.onap.vid.aai.model.AaiGetTenatns.GetTenantsResponse;
77 import org.onap.vid.aai.model.CustomQuerySimpleResult;
78 import org.onap.vid.aai.model.GetServiceModelsByDistributionStatusResponse;
79 import org.onap.vid.aai.model.LogicalLinkResponse;
80 import org.onap.vid.aai.model.ModelVer;
81 import org.onap.vid.aai.model.ModelVersions;
82 import org.onap.vid.aai.model.OwningEntityResponse;
83 import org.onap.vid.aai.model.PortDetailsTranslator;
84 import org.onap.vid.aai.model.ProjectResponse;
85 import org.onap.vid.aai.model.Properties;
86 import org.onap.vid.aai.model.ResourceType;
87 import org.onap.vid.aai.model.ServiceRelationships;
88 import org.onap.vid.aai.model.SimpleResult;
89 import org.onap.vid.aai.util.AAIRestInterface;
90 import org.onap.vid.aai.util.CacheProvider;
91 import org.onap.vid.aai.util.VidObjectMapperType;
92 import org.onap.vid.exceptions.GenericUncheckedException;
93 import org.onap.vid.model.SubscriberList;
94 import org.onap.vid.model.probes.ErrorMetadata;
95 import org.onap.vid.model.probes.ExternalComponentStatus;
96 import org.onap.vid.model.probes.HttpRequestMetadata;
97 import org.onap.vid.utils.Logging;
98 import org.onap.vid.utils.Unchecked;
99 import org.springframework.http.HttpMethod;
100 import org.springframework.web.util.UriUtils;
102 public class AaiClient implements AaiClientInterface {
105 public static final String QUERY_FORMAT_RESOURCE = "query?format=resource";
106 private static final String SERVICE_SUBSCRIPTIONS_PATH = "/service-subscriptions/service-subscription/";
107 private static final String MODEL_INVARIANT_ID = "&model-invariant-id=";
108 private static final String QUERY_FORMAT_SIMPLE = "query?format=simple";
109 private static final String BUSINESS_CUSTOMER = "/business/customers/customer/";
110 private static final String SERVICE_INSTANCE = "/service-instances/service-instance/";
111 private static final String BUSINESS_CUSTOMERS_CUSTOMER = "business/customers/customer/";
113 protected String fromAppId = "VidAaiController";
115 private PortDetailsTranslator portDetailsTranslator;
117 private final AAIRestInterface restController;
119 private final CacheProvider cacheProvider;
121 ObjectMapper objectMapper = jacksonObjectMapper();
126 EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(AaiClient.class);
128 private static final String GET_SERVICE_MODELS_REQUEST_BODY = "{\"start\" : \"service-design-and-creation/models/\", \"query\" : \"query/serviceModels-byDistributionStatus?distributionStatus=DISTRIBUTION_COMPLETE_OK\"}";
131 public AaiClient(AAIRestInterface restController, PortDetailsTranslator portDetailsTranslator, CacheProvider cacheProvider) {
132 this.restController = restController;
133 this.portDetailsTranslator = portDetailsTranslator;
134 this.cacheProvider = cacheProvider;
138 private static String checkForNull(String local) {
147 public AaiResponse<OwningEntityResponse> getServicesByOwningEntityId(List<String> owningEntityIds){
148 Response resp = doAaiGet(getUrlFromLIst("business/owning-entities?", "owning-entity-id=", owningEntityIds), false);
149 return processAaiResponse(resp, OwningEntityResponse.class, null);
153 public AaiResponse<ProjectResponse> getServicesByProjectNames(List<String> projectNames){
154 Response resp = doAaiGet(getUrlFromLIst("business/projects?", "project-name=", projectNames), false);
155 return processAaiResponse(resp, ProjectResponse.class, null);
159 public AaiResponse getServiceModelsByDistributionStatus() {
160 return getFromCache("getServiceModelsByDistributionStatus", this::getServiceModelsByDistributionStatusNonCached,
161 true, "Failed to get service models by distribution status");
164 private AaiResponse getServiceModelsByDistributionStatusNonCached(boolean propagateExceptions) {
165 GetServiceModelsByDistributionStatusResponse response = typedAaiRest(QUERY_FORMAT_RESOURCE, GetServiceModelsByDistributionStatusResponse.class,
166 GET_SERVICE_MODELS_REQUEST_BODY, HttpMethod.PUT, propagateExceptions);
167 return new AaiResponse(response, "", HttpStatus.SC_OK);
171 public AaiResponse getNetworkCollectionDetails(String serviceInstanceId) {
172 Response resp = doAaiPut(QUERY_FORMAT_RESOURCE, "{\"start\": [\"nodes/service-instances/service-instance/" + serviceInstanceId + "\"],\"query\": \"query/network-collection-ByServiceInstance\"}\n", false);
173 AaiResponse<AaiGetNetworkCollectionDetailsHelper> aaiResponse = processAaiResponse(resp, AaiGetNetworkCollectionDetailsHelper.class, null, VidObjectMapperType.FASTERXML);
174 return getNetworkCollectionDetailsResponse(aaiResponse);
178 public AaiResponse getInstanceGroupsByCloudRegion(String cloudOwner, String cloudRegionId, String networkFunction) {
179 Response resp = doAaiPut(QUERY_FORMAT_RESOURCE,
180 "{\"start\": [\"cloud-infrastructure/cloud-regions/cloud-region/" + encodePathSegment(cloudOwner) + "/" + encodePathSegment(cloudRegionId) + "\"]," +
181 "\"query\": \"query/instance-groups-byCloudRegion?type=L3-NETWORK&role=SUB-INTERFACE&function=" + encodePathSegment(networkFunction) + "\"}\n", false);
182 return processAaiResponse(resp, AaiGetInstanceGroupsByCloudRegion.class, null, VidObjectMapperType.FASTERXML);
185 private AaiResponse getNetworkCollectionDetailsResponse(AaiResponse<AaiGetNetworkCollectionDetailsHelper> aaiResponse){
186 if(aaiResponse.getHttpCode() == 200) {
187 ObjectMapper om = objectMapper;
188 AaiGetNetworkCollectionDetails aaiGetNetworkCollectionDetails = new AaiGetNetworkCollectionDetails();
190 for (int i = 0; i < aaiResponse.getT().getResults().size(); i++) {
191 LinkedHashMap<String, Object> temp = ((LinkedHashMap) aaiResponse.getT().getResults().get(i));
192 if (temp.get("service-instance") != null)
193 aaiGetNetworkCollectionDetails.getResults().setServiceInstance(om.readValue(om.writeValueAsString(temp.get("service-instance")), org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.ServiceInstance.class));
194 else if (temp.get("collection") != null)
195 aaiGetNetworkCollectionDetails.getResults().setCollection(om.readValue(om.writeValueAsString(temp.get("collection")), org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Collection.class));
196 else if (temp.get("instance-group") != null)
197 aaiGetNetworkCollectionDetails.getResults().setInstanceGroup(om.readValue(om.writeValueAsString(temp.get("instance-group")), InstanceGroup.class));
198 else if (temp.get("l3-network") != null)
199 aaiGetNetworkCollectionDetails.getResults().getNetworks().add(om.readValue(om.writeValueAsString(temp.get("l3-network")), Network.class));
201 return new AaiResponse(aaiGetNetworkCollectionDetails, null, HttpStatus.SC_OK);
203 catch (com.fasterxml.jackson.databind.JsonMappingException e) {
204 logger.error(EELFLoggerDelegate.errorLogger, "AAI response parsing Error", e);
205 return new AaiResponse(e.getCause(), "AAI response parsing Error" , HttpStatus.SC_INTERNAL_SERVER_ERROR);
207 catch (Exception e) {
208 logger.error(EELFLoggerDelegate.errorLogger,"Exception in aai response parsing", e);
209 return new AaiResponse(e.getCause(), "Got " + aaiResponse.getHttpCode() + " from a&ai" , aaiResponse.getHttpCode());
216 public AaiResponse getPNFData(String globalCustomerId, String serviceType, String modelVersionId, String modelInvariantId, String cloudRegion, String equipVendor, String equipModel) {
217 String siQuery = BUSINESS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + "/service-instances?model-version-id=" + modelVersionId + MODEL_INVARIANT_ID + modelInvariantId;
218 String pnfQuery = "query/pnf-fromModel-byRegion?cloudRegionId=" + encodePathSegment(cloudRegion) + "&equipVendor=" + encodePathSegment(equipVendor) + "&equipModel=" + encodePathSegment(equipModel);
219 String payload = "{\"start\":\"" + siQuery + "\",\"query\":\"" + pnfQuery + "\"}";
220 Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
221 return processAaiResponse(resp, AaiGetPnfResponse.class, null);
226 public AaiResponse<Pnf> getSpecificPnf(String pnfId) {
227 Response resp = doAaiGet("network/pnfs/pnf/"+pnfId, false);
228 return processAaiResponse(resp, Pnf.class, null);
232 public AaiResponse getInstanceGroupsByVnfInstanceId(String vnfInstanceId){
233 Response resp = doAaiGet("network/generic-vnfs/generic-vnf/" + vnfInstanceId + "?depth=0", false);
234 return processAaiResponse(resp, AaiGetRelatedInstanceGroupsByVnfId.class , null, null);
239 public List<PortDetailsTranslator.PortDetails> getPortMirroringSourcePorts(String configurationID) {
240 String payload = "{\"start\":\"/network/configurations/configuration/" + configurationID + "\",\"query\":\"query/pserver-fromConfiguration\"}";
241 Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
242 resp.bufferEntity(); // avoid later "Entity input stream has already been closed" problems
243 String rawPayload = resp.readEntity(String.class);
244 AaiResponse<CustomQuerySimpleResult> aaiResponse = processAaiResponse(resp, CustomQuerySimpleResult.class, rawPayload);
245 return portDetailsTranslator.extractPortDetails(aaiResponse, rawPayload);
250 public AaiResponse getServiceInstance(String globalCustomerId, String serviceType, String serviceInstanceId) {
251 String getServiceInstancePath = BUSINESS_CUSTOMERS_CUSTOMER + globalCustomerId+ SERVICE_SUBSCRIPTIONS_PATH +serviceType+ SERVICE_INSTANCE +serviceInstanceId;
252 Response resp = doAaiGet(getServiceInstancePath , false);
253 return processAaiResponse(resp, ServiceRelationships.class, null);
257 public AaiResponse getLogicalLink(String link) {
258 Response resp = doAaiGet("network/logical-links/logical-link/" + link , false);
259 return processAaiResponse(resp, LogicalLinkResponse.class, null);
263 public boolean isNodeTypeExistsByName(String name, ResourceType type) {
265 throw new GenericUncheckedException("Empty resource-name provided to searchNodeTypeByName; request is rejected as this will cause full resources listing");
268 URI path = Unchecked.toURI(String.format( // e.g. GET /aai/v$/nodes/vf-modules?vf-module-name={vf-module-name}
271 type.getNameFilter(),
272 encodePathSegment(name)
274 final ResponseWithRequestInfo responseWithRequestInfo = restController.RestGet(fromAppId, UUID.randomUUID().toString(), path, false, true);
276 return isResourceExistByStatusCode(responseWithRequestInfo);
279 public Map<String, Properties> getCloudRegionAndTenantByVnfId(String vnfId) {
280 String start = "/network/generic-vnfs/generic-vnf/" + vnfId;
281 String query = "/query/cloud-region-fromVnf";
283 String payload = "{\"start\":[\"" + start + "\"],\"query\":\"" + query + "\"}";
284 CustomQuerySimpleResult result = typedAaiRest(QUERY_FORMAT_SIMPLE, CustomQuerySimpleResult.class, payload, HttpMethod.PUT, false);
286 return result.getResults().stream()
287 .filter(res -> StringUtils.equals(res.getNodeType(), "tenant") ||
288 StringUtils.equals(res.getNodeType(), "cloud-region"))
289 .collect(toMap(SimpleResult::getNodeType, SimpleResult::getProperties));
293 public AaiResponse<AaiGetVnfResponse> getVnfsByParamsForChangeManagement(String subscriberId, String serviceType, @Nullable String nfRole,
294 @Nullable String cloudRegion) {
295 String payloadAsString = "";
296 ResponseWithRequestInfo response;
297 ImmutableMap<String, Serializable> payload = getMapForAAIQueryByParams(subscriberId, serviceType,
298 nfRole, cloudRegion);
300 payloadAsString = JACKSON_OBJECT_MAPPER.writeValueAsString(payload);
301 } catch (JsonProcessingException e) {
302 logger.error(e.getMessage());
303 ExceptionUtils.rethrow(e);
305 response = doAaiPut(QUERY_FORMAT_SIMPLE, payloadAsString, false, false);
306 AaiResponseWithRequestInfo<AaiGetVnfResponse> aaiResponse = processAaiResponse(response, AaiGetVnfResponse.class, false);
307 verifyAaiResponseValidityOrThrowExc(aaiResponse, aaiResponse.getAaiResponse().getHttpCode());
308 return aaiResponse.getAaiResponse();
311 private ImmutableMap<String, Serializable> getMapForAAIQueryByParams(String subscriberId,
312 String serviceType, @Nullable String nfRole, @Nullable String cloudRegion) {
313 // in a case cloudRegion is null using query/vnfs-fromServiceInstance-filter,
314 // otherwise using query/vnfs-fromServiceInstance-filterByCloudRegion
316 if (cloudRegion != null){
317 return ImmutableMap.of(
318 "start", ImmutableList
319 .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
320 "query", "query/vnfs-fromServiceInstance-filterByCloudRegion?nfRole=" + nfRole + "&cloudRegionID=" + cloudRegion + ""
323 return ImmutableMap.of(
324 "start", ImmutableList
325 .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
326 "query", "query/vnfs-fromServiceInstance-filter?nfRole=" + nfRole + ""
331 if (cloudRegion != null){
332 return ImmutableMap.of(
333 "start", ImmutableList
334 .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
335 "query", "query/vnfs-fromServiceInstance-filterByCloudRegion?cloudRegionID=" + cloudRegion + ""
339 return ImmutableMap.of(
340 "start", ImmutableList
341 .of("/business/customers/customer/" + encodePathSegment(subscriberId) + "/service-subscriptions/service-subscription/" + encodePathSegment(serviceType) + "/service-instances"),
342 "query", "query/vnfs-fromServiceInstance-filter"
346 private boolean isResourceExistByStatusCode(ResponseWithRequestInfo responseWithRequestInfo) {
348 // 404 - resource not found
349 Response.Status statusInfo = responseWithRequestInfo.getResponse().getStatusInfo().toEnum();
350 switch (statusInfo) {
356 throw new GenericUncheckedException("Unexpected response-code (only OK and NOT_FOUND are expected): " +
357 responseWithRequestInfo.getResponse().getStatusInfo());
362 public <T> T typedAaiGet(URI uri, Class<T> clz) {
363 return typedAaiRest(uri, clz, null, HttpMethod.GET, false);
366 public <T> T typedAaiRest(String path, Class<T> clz, String payload, HttpMethod method, boolean propagateExceptions) {
367 return typedAaiRest(Unchecked.toURI(path), clz, payload, method, propagateExceptions);
371 public <T> T typedAaiRest(URI path, Class<T> clz, String payload, HttpMethod method, boolean propagateExceptions) {
372 ResponseWithRequestInfo responseWithRequestInfo;
374 responseWithRequestInfo = restController.doRest(fromAppId, UUID.randomUUID().toString(), path, payload, method, false, propagateExceptions);
375 } catch (Exception e) {
376 responseWithRequestInfo = handleExceptionFromRestCall(propagateExceptions, "doAai"+method.name(), e);
379 final AaiResponseWithRequestInfo<T> aaiResponse = processAaiResponse(responseWithRequestInfo, clz, VidObjectMapperType.FASTERXML, true);
380 verifyAaiResponseValidityOrThrowExc(aaiResponse, responseWithRequestInfo.getResponse().getStatus());
381 return aaiResponse.getAaiResponse().getT();
384 private void verifyAaiResponseValidityOrThrowExc(AaiResponseWithRequestInfo aaiResponse, int httpCode) {
385 if (aaiResponse.getAaiResponse().getHttpCode() > 399 || aaiResponse.getAaiResponse().getT() == null) {
386 throw new ExceptionWithRequestInfo(aaiResponse.getHttpMethod(),
387 aaiResponse.getRequestedUrl(),
388 aaiResponse.getRawData(),
390 new InvalidAAIResponseException(aaiResponse.getAaiResponse()));
394 private String getUrlFromLIst(String url, String paramKey, List<String> params){
396 for(String param: params){
398 url = url.concat(paramKey);
399 String encodedParam= param;
401 encodedParam= URLEncoder.encode(param, "UTF-8");
402 } catch (UnsupportedEncodingException e) {
403 String methodName = "getUrlFromList";
404 logger.error(EELFLoggerDelegate.errorLogger, methodName, e);
405 logger.debug(EELFLoggerDelegate.debugLogger, methodName, e);
407 url = url.concat(encodedParam);
408 if(i != params.size()){
409 url = url.concat("&");
418 public AaiResponse<SubscriberList> getAllSubscribers() {
419 return getFromCache("getAllSubscribers", this::getAllSubscribersNonCached, true, "Failed to get all subscribers");
422 private <K> AaiResponse getFromCache(String cacheName, Function<K, AaiResponse> function, K argument, String errorMessage) {
425 .aaiClientCacheFor(cacheName, function)
427 } catch (ExceptionWithRequestInfo exception) {
428 logger.error(errorMessage, exception);
429 return new AaiResponse(null, exception.getRawData(), exception.getHttpCode());
431 catch (Exception exception) {
432 logger.error(errorMessage, exception);
433 return new AaiResponse(null, exception.getMessage(), HttpStatus.SC_INTERNAL_SERVER_ERROR);
437 private AaiResponse<SubscriberList> getAllSubscribersNonCached(boolean propagateExceptions) {
438 AaiResponse<SubscriberList> aaiResponse = getAllSubscribers(propagateExceptions).getAaiResponse();
439 if (propagateExceptions && (aaiResponse.getT() == null || aaiResponse.getT().customer == null || aaiResponse.getT().customer.isEmpty())) {
440 throw new GenericUncheckedException("Failed to get Subscribers data. The data is null or empty.");
446 AaiResponseWithRequestInfo<SubscriberList> getAllSubscribers(boolean propagateExceptions){
448 ResponseWithRequestInfo aaiGetResult = doAaiGet("business/customers?subscriber-type=INFRA&depth=" + depth, false, propagateExceptions);
449 AaiResponseWithRequestInfo<SubscriberList> responseWithRequestInfo = processAaiResponse(aaiGetResult, SubscriberList.class, propagateExceptions);
450 responseWithRequestInfo.setRequestedUrl(aaiGetResult.getRequestUrl());
451 responseWithRequestInfo.setHttpMethod(aaiGetResult.getRequestHttpMethod());
452 return responseWithRequestInfo;
457 public AaiResponse getAllAicZones() {
458 Response resp = doAaiGet("network/zones", false);
459 return processAaiResponse(resp, AicZones.class, null);
463 public AaiResponse<AaiGetVnfResponse> getVNFData(String globalSubscriberId, String serviceType) {
464 String payload = "{\"start\": [\"business/customers/customer/" + globalSubscriberId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) +"/service-instances\"]," +
465 "\"query\": \"query/vnf-topology-fromServiceInstance\"}";
466 Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
467 return processAaiResponse(resp, AaiGetVnfResponse.class, null);
471 public AaiResponse getVNFData(String globalSubscriberId, String serviceType, String serviceInstanceId) {
472 String payload = "{\"start\": [\"/business/customers/customer/" + globalSubscriberId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + SERVICE_INSTANCE + serviceInstanceId + "\"], \"query\": \"query/vnf-topology-fromServiceInstance\"}";
473 Response resp = doAaiPut(QUERY_FORMAT_SIMPLE, payload, false);
474 return processAaiResponse(resp, AaiGetVnfResponse.class, null);
478 public Response getVersionByInvariantId(List<String> modelInvariantId) {
479 if (modelInvariantId.isEmpty()) {
480 throw new GenericUncheckedException("Zero invariant-ids provided to getVersionByInvariantId; request is rejected as this will cause full models listing");
483 StringBuilder sb = new StringBuilder();
484 for (String id : modelInvariantId){
485 sb.append(MODEL_INVARIANT_ID);
489 return doAaiGet("service-design-and-creation/models?depth=2" + sb.toString(), false);
493 public ModelVer getLatestVersionByInvariantId(String modelInvariantId) {
494 return maxModelVer(getAllVersionsByInvariantId(modelInvariantId));
498 public List<ModelVer> getSortedVersionsByInvariantId(String modelInvariantId) {
499 return sortedModelVer(getAllVersionsByInvariantId(modelInvariantId));
502 private Stream<ModelVer> getAllVersionsByInvariantId(String modelInvariantId) {
503 if (modelInvariantId.isEmpty()) {
504 throw new GenericUncheckedException("no invariant-id provided to getLatestVersionByInvariantId; request is rejected");
507 Response response = doAaiPut("query?format=resource&depth=0", "{\"start\": [\"service-design-and-creation/models/model/" + modelInvariantId + "\"],\"query\": \"query/serviceModels-byDistributionStatus?distributionStatus=DISTRIBUTION_COMPLETE_OK\"}",false);
508 AaiResponse<ModelVersions> aaiResponse = processAaiResponse(response, ModelVersions.class, null, VidObjectMapperType.FASTERXML);
510 return toModelVerStream(aaiResponse.getT());
513 protected Stream<ModelVer> toModelVerStream(ModelVersions modelVersions) {
514 if (modelVersions == null)
517 return Stream.of(modelVersions)
518 .map(ModelVersions::getResults)
519 .flatMap(java.util.Collection::stream)
520 .flatMap(map -> map.entrySet().stream())
521 .filter(kv -> StringUtils.equals(kv.getKey(), "model-ver"))
522 .map(Map.Entry::getValue);
526 protected ModelVer maxModelVer(Stream<ModelVer> modelVerStream) {
527 if (modelVerStream == null)
530 return modelVerStream
531 .filter(modelVer -> StringUtils.isNotEmpty(modelVer.getModelVersion()))
532 .max(comparing(ModelVer::getModelVersion, comparing(DefaultArtifactVersion::new)))
533 .orElseThrow(() -> new GenericUncheckedException("Could not find any version"));
536 protected List<ModelVer> sortedModelVer(Stream<ModelVer> modelVerStream) {
537 if (modelVerStream == null)
540 return modelVerStream
541 .sorted(comparing(ModelVer::getModelVersion, comparing(DefaultArtifactVersion::new)).reversed())
542 .collect(Collectors.toList());
546 public AaiResponse<Services> getSubscriberData(String subscriberId, boolean omitServiceInstances) {
547 String depth = omitServiceInstances ? "1" : "2";
548 AaiResponse<Services> subscriberDataResponse;
549 Response resp = doAaiGet(BUSINESS_CUSTOMERS_CUSTOMER + subscriberId + "?depth=" + depth, false);
550 subscriberDataResponse = processAaiResponse(resp, Services.class, null);
551 return subscriberDataResponse;
555 public AaiResponse getServices() {
556 Response resp = doAaiGet("service-design-and-creation/services", false);
557 return processAaiResponse(resp, GetServicesAAIRespone.class, null);
561 public AaiResponse getOperationalEnvironments(String operationalEnvironmentType, String operationalEnvironmentStatus) {
562 String url = "cloud-infrastructure/operational-environments";
563 URIBuilder urlBuilder = new URIBuilder();
564 if (operationalEnvironmentType != null)
565 urlBuilder.addParameter("operational-environment-type", operationalEnvironmentType);
566 if (operationalEnvironmentStatus != null)
567 urlBuilder.addParameter("operational-environment-status", operationalEnvironmentStatus);
568 url += urlBuilder.toString();
569 Response resp = doAaiGet(url, false);
570 return processAaiResponse(resp, OperationalEnvironmentList.class, null);
574 public AaiResponse getTenants(String globalCustomerId, String serviceType) {
575 if ((globalCustomerId == null || globalCustomerId.isEmpty()) || ((serviceType == null) || (serviceType.isEmpty()))){
576 return buildAaiResponseForGetTenantsFailure(" Failed to retrieve LCP Region & Tenants from A&AI, Subscriber ID or Service Type is missing.");
580 .aaiClientCacheFor("getTenants", this::getTenantsByKey)
581 .get(CacheProvider.compileKey(globalCustomerId, serviceType));
583 catch (ParsingGetTenantsResponseFailure exception) {
584 logger.error("Failed to get tenants ", exception);
585 return buildAaiResponseForGetTenantsFailure(exception.getMessage());
590 public AaiResponse getNodeTemplateInstances(String globalCustomerId, String serviceType, String modelVersionId, String modelInvariantId, String cloudRegion) {
592 String siQuery = BUSINESS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + encodePathSegment(serviceType) + "/service-instances?model-version-id=" + modelVersionId + MODEL_INVARIANT_ID + modelInvariantId;
593 String vnfQuery = "query/queryvnfFromModelbyRegion?cloudRegionId=" + encodePathSegment(cloudRegion);
594 String payload1 = "{\"start\":\"" + siQuery + "\",\"query\":\"" + vnfQuery + "\"}";
596 Response resp1 = doAaiPut(QUERY_FORMAT_SIMPLE, payload1, false);
597 AaiResponse aaiResponse1 = processAaiResponse(resp1, AaiGetVnfResponse.class, null);
598 logger.debug(EELFLoggerDelegate.debugLogger, "getNodeTemplateInstances AAI's response: {}", aaiResponse1);
603 public AaiResponse<JsonNode> getCloudRegionAndSourceByPortMirroringConfigurationId(String configurationId) {
604 final String start = "[\"network/configurations/configuration/" + configurationId + "\"]";
605 final String query = "\"query/cloud-region-and-source-FromConfiguration\"";
606 String payload = "{\"start\":" + start + ",\"query\":" + query + "}";
608 Response response = doAaiPut("query?format=simple&nodesOnly=true", payload, false);
609 AaiResponse<JsonNode> aaiResponse = processAaiResponse(response, JsonNode.class, null);
611 logger.debug(EELFLoggerDelegate.debugLogger, "getNodeTemplateInstances AAI's response: {}", aaiResponse);
615 private <T> AaiResponseWithRequestInfo<T> processAaiResponse(ResponseWithRequestInfo responseWithRequestInfo, Class<? extends T> classType, boolean propagateExceptions) {
616 return processAaiResponse(responseWithRequestInfo, classType, VidObjectMapperType.CODEHAUS, propagateExceptions);
619 private <T> AaiResponseWithRequestInfo<T> processAaiResponse(ResponseWithRequestInfo responseWithRequestInfo, Class<? extends T> classType, VidObjectMapperType omType, boolean propagateExceptions) {
620 String responseBody = null;
621 Integer responseHttpCode = null;
623 Response response = responseWithRequestInfo.getResponse();
624 responseHttpCode = (response != null) ? response.getStatus() : null;
625 responseBody = (response != null) ? response.readEntity(String.class) : null;
626 AaiResponse<T> processedAaiResponse = processAaiResponse(response, classType, responseBody, omType, propagateExceptions);
627 return new AaiResponseWithRequestInfo<>(responseWithRequestInfo.getRequestHttpMethod(), responseWithRequestInfo.getRequestUrl(), processedAaiResponse,
629 } catch (Exception e) {
630 throw new ExceptionWithRequestInfo(responseWithRequestInfo.getRequestHttpMethod(),
631 responseWithRequestInfo.getRequestUrl(), responseBody, responseHttpCode, e);
635 private <T> AaiResponse<T> processAaiResponse(Response resp, Class<? extends T> classType, String responseBody) {
636 return processAaiResponse(resp, classType, responseBody, VidObjectMapperType.CODEHAUS);
639 private <T> AaiResponse<T> processAaiResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType) {
640 return processAaiResponse(resp, classType, responseBody, omType, false);
643 private <T> AaiResponse<T> processAaiResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType, boolean propagateExceptions) {
644 AaiResponse<T> subscriberDataResponse;
646 subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR);
647 logger.debug(EELFLoggerDelegate.debugLogger, "Invalid response from AAI");
649 logger.debug(EELFLoggerDelegate.debugLogger, "getSubscribers() resp=" + resp.getStatusInfo().toString());
650 if (resp.getStatus() != HttpStatus.SC_OK) {
651 subscriberDataResponse = processFailureResponse(resp,responseBody);
653 subscriberDataResponse = processOkResponse(resp, classType, responseBody, omType, propagateExceptions);
656 return subscriberDataResponse;
659 private AaiResponse processFailureResponse(Response resp, String responseBody) {
660 logger.debug(EELFLoggerDelegate.debugLogger, "Invalid response from AAI");
662 if (responseBody != null) {
663 rawData = responseBody;
665 rawData = resp.readEntity(String.class);
667 return new AaiResponse<>(null, rawData, resp.getStatus());
670 private <T> AaiResponse<T> processOkResponse(Response resp, Class<? extends T> classType, String responseBody, VidObjectMapperType omType, boolean propagateExceptions) {
671 AaiResponse<T> subscriberDataResponse;
672 String finalResponse = null;
674 if (responseBody != null) {
675 finalResponse = responseBody;
677 finalResponse = resp.readEntity(String.class);
680 if(omType == VidObjectMapperType.CODEHAUS)
681 subscriberDataResponse = parseCodeHausObject(classType, finalResponse);
683 subscriberDataResponse = parseFasterXmlObject(classType, finalResponse);
685 } catch(Exception e){
686 if (propagateExceptions) {
687 throw new GenericUncheckedException(e);
689 subscriberDataResponse = new AaiResponse<>(null, null, HttpStatus.SC_INTERNAL_SERVER_ERROR);
690 logger.error("Failed to parse aai response: \"{}\" to class {}", finalResponse, classType, e);
693 return subscriberDataResponse;
696 private <T> AaiResponse<T> parseFasterXmlObject(Class<? extends T> classType, String finalResponse) throws IOException {
697 return new AaiResponse<>((objectMapper.readValue(finalResponse, classType)), null, HttpStatus.SC_OK);
700 private <T> AaiResponse<T> parseCodeHausObject(Class<? extends T> classType, String finalResponse) throws IOException {
701 return new AaiResponse<>((objectMapper.readValue(finalResponse, classType)), null, HttpStatus.SC_OK);
705 public Response doAaiGet(String uri, boolean xml) {
706 return doAaiGet(uri, xml, false).getResponse();
710 public ResponseWithRequestInfo doAaiGet(String uri, boolean xml, boolean propagateExceptions) {
711 return doAaiGet(Unchecked.toURI(uri), xml, propagateExceptions);
714 public ResponseWithRequestInfo doAaiGet(URI uri, boolean xml, boolean propagateExceptions) {
715 String methodName = "doAaiGet";
716 logger.debug(EELFLoggerDelegate.debugLogger, methodName + " start");
718 ResponseWithRequestInfo resp;
720 resp = restController.RestGet(fromAppId, UUID.randomUUID().toString(), uri, xml, propagateExceptions);
722 } catch (Exception e) {
723 resp = handleExceptionFromRestCall(propagateExceptions, methodName, e);
729 protected ResponseWithRequestInfo handleExceptionFromRestCall(boolean propagateExceptions, String methodName, Exception e) {
730 ResponseWithRequestInfo resp;
731 if (propagateExceptions) {
732 throw (e instanceof RuntimeException) ? (RuntimeException)e : new GenericUncheckedException(e);
734 final Exception actual =
735 e instanceof ExceptionWithRequestInfo ? (Exception) e.getCause() : e;
737 final String message =
738 actual instanceof WebApplicationException ? ((WebApplicationException) actual).getResponse().readEntity(String.class) : e.toString();
740 //ToDo: change parameter of requestUrl to real url from doRest function
741 resp = new ResponseWithRequestInfo(null, null, org.springframework.http.HttpMethod.GET);
742 logger.info(EELFLoggerDelegate.errorLogger, methodName + message);
743 logger.debug(EELFLoggerDelegate.debugLogger, methodName + message);
748 private String parseForTenantsByServiceSubscription(String relatedToKey, String resp) {
749 String tenantList = "";
752 JSONParser jsonParser = new JSONParser();
754 JSONObject jsonObject = (JSONObject) jsonParser.parse(resp);
756 return parseServiceSubscriptionObjectForTenants(relatedToKey, jsonObject);
757 } catch (Exception ex) {
758 logger.debug(EELFLoggerDelegate.debugLogger, "parseForTenantsByServiceSubscription error while parsing tenants by service subscription", ex);
763 protected Response doAaiPut(String uri, String payload, boolean xml) {
764 return doAaiPut(uri, payload, xml, false).getResponse();
767 protected ResponseWithRequestInfo doAaiPut(String uri, String payload, boolean xml, boolean propagateExceptions) {
768 String methodName = "doAaiPut";
769 logger.debug(EELFLoggerDelegate.debugLogger, methodName + " start");
771 ResponseWithRequestInfo resp;
774 resp = restController.RestPut(fromAppId, uri, payload, xml, propagateExceptions);
776 } catch (Exception e) {
777 resp = handleExceptionFromRestCall(propagateExceptions, methodName, e);
783 private String parseServiceSubscriptionObjectForTenants(String relatedToKey, JSONObject jsonObject) {
784 JSONArray tenantArray = new JSONArray();
785 boolean bconvert = false;
787 JSONObject relationShipListsObj = (JSONObject) jsonObject.get("relationship-list");
788 if (relationShipListsObj != null) {
789 JSONArray rShipArray = (JSONArray) relationShipListsObj.get("relationship");
790 for (Object innerObj : defaultIfNull(rShipArray, emptyList())) {
791 if (innerObj != null) {
792 bconvert = parseTenant(relatedToKey, tenantArray, bconvert, (JSONObject) innerObj);
796 } catch (NullPointerException ex) {
797 logger.debug(EELFLoggerDelegate.debugLogger, "parseServiceSubscriptionObjectForTenants. error while parsing service subscription object for tenants", ex);
801 return tenantArray.toJSONString();
807 private static boolean parseTenant(String relatedToKey, JSONArray tenantArray, boolean bconvert, JSONObject inner1Obj) {
808 String relatedTo = checkForNull((String) inner1Obj.get("related-to"));
809 if (relatedTo.equalsIgnoreCase(relatedToKey)) {
810 JSONObject tenantNewObj = new JSONObject();
812 String relatedLink = checkForNull((String) inner1Obj.get("related-link"));
813 tenantNewObj.put("link", relatedLink);
815 JSONArray rDataArray = (JSONArray) inner1Obj.get("relationship-data");
816 for (Object innerObj : defaultIfNull(rDataArray, emptyList())) {
817 parseRelationShip(tenantNewObj, (JSONObject) innerObj);
820 JSONArray relatedTPropArray = (JSONArray) inner1Obj.get("related-to-property");
821 for (Object innerObj : defaultIfNull(relatedTPropArray, emptyList())) {
822 parseRelatedTProp(tenantNewObj, (JSONObject) innerObj);
825 tenantArray.add(tenantNewObj);
830 private static void parseRelatedTProp(JSONObject tenantNewObj, JSONObject innerObj) {
831 if (innerObj == null)
834 String propKey = checkForNull((String) innerObj.get("property-key"));
835 String propVal = checkForNull((String) innerObj.get("property-value"));
836 if (equalsIgnoreCase(propKey, "tenant.tenant-name")) {
837 tenantNewObj.put("tenantName", propVal);
841 private static void parseRelationShip(JSONObject tenantNewObj, JSONObject inner2Obj) {
842 if (inner2Obj == null)
845 String rShipKey = checkForNull((String) inner2Obj.get("relationship-key"));
846 String rShipVal = checkForNull((String) inner2Obj.get("relationship-value"));
847 if (equalsIgnoreCase(rShipKey, "cloud-region.cloud-owner")) {
848 tenantNewObj.put("cloudOwner", rShipVal);
849 } else if (equalsIgnoreCase(rShipKey, "cloud-region.cloud-region-id")) {
850 tenantNewObj.put("cloudRegionID", rShipVal);
851 } else if (equalsIgnoreCase(rShipKey, "tenant.tenant-id")) {
852 tenantNewObj.put("tenantID", rShipVal);
856 private static String encodePathSegment(String segmentToEncode) {
857 return UriUtils.encodePathSegment(segmentToEncode, "UTF-8");
861 public ExternalComponentStatus probeComponent(){
862 long startTime = System.currentTimeMillis();
864 AaiResponseWithRequestInfo<SubscriberList> responseWithRequestInfo = getAllSubscribers(true);
865 AaiResponse<SubscriberList> aaiResponse = responseWithRequestInfo.getAaiResponse();
866 long duration = System.currentTimeMillis() - startTime;
868 SubscriberList subscribersList = (aaiResponse != null) ? aaiResponse.getT() : null;
869 boolean isAvailable = subscribersList != null && subscribersList.customer != null && !subscribersList.customer.isEmpty();
871 HttpRequestMetadata metadata = new HttpRequestMetadata(
872 responseWithRequestInfo.getHttpMethod(),
873 (aaiResponse != null) ? aaiResponse.getHttpCode() : 0,
874 responseWithRequestInfo.getRequestedUrl(),
875 responseWithRequestInfo.getRawData(),
876 isAvailable ? "OK" : "No subscriber received",
879 return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, isAvailable, metadata);
881 } catch (ExceptionWithRequestInfo e) {
882 long duration = System.currentTimeMillis() - startTime;
883 return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, false,
884 new HttpRequestMetadata(e, duration));
885 } catch (Exception e) {
886 long duration = System.currentTimeMillis() - startTime;
887 return new ExternalComponentStatus(ExternalComponentStatus.Component.AAI, false,
888 new ErrorMetadata(Logging.exceptionToDescription(e), duration));
893 public String getCloudOwnerByCloudRegionId(String cloudRegionId) {
895 .aaiClientCacheFor("getCloudOwnerByCloudRegionId", this::getCloudOwnerByCloudRegionIdNonCached)
901 public GetTenantsResponse getHomingDataByVfModule(String vnfInstanceId, String vfModuleId) {
903 if (isEmpty(vnfInstanceId)|| isEmpty(vfModuleId)){
904 throw new GenericUncheckedException("Failed to retrieve homing data associated to vfModule from A&AI, VNF InstanceId or VF Module Id is missing.");
906 Response resp = doAaiGet("network/generic-vnfs/generic-vnf/" + vnfInstanceId +"/vf-modules/vf-module/"+ vfModuleId, false);
907 String responseAsString = parseForTenantsByServiceSubscription("vserver",resp.readEntity(String.class));
908 if (isEmpty(responseAsString)){
909 throw new GenericUncheckedException( String.format("A&AI has no homing data associated to vfModule '%s' of vnf '%s'", vfModuleId, vnfInstanceId));
912 AaiResponse aaiResponse = processAaiResponse(resp, GetTenantsResponse[].class, responseAsString);
913 return ((GetTenantsResponse[])aaiResponse.getT())[0];
918 public void resetCache(String cacheName) {
919 cacheProvider.resetCache(cacheName);
922 String getCloudOwnerByCloudRegionIdNonCached(String cloudRegionId) {
923 String uri = "cloud-infrastructure/cloud-regions?cloud-region-id=" + encodePathSegment(cloudRegionId);
925 final CloudRegion.Collection cloudRegionCollection =
926 typedAaiGet(Unchecked.toURI(uri), CloudRegion.Collection.class);
928 return cloudRegionCollection
929 .getCloudRegions().stream()
930 .map(CloudRegion::getCloudOwner)
931 // from here we assure that the cloud owner is given, and not null
932 // and non-empty, and that if more than one cloud-owner is given -
933 // it is only a single value.
934 // exception is thrown if none or more than a single values are
936 .filter(StringUtils::isNotEmpty)
939 // will be invoked only if distinct() leaves more than a single element
940 throw new GenericUncheckedException("Conflicting cloud-owner found for " + cloudRegionId + ": '" + a + "' / '" + b + "'");
942 .orElseThrow(() -> new GenericUncheckedException("No cloud-owner found for " + cloudRegionId));
945 private AaiResponse getTenantsByKey(String key) {
946 String[] args = CacheProvider.decompileKey(key);
947 String globalCustomerId = safeGetFromArray(args, 0);
948 String serviceType = safeGetFromArray(args, 1);
949 return getTenantsNonCached(globalCustomerId, serviceType);
952 AaiResponse getTenantsNonCached(String globalCustomerId, String serviceType) {
953 String url = BUSINESS_CUSTOMERS_CUSTOMER + globalCustomerId + SERVICE_SUBSCRIPTIONS_PATH + serviceType;
955 Response resp = doAaiGet(url, false);
956 String responseAsString = parseForTenantsByServiceSubscription("tenant",resp.readEntity(String.class));
957 if (isEmpty(responseAsString)){
958 throw new ParsingGetTenantsResponseFailure(String.format("A&AI has no LCP Region & Tenants associated to subscriber '%s' and service type '%s'", globalCustomerId, serviceType));
961 return processAaiResponse(resp, GetTenantsResponse[].class, responseAsString);
965 public static class ParsingGetTenantsResponseFailure extends GenericUncheckedException {
967 public ParsingGetTenantsResponseFailure(String message) {
973 private AaiResponse<String> buildAaiResponseForGetTenantsFailure(String errorText) {
974 return new AaiResponse<>(null, String.format("{\"statusText\":\"%s\"}", errorText), HttpStatus.SC_INTERNAL_SERVER_ERROR);
977 private static String safeGetFromArray(String[] array, int i) {
978 if (i < 0 || i >= array.length) {