44bcfad37447d7fa429c3f3cc09a8b7fda71c418
[netconf.git] / netconf / mdsal-netconf-yang-library / src / main / java / org / opendaylight / netconf / mdsal / yang / library / YangLibraryWriter.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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.mdsal.yang.library;
9
10 import static java.util.Objects.requireNonNull;
11
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;
16 import java.util.Set;
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.ModulesState;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.ModulesStateBuilder;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibrary;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangLibraryBuilder;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.CommonLeafsRevisionBuilder;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.list.Module.ConformanceType;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.module.set.parameters.ImportOnlyModuleRevisionBuilder;
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.ListenerRegistration;
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;
57
58 /**
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.
61  */
62 @Singleton
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";
68
69     private static final InstanceIdentifier<YangLibrary> YANG_LIBRARY_INSTANCE_IDENTIFIER =
70         InstanceIdentifier.create(YangLibrary.class);
71     @Deprecated
72     private static final InstanceIdentifier<ModulesState> MODULES_STATE_INSTANCE_IDENTIFIER =
73         InstanceIdentifier.create(ModulesState.class);
74
75     private final DataBroker dataBroker;
76     @GuardedBy("this")
77     private long moduleSetId;
78     @GuardedBy("this")
79     private ListenerRegistration<?> reg;
80
81     @Inject
82     @Activate
83     public YangLibraryWriter(final @Reference DOMSchemaService schemaService,
84             final @Reference DataBroker dataBroker) {
85         this.dataBroker = requireNonNull(dataBroker);
86         reg = schemaService.registerSchemaContextListener(this);
87     }
88
89     @Deactivate
90     @PreDestroy
91     @Override
92     public synchronized void close() throws InterruptedException, ExecutionException {
93         if (reg == null) {
94             // Already shut down
95             return;
96         }
97
98         reg.close();
99         reg = null;
100
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);
106
107         final FluentFuture<? extends CommitInfo> future = tx.commit();
108         future.addCallback(new FutureCallback<CommitInfo>() {
109             @Override
110             public void onSuccess(final CommitInfo info) {
111                 LOG.debug("YANG library cleared successfully");
112             }
113
114             @Override
115             public void onFailure(final Throwable throwable) {
116                 LOG.warn("Unable to clear YANG library", throwable);
117             }
118         }, MoreExecutors.directExecutor());
119
120         // We need to synchronize here, otherwise we'd end up trampling over ourselves
121         future.get();
122     }
123
124     @Override
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);
129         } else {
130             LOG.debug("ietf-yang-library not present in context, skipping update");
131         }
132     }
133
134     private synchronized void updateYangLibrary(final EffectiveModelContext context) {
135         if (reg == null) {
136             // Already shut down, do not do anything
137             return;
138         }
139
140         final long currentSetId = moduleSetId++;
141
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);
146
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>() {
151             @Override
152             public void onSuccess(final CommitInfo result) {
153                 LOG.debug("Yang library updated successfully");
154             }
155
156             @Override
157             public void onFailure(final Throwable throwable) {
158                 LOG.warn("Failed to update yang library", throwable);
159             }
160         }, MoreExecutors.directExecutor());
161     }
162
163     private static YangLibrary createYangLibraryFromContext(final Collection<? extends Module> modules,
164             final long moduleSetId) {
165         final var moduleMap = modules.stream()
166             .map(module -> {
167                 final var submoduleMap = module.getSubmodules().stream()
168                     .map(subModule -> new SubmoduleBuilder()
169                         .setName(new YangIdentifier(subModule.getName()))
170                         .setRevision(ImportOnlyModuleRevisionBuilder.fromYangCommon(subModule.getQNameModule()
171                             .getRevision()).getRevisionIdentifier())
172                         .build())
173                     .collect(BindingMap.toMap());
174
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(ImportOnlyModuleRevisionBuilder.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)
186                     .build();
187             })
188             .collect(BindingMap.toMap());
189
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)
195                 .build()))
196             .setSchema(BindingMap.of(new SchemaBuilder()
197                 .setName(SCHEMA_NAME)
198                 .setModuleSet(Set.of(MODULE_SET_NAME))
199                 .build()))
200             .setDatastore(BindingMap.of(new DatastoreBuilder()
201                 .setName(Operational.class)
202                 .setSchema(SCHEMA_NAME)
203                 .build()))
204             .setContentId(String.valueOf(moduleSetId))
205             .build();
206     }
207
208     @Deprecated
209     private static ModulesState createModuleStateFromModules(final Collection<? extends Module> modules,
210             final long moduleSetId) {
211         final var moduleMap = modules.stream()
212             .map(module -> {
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(CommonLeafsRevisionBuilder.fromYangCommon(subModule.getQNameModule()
218                             .getRevision()))
219                         .build())
220                     .collect(BindingMap.toMap());
221
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(CommonLeafsRevisionBuilder.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
233                     .build();
234             })
235             .collect(BindingMap.toMap());
236
237         return new ModulesStateBuilder()
238             // FIXME: inline this once it's disambiguated
239             .setModule(moduleMap)
240             .setModuleSetId(String.valueOf(moduleSetId))
241             .build();
242     }
243
244     private static Set<YangIdentifier> extractFeatures(final ModuleLike module) {
245         final var namespace = module.getQNameModule();
246
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());
253     }
254 }