6199c4e58ab4c6897d4d34d8d5e6d6b6a966ce4f
[vid.git] / vid-app-common / src / main / java / org / onap / vid / services / AAIServiceTree.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.services;
22
23 import com.fasterxml.jackson.databind.JsonNode;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.google.common.collect.ImmutableList;
26 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
27 import org.onap.vid.aai.AaiClientInterface;
28 import org.onap.vid.aai.util.AAITreeConverter;
29 import org.onap.vid.asdc.AsdcCatalogException;
30 import org.onap.vid.asdc.parser.ServiceModelInflator;
31 import org.onap.vid.exceptions.GenericUncheckedException;
32 import org.onap.vid.model.ServiceModel;
33 import org.onap.vid.model.aaiTree.AAITreeNode;
34 import org.onap.vid.model.aaiTree.ServiceInstance;
35 import org.onap.vid.utils.Tree;
36 import org.springframework.stereotype.Component;
37
38 import javax.inject.Inject;
39 import javax.ws.rs.core.Response;
40 import java.util.*;
41 import java.util.concurrent.*;
42 import java.util.concurrent.atomic.AtomicInteger;
43
44 import static java.lang.Thread.sleep;
45 import static java.util.Comparator.comparing;
46 import static java.util.stream.Collectors.toSet;
47 import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
48 import static org.onap.vid.services.AAITreeNodeBuilder.*;
49
50 @Component
51 public class AAIServiceTree {
52
53     private final AAITreeNodeBuilder aaiTreeNodeBuilder;
54
55     private final AAITreeConverter aaiTreeConverter;
56
57     private final AaiClientInterface aaiClient;
58
59     private final VidService sdcService;
60
61     private final ServiceModelInflator serviceModelInflator;
62
63     private final ObjectMapper mapper = new ObjectMapper();
64
65     private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(AAIServiceTree.class);
66
67     public static final Tree<AaiRelationship> AAI_TREE_PATHS =
68             new Tree<>(new AaiRelationship(SERVICE_INSTANCE));
69
70     static {
71         AAI_TREE_PATHS.addPath(toAaiRelationshipList(GENERIC_VNF, VG));
72         AAI_TREE_PATHS.addPath(toAaiRelationshipList(NETWORK));
73         AAI_TREE_PATHS.addPath(toAaiRelationshipList(GENERIC_VNF, NETWORK));
74         AAI_TREE_PATHS.addPath(toAaiRelationshipList(INSTANCE_GROUP));
75     }
76
77     @Inject
78     public AAIServiceTree(AaiClientInterface aaiClient, AAITreeNodeBuilder aaiTreeNodeBuilder,
79                           AAITreeConverter aaiTreeConverter, VidService sdcService,
80                           ServiceModelInflator serviceModelInflator) {
81         this.aaiClient = aaiClient;
82         this.aaiTreeNodeBuilder = aaiTreeNodeBuilder;
83         this.aaiTreeConverter = aaiTreeConverter;
84         this.sdcService = sdcService;
85         this.serviceModelInflator = serviceModelInflator;
86     }
87
88     public List<AAITreeNode> buildAAITree(String getUrl, Tree<AaiRelationship> pathsToSearch) {
89
90         ConcurrentSkipListSet<AAITreeNode> nodesAccumulator = createNodesAccumulator();
91
92         List<AAITreeNode> aaiTreeNodes = fetchAAITree(getUrl, pathsToSearch, nodesAccumulator, true);
93
94         enrichNodesWithModelVersionAndModelName(nodesAccumulator);
95
96         return aaiTreeNodes;
97     }
98
99     public ServiceInstance getServiceInstanceTopology(String globalCustomerId, String serviceType, String serviceInstanceId) {
100
101         String getURL = "business/customers/customer/" +
102                 globalCustomerId + "/service-subscriptions/service-subscription/" +
103                 serviceType + "/service-instances/service-instance/" + serviceInstanceId;
104
105         //Used later to get the nodes UUID
106         ConcurrentSkipListSet<AAITreeNode> nodesAccumulator = createNodesAccumulator();
107
108         AAITreeNode aaiTree = fetchAAITree(getURL, AAI_TREE_PATHS, nodesAccumulator, false).get(0);
109
110         //Populate nodes with model-name & model-version (from aai)
111         enrichNodesWithModelVersionAndModelName(nodesAccumulator);
112
113         final ServiceModel serviceModel = getServiceModel(aaiTree.getModelVersionId());
114
115         //Populate nodes with model-customization-name (from sdc model)
116         enrichNodesWithModelCustomizationName(nodesAccumulator, serviceModel);
117
118         return aaiTreeConverter.convertTreeToUIModel(aaiTree, globalCustomerId, serviceType, getInstantiationType(serviceModel));
119     }
120
121     private List<AAITreeNode> fetchAAITree(String getUrl, Tree<AaiRelationship> pathsToSearch,
122                                            ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, boolean partialTreeOnTimeout) {
123         ThreadPoolExecutor threadPool = getThreadPool();
124
125         List<AAITreeNode> aaiTree =  aaiTreeNodeBuilder.buildNode(SERVICE_INSTANCE,
126                 getUrl, defaultIfNull(nodesAccumulator, createNodesAccumulator()),
127                 threadPool, new ConcurrentLinkedQueue<>(),
128                 new AtomicInteger(0), pathsToSearch);
129
130         boolean timeoutOccurred = waitForTreeFetch(threadPool);
131
132         if (timeoutOccurred) {
133             if (!partialTreeOnTimeout) {
134                 throw new GenericUncheckedException("Timeout on fetchAAITree. Fetched " + nodesAccumulator.size() + " nodes for url: " + getUrl);
135             }
136             LOGGER.warn(EELFLoggerDelegate.errorLogger, "Timeout on fetchAAITree for url: " + getUrl);
137         }
138
139         return aaiTree;
140     }
141
142     private ConcurrentSkipListSet<AAITreeNode> createNodesAccumulator() {
143         return new ConcurrentSkipListSet<>(comparing(AAITreeNode::getUniqueNodeKey));
144     }
145
146     private String getInstantiationType(ServiceModel serviceModel) {
147         if (serviceModel.getService() != null && serviceModel.getService().getInstantiationType() != null) {
148             return serviceModel.getService().getInstantiationType();
149         } else {
150             return null;
151         }
152     }
153
154     private ServiceModel getServiceModel(String modelVersionId) {
155         try {
156             final ServiceModel serviceModel = sdcService.getService(modelVersionId);
157             if (serviceModel == null) {
158                 throw new GenericUncheckedException("Model version '" + modelVersionId + "' not found");
159             }
160             return serviceModel;
161         } catch (AsdcCatalogException e) {
162             throw new GenericUncheckedException("Exception while loading model version '" + modelVersionId + "'", e);
163         }
164
165     }
166
167     void enrichNodesWithModelCustomizationName(Collection<AAITreeNode> nodes, ServiceModel serviceModel) {
168         final Map<String, ServiceModelInflator.Names> customizationNameByVersionId = serviceModelInflator.toNamesByVersionId(serviceModel);
169
170         nodes.forEach(node -> {
171             final ServiceModelInflator.Names names = customizationNameByVersionId.get(node.getModelVersionId());
172             if (names != null) {
173                 node.setKeyInModel(names.getModelKey());
174                 node.setModelCustomizationName(names.getModelCustomizationName());
175             }
176         });
177     }
178
179
180     private void enrichNodesWithModelVersionAndModelName(Collection<AAITreeNode> nodes) {
181
182         Collection<String> invariantIDs = getModelInvariantIds(nodes);
183
184         Map<String, String> modelVersionByModelVersionId = new HashMap<>();
185         Map<String, String> modelNameByModelVersionId = new HashMap<>();
186
187         JsonNode models = getModels(aaiClient, invariantIDs);
188         for (JsonNode model: models) {
189             JsonNode modelVersions = model.get("model-vers").get("model-ver");
190             for (JsonNode modelVersion: modelVersions) {
191                 final String modelVersionId = modelVersion.get("model-version-id").asText();
192                 modelVersionByModelVersionId.put(modelVersionId, modelVersion.get("model-version").asText());
193                 modelNameByModelVersionId.put(modelVersionId, modelVersion.get("model-name").asText());
194             }
195         }
196
197         nodes.forEach(node -> {
198             node.setModelVersion(modelVersionByModelVersionId.get(node.getModelVersionId()));
199             node.setModelName(modelNameByModelVersionId.get(node.getModelVersionId()));
200         });
201
202     }
203
204     private JsonNode getModels(AaiClientInterface aaiClient, Collection<String> invariantIDs) {
205         Response response = aaiClient.getVersionByInvariantId(ImmutableList.copyOf(invariantIDs));
206         try {
207             JsonNode responseJson = mapper.readTree(response.readEntity(String.class));
208             return responseJson.get("model");
209         } catch (Exception e) {
210             LOGGER.error(EELFLoggerDelegate.errorLogger, "Failed to getVersionByInvariantId from A&AI", e);
211         }
212         return mapper.createObjectNode();
213     }
214
215     private Set<String> getModelInvariantIds(Collection<AAITreeNode> nodes) {
216         return nodes.stream()
217                 .map(AAITreeNode::getModelInvariantId)
218                 .filter(Objects::nonNull)
219                 .collect(toSet());
220     }
221
222     private boolean waitForTreeFetch(ThreadPoolExecutor threadPool) {
223         int timer = 60;
224         try {
225             //Stop fetching information if it takes more than 1 minute
226             while (threadPool.getActiveCount() != 0 &&
227                     timer > 0) {
228                 sleep(1000);
229                 timer--;
230             }
231         } catch (InterruptedException e) {
232             Thread.currentThread().interrupt();
233             throw new GenericUncheckedException(e);
234         }
235         threadPool.shutdown();
236         return (timer == 0);
237     }
238
239     private ThreadPoolExecutor getThreadPool() {
240         //Use at least one thread, and never more than 75% of the available thread.
241         int cores = Math.max((int)(Runtime.getRuntime().availableProcessors() * 0.75), 1);
242         BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
243         return new ThreadPoolExecutor(1, cores, 10, TimeUnit.SECONDS, queue);
244     }
245
246     public static class AaiRelationship {
247
248         public final String type;
249
250         public AaiRelationship(String type) {
251             this.type = type;
252         }
253
254         @Override
255         public boolean equals(Object o) {
256             if (this == o) return true;
257             if (!(o instanceof AaiRelationship)) return false;
258             AaiRelationship that = (AaiRelationship) o;
259             return Objects.equals(type, that.type);
260         }
261
262         @Override
263         public int hashCode() {
264             return Objects.hash(type);
265         }
266     }
267 }