2 * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.netconf.yanglib.writer;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.Futures;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import java.util.concurrent.atomic.AtomicBoolean;
17 import java.util.concurrent.atomic.AtomicLong;
18 import org.eclipse.jdt.annotation.NonNull;
19 import org.opendaylight.mdsal.binding.api.DataBroker;
20 import org.opendaylight.mdsal.binding.api.TransactionChain;
21 import org.opendaylight.mdsal.common.api.CommitInfo;
22 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
23 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesState;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
26 import org.opendaylight.yangtools.concepts.Registration;
27 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
28 import org.opendaylight.yangtools.yang.common.Empty;
29 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory;
34 * Listens for updates on global schema context, transforms context to ietf-yang-library/yang-library and writes this
35 * state to operational data store.
37 final class YangLibraryWriter implements FutureCallback<Empty> {
38 private static final Logger LOG = LoggerFactory.getLogger(YangLibraryWriter.class);
39 private static final InstanceIdentifier<YangLibrary> YANG_LIBRARY_INSTANCE_IDENTIFIER =
40 InstanceIdentifier.create(YangLibrary.class);
42 private static final InstanceIdentifier<ModulesState> MODULES_STATE_INSTANCE_IDENTIFIER =
43 InstanceIdentifier.create(ModulesState.class);
45 private final AtomicLong idCounter = new AtomicLong();
46 private final AtomicBoolean closed = new AtomicBoolean();
47 private final @NonNull YangLibrarySchemaSourceUrlProvider urlProvider;
48 private final DataBroker dataBroker;
49 private final boolean writeLegacy;
50 private final Registration reg;
52 // FIXME: this really should be a dynamically-populated shard (i.e. no write operations whatsoever)!
53 private TransactionChain currentChain;
55 YangLibraryWriter(final DOMSchemaService schemaService, final DataBroker dataBroker,
56 final boolean writeLegacy, final YangLibrarySchemaSourceUrlProvider urlProvider) {
57 this.dataBroker = requireNonNull(dataBroker);
58 this.urlProvider = requireNonNull(urlProvider);
59 this.writeLegacy = writeLegacy;
60 reg = schemaService.registerSchemaContextListener(this::onModelContextUpdated);
61 LOG.info("ietf-yang-library writer started with modules-state {}", writeLegacy ? "enabled" : "disabled");
64 synchronized ListenableFuture<Empty> shutdown() {
65 if (!closed.compareAndSet(false, true)) {
71 final ListenableFuture<Empty> future;
72 try (var chain = acquireChain()) {
73 future = chain.future();
74 final var tx = chain.newWriteOnlyTransaction();
75 tx.delete(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER);
77 tx.delete(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER);
80 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
82 public void onSuccess(final CommitInfo info) {
83 LOG.debug("ietf-yang-library cleaned successfully");
87 public void onFailure(final Throwable throwable) {
88 // Handled via transaction chain listener
90 }, MoreExecutors.directExecutor());
94 Futures.addCallback(future, new FutureCallback<>() {
96 public void onSuccess(final Empty value) {
97 LOG.info("ietf-yang-library writer stopped");
101 public void onFailure(final Throwable throwable) {
102 LOG.warn("ietf-yang-library writer stopped uncleanly", throwable);
104 }, MoreExecutors.directExecutor());
109 private void onModelContextUpdated(final EffectiveModelContext context) {
110 if (context.findModule(YangLibrary.QNAME.getModule()).isPresent()) {
111 updateYangLibrary(context);
113 LOG.warn("ietf-yang-library not present in context, skipping update");
117 private synchronized void updateYangLibrary(final EffectiveModelContext context) {
119 // Already shut down, do not do anything
120 LOG.debug("ietf-yang-library writer closed, skipping update");
124 final var nextId = String.valueOf(idCounter.incrementAndGet());
125 LOG.debug("ietf-yang-library writer starting update to {}", nextId);
126 final var tx = acquireChain().newWriteOnlyTransaction();
127 tx.put(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER,
128 YangLibraryContentBuilderUtil.buildYangLibrary(context, nextId, urlProvider));
130 tx.put(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER,
131 YangLibraryContentBuilderUtil.buildModuleState(context, nextId, urlProvider));
134 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
136 public void onSuccess(final CommitInfo result) {
137 LOG.debug("ietf-yang-library updated successfully");
141 public void onFailure(final Throwable throwable) {
142 // Handled via transaction chain listener
144 }, MoreExecutors.directExecutor());
147 private TransactionChain acquireChain() {
148 var local = currentChain;
150 currentChain = local = dataBroker.createMergingTransactionChain();
151 LOG.debug("Allocated new transaction chain");
152 local.addCallback(this);
158 public synchronized void onSuccess(final Empty result) {
159 LOG.debug("ietf-yang-library writer transaction chain succeeded");
164 public synchronized void onFailure(final Throwable cause) {
165 LOG.info("ietf-yang-library writer transaction chain failed", cause);