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

org.opendaylight.restconf.server.mdsal.CapabilitiesWriter Maven / Gradle / Ivy

There is a newer version: 8.0.3
Show newest version
/*
 * Copyright (c) 2022 PANTHEON.tech, s.r.o. 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.server.mdsal;

import static java.util.Objects.requireNonNull;
import static org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.YangModuleInfoImpl.qnameOf;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.MoreExecutors;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.checkerframework.checker.lock.qual.Holding;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.common.api.CommitInfo;
import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
import org.opendaylight.mdsal.dom.api.DOMDataBroker;
import org.opendaylight.mdsal.dom.api.DOMSchemaService;
import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
import org.opendaylight.restconf.api.CapabilityURN;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.RestconfState;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.restconf.state.Capabilities;
import org.opendaylight.yangtools.concepts.Registration;
import org.opendaylight.yangtools.yang.common.Empty;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A simple component which maintains {@link Capabilities} in the operational datastore.
 */
@Singleton
@Component(service = { })
public final class CapabilitiesWriter implements AutoCloseable, FutureCallback {
    private static final Logger LOG = LoggerFactory.getLogger(CapabilitiesWriter.class);

    @VisibleForTesting
    static final @NonNull NodeIdentifier CAPABILITY = NodeIdentifier.create(qnameOf("capability"));

    private static final YangInstanceIdentifier PATH = YangInstanceIdentifier.of(
        NodeIdentifier.create(RestconfState.QNAME), NodeIdentifier.create(Capabilities.QNAME), CAPABILITY);

    private final DOMDataBroker dataBroker;

    private DOMTransactionChain txChain;
    private Registration reg;

    private boolean written;

    @Inject
    @Activate
    public CapabilitiesWriter(@Reference final DOMDataBroker dataBroker,
            @Reference final DOMSchemaService schemaService) {
        this.dataBroker = requireNonNull(dataBroker);
        reg = schemaService.registerSchemaContextListener(this::onModelContextUpdated);
    }

    @PreDestroy
    @Deactivate
    @Override
    public synchronized void close() {
        if (reg == null) {
            return;
        }
        reg.close();
        reg = null;
        deleteRestconfState();
        if (txChain != null) {
            txChain.close();
        }
    }

    @Override
    public synchronized void onFailure(final Throwable cause) {
        LOG.warn("Transaction chain failed, updates may not have been propagated", cause);
        txChain = null;
    }

    @Override
    public synchronized void onSuccess(final Empty result) {
        LOG.debug("Transaction chain closed successfully");
        txChain = null;
    }

    private synchronized void onModelContextUpdated(final EffectiveModelContext newModelContext) {
        if (reg != null) {
            LOG.debug("Ignoring model context update");
            return;
        }

        if (newModelContext.findModuleStatement(RestconfState.QNAME.getModule()).isPresent()) {
            writeRestconfState();
        } else {
            deleteRestconfState();
        }
    }

    @Holding("this")
    private void deleteRestconfState() {
        if (!written) {
            LOG.debug("No state recorded as written, not attempting removal");
            return;
        }

        LOG.debug("Removing ietf-restconf-monitoring state");
        if (txChain == null) {
            txChain = dataBroker.createMergingTransactionChain();
            txChain.addCallback(this);
        }

        final var tx = txChain.newWriteOnlyTransaction();
        tx.delete(LogicalDatastoreType.OPERATIONAL, PATH);
        tx.commit().addCallback(new FutureCallback() {
            @Override
            public void onSuccess(final CommitInfo result) {
                markUnwritten();
            }

            @Override
            public void onFailure(final Throwable cause) {
                // Ignored, will be reported on the transaction chain
            }
        }, MoreExecutors.directExecutor());
    }

    @Holding("this")
    private void writeRestconfState() {
        if (written) {
            LOG.debug("State recorded as written, not updating it");
            return;
        }

        LOG.debug("Updating state of ietf-restconf-monitoring");
        if (txChain == null) {
            txChain = dataBroker.createMergingTransactionChain();
            txChain.addCallback(this);
        }

        final var tx = txChain.newWriteOnlyTransaction();
        tx.put(LogicalDatastoreType.OPERATIONAL, PATH, mapCapabilities());
        tx.commit().addCallback(new FutureCallback() {
            @Override
            public void onSuccess(final CommitInfo result) {
                markWritten();
            }

            @Override
            public void onFailure(final Throwable cause) {
                // Ignored, will be reported on the transaction chain
            }
        }, MoreExecutors.directExecutor());
    }

    private synchronized void markWritten() {
        LOG.debug("State of ietf-restconf-monitoring updated");
        written = true;
    }

    private synchronized void markUnwritten() {
        LOG.debug("State of ietf-restconf-monitoring removed");
        written = false;
    }

    /**
     * Create a {@code restconf-state} container.
     *
     * @return A container holding capabilities
     */
    @VisibleForTesting
    static @NonNull LeafSetNode mapCapabilities() {
        return ImmutableNodes.newSystemLeafSetBuilder()
            .withNodeIdentifier(CAPABILITY)
            // Closest match to DOMDataBroker's semantics
            .withChildValue(CapabilityURN.DEFAULTS + "?basic-mode=explicit")
            .withChildValue(CapabilityURN.DEPTH)
            .withChildValue(CapabilityURN.FIELDS)
            .withChildValue(CapabilityURN.FILTER)
            .withChildValue(CapabilityURN.REPLAY)
            .withChildValue(CapabilityURN.WITH_DEFAULTS)
            .withChildValue(CapabilityURN.YANG_PATCH)
            .withChildValue(CapabilityURN.ODL_PRETTY_PRINT)
            .withChildValue(CapabilityURN.ODL_LEAF_NODES_ONLY)
            .withChildValue(CapabilityURN.ODL_CHANGED_LEAF_NODES_ONLY)
            .withChildValue(CapabilityURN.ODL_SKIP_NOTIFICATION_DATA)
            .withChildValue(CapabilityURN.ODL_CHILD_NODES_ONLY)
            .build();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy