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.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;
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(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;
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);
61 private final AtomicLong idCounter = new AtomicLong(0L);
62 private final DataBroker dataBroker;
63 private final boolean writeLegacy;
65 @Reference(cardinality = ReferenceCardinality.OPTIONAL)
66 volatile YangLibrarySchemaSourceUrlProvider schemaSourceUrlProvider;
69 private Registration reg;
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);
83 public synchronized void close() throws InterruptedException, ExecutionException {
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);
96 tx.delete(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER);
99 final var future = tx.commit();
100 future.addCallback(new FutureCallback<CommitInfo>() {
102 public void onSuccess(final CommitInfo info) {
103 LOG.debug("YANG library cleared successfully");
107 public void onFailure(final Throwable throwable) {
108 LOG.warn("Unable to clear YANG library", throwable);
110 }, MoreExecutors.directExecutor());
112 // We need to synchronize here, otherwise we'd end up trampling over ourselves
116 @VisibleForTesting void onModelContextUpdated(final EffectiveModelContext context) {
117 if (context.findModule(YangLibrary.QNAME.getModule()).isPresent()) {
118 updateYangLibrary(context);
120 LOG.warn("ietf-yang-library not present in context, skipping update");
124 private synchronized void updateYangLibrary(final EffectiveModelContext context) {
126 // Already shut down, do not do anything
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));
134 tx.put(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER,
135 YangLibraryContentBuilderUtil.buildModuleState(context, nextId, schemaSourceUrlProvider));
138 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
140 public void onSuccess(final CommitInfo result) {
141 LOG.debug("Yang library updated successfully");
145 public void onFailure(final Throwable throwable) {
146 LOG.warn("Failed to update yang library", throwable);
148 }, MoreExecutors.directExecutor());