beb5a645bd5e2e2029f2d95c973dca7c3a6e1988
[netconf.git] / apps / yanglib-mdsal-writer / src / main / java / org / opendaylight / netconf / yanglib / writer / YangLibraryWriter.java
1 /*
2  * Copyright (c) 2023 PANTHEON.tech s.r.o. and others. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netconf.yanglib.writer;
9
10 import static java.util.Objects.requireNonNull;
11
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;
32
33 /**
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.
36  */
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);
41     @Deprecated
42     private static final InstanceIdentifier<ModulesState> MODULES_STATE_INSTANCE_IDENTIFIER =
43         InstanceIdentifier.create(ModulesState.class);
44
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;
51
52     // FIXME: this really should be a dynamically-populated shard (i.e. no write operations whatsoever)!
53     private TransactionChain currentChain;
54
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");
62     }
63
64     synchronized ListenableFuture<Empty> shutdown() {
65         if (!closed.compareAndSet(false, true)) {
66             // Already shut down
67             return null;
68         }
69         reg.close();
70
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);
76             tx.delete(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER);
77
78             tx.commit().addCallback(new FutureCallback<CommitInfo>() {
79                 @Override
80                 public void onSuccess(final CommitInfo info) {
81                     LOG.debug("ietf-yang-library cleaned successfully");
82                 }
83
84                 @Override
85                 public void onFailure(final Throwable throwable) {
86                     // Handled via transaction chain listener
87                 }
88             }, MoreExecutors.directExecutor());
89         }
90         currentChain = null;
91
92         Futures.addCallback(future, new FutureCallback<>() {
93             @Override
94             public void onSuccess(final Empty value) {
95                 LOG.info("ietf-yang-library writer stopped");
96             }
97
98             @Override
99             public void onFailure(final Throwable throwable) {
100                 LOG.warn("ietf-yang-library writer stopped uncleanly", throwable);
101             }
102         }, MoreExecutors.directExecutor());
103
104         return future;
105     }
106
107     private void onModelContextUpdated(final EffectiveModelContext context) {
108         if (context.findModule(YangLibrary.QNAME.getModule()).isPresent()) {
109             updateYangLibrary(context);
110         } else {
111             LOG.warn("ietf-yang-library not present in context, skipping update");
112         }
113     }
114
115     private synchronized void updateYangLibrary(final EffectiveModelContext context) {
116         if (closed.get()) {
117             // Already shut down, do not do anything
118             LOG.debug("ietf-yang-library writer closed, skipping update");
119             return;
120         }
121
122         final var nextId = String.valueOf(idCounter.incrementAndGet());
123         LOG.debug("ietf-yang-library writer starting update to {}", nextId);
124         final var tx = acquireChain().newWriteOnlyTransaction();
125         tx.put(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER,
126             YangLibraryContentBuilderUtil.buildYangLibrary(context, nextId, urlProvider));
127         if (writeLegacy) {
128             tx.put(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER,
129                 YangLibraryContentBuilderUtil.buildModuleState(context, nextId, urlProvider));
130         } else {
131             tx.delete(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER);
132         }
133
134         tx.commit().addCallback(new FutureCallback<CommitInfo>() {
135             @Override
136             public void onSuccess(final CommitInfo result) {
137                 LOG.debug("ietf-yang-library updated successfully");
138             }
139
140             @Override
141             public void onFailure(final Throwable throwable) {
142                 // Handled via transaction chain listener
143             }
144         }, MoreExecutors.directExecutor());
145     }
146
147     private TransactionChain acquireChain() {
148         var local = currentChain;
149         if (local == null) {
150             currentChain = local = dataBroker.createMergingTransactionChain();
151             LOG.debug("Allocated new transaction chain");
152             local.addCallback(this);
153         }
154         return local;
155     }
156
157     @Override
158     public synchronized void onSuccess(final Empty result) {
159         LOG.debug("ietf-yang-library writer transaction chain succeeded");
160         currentChain = null;
161     }
162
163     @Override
164     public synchronized void onFailure(final Throwable cause) {
165         LOG.info("ietf-yang-library writer transaction chain failed", cause);
166         currentChain = null;
167     }
168 }