/* * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.restconf.restful.utils; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.CheckedFuture; import java.util.Collection; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag; import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType; import org.opendaylight.restconf.restful.transaction.TransactionVarsWrapper; import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; /** * Util class for read data from data store via transaction. * * */ public final class ReadDataTransactionUtil { private ReadDataTransactionUtil() { throw new UnsupportedOperationException("Util class."); } /** * Read specific type of data from data store via transaction. * * @param valueOfContent * - type of data to read (config, state, all) * @param transactionNode * - {@link TransactionVarsWrapper} - wrapper for variables * @return {@link NormalizedNode} */ public static NormalizedNode readData(final String valueOfContent, final TransactionVarsWrapper transactionNode) { if (valueOfContent != null) { switch (valueOfContent) { case RestconfDataServiceConstant.ReadData.CONFIG: transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION); return readDataViaTransaction(transactionNode); case RestconfDataServiceConstant.ReadData.NONCONFIG: transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL); return readDataViaTransaction(transactionNode); case RestconfDataServiceConstant.ReadData.ALL: return readDataViaTransaction(transactionNode); default: throw new RestconfDocumentedException("Bad querry parameter for content.", ErrorType.APPLICATION, ErrorTag.INVALID_VALUE); } } else { return readDataViaTransaction(transactionNode); } } /** * If is set specific {@link LogicalDatastoreType} in * {@link TransactionVarsWrapper}, then read this type of data from DS. If * don't, we have to read all data from DS (state + config) * * @param transactionNode * - {@link TransactionVarsWrapper} - wrapper for variables * @return {@link NormalizedNode} */ private static NormalizedNode readDataViaTransaction(final TransactionVarsWrapper transactionNode) { if (transactionNode.getLogicalDatastoreType() != null) { final CheckedFuture>, ReadFailedException> listenableFuture = transactionNode .getTransactionChain().newReadOnlyTransaction().read(transactionNode.getLogicalDatastoreType(), transactionNode.getInstanceIdentifier().getInstanceIdentifier()); final NormalizedNodeFactory dataFactory = new NormalizedNodeFactory(); FutureCallbackTx.addCallback(listenableFuture, RestconfDataServiceConstant.ReadData.READ_TYPE_TX, dataFactory); return dataFactory.build(); } else { return readAllData(transactionNode); } } /** * Read config and state data, then map them. * * @param transactionNode * - {@link TransactionVarsWrapper} - wrapper for variables * @return {@link NormalizedNode} */ private static NormalizedNode readAllData(final TransactionVarsWrapper transactionNode) { // PREPARE STATE DATA NODE transactionNode.setLogicalDatastoreType(LogicalDatastoreType.OPERATIONAL); final NormalizedNode stateDataNode = readDataViaTransaction(transactionNode); // PREPARE CONFIG DATA NODE transactionNode.setLogicalDatastoreType(LogicalDatastoreType.CONFIGURATION); final NormalizedNode configDataNode = readDataViaTransaction(transactionNode); // if no data exists if ((stateDataNode == null) && (configDataNode == null)) { throw new RestconfDocumentedException( "Request could not be completed because the relevant data model content does not exist", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); } // return config data if (stateDataNode == null) { return configDataNode; } // return state data if (configDataNode == null) { return stateDataNode; } // merge data from config and state return mapNode(stateDataNode, configDataNode); } /** * Map data by type of read node. * * @param stateDataNode * - data node of state data * @param configDataNode * - data node of config data * @param transactionNode * - {@link TransactionVarsWrapper} - wrapper for variables * @return {@link NormalizedNode} */ private static NormalizedNode mapNode(final NormalizedNode stateDataNode, final NormalizedNode configDataNode) { validPossibilityOfMergeNodes(stateDataNode, configDataNode); if (configDataNode instanceof RpcDefinition) { return prepareRpcData(configDataNode, stateDataNode); } else { return prepareData(configDataNode, stateDataNode); } } /** * Prepare and map data for rpc * * @param configDataNode * - data node of config data * @param stateDataNode * - data node of state data * @return {@link NormalizedNode} */ private static NormalizedNode prepareRpcData(final NormalizedNode configDataNode, final NormalizedNode stateDataNode) { final DataContainerNodeBuilder mapEntryBuilder = ImmutableNodes .mapEntryBuilder(); mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier()); // MAP CONFIG DATA mapRpcDataNode(configDataNode, mapEntryBuilder); // MAP STATE DATA mapRpcDataNode(stateDataNode, mapEntryBuilder); return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build(); } /** * Map node to map entry builder. * * @param dataNode * - data node * @param mapEntryBuilder * - builder for mapping data */ private static void mapRpcDataNode(final NormalizedNode dataNode, final DataContainerNodeBuilder mapEntryBuilder) { for (final DataContainerChild child : ((ContainerNode) dataNode).getValue()) { mapEntryBuilder.addChild(child); } } /** * Prepare and map all data from DS * * @param configDataNode * - data node of config data * @param stateDataNode * - data node of state data * @return {@link NormalizedNode} */ private static NormalizedNode prepareData(final NormalizedNode configDataNode, final NormalizedNode stateDataNode) { if (configDataNode instanceof MapNode) { // part for lists mapping final MapNode immutableStateData = ImmutableNodes.mapNodeBuilder(stateDataNode.getNodeType()) .addChild((MapEntryNode) stateDataNode).build(); final MapNode immutableConfigData = ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()) .addChild((MapEntryNode) configDataNode).build(); final DataContainerNodeBuilder mapEntryBuilder = ImmutableNodes .mapEntryBuilder(); mapEntryBuilder.withNodeIdentifier((NodeIdentifierWithPredicates) configDataNode.getIdentifier()); // MAP CONFIG DATA mapDataNode(immutableConfigData, mapEntryBuilder); // MAP STATE DATA mapDataNode(immutableStateData, mapEntryBuilder); return ImmutableNodes.mapNodeBuilder(configDataNode.getNodeType()).addChild(mapEntryBuilder.build()).build(); } else if (configDataNode instanceof ContainerNode) { // part for // containers // mapping final DataContainerNodeAttrBuilder containerBuilder = Builders .containerBuilder((ContainerNode) configDataNode); // MAP CONFIG DATA mapCont(containerBuilder, ((ContainerNode) configDataNode).getValue()); // MAP STATE DATA mapCont(containerBuilder, ((ContainerNode) stateDataNode).getValue()); return containerBuilder.build(); } else { throw new RestconfDocumentedException("Bad type of node."); } } /** * Map data to builder * * @param containerBuilder * - builder for mapping data * @param childs * - childs of data (container) */ private static void mapCont(final DataContainerNodeAttrBuilder containerBuilder, final Collection> childs) { for (final DataContainerChild child : childs) { containerBuilder.addChild(child); } } /** * Map data to builder * * @param immutableData * - immutable data - {@link MapNode} * @param mapEntryBuilder * - builder for mapping data */ private static void mapDataNode(final MapNode immutableData, final DataContainerNodeBuilder mapEntryBuilder) { for (final DataContainerChild child : immutableData.getValue().iterator() .next().getValue()) { Preconditions.checkNotNull(child); if (child instanceof ContainerNode) { addChildToMap(ContainerNode.class, child, mapEntryBuilder); } else if (child instanceof AugmentationNode) { addChildToMap(AugmentationNode.class, child, mapEntryBuilder); } else if(child instanceof MapNode){ final MapNode listNode = (MapNode) child; for (final MapEntryNode listChild : listNode.getValue()) { for (final DataContainerChild entryChild : listChild.getValue()) { addChildToMap(MapEntryNode.class, entryChild, mapEntryBuilder); } } } else if (child instanceof ChoiceNode) { addChildToMap(ChoiceNode.class, child, mapEntryBuilder); } else if ((child instanceof LeafSetNode) || (child instanceof LeafNode)) { mapEntryBuilder.addChild(child); } } } /** * Mapping child * * @param type * - type of data * @param child * - child to map * @param mapEntryBuilder * - builder for mapping child */ private static > void addChildToMap(final Class type, final DataContainerChild child, final DataContainerNodeBuilder mapEntryBuilder) { @SuppressWarnings("unchecked") final T node = (T) child; for (final DataContainerChild childNode : node.getValue()) { mapEntryBuilder.addChild(childNode); } } /** * Valid of can be data merged together. * * @param stateDataNode * - data node of state data * @param configDataNode * - data node of config data */ private static void validPossibilityOfMergeNodes(@Nonnull final NormalizedNode stateDataNode, @Nonnull final NormalizedNode configDataNode) { final QNameModule moduleOfStateData = stateDataNode.getIdentifier().getNodeType().getModule(); final QNameModule moduleOfConfigData = configDataNode.getIdentifier().getNodeType().getModule(); if (moduleOfStateData != moduleOfConfigData) { throw new RestconfDocumentedException("It is not possible to merge "); } } }