2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.vid.services;
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;
40 import javax.inject.Inject;
41 import java.util.HashMap;
42 import java.util.List;
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;
52 import static java.util.stream.Collectors.*;
53 import static org.onap.vid.utils.Streams.not;
57 public class AAITreeNodeBuilder {
59 private AaiClientInterface aaiClient;
61 private final ObjectMapper mapper = new ObjectMapper();
63 private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(AAITreeNodeBuilder.class);
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";
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();
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();
85 public enum AAIBaseProperties {
86 ORCHESTRATION_STATUS("orchestration-status"),
87 PROV_STATUS("prov-status"),
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");
94 private final String aaiKey;
96 AAIBaseProperties(String aaiKey) {
100 public String getAaiKey() {
105 public static List<AAIServiceTree.AaiRelationship> toAaiRelationshipList(String... types) {
106 return Stream.of(types).map(AAIServiceTree.AaiRelationship::new).collect(Collectors.toList());
110 public AAITreeNodeBuilder(AaiClientInterface aaiClient) {
111 this.aaiClient = aaiClient;
114 public List<AAITreeNode> buildNode(String nodeType,
116 ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, ExecutorService threadPool,
117 ConcurrentLinkedQueue<String> visitedNodes,
118 AtomicInteger nodesCounter,
119 Tree<AAIServiceTree.AaiRelationship> pathsTree) {
121 JsonNode topLevelJson = aaiClient.typedAaiGet(Unchecked.toURI(requestURL), JsonNode.class);
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))
129 return ImmutableList.of(parseNodeAndGetChildren(nodeType, requestURL, topLevelJson,
130 nodesAccumulator, threadPool, visitedNodes, nodesCounter, pathsTree));
134 private AAITreeNode parseNodeAndGetChildren(String nodeType,
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);
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);
147 if (StringUtils.equals(node.getType(), GENERIC_VNF)) {
148 getRelatedVfModules(threadPool, nodesAccumulator, requestURL, node, nodesCounter);
153 private AAITreeNode jsonToAaiNode(String nodeType, JsonNode topLevelJson, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, AtomicInteger nodesCounter) {
154 AAITreeNode node = fillNodeMetaData(nodeType, topLevelJson, nodesCounter);
156 nodesAccumulator.add(node);
161 private void getRelatedVfModules(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, String parentURL, AAITreeNode parentNode, AtomicInteger nodesCounter) {
163 VNFs do not report their direct related-to vf-modules, so try
164 directly fetching a resource URI.
167 threadPool.execute(() -> {
168 // the response is an array of vf-modules
169 final JsonNode topLevelJson;
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
183 if (topLevelJson != null) {
184 parentNode.getChildren().addAll(
185 Streams.fromIterable(topLevelJson.get(VF_MODULE))
186 .map(vfModuleNode -> jsonToAaiNode(VF_MODULE, vfModuleNode, nodesAccumulator, nodesCounter))
190 LOGGER.error(EELFLoggerDelegate.errorLogger, "Failed to get vf-modules for vnf " + parentNode.getId());
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);
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));
207 String newNodeUrl = relationship.getRelatedLink();
208 if (!visitedNodes.contains(newNodeUrl)) {
209 visitedNodes.add(newNodeUrl);
210 threadPool.execute(() -> {
212 parent.addChildren(buildNode(newNodeType, newNodeUrl, nodesAccumulator, threadPool, visitedNodes, nodesCounter, subTree));
213 } catch (Exception e) {
214 parent.getChildren().add(createFailureNode(e));
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));
239 private AAITreeNode createFailureNode(Exception exception) {
240 return FailureAAITreeNode.of(exception);
243 private String getStringDataFromJsonIfExists(JsonNode model, String key) {
244 if (model.has(key)) {
245 return model.get(key).asText();
250 private Boolean getBooleanDataFromJsonIfExists(JsonNode model, String key) {
251 if (model.has(key)) {
252 return model.get(key).asBoolean();
257 private Map<String, Object> aggregateAllOtherProperties(JsonNode model, String nodeType) {
258 Set<String> ignoreProperties = Stream.of(AAIBaseProperties.values())
259 .map(AAIBaseProperties::getAaiKey).collect(toSet());
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()));
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");
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");