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.MoreExecutors;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.atomic.AtomicLong;
16 import javax.annotation.PreDestroy;
17 import javax.inject.Inject;
18 import javax.inject.Singleton;
19 import org.checkerframework.checker.lock.qual.GuardedBy;
20 import org.opendaylight.mdsal.binding.api.DataBroker;
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.model.api.EffectiveModelContext;
29 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
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;
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.
46 @Component(immediate = true, configurationPid = "org.opendaylight.netconf.yanglib")
47 @Designate(ocd = YangLibraryWriter.Configuration.class)
48 public final class YangLibraryWriter implements EffectiveModelContextListener, AutoCloseable {
50 @ObjectClassDefinition
51 public @interface Configuration {
52 @AttributeDefinition(description = "Enables legacy content to be written")
53 boolean write$_$legacy() default false;
56 private static final Logger LOG = LoggerFactory.getLogger(YangLibraryWriter.class);
57 private static final InstanceIdentifier<YangLibrary> YANG_LIBRARY_INSTANCE_IDENTIFIER =
58 InstanceIdentifier.create(YangLibrary.class);
59 private static final InstanceIdentifier<ModulesState> MODULES_STATE_INSTANCE_IDENTIFIER =
60 InstanceIdentifier.create(ModulesState.class);
62 private final AtomicLong idCounter = new AtomicLong(0L);
63 private final DataBroker dataBroker;
64 private final boolean writeLegacy;
66 @Reference(cardinality = ReferenceCardinality.OPTIONAL)
67 volatile YangLibrarySchemaSourceUrlProvider schemaSourceUrlProvider;
70 private Registration reg;
74 public YangLibraryWriter(final @Reference DOMSchemaService schemaService,
75 final @Reference DataBroker dataBroker, final Configuration configuration) {
76 this.dataBroker = requireNonNull(dataBroker);
77 this.writeLegacy = configuration.write$_$legacy();
78 reg = schemaService.registerSchemaContextListener(this);
84 public synchronized void close() throws InterruptedException, ExecutionException {
92 // FIXME: we should be using a transaction chain for this, but, really, this should be a dynamically-populated
93 // shard (i.e. no storage whatsoever)!
94 final var tx = dataBroker.newWriteOnlyTransaction();
95 tx.delete(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER);
97 tx.delete(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER);
100 final var future = tx.commit();
101 future.addCallback(new FutureCallback<CommitInfo>() {
103 public void onSuccess(final CommitInfo info) {
104 LOG.debug("YANG library cleared successfully");
108 public void onFailure(final Throwable throwable) {
109 LOG.warn("Unable to clear YANG library", throwable);
111 }, MoreExecutors.directExecutor());
113 // We need to synchronize here, otherwise we'd end up trampling over ourselves
118 public void onModelContextUpdated(final EffectiveModelContext context) {
119 if (context.findModule(YangLibrary.QNAME.getModule()).isPresent()) {
120 updateYangLibrary(context);
122 LOG.warn("ietf-yang-library not present in context, skipping update");
126 private synchronized void updateYangLibrary(final EffectiveModelContext context) {
128 // Already shut down, do not do anything
131 final var nextId = String.valueOf(idCounter.incrementAndGet());
132 final var tx = dataBroker.newWriteOnlyTransaction();
133 tx.put(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER,
134 YangLibraryContentBuilderUtil.buildYangLibrary(context, nextId, schemaSourceUrlProvider));
136 tx.put(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER,
137 YangLibraryContentBuilderUtil.buildModuleState(context, nextId, schemaSourceUrlProvider));
140 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
142 public void onSuccess(final CommitInfo result) {
143 LOG.debug("Yang library updated successfully");
147 public void onFailure(final Throwable throwable) {
148 LOG.warn("Failed to update yang library", throwable);
150 }, MoreExecutors.directExecutor());