X-Git-Url: https://gerrit.onap.org/r/gitweb?a=blobdiff_plain;f=vid-app-common%2Fsrc%2Fmain%2Fjava%2Forg%2Fonap%2Fvid%2Fservices%2FAAITreeNodeBuilder.java;h=d53eba8d30f2b0e7431ecfc02166d59c3c2c9625;hb=e601bbdc43bae9a08e2e10c5139a6f76b47860d7;hp=e060882644fd40d1b7aae48d3a22b6700d7b018b;hpb=76c6ee4a697617ec4cdee2f3b48bc83136c858c5;p=vid.git diff --git a/vid-app-common/src/main/java/org/onap/vid/services/AAITreeNodeBuilder.java b/vid-app-common/src/main/java/org/onap/vid/services/AAITreeNodeBuilder.java index e06088264..d53eba8d3 100644 --- a/vid-app-common/src/main/java/org/onap/vid/services/AAITreeNodeBuilder.java +++ b/vid-app-common/src/main/java/org/onap/vid/services/AAITreeNodeBuilder.java @@ -7,9 +7,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -21,66 +21,49 @@ package org.onap.vid.services; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import org.apache.commons.lang3.StringUtils; -import org.jetbrains.annotations.NotNull; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; import org.onap.portalsdk.core.logging.logic.EELFLoggerDelegate; +import org.onap.portalsdk.core.util.SystemProperties; import org.onap.vid.aai.AaiClientInterface; import org.onap.vid.aai.ExceptionWithRequestInfo; import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.Relationship; +import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.RelationshipData; import org.onap.vid.aai.model.AaiGetNetworkCollectionDetails.RelationshipList; +import org.onap.vid.aai.util.AAITreeNodeUtils; +import org.onap.vid.exceptions.GenericUncheckedException; import org.onap.vid.model.aaiTree.AAITreeNode; import org.onap.vid.model.aaiTree.FailureAAITreeNode; +import org.onap.vid.model.aaiTree.NodeType; +import org.onap.vid.mso.model.CloudConfiguration; +import org.onap.vid.properties.VidProperties; import org.onap.vid.utils.Streams; import org.onap.vid.utils.Tree; import org.onap.vid.utils.Unchecked; +import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; import javax.inject.Inject; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.*; +import java.util.concurrent.*; import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.stream.Collectors.*; +import static org.onap.vid.utils.KotlinUtilsKt.JACKSON_OBJECT_MAPPER; import static org.onap.vid.utils.Streams.not; @Component public class AAITreeNodeBuilder { + private static final String RESULTS = "results"; private AaiClientInterface aaiClient; - private final ObjectMapper mapper = new ObjectMapper(); - private static final EELFLoggerDelegate LOGGER = EELFLoggerDelegate.getLogger(AAITreeNodeBuilder.class); - //List of all the node types the tree should include - public static final String SERVICE_INSTANCE = "service-instance"; - public static final String GENERIC_VNF = "generic-vnf"; - public static final String NETWORK = "l3-network"; - public static final String FAILURE = "failure_node"; - public static final String COLLECTION_RESOURCE = "collection"; - public static final String CONFIGURATION = "configuration"; - public static final String PNF = "pnf"; - public static final String VF_MODULE = "vf-module"; - public static final String INSTANCE_GROUP = "instance-group"; - public static final String PORT = "l-interface"; - public static final String VG = "volume-group"; - public static final String VLAN_TAG = "vlan-tag"; - - //Hashmap that defines the node-type and the tag that should be used to find it's ID key in the JSON. - private static HashMap nodeTypeToIdKeyMap = generateTypeToIdKeyMap(); - - //Hashmap that defines the node-type and the tag that should be used to find it's NAMR key in the JSON. - private static HashMap nodeTypeToNameKeyMap = generateTypeToNameKeyMap(); public enum AAIBaseProperties { ORCHESTRATION_STATUS("orchestration-status"), @@ -102,63 +85,121 @@ public class AAITreeNodeBuilder { } } - public static List toAaiRelationshipList(String... types) { - return Stream.of(types).map(AAIServiceTree.AaiRelationship::new).collect(Collectors.toList()); - } - @Inject public AAITreeNodeBuilder(AaiClientInterface aaiClient) { this.aaiClient = aaiClient; } - public List buildNode(String nodeType, - String requestURL, - ConcurrentSkipListSet nodesAccumulator, ExecutorService threadPool, - ConcurrentLinkedQueue visitedNodes, - AtomicInteger nodesCounter, - Tree pathsTree) { + List buildNode(NodeType nodeType, + String requestURL, + String payload, + HttpMethod method, + ConcurrentSkipListSet nodesAccumulator, + ExecutorService threadPool, + Tree pathsTree) { - JsonNode topLevelJson = aaiClient.typedAaiGet(Unchecked.toURI(requestURL), JsonNode.class); + JsonNode jsonNode = aaiClient.typedAaiRest(Unchecked.toURI(requestURL), JsonNode.class, payload, method, false); - if (topLevelJson.has(nodeType) && topLevelJson.get(nodeType).isArray()) { - return Streams.fromIterable(topLevelJson.get(nodeType)) - .map(item -> parseNodeAndGetChildren(nodeType, requestURL, item, - nodesAccumulator, threadPool, visitedNodes, nodesCounter, pathsTree)) - .collect(toList()); - } else { - return ImmutableList.of(parseNodeAndGetChildren(nodeType, requestURL, topLevelJson, - nodesAccumulator, threadPool, visitedNodes, nodesCounter, pathsTree)); + List>> nodes = getNodesWithRelationships(jsonNode, nodeType, nodesAccumulator, pathsTree); + + String timeout = SystemProperties.getProperty(VidProperties.VID_THREAD_TIMEOUT); + long timeoutNum = Long.parseLong(StringUtils.defaultIfEmpty(timeout, "30")); + + for (Pair> entry : nodes) { + fetchChildrenAsync(threadPool, nodesAccumulator, entry.getKey(), entry.getValue(), pathsTree, timeoutNum); + + if (getNextLevelInPathsTree(pathsTree, NodeType.VF_MODULE.getType()) != null) { + getRelatedVfModules(threadPool, nodesAccumulator, requestURL, entry.getKey()); + } } + + return nodes.stream() + .map(Pair::getKey) + .collect(Collectors.toList()); } - private AAITreeNode parseNodeAndGetChildren(String nodeType, - String requestURL, - JsonNode topLevelJson, - ConcurrentSkipListSet nodesAccumulator, ExecutorService threadPool, - ConcurrentLinkedQueue visitedNodes, - AtomicInteger nodesCounter, - Tree pathsTree) { - AAITreeNode node = jsonToAaiNode(nodeType, topLevelJson, nodesAccumulator, nodesCounter); - - RelationshipList relationships = mapper.convertValue(topLevelJson.get(AAIBaseProperties.RELATIONSHIP_LIST.getAaiKey()), RelationshipList.class); - if (relationships != null) { - getChildren(threadPool, nodesAccumulator, relationships.getRelationship(), visitedNodes, node, nodesCounter, pathsTree); - } - if (StringUtils.equals(node.getType(), GENERIC_VNF)) { - getRelatedVfModules(threadPool, nodesAccumulator, requestURL, node, nodesCounter); + private List>> getNodesWithRelationships(JsonNode jsonNode, NodeType nodeType, + ConcurrentSkipListSet nodesAccumulator, + Tree pathsTree) { + if (isListOfKeyResults(jsonNode)) { + return Streams.fromIterable(jsonNode.get(RESULTS)) + .filter(item -> item.has(nodeType.getType())) + .map(item -> item.get(nodeType.getType())) + .map(item -> parseNodeAndFilterRelationships(item, nodeType, nodesAccumulator, pathsTree)) + .collect(Collectors.toList()); + } else if (isArray(jsonNode, nodeType)) { + return Streams.fromIterable(jsonNode.get(nodeType.getType())) + .map(item -> parseNodeAndFilterRelationships(item, nodeType, nodesAccumulator, pathsTree)) + .collect(Collectors.toList()); + } else { + return ImmutableList.of(parseNodeAndFilterRelationships(jsonNode, nodeType, nodesAccumulator, pathsTree)); } - return node; } - private AAITreeNode jsonToAaiNode(String nodeType, JsonNode topLevelJson, ConcurrentSkipListSet nodesAccumulator, AtomicInteger nodesCounter) { - AAITreeNode node = fillNodeMetaData(nodeType, topLevelJson, nodesCounter); + Pair> parseNodeAndFilterRelationships(JsonNode jsonNode, NodeType nodeType, + ConcurrentSkipListSet nodesAccumulator, + Tree pathsTree) { + AAITreeNode node = createAaiNode(nodeType, jsonNode, nodesAccumulator); + + enrichPlacementData(node); + + List filteredRelationships = getFilteredRelationships(jsonNode, pathsTree); + + return ImmutablePair.of(node, filteredRelationships); + } + + boolean isArray(JsonNode json, NodeType nodeType) { + return json != null && json.has(nodeType.getType()) && json.get(nodeType.getType()).isArray(); + } + + boolean isListOfKeyResults(JsonNode jsonNode) { + return jsonNode != null && jsonNode.has(RESULTS) && jsonNode.get(RESULTS).isArray(); + } + + AAITreeNode createAaiNode(NodeType nodeType, JsonNode jsonNode, ConcurrentSkipListSet nodesAccumulator) { + AAITreeNode node = jsonNodeToAaiNode(nodeType, jsonNode); nodesAccumulator.add(node); return node; } - private void getRelatedVfModules(ExecutorService threadPool, ConcurrentSkipListSet nodesAccumulator, String parentURL, AAITreeNode parentNode, AtomicInteger nodesCounter) { + private void addChildren(AAITreeNode node, Future> children) { + try { + node.addChildren(children.get()); + } catch (Exception e) { + node.getChildren().add(createFailureNode(e)); + } + } + + private Map convertRelationshipDataToMap(List relationshipData) { + return relationshipData.stream().collect( + Collectors.toMap(RelationshipData::getKey, RelationshipData::getValue)); + } + + void enrichPlacementData(AAITreeNode node){ + Optional tenantRelationShip = AAITreeNodeUtils.findFirstRelationshipByRelatedTo(node.getRelationshipList(), "tenant"); + enrichPlacementDataUsingTenantInfo(node, tenantRelationShip); + } + + void enrichPlacementDataUsingTenantInfo(AAITreeNode node, Optional tenantRelationShip) { + //no tenant relationship in this node - so no placement data + if (!tenantRelationShip.isPresent()) { + return; + } + try { + Map relationshipsDataMap = convertRelationshipDataToMap(tenantRelationShip.get().getRelationDataList()); + node.setCloudConfiguration(new CloudConfiguration( + relationshipsDataMap.get("cloud-region.cloud-region-id"), + relationshipsDataMap.get("tenant.tenant-id"), + relationshipsDataMap.get("cloud-region.cloud-owner"))); + } + catch (Exception exception) { + LOGGER.error("Failed to extract placement form tenant relationship of {}:{}", node.getType(), node.getId(), exception); + } + } + + private void getRelatedVfModules(ExecutorService threadPool, ConcurrentSkipListSet nodesAccumulator, String parentURL, AAITreeNode parentNode) { /* VNFs do not report their direct related-to vf-modules, so try directly fetching a resource URI. @@ -166,73 +207,100 @@ public class AAITreeNodeBuilder { threadPool.execute(() -> { // the response is an array of vf-modules - final JsonNode topLevelJson; + final JsonNode jsonNode; try { - topLevelJson = aaiClient.typedAaiGet(Unchecked.toURI(parentURL + "/vf-modules"), JsonNode.class); + jsonNode = aaiClient.typedAaiGet(Unchecked.toURI(parentURL + "/vf-modules"), JsonNode.class); } catch (ExceptionWithRequestInfo e) { if (e.getHttpCode().equals(404)) { // it's ok, as we're just optimistically fetching - // the /vf-modules uri; 404 says this time it was - // a bad guess + // the /vf-modules uri; 404 says this time it was a bad guess return; } else { throw e; } } - if (topLevelJson != null) { - parentNode.getChildren().addAll( - Streams.fromIterable(topLevelJson.get(VF_MODULE)) - .map(vfModuleNode -> jsonToAaiNode(VF_MODULE, vfModuleNode, nodesAccumulator, nodesCounter)) - .collect(toList()) - ); + if (isArray(jsonNode, NodeType.VF_MODULE)) { + + //create list of AAITreeNode represent the VfModules from AAI result + List vfModules = Streams.fromIterable(jsonNode.get(NodeType.VF_MODULE.getType())) + .map(vfModuleNode -> createAaiNode(NodeType.VF_MODULE, vfModuleNode, nodesAccumulator)) + .collect(toList()); + //enrich each of the VfModule with placement info + vfModules.forEach(vfModule-> enrichPlacementDataUsingTenantInfo( + vfModule, + AAITreeNodeUtils.findFirstRelationshipByRelatedTo(vfModule.getRelationshipList(), "vserver") + )); + //add all VfModules to children list of parent node + parentNode.getChildren().addAll(vfModules); } else { LOGGER.error(EELFLoggerDelegate.errorLogger, "Failed to get vf-modules for vnf " + parentNode.getId()); } }); } - private void getChildren(ExecutorService threadPool, ConcurrentSkipListSet nodesAccumulator, - List relationships, ConcurrentLinkedQueue visitedNodes, AAITreeNode parent, AtomicInteger nodesCounter, Tree pathsTree) { - for (Relationship relationship : relationships) { - createChildNode(threadPool, nodesAccumulator, relationship, visitedNodes, parent, nodesCounter, pathsTree); + List getFilteredRelationships(JsonNode json, Tree pathsTree) { + RelationshipList relationshipList = JACKSON_OBJECT_MAPPER.convertValue(json.get(AAIBaseProperties.RELATIONSHIP_LIST.getAaiKey()), RelationshipList.class); + if (relationshipList != null) { + return relationshipList.getRelationship().stream() + .filter(rel -> getNextLevelInPathsTree(pathsTree, rel.getRelatedTo()) != null) + .filter(rel -> !Objects.equals(rel.getRelatedTo(), NodeType.VF_MODULE.getType())) // vf-modules are handled separately + .collect(toList()); } + + return Collections.emptyList(); } - private void createChildNode(ExecutorService threadPool, ConcurrentSkipListSet nodesAccumulator, - Relationship relationship, ConcurrentLinkedQueue visitedNodes, AAITreeNode parent, AtomicInteger nodesCounter, Tree pathsTree) { - String newNodeType = relationship.getRelatedTo(); - Tree subTree = pathsTree.getSubTree(new AAIServiceTree.AaiRelationship(newNodeType)); - if (subTree!=null) { - String newNodeUrl = relationship.getRelatedLink(); - if (!visitedNodes.contains(newNodeUrl)) { - visitedNodes.add(newNodeUrl); - threadPool.execute(() -> { - try { - parent.addChildren(buildNode(newNodeType, newNodeUrl, nodesAccumulator, threadPool, visitedNodes, nodesCounter, subTree)); - } catch (Exception e) { - parent.getChildren().add(createFailureNode(e)); - } - } - ); + void fetchChildrenAsync(ExecutorService threadPool, ConcurrentSkipListSet nodesAccumulator, + AAITreeNode node, List relationships, Tree pathsTree, long timeout) { + + if (!relationships.isEmpty()) { + List>> tasks = relationships.stream() + .map(relationship -> + (Callable>) () -> + getChildNode(threadPool, nodesAccumulator, relationship.getRelatedTo(), + relationship.getRelatedLink(), pathsTree)) + .collect(Collectors.toList()); + + try { + int depth = pathsTree.getChildrenDepth(); + threadPool.invokeAll(tasks, timeout * depth, TimeUnit.SECONDS) + .forEach(future -> + addChildren(node, future) + ); + } catch (Exception e) { + throw new GenericUncheckedException(e); } } } - private AAITreeNode fillNodeMetaData(String nodeType, JsonNode model, @NotNull AtomicInteger nodesCounter) { + private List getChildNode(ExecutorService threadPool, ConcurrentSkipListSet nodesAccumulator, + String childNodeType, String childNodeUrl, + Tree pathsTree) { + + Tree subTree = getNextLevelInPathsTree(pathsTree, childNodeType); + + return buildNode(NodeType.fromString(childNodeType), childNodeUrl, null, HttpMethod.GET, nodesAccumulator, threadPool, subTree); + } + + Tree getNextLevelInPathsTree(Tree pathsTree, String nodeType) { + return pathsTree.getSubTree(new AAIServiceTree.AaiRelationship(nodeType)); + } + + //ADD TEST + private AAITreeNode jsonNodeToAaiNode(NodeType nodeType, JsonNode jsonNode) { AAITreeNode node = new AAITreeNode(); node.setType(nodeType); - node.setUniqueNumber(nodesCounter.getAndIncrement()); - node.setOrchestrationStatus(getStringDataFromJsonIfExists(model, AAIBaseProperties.ORCHESTRATION_STATUS.getAaiKey())); - node.setProvStatus(getStringDataFromJsonIfExists(model, AAIBaseProperties.PROV_STATUS.getAaiKey())); - node.setInMaint(getBooleanDataFromJsonIfExists(model, AAIBaseProperties.IN_MAINT.getAaiKey())); - node.setModelVersionId(getStringDataFromJsonIfExists(model, AAIBaseProperties.MODEL_VERSION_ID.getAaiKey())); - node.setModelCustomizationId(getStringDataFromJsonIfExists(model, AAIBaseProperties.MODEL_CUSTOMIZATION_ID.getAaiKey())); - node.setModelInvariantId(getStringDataFromJsonIfExists(model, AAIBaseProperties.MODEL_INVARIANT_ID.getAaiKey())); - node.setId(getStringDataFromJsonIfExists(model, nodeTypeToIdKeyMap.get(nodeType))); - node.setName(getStringDataFromJsonIfExists(model, nodeTypeToNameKeyMap.get(nodeType))); - node.setAdditionalProperties(aggregateAllOtherProperties(model, nodeType)); - + node.setOrchestrationStatus(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.ORCHESTRATION_STATUS.getAaiKey())); + node.setProvStatus(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.PROV_STATUS.getAaiKey())); + node.setInMaint(getBooleanDataFromJsonIfExists(jsonNode, AAIBaseProperties.IN_MAINT.getAaiKey())); + node.setModelVersionId(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.MODEL_VERSION_ID.getAaiKey())); + node.setModelCustomizationId(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.MODEL_CUSTOMIZATION_ID.getAaiKey())); + node.setModelInvariantId(getStringDataFromJsonIfExists(jsonNode, AAIBaseProperties.MODEL_INVARIANT_ID.getAaiKey())); + node.setId(getStringDataFromJsonIfExists(jsonNode, nodeType.getId())); + node.setName(getStringDataFromJsonIfExists(jsonNode, nodeType.getName())); + node.setAdditionalProperties(aggregateAllOtherProperties(jsonNode, nodeType)); + node.setRelationshipList(JACKSON_OBJECT_MAPPER.convertValue(jsonNode.get(AAIBaseProperties.RELATIONSHIP_LIST.getAaiKey()), RelationshipList.class)); return node; } @@ -241,7 +309,7 @@ public class AAITreeNodeBuilder { } private String getStringDataFromJsonIfExists(JsonNode model, String key) { - if (model.has(key)) { + if (!NodeType.NONE.equals(key) && model.has(key)) { return model.get(key).asText(); } return null; @@ -254,48 +322,17 @@ public class AAITreeNodeBuilder { return false; } - private Map aggregateAllOtherProperties(JsonNode model, String nodeType) { + Map aggregateAllOtherProperties(JsonNode model, NodeType nodeType) { Set ignoreProperties = Stream.of(AAIBaseProperties.values()) .map(AAIBaseProperties::getAaiKey).collect(toSet()); - return Streams.fromIterator(model.fields()) - .filter(not(field -> StringUtils.equals(field.getKey(), nodeTypeToIdKeyMap.get(nodeType)))) - .filter(not(field -> StringUtils.equals(field.getKey(), nodeTypeToNameKeyMap.get(nodeType)))) + .filter(not(field -> StringUtils.equals(field.getKey(), nodeType.getId()))) + .filter(not(field -> StringUtils.equals(field.getKey(), nodeType.getName()))) .filter(not(field -> ignoreProperties.contains(field.getKey()))) - .collect(toMap(Map.Entry::getKey, v -> v.getValue().asText())); - } - - private static HashMap generateTypeToIdKeyMap() { - HashMap result = new HashMap<>(); - result.put(SERVICE_INSTANCE, "service-instance-id"); - result.put(GENERIC_VNF, "vnf-id"); - result.put(NETWORK, "network-id"); - result.put(COLLECTION_RESOURCE, "collection-id"); - result.put(CONFIGURATION, "configuration-id"); - result.put(PNF, "pnf-id"); - result.put(VF_MODULE, "vf-module-id"); - result.put(INSTANCE_GROUP, "id"); - result.put(PORT, "l-interface-id"); - result.put(VG, "volume-group-id"); - result.put(VLAN_TAG, "vlan-id"); - - return result; + .collect(toMap(Map.Entry::getKey, v -> ifTextualGetAsText(v.getValue()))); } - private static HashMap generateTypeToNameKeyMap() { - HashMap result = new HashMap<>(); - result.put(SERVICE_INSTANCE, "service-instance-name"); - result.put(GENERIC_VNF, "vnf-name"); - result.put(NETWORK, "network-name"); - result.put(COLLECTION_RESOURCE, "collection-name"); - result.put(CONFIGURATION, "configuration-name"); - result.put(PNF, "pnf-name"); - result.put(VF_MODULE, "vf-module-name"); - result.put(INSTANCE_GROUP, "instance-group-name"); - result.put(PORT, "l-interface-name"); - result.put(VG, "volume-group-name"); - result.put(VLAN_TAG, "vlan-name"); - - return result; + private Object ifTextualGetAsText(JsonNode jsonNode) { + return jsonNode.isTextual() ? jsonNode.asText() : jsonNode; } -} +} \ No newline at end of file