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