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.google.common.collect.ImmutableList;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.commons.lang3.tuple.ImmutablePair;
27 import org.apache.commons.lang3.tuple.Pair;
28 import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate;
29 import org.onap.portalsdk.core.util.SystemProperties;
30 import org.onap.vid.aai.AaiClientInterface;
31 import org.onap.vid.aai.ExceptionWithRequestInfo;
32 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Relationship;
33 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.RelationshipData;
34 import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.RelationshipList;
35 import org.onap.vid.aai.util.AAITreeNodeUtils;
36 import org.onap.vid.exceptions.GenericUncheckedException;
37 import org.onap.vid.model.aaiTree.AAITreeNode;
38 import org.onap.vid.model.aaiTree.FailureAAITreeNode;
39 import org.onap.vid.model.aaiTree.NodeType;
40 import org.onap.vid.mso.model.CloudConfiguration;
41 import org.onap.vid.properties.VidProperties;
42 import org.onap.vid.utils.Streams;
43 import org.onap.vid.utils.Tree;
44 import org.onap.vid.utils.Unchecked;
45 import org.springframework.http.HttpMethod;
46 import org.springframework.stereotype.Component;
48 import javax.inject.Inject;
50 import java.util.concurrent.*;
51 import java.util.stream.Collectors;
52 import java.util.stream.Stream;
54 import static java.util.stream.Collectors.*;
55 import static org.onap.vid.utils.KotlinUtilsKt.JACKSON_OBJECT_MAPPER;
56 import static org.onap.vid.utils.Streams.not;
60 public class AAITreeNodeBuilder {
62 private static final String RESULTS = "results";
63 private AaiClientInterface aaiClient;
65 private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(AAITreeNodeBuilder.class);
68 public enum AAIBaseProperties {
69 ORCHESTRATION_STATUS("orchestration-status"),
70 PROV_STATUS("prov-status"),
72 MODEL_VERSION_ID("model-version-id"),
73 MODEL_CUSTOMIZATION_ID("model-customization-id"),
74 MODEL_INVARIANT_ID("model-invariant-id"),
75 RELATIONSHIP_LIST("relationship-list");
77 private final String aaiKey;
79 AAIBaseProperties(String aaiKey) {
83 public String getAaiKey() {
89 public AAITreeNodeBuilder(AaiClientInterface aaiClient) {
90 this.aaiClient = aaiClient;
93 List<AAITreeNode> buildNode(NodeType nodeType,
97 ConcurrentSkipListSet<AAITreeNode> nodesAccumulator,
98 ExecutorService threadPool,
99 Tree<AAIServiceTree.AaiRelationship> pathsTree) {
101 JsonNode jsonNode = aaiClient.typedAaiRest(Unchecked.toURI(requestURL), JsonNode.class, payload, method, false);
103 List<Pair<AAITreeNode, List<Relationship>>> nodes = getNodesWithRelationships(jsonNode, nodeType, nodesAccumulator, pathsTree);
105 String timeout = SystemProperties.getProperty(VidProperties.VID_THREAD_TIMEOUT);
106 long timeoutNum = Long.parseLong(StringUtils.defaultIfEmpty(timeout, "30"));
108 for (Pair<AAITreeNode, List<Relationship>> entry : nodes) {
109 fetchChildrenAsync(threadPool, nodesAccumulator, entry.getKey(), entry.getValue(), pathsTree, timeoutNum);
111 if (getNextLevelInPathsTree(pathsTree, NodeType.VF_MODULE.getType()) != null) {
112 getRelatedVfModules(threadPool, nodesAccumulator, requestURL, entry.getKey());
116 return nodes.stream()
118 .collect(Collectors.toList());
121 private List<Pair<AAITreeNode, List<Relationship>>> getNodesWithRelationships(JsonNode jsonNode, NodeType nodeType,
122 ConcurrentSkipListSet<AAITreeNode> nodesAccumulator,
123 Tree<AAIServiceTree.AaiRelationship> pathsTree) {
124 if (isListOfKeyResults(jsonNode)) {
125 return Streams.fromIterable(jsonNode.get(RESULTS))
126 .filter(item -> item.has(nodeType.getType()))
127 .map(item -> item.get(nodeType.getType()))
128 .map(item -> parseNodeAndFilterRelationships(item, nodeType, nodesAccumulator, pathsTree))
129 .collect(Collectors.toList());
130 } else if (isArray(jsonNode, nodeType)) {
131 return Streams.fromIterable(jsonNode.get(nodeType.getType()))
132 .map(item -> parseNodeAndFilterRelationships(item, nodeType, nodesAccumulator, pathsTree))
133 .collect(Collectors.toList());
135 return ImmutableList.of(parseNodeAndFilterRelationships(jsonNode, nodeType, nodesAccumulator, pathsTree));
139 Pair<AAITreeNode, List<Relationship>> parseNodeAndFilterRelationships(JsonNode jsonNode, NodeType nodeType,
140 ConcurrentSkipListSet<AAITreeNode> nodesAccumulator,
141 Tree<AAIServiceTree.AaiRelationship> pathsTree) {
142 AAITreeNode node = createAaiNode(nodeType, jsonNode, nodesAccumulator);
144 enrichPlacementData(node);
146 List<Relationship> filteredRelationships = getFilteredRelationships(jsonNode, pathsTree);
148 return ImmutablePair.of(node, filteredRelationships);
151 boolean isArray(JsonNode json, NodeType nodeType) {
152 return json != null && json.has(nodeType.getType()) && json.get(nodeType.getType()).isArray();
155 boolean isListOfKeyResults(JsonNode jsonNode) {
156 return jsonNode != null && jsonNode.has(RESULTS) && jsonNode.get(RESULTS).isArray();
159 AAITreeNode createAaiNode(NodeType nodeType, JsonNode jsonNode, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator) {
160 AAITreeNode node = jsonNodeToAaiNode(nodeType, jsonNode);
162 nodesAccumulator.add(node);
167 private void addChildren(AAITreeNode node, Future<List<AAITreeNode>> children) {
169 node.addChildren(children.get());
170 } catch (Exception e) {
171 node.getChildren().add(createFailureNode(e));
175 private Map<String,String> convertRelationshipDataToMap(List<RelationshipData> relationshipData) {
176 return relationshipData.stream().collect(
177 Collectors.toMap(RelationshipData::getKey, RelationshipData::getValue));
180 void enrichPlacementData(AAITreeNode node){
181 Optional<Relationship> tenantRelationShip = AAITreeNodeUtils.findFirstRelationshipByRelatedTo(node.getRelationshipList(), "tenant");
182 enrichPlacementDataUsingTenantInfo(node, tenantRelationShip);
185 void enrichPlacementDataUsingTenantInfo(AAITreeNode node, Optional<Relationship> tenantRelationShip) {
186 //no tenant relationship in this node - so no placement data
187 if (!tenantRelationShip.isPresent()) {
191 Map<String, String> relationshipsDataMap = convertRelationshipDataToMap(tenantRelationShip.get().getRelationDataList());
192 node.setCloudConfiguration(new CloudConfiguration(
193 relationshipsDataMap.get("cloud-region.cloud-region-id"),
194 relationshipsDataMap.get("tenant.tenant-id"),
195 relationshipsDataMap.get("cloud-region.cloud-owner")));
197 catch (Exception exception) {
198 LOGGER.error("Failed to extract placement form tenant relationship of {}:{}", node.getType(), node.getId(), exception);
202 private void getRelatedVfModules(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator, String parentURL, AAITreeNode parentNode) {
204 VNFs do not report their direct related-to vf-modules, so try
205 directly fetching a resource URI.
208 threadPool.execute(() -> {
209 // the response is an array of vf-modules
210 final JsonNode jsonNode;
212 jsonNode = aaiClient.typedAaiGet(Unchecked.toURI(parentURL + "/vf-modules"), JsonNode.class);
213 } catch (ExceptionWithRequestInfo e) {
214 if (e.getHttpCode().equals(404)) {
215 // it's ok, as we're just optimistically fetching
216 // the /vf-modules uri; 404 says this time it was a bad guess
223 if (isArray(jsonNode, NodeType.VF_MODULE)) {
225 //create list of AAITreeNode represent the VfModules from AAI result
226 List<AAITreeNode> vfModules = Streams.fromIterable(jsonNode.get(NodeType.VF_MODULE.getType()))
227 .map(vfModuleNode -> createAaiNode(NodeType.VF_MODULE, vfModuleNode, nodesAccumulator))
229 //enrich each of the VfModule with placement info
230 vfModules.forEach(vfModule-> enrichPlacementDataUsingTenantInfo(
232 AAITreeNodeUtils.findFirstRelationshipByRelatedTo(vfModule.getRelationshipList(), "vserver")
234 //add all VfModules to children list of parent node
235 parentNode.getChildren().addAll(vfModules);
237 LOGGER.error(EELFLoggerDelegate.errorLogger, "Failed to get vf-modules for vnf " + parentNode.getId());
242 List<Relationship> getFilteredRelationships(JsonNode json, Tree<AAIServiceTree.AaiRelationship> pathsTree) {
243 RelationshipList relationshipList = JACKSON_OBJECT_MAPPER.convertValue(json.get(AAIBaseProperties.RELATIONSHIP_LIST.getAaiKey()), RelationshipList.class);
244 if (relationshipList != null) {
245 return relationshipList.getRelationship().stream()
246 .filter(rel -> getNextLevelInPathsTree(pathsTree, rel.getRelatedTo()) != null)
247 .filter(rel -> !Objects.equals(rel.getRelatedTo(), NodeType.VF_MODULE.getType())) // vf-modules are handled separately
251 return Collections.emptyList();
254 void fetchChildrenAsync(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator,
255 AAITreeNode node, List<Relationship> relationships, Tree<AAIServiceTree.AaiRelationship> pathsTree, long timeout) {
257 if (!relationships.isEmpty()) {
258 List<Callable<List<AAITreeNode>>> tasks = relationships.stream()
260 (Callable<List<AAITreeNode>>) () ->
261 getChildNode(threadPool, nodesAccumulator, relationship.getRelatedTo(),
262 relationship.getRelatedLink(), pathsTree))
263 .collect(Collectors.toList());
266 int depth = pathsTree.getChildrenDepth();
267 threadPool.invokeAll(tasks, timeout * depth, TimeUnit.SECONDS)
269 addChildren(node, future)
271 } catch (Exception e) {
272 throw new GenericUncheckedException(e);
277 private List<AAITreeNode> getChildNode(ExecutorService threadPool, ConcurrentSkipListSet<AAITreeNode> nodesAccumulator,
278 String childNodeType, String childNodeUrl,
279 Tree<AAIServiceTree.AaiRelationship> pathsTree) {
281 Tree<AAIServiceTree.AaiRelationship> subTree = getNextLevelInPathsTree(pathsTree, childNodeType);
283 return buildNode(NodeType.fromString(childNodeType), childNodeUrl, null, HttpMethod.GET, nodesAccumulator, threadPool, subTree);
286 Tree<AAIServiceTree.AaiRelationship> getNextLevelInPathsTree(Tree<AAIServiceTree.AaiRelationship> pathsTree, String nodeType) {
287 return pathsTree.getSubTree(new AAIServiceTree.AaiRelationship(nodeType));
291 private AAITreeNode jsonNodeToAaiNode(NodeType nodeType, JsonNode jsonNode) {
292 AAITreeNode node = new AAITreeNode();
293 node.setType(nodeType);
294 node.setOrchestrationStatus(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.ORCHESTRATION_STATUS.getAaiKey()));
295 node.setProvStatus(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.PROV_STATUS.getAaiKey()));
296 node.setInMaint(getBooleanDataFromJsonIfExists(jsonNode, AAIBaseProperties.IN_MAINT.getAaiKey()));
297 node.setModelVersionId(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.MODEL_VERSION_ID.getAaiKey()));
298 node.setModelCustomizationId(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.MODEL_CUSTOMIZATION_ID.getAaiKey()));
299 node.setModelInvariantId(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.MODEL_INVARIANT_ID.getAaiKey()));
300 node.setId(getStringDataFromJsonIfExists(jsonNode, nodeType.getId()));
301 node.setName(getStringDataFromJsonIfExists(jsonNode, nodeType.getName()));
302 node.setAdditionalProperties(aggregateAllOtherProperties(jsonNode, nodeType));
303 node.setRelationshipList(JACKSON_OBJECT_MAPPER.convertValue(jsonNode.get(AAIBaseProperties.RELATIONSHIP_LIST.getAaiKey()), RelationshipList.class));
307 private AAITreeNode createFailureNode(Exception exception) {
308 return FailureAAITreeNode.of(exception);
311 private String getStringDataFromJsonIfExists(JsonNode model, String key) {
312 if (!NodeType.NONE.equals(key) && model.has(key)) {
313 return model.get(key).asText();
318 private Boolean getBooleanDataFromJsonIfExists(JsonNode model, String key) {
319 if (model.has(key)) {
320 return model.get(key).asBoolean();
325 Map<String, Object> aggregateAllOtherProperties(JsonNode model, NodeType nodeType) {
326 Set<String> ignoreProperties = Stream.of(AAIBaseProperties.values())
327 .map(AAIBaseProperties::getAaiKey).collect(toSet());
328 return Streams.fromIterator(model.fields())
329 .filter(not(field -> StringUtils.equals(field.getKey(), nodeType.getId())))
330 .filter(not(field -> StringUtils.equals(field.getKey(), nodeType.getName())))
331 .filter(not(field -> ignoreProperties.contains(field.getKey())))
332 .collect(toMap(Map.Entry::getKey, v -> ifTextualGetAsText(v.getValue())));
335 private Object ifTextualGetAsText(JsonNode jsonNode) {
336 return jsonNode.isTextual() ? jsonNode.asText() : jsonNode;