e060882644fd40d1b7aae48d3a22b6700d7b018b
[vid.git] / vid-app-common / src / main / java / org / onap / vid / services / AAITreeNodeBuilder.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.apache.commons.lang3.StringUtils;
27 import org.jetbrains.annotations.NotNull;
28 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
29 import org.onap.vid.aai.AaiClientInterface;
30 import org.onap.vid.aai.ExceptionWithRequestInfo;
31 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Relationship;
32 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.RelationshipList;
33 import org.onap.vid.model.aaiTree.AAITreeNode;
34 import org.onap.vid.model.aaiTree.FailureAAITreeNode;
35 import org.onap.vid.utils.Streams;
36 import org.onap.vid.utils.Tree;
37 import org.onap.vid.utils.Unchecked;
38 import org.springframework.stereotype.Component;
39
40 import javax.inject.Inject;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.concurrent.ConcurrentLinkedQueue;
46 import java.util.concurrent.ConcurrentSkipListSet;
47 import java.util.concurrent.ExecutorService;
48 import java.util.concurrent.atomic.AtomicInteger;
49 import java.util.stream.Collectors;
50 import java.util.stream.Stream;
51
52 import static java.util.stream.Collectors.*;
53 import static org.onap.vid.utils.Streams.not;
54
55
56 @Component
57 public class AAITreeNodeBuilder {
58
59     private AaiClientInterface aaiClient;
60
61     private final ObjectMapper mapper = new ObjectMapper();
62
63     private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(AAITreeNodeBuilder.class);
64
65     //List of all the node types the tree should include
66     public static final String SERVICE_INSTANCE = "service-instance";
67     public static final String GENERIC_VNF = "generic-vnf";
68     public static final String NETWORK = "l3-network";
69     public static final String FAILURE = "failure_node";
70     public static final String COLLECTION_RESOURCE = "collection";
71     public static final String CONFIGURATION = "configuration";
72     public static final String PNF = "pnf";
73     public static final String VF_MODULE = "vf-module";
74     public static final String INSTANCE_GROUP = "instance-group";
75     public static final String PORT = "l-interface";
76     public static final String VG = "volume-group";
77     public static final String VLAN_TAG = "vlan-tag";
78
79     //Hashmap that defines the node-type and the tag that should be used to find it's ID key in the JSON.
80     private static HashMap<String, String> nodeTypeToIdKeyMap = generateTypeToIdKeyMap();
81
82     //Hashmap that defines the node-type and the tag that should be used to find it's NAMR key in the JSON.
83     private static HashMap<String, String> nodeTypeToNameKeyMap = generateTypeToNameKeyMap();
84
85     public enum AAIBaseProperties {
86         ORCHESTRATION_STATUS("orchestration-status"),
87         PROV_STATUS("prov-status"),
88         IN_MAINT("in-maint"),
89         MODEL_VERSION_ID("model-version-id"),
90         MODEL_CUSTOMIZATION_ID("model-customization-id"),
91         MODEL_INVARIANT_ID("model-invariant-id"),
92         RELATIONSHIP_LIST("relationship-list");
93
94         private final String aaiKey;
95
96         AAIBaseProperties(String aaiKey) {
97             this.aaiKey = aaiKey;
98         }
99
100         public String getAaiKey() {
101             return aaiKey;
102         }
103     }
104
105     public static List<AAIServiceTree.AaiRelationship> toAaiRelationshipList(String... types) {
106         return Stream.of(types).map(AAIServiceTree.AaiRelationship::new).collect(Collectors.toList());
107     }
108
109     @Inject
110     public AAITreeNodeBuilder(AaiClientInterface aaiClient) {
111         this.aaiClient = aaiClient;
112     }
113
114     public List<AAITreeNode> buildNode(String nodeType,
115                                  String requestURL,
116                                  ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, ExecutorService threadPool,
117                                  ConcurrentLinkedQueue<String> visitedNodes,
118                                  AtomicInteger nodesCounter,
119                                  Tree<AAIServiceTree.AaiRelationship> pathsTree) {
120
121         JsonNode topLevelJson = aaiClient.typedAaiGet(Unchecked.toURI(requestURL), JsonNode.class);
122
123         if (topLevelJson.has(nodeType) && topLevelJson.get(nodeType).isArray()) {
124             return Streams.fromIterable(topLevelJson.get(nodeType))
125                     .map(item -> parseNodeAndGetChildren(nodeType, requestURL, item,
126                             nodesAccumulator, threadPool, visitedNodes, nodesCounter, pathsTree))
127                     .collect(toList());
128         } else {
129             return ImmutableList.of(parseNodeAndGetChildren(nodeType, requestURL, topLevelJson,
130                     nodesAccumulator, threadPool, visitedNodes, nodesCounter, pathsTree));
131         }
132     }
133
134     private AAITreeNode parseNodeAndGetChildren(String nodeType,
135                                                 String requestURL,
136                                                 JsonNode topLevelJson,
137                                                 ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, ExecutorService threadPool,
138                                                 ConcurrentLinkedQueue<String> visitedNodes,
139                                                 AtomicInteger nodesCounter,
140                                                 Tree<AAIServiceTree.AaiRelationship> pathsTree) {
141         AAITreeNode node = jsonToAaiNode(nodeType, topLevelJson, nodesAccumulator, nodesCounter);
142
143         RelationshipList relationships = mapper.convertValue(topLevelJson.get(AAIBaseProperties.RELATIONSHIP_LIST.getAaiKey()), RelationshipList.class);
144         if (relationships != null) {
145             getChildren(threadPool, nodesAccumulator, relationships.getRelationship(), visitedNodes, node, nodesCounter, pathsTree);
146         }
147         if (StringUtils.equals(node.getType(), GENERIC_VNF)) {
148             getRelatedVfModules(threadPool, nodesAccumulator, requestURL, node, nodesCounter);
149         }
150         return node;
151     }
152
153     private AAITreeNode jsonToAaiNode(String nodeType, JsonNode topLevelJson, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, AtomicInteger nodesCounter) {
154         AAITreeNode node = fillNodeMetaData(nodeType, topLevelJson, nodesCounter);
155
156         nodesAccumulator.add(node);
157
158         return node;
159     }
160
161     private void getRelatedVfModules(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, String parentURL, AAITreeNode parentNode, AtomicInteger nodesCounter) {
162         /*
163         VNFs do not report their direct related-to vf-modules, so try
164         directly fetching a resource URI.
165          */
166
167         threadPool.execute(() -> {
168             // the response is an array of vf-modules
169             final JsonNode topLevelJson;
170             try {
171                 topLevelJson = aaiClient.typedAaiGet(Unchecked.toURI(parentURL + "/vf-modules"), JsonNode.class);
172             } catch (ExceptionWithRequestInfo e) {
173                 if (e.getHttpCode().equals(404)) {
174                     // it's ok, as we're just optimistically fetching
175                     // the /vf-modules uri; 404 says this time it was
176                     // a bad guess
177                     return;
178                 } else {
179                     throw e;
180                 }
181             }
182
183             if (topLevelJson != null) {
184                 parentNode.getChildren().addAll(
185                         Streams.fromIterable(topLevelJson.get(VF_MODULE))
186                                 .map(vfModuleNode -> jsonToAaiNode(VF_MODULE, vfModuleNode, nodesAccumulator, nodesCounter))
187                                 .collect(toList())
188                 );
189             } else {
190                 LOGGER.error(EELFLoggerDelegate.errorLogger, "Failed to get vf-modules for vnf " + parentNode.getId());
191             }
192         });
193     }
194
195     private void getChildren(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator,
196                              List<Relationship> relationships, ConcurrentLinkedQueue<String> visitedNodes, AAITreeNode parent, AtomicInteger nodesCounter, Tree<AAIServiceTree.AaiRelationship> pathsTree) {
197         for (Relationship relationship : relationships) {
198             createChildNode(threadPool, nodesAccumulator, relationship, visitedNodes, parent, nodesCounter, pathsTree);
199         }
200     }
201
202     private void createChildNode(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator,
203                                  Relationship relationship, ConcurrentLinkedQueue<String> visitedNodes, AAITreeNode parent, AtomicInteger nodesCounter, Tree<AAIServiceTree.AaiRelationship> pathsTree) {
204         String newNodeType = relationship.getRelatedTo();
205         Tree<AAIServiceTree.AaiRelationship> subTree = pathsTree.getSubTree(new AAIServiceTree.AaiRelationship(newNodeType));
206         if (subTree!=null) {
207             String newNodeUrl = relationship.getRelatedLink();
208             if (!visitedNodes.contains(newNodeUrl)) {
209                 visitedNodes.add(newNodeUrl);
210                 threadPool.execute(() -> {
211                             try {
212                                 parent.addChildren(buildNode(newNodeType, newNodeUrl, nodesAccumulator, threadPool, visitedNodes, nodesCounter,  subTree));
213                             } catch (Exception e) {
214                                 parent.getChildren().add(createFailureNode(e));
215                             }
216                         }
217                 );
218             }
219         }
220     }
221
222     private AAITreeNode fillNodeMetaData(String nodeType, JsonNode model, @NotNull AtomicInteger nodesCounter) {
223         AAITreeNode node = new AAITreeNode();
224         node.setType(nodeType);
225         node.setUniqueNumber(nodesCounter.getAndIncrement());
226         node.setOrchestrationStatus(getStringDataFromJsonIfExists(model, AAIBaseProperties.ORCHESTRATION_STATUS.getAaiKey()));
227         node.setProvStatus(getStringDataFromJsonIfExists(model, AAIBaseProperties.PROV_STATUS.getAaiKey()));
228         node.setInMaint(getBooleanDataFromJsonIfExists(model, AAIBaseProperties.IN_MAINT.getAaiKey()));
229         node.setModelVersionId(getStringDataFromJsonIfExists(model, AAIBaseProperties.MODEL_VERSION_ID.getAaiKey()));
230         node.setModelCustomizationId(getStringDataFromJsonIfExists(model, AAIBaseProperties.MODEL_CUSTOMIZATION_ID.getAaiKey()));
231         node.setModelInvariantId(getStringDataFromJsonIfExists(model, AAIBaseProperties.MODEL_INVARIANT_ID.getAaiKey()));
232         node.setId(getStringDataFromJsonIfExists(model, nodeTypeToIdKeyMap.get(nodeType)));
233         node.setName(getStringDataFromJsonIfExists(model, nodeTypeToNameKeyMap.get(nodeType)));
234         node.setAdditionalProperties(aggregateAllOtherProperties(model, nodeType));
235
236         return node;
237     }
238
239     private AAITreeNode createFailureNode(Exception exception) {
240         return FailureAAITreeNode.of(exception);
241     }
242
243     private String getStringDataFromJsonIfExists(JsonNode model, String key) {
244         if (model.has(key)) {
245             return model.get(key).asText();
246         }
247         return null;
248     }
249
250     private Boolean getBooleanDataFromJsonIfExists(JsonNode model, String key) {
251         if (model.has(key)) {
252             return model.get(key).asBoolean();
253         }
254         return false;
255     }
256
257     private Map<String, Object> aggregateAllOtherProperties(JsonNode model, String nodeType) {
258         Set<String> ignoreProperties = Stream.of(AAIBaseProperties.values())
259                 .map(AAIBaseProperties::getAaiKey).collect(toSet());
260
261         return Streams.fromIterator(model.fields())
262                 .filter(not(field -> StringUtils.equals(field.getKey(), nodeTypeToIdKeyMap.get(nodeType))))
263                 .filter(not(field -> StringUtils.equals(field.getKey(), nodeTypeToNameKeyMap.get(nodeType))))
264                 .filter(not(field -> ignoreProperties.contains(field.getKey())))
265                 .collect(toMap(Map.Entry::getKey, v -> v.getValue().asText()));
266     }
267
268     private static HashMap<String, String> generateTypeToIdKeyMap() {
269         HashMap<String, String> result = new HashMap<>();
270         result.put(SERVICE_INSTANCE, "service-instance-id");
271         result.put(GENERIC_VNF, "vnf-id");
272         result.put(NETWORK, "network-id");
273         result.put(COLLECTION_RESOURCE, "collection-id");
274         result.put(CONFIGURATION, "configuration-id");
275         result.put(PNF, "pnf-id");
276         result.put(VF_MODULE, "vf-module-id");
277         result.put(INSTANCE_GROUP, "id");
278         result.put(PORT, "l-interface-id");
279         result.put(VG, "volume-group-id");
280         result.put(VLAN_TAG, "vlan-id");
281
282         return result;
283     }
284
285     private static HashMap<String, String> generateTypeToNameKeyMap() {
286         HashMap<String, String> result = new HashMap<>();
287         result.put(SERVICE_INSTANCE, "service-instance-name");
288         result.put(GENERIC_VNF, "vnf-name");
289         result.put(NETWORK, "network-name");
290         result.put(COLLECTION_RESOURCE, "collection-name");
291         result.put(CONFIGURATION, "configuration-name");
292         result.put(PNF, "pnf-name");
293         result.put(VF_MODULE, "vf-module-name");
294         result.put(INSTANCE_GROUP, "instance-group-name");
295         result.put(PORT, "l-interface-name");
296         result.put(VG, "volume-group-name");
297         result.put(VLAN_TAG, "vlan-name");
298
299         return result;
300     }
301 }