2 * Copyright (c) 2016 Cisco Systems, Inc. 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.mdsal.yang.library;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.util.concurrent.FluentFuture;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import java.util.Collection;
17 import java.util.concurrent.ExecutionException;
18 import java.util.stream.Collectors;
19 import javax.annotation.PreDestroy;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.checkerframework.checker.lock.qual.GuardedBy;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.mdsal.binding.api.WriteTransaction;
25 import org.opendaylight.mdsal.common.api.CommitInfo;
26 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
27 import org.opendaylight.mdsal.dom.api.DOMSchemaService;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.datastores.rev180214.Operational;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.LegacyRevisionUtils;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesState;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesStateBuilder;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.RevisionUtils;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibraryBuilder;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType;
37 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.module.SubmoduleBuilder;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.DatastoreBuilder;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.ModuleSetBuilder;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.yang.library.parameters.SchemaBuilder;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.YangIdentifier;
42 import org.opendaylight.yangtools.concepts.Registration;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.opendaylight.yangtools.yang.binding.util.BindingMap;
45 import org.opendaylight.yangtools.yang.common.Revision;
46 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
47 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContextListener;
48 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
49 import org.opendaylight.yangtools.yang.model.api.Module;
50 import org.opendaylight.yangtools.yang.model.api.ModuleLike;
51 import org.osgi.service.component.annotations.Activate;
52 import org.osgi.service.component.annotations.Component;
53 import org.osgi.service.component.annotations.Deactivate;
54 import org.osgi.service.component.annotations.Reference;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
59 * Listens for updates on global schema context, transforms context to ietf-yang-library/yang-library and writes this
60 * state to operational data store.
63 @Component(immediate = true, service = {})
64 public final class YangLibraryWriter implements EffectiveModelContextListener, AutoCloseable {
65 private static final Logger LOG = LoggerFactory.getLogger(YangLibraryWriter.class);
66 private static final String MODULE_SET_NAME = "state-modules";
67 private static final String SCHEMA_NAME = "state-schema";
69 private static final InstanceIdentifier<YangLibrary> YANG_LIBRARY_INSTANCE_IDENTIFIER =
70 InstanceIdentifier.create(YangLibrary.class);
72 private static final InstanceIdentifier<ModulesState> MODULES_STATE_INSTANCE_IDENTIFIER =
73 InstanceIdentifier.create(ModulesState.class);
75 private final DataBroker dataBroker;
77 private long moduleSetId;
79 private Registration reg;
83 public YangLibraryWriter(final @Reference DOMSchemaService schemaService,
84 final @Reference DataBroker dataBroker) {
85 this.dataBroker = requireNonNull(dataBroker);
86 reg = schemaService.registerSchemaContextListener(this);
92 public synchronized void close() throws InterruptedException, ExecutionException {
101 // FIXME: we should be using a transaction chain for this, but, really, this should be a dynamically-populated
102 // shard (i.e. no storage whatsoever)!
103 final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
104 tx.delete(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER);
105 tx.delete(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER);
107 final FluentFuture<? extends CommitInfo> future = tx.commit();
108 future.addCallback(new FutureCallback<CommitInfo>() {
110 public void onSuccess(final CommitInfo info) {
111 LOG.debug("YANG library cleared successfully");
115 public void onFailure(final Throwable throwable) {
116 LOG.warn("Unable to clear YANG library", throwable);
118 }, MoreExecutors.directExecutor());
120 // We need to synchronize here, otherwise we'd end up trampling over ourselves
125 public void onModelContextUpdated(final EffectiveModelContext context) {
126 final Module ietfYangLibraryModule = context.findModule(YangLibrary.QNAME.getModule()).orElse(null);
127 if (ietfYangLibraryModule != null) {
128 updateYangLibrary(context);
130 LOG.debug("ietf-yang-library not present in context, skipping update");
134 private synchronized void updateYangLibrary(final EffectiveModelContext context) {
136 // Already shut down, do not do anything
140 final long currentSetId = moduleSetId++;
142 final YangLibrary newYangLibrary = createYangLibraryFromContext(context.getModules(), currentSetId);
143 final ModulesState newModuleState = createModuleStateFromModules(context.getModules(), currentSetId);
144 LOG.debug("Trying to write new yang-library: {}", newYangLibrary);
145 LOG.debug("Trying to write new module-state: {}", newModuleState);
147 final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
148 tx.put(LogicalDatastoreType.OPERATIONAL, YANG_LIBRARY_INSTANCE_IDENTIFIER, newYangLibrary);
149 tx.put(LogicalDatastoreType.OPERATIONAL, MODULES_STATE_INSTANCE_IDENTIFIER, newModuleState);
150 tx.commit().addCallback(new FutureCallback<CommitInfo>() {
152 public void onSuccess(final CommitInfo result) {
153 LOG.debug("Yang library updated successfully");
157 public void onFailure(final Throwable throwable) {
158 LOG.warn("Failed to update yang library", throwable);
160 }, MoreExecutors.directExecutor());
163 private static YangLibrary createYangLibraryFromContext(final Collection<? extends Module> modules,
164 final long moduleSetId) {
165 final var moduleMap = modules.stream()
167 final var submoduleMap = module.getSubmodules().stream()
168 .map(subModule -> new SubmoduleBuilder()
169 .setName(new YangIdentifier(subModule.getName()))
170 .setRevision(RevisionUtils.fromYangCommon(subModule.getQNameModule().getRevision())
171 .getRevisionIdentifier())
173 .collect(BindingMap.toMap());
175 return new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library
176 .rev190104.module.set.parameters.ModuleBuilder()
177 .setName(new YangIdentifier(module.getName() + "_"
178 // FIXME: 'orElse' seems to be wrong here
179 + module.getRevision().map(Revision::toString).orElse(null)))
180 .setRevision(RevisionUtils.fromYangCommon(module.getQNameModule().getRevision())
181 .getRevisionIdentifier())
182 .setNamespace(new Uri(module.getNamespace().toString()))
183 .setFeature(extractFeatures(module))
184 // FIXME: inline this once it's disambiguated
185 .setSubmodule(submoduleMap)
188 .collect(BindingMap.toMap());
190 return new YangLibraryBuilder()
191 .setModuleSet(BindingMap.of(new ModuleSetBuilder()
192 .setName(MODULE_SET_NAME)
193 // FIXME: inline this once it's disambiguated
194 .setModule(moduleMap)
196 .setSchema(BindingMap.of(new SchemaBuilder()
197 .setName(SCHEMA_NAME)
198 .setModuleSet(Set.of(MODULE_SET_NAME))
200 .setDatastore(BindingMap.of(new DatastoreBuilder()
201 .setName(Operational.VALUE)
202 .setSchema(SCHEMA_NAME)
204 .setContentId(String.valueOf(moduleSetId))
209 private static ModulesState createModuleStateFromModules(final Collection<? extends Module> modules,
210 final long moduleSetId) {
211 final var moduleMap = modules.stream()
213 final var submoduleMap = module.getSubmodules().stream()
214 .map(subModule -> new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library
215 .rev190104.module.list.module.SubmoduleBuilder()
216 .setName(new YangIdentifier(subModule.getName()))
217 .setRevision(LegacyRevisionUtils.fromYangCommon(subModule.getQNameModule()
220 .collect(BindingMap.toMap());
222 return new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library
223 .rev190104.module.list.ModuleBuilder()
224 .setName(new YangIdentifier(module.getName()))
225 .setRevision(LegacyRevisionUtils.fromYangCommon(module.getQNameModule().getRevision()))
226 .setNamespace(new Uri(module.getNamespace().toString()))
227 // FIXME: Conformance type is always set to Implement value, but it should it really be like this?
228 .setConformanceType(ConformanceType.Implement)
229 // FIXME: inline this once it's disambiguated
230 .setSubmodule(submoduleMap)
231 .setFeature(extractFeatures(module))
232 // FIXME: Add also deviations to module entries
235 .collect(BindingMap.toMap());
237 return new ModulesStateBuilder()
238 // FIXME: inline this once it's disambiguated
239 .setModule(moduleMap)
240 .setModuleSetId(String.valueOf(moduleSetId))
244 private static Set<YangIdentifier> extractFeatures(final ModuleLike module) {
245 final var namespace = module.getQNameModule();
247 return module.getFeatures().stream()
248 .map(FeatureDefinition::getQName)
249 // belt-and-suspenders: make sure the feature namespace matches
250 .filter(featureName -> namespace.equals(featureName.getModule()))
251 .map(featureName -> new YangIdentifier(featureName.getLocalName()))
252 .collect(Collectors.toUnmodifiableSet());