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.restconf.nb.rfc8040.utils.mapping;
11 import java.time.Instant;
12 import java.time.OffsetDateTime;
13 import java.time.ZoneId;
14 import java.time.format.DateTimeFormatter;
15 import java.util.Collection;
16 import java.util.List;
18 import java.util.Optional;
19 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
20 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
21 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule;
22 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.MonitoringModule.QueryParams;
23 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
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.module.list.Module.ConformanceType;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.Revision;
28 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
29 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
30 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
31 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
37 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
38 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
39 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
42 import org.opendaylight.yangtools.yang.model.api.Deviation;
43 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
44 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
45 import org.opendaylight.yangtools.yang.model.api.Module;
46 import org.opendaylight.yangtools.yang.model.api.ModuleLike;
47 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
48 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
49 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.Submodule;
53 * Util class for mapping nodes.
56 public final class RestconfMappingNodeUtil {
57 private RestconfMappingNodeUtil() {
62 * Map data from modules to {@link NormalizedNode}.
64 * @param modules modules for mapping
65 * @param context schema context
66 * @param moduleSetId module-set-id of actual set
67 * @return mapped data as {@link NormalizedNode}
69 public static ContainerNode mapModulesByIetfYangLibraryYang(final Collection<? extends Module> modules,
70 final SchemaContext context, final String moduleSetId) {
71 final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder = Builders.orderedMapBuilder()
72 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.MODULE_QNAME_LIST));
73 for (final Module module : context.getModules()) {
74 fillMapByModules(mapBuilder, IetfYangLibrary.MODULE_QNAME_LIST, false, module, context);
76 return Builders.containerBuilder()
77 .withNodeIdentifier(new NodeIdentifier(ModulesState.QNAME))
78 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.MODULE_SET_ID_LEAF_QNAME, moduleSetId))
79 .withChild(mapBuilder.build()).build();
83 * Map data by the specific module or submodule.
86 * ordered list builder for children
88 * QName corresponding to the list builder
90 * true if module is specified as submodule, false otherwise
92 * specific module or submodule
96 private static void fillMapByModules(final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder,
97 final QName mapQName, final boolean isSubmodule, final ModuleLike module, final SchemaContext context) {
98 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
99 newCommonLeafsMapEntryBuilder(mapQName, module);
101 mapEntryBuilder.withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_SCHEMA_LEAF_QNAME,
102 IetfYangLibrary.BASE_URI_OF_SCHEMA + module.getName() + "/"
103 // FIXME: orElse(null) does not seem appropriate here
104 + module.getQNameModule().getRevision().map(Revision::toString).orElse(null)));
107 mapEntryBuilder.withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_NAMESPACE_LEAF_QNAME,
108 module.getNamespace().toString()));
110 // features - not mandatory
111 if (module.getFeatures() != null && !module.getFeatures().isEmpty()) {
112 addFeatureLeafList(mapEntryBuilder, module.getFeatures());
114 // deviations - not mandatory
115 final ConformanceType conformance;
116 if (module.getDeviations() != null && !module.getDeviations().isEmpty()) {
117 addDeviationList(module, mapEntryBuilder, context);
118 conformance = ConformanceType.Implement;
120 conformance = ConformanceType.Import;
122 mapEntryBuilder.withChild(
123 ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_CONFORMANCE_LEAF_QNAME, conformance.getName()));
125 // submodules - not mandatory
126 if (module.getSubmodules() != null && !module.getSubmodules().isEmpty()) {
127 addSubmodules(module, mapEntryBuilder, context);
130 mapBuilder.withChild(mapEntryBuilder.build());
134 * Mapping submodules of specific module.
137 * module with submodules
138 * @param mapEntryBuilder
139 * mapEntryBuilder of parent for mapping children
140 * @param ietfYangLibraryModule
141 * ietf-yang-library module
145 private static void addSubmodules(final ModuleLike module,
146 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
147 final SchemaContext context) {
148 final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder = Builders.orderedMapBuilder()
149 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_SUBMODULE_LIST_QNAME));
151 for (final Submodule submodule : module.getSubmodules()) {
152 fillMapByModules(mapBuilder, IetfYangLibrary.SPECIFIC_MODULE_SUBMODULE_LIST_QNAME, true, submodule,
155 mapEntryBuilder.withChild(mapBuilder.build());
159 * Mapping deviations of specific module.
162 * module with deviations
163 * @param mapEntryBuilder
164 * mapEntryBuilder of parent for mapping children
168 private static void addDeviationList(final ModuleLike module,
169 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
170 final SchemaContext context) {
171 final CollectionNodeBuilder<MapEntryNode, MapNode> deviations = Builders.mapBuilder()
172 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_DEVIATION_LIST_QNAME));
173 for (final Deviation deviation : module.getDeviations()) {
174 final List<QName> ids = deviation.getTargetPath().getNodeIdentifiers();
175 final QName lastComponent = ids.get(ids.size() - 1);
177 deviations.withChild(newCommonLeafsMapEntryBuilder(IetfYangLibrary.SPECIFIC_MODULE_DEVIATION_LIST_QNAME,
178 context.findModule(lastComponent.getModule()).get())
181 mapEntryBuilder.withChild(deviations.build());
185 * Mapping features of specific module.
187 * @param mapEntryBuilder mapEntryBuilder of parent for mapping children
188 * @param features features of specific module
190 private static void addFeatureLeafList(
191 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
192 final Collection<? extends FeatureDefinition> features) {
193 final ListNodeBuilder<String, LeafSetEntryNode<String>> leafSetBuilder = Builders.<String>leafSetBuilder()
194 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_FEATURE_LEAF_LIST_QNAME));
195 for (final FeatureDefinition feature : features) {
196 leafSetBuilder.withChildValue(feature.getQName().getLocalName());
198 mapEntryBuilder.withChild(leafSetBuilder.build());
201 private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> newCommonLeafsMapEntryBuilder(
202 final QName qname, final ModuleLike module) {
203 final var name = module.getName();
204 final var revision = module.getQNameModule().getRevision().map(Revision::toString).orElse("");
205 return Builders.mapEntryBuilder()
206 .withNodeIdentifier(NodeIdentifierWithPredicates.of(qname, Map.of(
207 IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, name,
208 IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, revision)))
209 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, name))
210 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, revision));
214 * Map capabilites by ietf-restconf-monitoring.
216 * @param monitoringModule ietf-restconf-monitoring module
217 * @return mapped capabilites
219 public static ContainerNode mapCapabilites(final Module monitoringModule) {
220 return Builders.containerBuilder()
221 .withNodeIdentifier(new NodeIdentifier(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
222 .withChild(Builders.containerBuilder()
223 .withNodeIdentifier(new NodeIdentifier(MonitoringModule.CONT_CAPABILITES_QNAME))
224 .withChild(Builders.<String>orderedLeafSetBuilder()
225 .withNodeIdentifier(new NodeIdentifier(MonitoringModule.LEAF_LIST_CAPABILITY_QNAME))
226 .withChildValue(QueryParams.DEPTH)
227 .withChildValue(QueryParams.FIELDS)
228 .withChildValue(QueryParams.FILTER)
229 .withChildValue(QueryParams.REPLAY)
230 .withChildValue(QueryParams.WITH_DEFAULTS)
237 * Map data of yang notification to normalized node according to ietf-restconf-monitoring.
239 * @param notifiQName qname of notification from listener
240 * @param notifications list of notifications for find schema of notification by notifiQName
241 * @param start start-time query parameter of notification
242 * @param outputType output type of notification
243 * @param uri location of registered listener for sending data of notification
244 * @return mapped data of notification - map entry node if parent exists,
245 * container streams with list and map entry node if not
247 public static MapEntryNode mapYangNotificationStreamByIetfRestconfMonitoring(final QName notifiQName,
248 final Collection<? extends NotificationDefinition> notifications, final Instant start,
249 final String outputType, final URI uri) {
250 for (final NotificationDefinition notificationDefinition : notifications) {
251 if (notificationDefinition.getQName().equals(notifiQName)) {
252 final String streamName = notifiQName.getLocalName();
253 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
254 Builders.mapEntryBuilder()
255 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MonitoringModule.LIST_STREAM_QNAME,
256 MonitoringModule.LEAF_NAME_STREAM_QNAME, streamName))
257 .withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_NAME_STREAM_QNAME, streamName));
259 notificationDefinition.getDescription().ifPresent(
260 desc -> streamEntry.withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_DESCR_STREAM_QNAME,
262 streamEntry.withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME,
265 streamEntry.withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_START_TIME_STREAM_QNAME,
266 DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start,
267 ZoneId.systemDefault()))));
271 .withChild(createAccessList(outputType, uri))
276 throw new RestconfDocumentedException(notifiQName + " doesn't exist in any modul");
279 private static MapNode createAccessList(final String outputType, final URI uriToWebsocketServer) {
280 return Builders.mapBuilder()
281 .withNodeIdentifier(new NodeIdentifier(MonitoringModule.LIST_ACCESS_STREAM_QNAME))
282 .withChild(Builders.mapEntryBuilder()
283 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MonitoringModule.LIST_ACCESS_STREAM_QNAME,
284 MonitoringModule.LEAF_ENCODING_ACCESS_QNAME, outputType))
285 .withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME, outputType))
286 .withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME,
287 uriToWebsocketServer.toString()))
293 * Map data of data change notification to normalized node according to ietf-restconf-monitoring.
295 * @param path path of data to listen on
296 * @param start start-time query parameter of notification
297 * @param outputType output type of notification
298 * @param uri location of registered listener for sending data of notification
299 * @param schemaContext schemaContext for parsing instance identifier to get schema node of data
300 * @return mapped data of notification - map entry node if parent exists,
301 * container streams with list and map entry node if not
303 public static MapEntryNode mapDataChangeNotificationStreamByIetfRestconfMonitoring(
304 final YangInstanceIdentifier path, final Instant start, final String outputType, final URI uri,
305 final EffectiveModelContext schemaContext, final String streamName) {
306 final SchemaNode schemaNode = ParserIdentifier
307 .toInstanceIdentifier(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext),
308 schemaContext, Optional.empty())
310 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
311 Builders.mapEntryBuilder()
312 .withNodeIdentifier(NodeIdentifierWithPredicates.of(MonitoringModule.LIST_STREAM_QNAME,
313 MonitoringModule.LEAF_NAME_STREAM_QNAME, streamName))
314 .withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_NAME_STREAM_QNAME, streamName));
316 schemaNode.getDescription().ifPresent(desc ->
317 streamEntry.withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_DESCR_STREAM_QNAME, desc)));
320 .withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME, Boolean.TRUE))
321 .withChild(ImmutableNodes.leafNode(MonitoringModule.LEAF_START_TIME_STREAM_QNAME,
322 DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start, ZoneId.systemDefault()))))
323 .withChild(createAccessList(outputType, uri))