1 package org.onap.vid.services;
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;
20 import javax.inject.Inject;
21 import java.util.HashMap;
22 import java.util.List;
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;
32 import static java.util.stream.Collectors.*;
33 import static org.onap.vid.utils.Streams.not;
37 public class AAITreeNodeBuilder {
39 private AaiClientInterface aaiClient;
41 private final ObjectMapper mapper = new ObjectMapper();
43 private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(AAITreeNodeBuilder.class);
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";
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();
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();
65 public enum AAIBaseProperties {
66 ORCHESTRATION_STATUS("orchestration-status"),
67 PROV_STATUS("prov-status"),
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");
74 private final String aaiKey;
76 AAIBaseProperties(String aaiKey) {
80 public String getAaiKey() {
85 public static List<AAIServiceTree.AaiRelationship> toAaiRelationshipList(String... types) {
86 return Stream.of(types).map(AAIServiceTree.AaiRelationship::new).collect(Collectors.toList());
90 public AAITreeNodeBuilder(AaiClientInterface aaiClient) {
91 this.aaiClient = aaiClient;
94 public List<AAITreeNode> buildNode(String nodeType,
96 ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, ExecutorService threadPool,
97 ConcurrentLinkedQueue<String> visitedNodes,
98 AtomicInteger nodesCounter,
99 Tree<AAIServiceTree.AaiRelationship> pathsTree) {
101 JsonNode topLevelJson = aaiClient.typedAaiGet(Unchecked.toURI(requestURL), JsonNode.class);
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))
109 return ImmutableList.of(parseNodeAndGetChildren(nodeType, requestURL, topLevelJson,
110 nodesAccumulator, threadPool, visitedNodes, nodesCounter, pathsTree));
114 private AAITreeNode parseNodeAndGetChildren(String nodeType,
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);
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);
127 if (StringUtils.equals(node.getType(), GENERIC_VNF)) {
128 getRelatedVfModules(threadPool, nodesAccumulator, requestURL, node, nodesCounter);
133 private AAITreeNode jsonToAaiNode(String nodeType, JsonNode topLevelJson, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, AtomicInteger nodesCounter) {
134 AAITreeNode node = fillNodeMetaData(nodeType, topLevelJson, nodesCounter);
136 nodesAccumulator.add(node);
141 private void getRelatedVfModules(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, String parentURL, AAITreeNode parentNode, AtomicInteger nodesCounter) {
143 VNFs do not report their direct related-to vf-modules, so try
144 directly fetching a resource URI.
147 threadPool.execute(() -> {
148 // the response is an array of vf-modules
149 final JsonNode topLevelJson;
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
163 if (topLevelJson != null) {
164 parentNode.getChildren().addAll(
165 Streams.fromIterable(topLevelJson.get(VF_MODULE))
166 .map(vfModuleNode -> jsonToAaiNode(VF_MODULE, vfModuleNode, nodesAccumulator, nodesCounter))
170 LOGGER.error(EELFLoggerDelegate.errorLogger, "Failed to get vf-modules for vnf " + parentNode.getId());
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);
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));
187 String newNodeUrl = relationship.getRelatedLink();
188 if (!visitedNodes.contains(newNodeUrl)) {
189 visitedNodes.add(newNodeUrl);
190 threadPool.execute(() -> {
192 parent.addChildren(buildNode(newNodeType, newNodeUrl, nodesAccumulator, threadPool, visitedNodes, nodesCounter, subTree));
193 } catch (Exception e) {
194 parent.getChildren().add(createFailureNode(e));
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));
219 private AAITreeNode createFailureNode(Exception exception) {
220 return FailureAAITreeNode.of(exception);
223 private String getStringDataFromJsonIfExists(JsonNode model, String key) {
224 if (model.has(key)) {
225 return model.get(key).asText();
230 private Boolean getBooleanDataFromJsonIfExists(JsonNode model, String key) {
231 if (model.has(key)) {
232 return model.get(key).asBoolean();
237 private Map<String, Object> aggregateAllOtherProperties(JsonNode model, String nodeType) {
238 Set<String> ignoreProperties = Stream.of(AAIBaseProperties.values())
239 .map(AAIBaseProperties::getAaiKey).collect(toSet());
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()));
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");
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");