Bump upstreams
[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.annotations.VisibleForTesting;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import java.util.concurrent.ExecutionException;
16 import java.util.concurrent.atomic.AtomicLong;
17 import javax.annotation.PreDestroy;
18 import javax.inject.Inject;
19 import javax.inject.Singleton;
20 import org.checkerframework.checker.lock.qual.GuardedBy;
21 import org.opendaylight.mdsal.binding.api.DataBroker;
22 import org.opendaylight.mdsal.common.api.CommitInfo;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesState;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
27 import org.opendaylight.yangtools.concepts.Registration;
28 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
29 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
30 import org.osgi.service.component.annotations.Activate;
31 import org.osgi.service.component.annotations.Component;
32 import org.osgi.service.component.annotations.Deactivate;
33 import org.osgi.service.component.annotations.Reference;
34 import org.osgi.service.component.annotations.ReferenceCardinality;
35 import org.osgi.service.metatype.annotations.AttributeDefinition;
36 import org.osgi.service.metatype.annotations.Designate;
37 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41 /**
42  * Listens for updates on global schema context, transforms context to ietf-yang-library/yang-library and writes this
43  * state to operational data store.
44  */
45 @Singleton
46 @Component(service = { }, configurationPid = "org.opendaylight.netconf.yanglib")
47 @Designate(ocd = YangLibraryWriter.Configuration.class)
48 public final class YangLibraryWriter implements AutoCloseable {
49     @ObjectClassDefinition
50     public @interface Configuration {
51         @AttributeDefinition(description = "Enables legacy content to be written")
52         boolean write$_$legacy() default false;
53     }
54
55     private static final Logger LOG = LoggerFactory.getLogger(YangLibraryWriter.class);
56     private static final InstanceIdentifier<YangLibrary> YANG_LIBRARY_INSTANCE_IDENTIFIER =
57         InstanceIdentifier.create(YangLibrary.class);
58     private static final InstanceIdentifier<ModulesState> MODULES_STATE_INSTANCE_IDENTIFIER =
59         InstanceIdentifier.create(ModulesState.class);
60
61     private final AtomicLong idCounter = new AtomicLong(0L);
62     private final DataBroker dataBroker;
63     private final boolean writeLegacy;
64
65     @Reference(cardinality = ReferenceCardinality.OPTIONAL)
66     volatile YangLibrarySchemaSourceUrlProvider schemaSourceUrlProvider;
67
68     @GuardedBy("this")
69     private Registration reg;
70
71     @Inject
72     @Activate
73     public YangLibraryWriter(final @Reference DOMSchemaService schemaService,
74             final @Reference DataBroker dataBroker, final Configuration configuration) {
75         this.dataBroker = requireNonNull(dataBroker);
76         writeLegacy = configuration.write$_$legacy();
77         reg = schemaService.registerSchemaContextListener(this::onModelContextUpdated);
78     }
79
80     @Deactivate
81     @PreDestroy
82     @Override
83     public synchronized void close() throws InterruptedException, ExecutionException {
84         if (reg == null) {
85             // Already shut down
86             return;
87         }
88         reg.close();
89         reg = null;
90
91         // FIXME: we should be using a transaction chain for this, but, really, this should be a dynamically-populated
92         //        shard (i.e. no storage whatsoever)!
93         final var tx = dataBroker.newWriteOnlyTransaction();
94         tx.delete(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER);
95         if (writeLegacy) {
96             tx.delete(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER);
97         }
98
99         final var future = tx.commit();
100         future.addCallback(new FutureCallback<CommitInfo>() {
101             @Override
102             public void onSuccess(final CommitInfo info) {
103                 LOG.debug("YANG library cleared successfully");
104             }
105
106             @Override
107             public void onFailure(final Throwable throwable) {
108                 LOG.warn("Unable to clear YANG library", throwable);
109             }
110         }, MoreExecutors.directExecutor());
111
112         // We need to synchronize here, otherwise we'd end up trampling over ourselves
113         future.get();
114     }
115
116     @VisibleForTesting void onModelContextUpdated(final EffectiveModelContext context) {
117         if (context.findModule(YangLibrary.QNAME.getModule()).isPresent()) {
118             updateYangLibrary(context);
119         } else {
120             LOG.warn("ietf-yang-library not present in context, skipping update");
121         }
122     }
123
124     private synchronized void updateYangLibrary(final EffectiveModelContext context) {
125         if (reg == null) {
126             // Already shut down, do not do anything
127             return;
128         }
129         final var nextId = String.valueOf(idCounter.incrementAndGet());
130         final var tx = dataBroker.newWriteOnlyTransaction();
131         tx.put(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER,
132             YangLibraryContentBuilderUtil.buildYangLibrary(context, nextId, schemaSourceUrlProvider));
133         if (writeLegacy) {
134             tx.put(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER,
135                 YangLibraryContentBuilderUtil.buildModuleState(context, nextId, schemaSourceUrlProvider));
136         }
137
138         tx.commit().addCallback(new FutureCallback<CommitInfo>() {
139             @Override
140             public void onSuccess(final CommitInfo result) {
141                 LOG.debug("Yang library updated successfully");
142             }
143
144             @Override
145             public void onFailure(final Throwable throwable) {
146                 LOG.warn("Failed to update yang library", throwable);
147             }
148         }, MoreExecutors.directExecutor());
149     }
150 }