All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.lighty.modules.gnmi.simulatordevice.yang.YangDataService Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2021 PANTHEON.tech s.r.o. 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 https://www.eclipse.org/legal/epl-v10.html
 */
package io.lighty.modules.gnmi.simulatordevice.yang;

import com.google.common.io.CharStreams;
import io.lighty.modules.gnmi.commons.util.DataConverter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.EnumMap;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import org.apache.commons.lang3.StringUtils;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeListener;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadTransaction;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreReadWriteTransaction;
import org.opendaylight.mdsal.dom.spi.store.DOMStoreThreePhaseCommitCohort;
import org.opendaylight.mdsal.dom.store.inmemory.InMemoryDOMDataStore;
import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SuppressWarnings("UnstableApiUsage")
public class YangDataService {
    private static final Logger LOG = LoggerFactory.getLogger(YangDataService.class);

    private final EnumMap datastoreMap;

    public YangDataService(final EffectiveModelContext schemaContext, final String initialConfigDataPath,
                           final String initialStateDataPath) throws IOException {
        this.datastoreMap = createDatastoreMap(schemaContext);
        initializeDataStore(initialConfigDataPath, initialStateDataPath, schemaContext);
    }

    public Optional readDataByPath(final DatastoreType datastoreType,
                                                         final YangInstanceIdentifier path) {
        try (DOMStoreReadTransaction tx = datastoreMap.get(datastoreType).newReadOnlyTransaction()) {
            return tx.read(path).get();
        } catch (final ExecutionException e) {
            LOG.error("Unable to fetch data from DataStore", e);
        } catch (InterruptedException e) {
            LOG.error("Interrupted while fetching data from DataStore", e);
            Thread.currentThread().interrupt();
        }
        return Optional.empty();
    }

    public void mergeDataByPath(final DatastoreType datastoreType, final YangInstanceIdentifier path,
                                final NormalizedNode node) {
        modifyDataByPath(datastoreType, path, node, ModificationType.MERGE);
    }

    public void writeDataByPath(final DatastoreType datastoreType, final YangInstanceIdentifier path,
                                final NormalizedNode node) {
        modifyDataByPath(datastoreType, path, node, ModificationType.WRITE);
    }

    public void deleteDataByPath(final DatastoreType datastoreType, final YangInstanceIdentifier path) {
        modifyDataByPath(datastoreType, path, null, ModificationType.DELETE);
    }

    private void modifyDataByPath(final DatastoreType datastoreType, final YangInstanceIdentifier path,
                                  final NormalizedNode node, final ModificationType modificationType) {
        try (DOMStoreReadWriteTransaction tx = datastoreMap.get(datastoreType).newReadWriteTransaction()) {
            if (modificationType == ModificationType.WRITE) {
                tx.write(path, node);
            } else if (modificationType == ModificationType.DELETE) {
                tx.delete(path);
            } else {
                tx.merge(path, node);
            }
            final DOMStoreThreePhaseCommitCohort tpcc = tx.ready();
            tpcc.canCommit().get();
            tpcc.preCommit().get();
            tpcc.commit().get();
        } catch (final ExecutionException exception) {
            LOG.error("Unable to commit changes to datastore", exception);
            throw new RuntimeException("Unable to commit changes to datastore", exception);
        } catch (InterruptedException e) {
            LOG.error("Interrupted while committing changes to datastore", e);
            Thread.currentThread().interrupt();
            throw new RuntimeException("Interrupted while committing changes to datastore", e);
        }
    }

    private void initializeDataStore(final String initialConfigDataPath, final String initialStateDataPath,
                                     final EffectiveModelContext schemaContext)
            throws IOException {
        // Init config data
        if (StringUtils.isNotEmpty(initialConfigDataPath)) {
            final InputStream configFile = Files.newInputStream(Path.of(initialConfigDataPath));
            initDataTree(configFile, DatastoreType.CONFIGURATION, schemaContext);
        }
        if (StringUtils.isNotEmpty(initialStateDataPath)) {
            final InputStream configFile = Files.newInputStream(Path.of(initialStateDataPath));
            initDataTree(configFile, DatastoreType.STATE, schemaContext);
        }
    }

    private void initDataTree(final InputStream stream, final DatastoreType datastoreType,
                              final EffectiveModelContext schemaContext) {
        try {
            // read json configuration from file
            final String configJson = CharStreams.toString(new InputStreamReader(stream, StandardCharsets.UTF_8));
            /*
             ROOT YII because we are writing one/or multiple top-level elements
              (interfaces,alarms, components ...).
            */
            final NormalizedNode node =
                    DataConverter.nodeFromJsonString(YangInstanceIdentifier.empty(), configJson, schemaContext);
            /*
             If QName of parsed node is a root node (SchemaContext.NAME), that means we parsed multiple
              top-level element, in that case we need to write this node on ROOT YII.
            */
            if (node.name().getNodeType().equals(SchemaContext.NAME)) {
                writeDataByPath(datastoreType, YangInstanceIdentifier.empty(), node);
            // Else we parsed only one top-level element, in that case we write this node on it's identifier.
            } else {
                writeDataByPath(datastoreType, YangInstanceIdentifier.of(node.name().getNodeType()), node);
            }

        } catch (final IOException e) {
            LOG.error("Unable to get data from stream {}", stream, e);
        }
    }

    public void registerListener(final DatastoreType datastoreType, final YangInstanceIdentifier identifier,
                                 final DOMDataTreeChangeListener listener) {
        datastoreMap.get(datastoreType).registerTreeChangeListener(identifier, listener);
    }

    private EnumMap createDatastoreMap(EffectiveModelContext schemaContext) {
        final InMemoryDOMDataStore configStore = new InMemoryDOMDataStore(DatastoreType.CONFIGURATION.getName(),
                LogicalDatastoreType.CONFIGURATION, createExecutorService(DatastoreType.CONFIGURATION.getName()),
                20, false);
        configStore.onModelContextUpdated(schemaContext);

        final InMemoryDOMDataStore operStore = new InMemoryDOMDataStore(DatastoreType.OPERATIONAL.getName(),
                LogicalDatastoreType.OPERATIONAL, createExecutorService(DatastoreType.OPERATIONAL.getName()),
                20, false);
        operStore.onModelContextUpdated(schemaContext);

        final InMemoryDOMDataStore stateStore = new InMemoryDOMDataStore(DatastoreType.STATE.getName(),
                LogicalDatastoreType.OPERATIONAL, createExecutorService(DatastoreType.STATE.getName()),
                20, false);
        stateStore.onModelContextUpdated(schemaContext);

        final EnumMap dataStoreTypeMap = new EnumMap<>(DatastoreType.class);
        dataStoreTypeMap.put(DatastoreType.CONFIGURATION, configStore);
        dataStoreTypeMap.put(DatastoreType.OPERATIONAL, operStore);
        dataStoreTypeMap.put(DatastoreType.STATE, stateStore);
        return dataStoreTypeMap;
    }

    private ExecutorService createExecutorService(final String name) {
        return SpecialExecutors.newBlockingBoundedFastThreadPool(20, 20, name + "-DCL", InMemoryDOMDataStore.class);
    }

    private enum ModificationType {
        WRITE,
        MERGE,
        DELETE
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy