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