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.YangInstanceIdentifier.NodeWithValue;
32 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
38 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
39 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
40 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
41 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
42 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
43 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.Deviation;
46 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
47 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
48 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
51 import org.opendaylight.yangtools.yang.model.api.Module;
52 import org.opendaylight.yangtools.yang.model.api.ModuleLike;
53 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
54 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
55 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.Submodule;
59 * Util class for mapping nodes.
62 public final class RestconfMappingNodeUtil {
63 private RestconfMappingNodeUtil() {
68 * Map data from modules to {@link NormalizedNode}.
70 * @param modules modules for mapping
71 * @param context schema context
72 * @param moduleSetId module-set-id of actual set
73 * @return mapped data as {@link NormalizedNode}
75 public static ContainerNode mapModulesByIetfYangLibraryYang(final Collection<? extends Module> modules,
76 final SchemaContext context, final String moduleSetId) {
77 final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder = Builders.orderedMapBuilder()
78 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.MODULE_QNAME_LIST));
79 for (final Module module : context.getModules()) {
80 fillMapByModules(mapBuilder, IetfYangLibrary.MODULE_QNAME_LIST, false, module, context);
82 return Builders.containerBuilder()
83 .withNodeIdentifier(new NodeIdentifier(ModulesState.QNAME))
84 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.MODULE_SET_ID_LEAF_QNAME, moduleSetId))
85 .withChild(mapBuilder.build()).build();
89 * Map data by the specific module or submodule.
92 * ordered list builder for children
94 * QName corresponding to the list builder
96 * true if module is specified as submodule, false otherwise
98 * specific module or submodule
102 private static void fillMapByModules(final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder,
103 final QName mapQName, final boolean isSubmodule, final ModuleLike module, final SchemaContext context) {
104 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
105 newCommonLeafsMapEntryBuilder(mapQName, module);
107 mapEntryBuilder.withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_SCHEMA_LEAF_QNAME,
108 IetfYangLibrary.BASE_URI_OF_SCHEMA + module.getName() + "/"
109 // FIXME: orElse(null) does not seem appropriate here
110 + module.getQNameModule().getRevision().map(Revision::toString).orElse(null)));
113 mapEntryBuilder.withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_NAMESPACE_LEAF_QNAME,
114 module.getNamespace().toString()));
116 // features - not mandatory
117 if (module.getFeatures() != null && !module.getFeatures().isEmpty()) {
118 addFeatureLeafList(mapEntryBuilder, module.getFeatures());
120 // deviations - not mandatory
121 final ConformanceType conformance;
122 if (module.getDeviations() != null && !module.getDeviations().isEmpty()) {
123 addDeviationList(module, mapEntryBuilder, context);
124 conformance = ConformanceType.Implement;
126 conformance = ConformanceType.Import;
128 mapEntryBuilder.withChild(
129 ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_CONFORMANCE_LEAF_QNAME, conformance.getName()));
131 // submodules - not mandatory
132 if (module.getSubmodules() != null && !module.getSubmodules().isEmpty()) {
133 addSubmodules(module, mapEntryBuilder, context);
136 mapBuilder.withChild(mapEntryBuilder.build());
140 * Mapping submodules of specific module.
143 * module with submodules
144 * @param mapEntryBuilder
145 * mapEntryBuilder of parent for mapping children
146 * @param ietfYangLibraryModule
147 * ietf-yang-library module
151 private static void addSubmodules(final ModuleLike module,
152 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
153 final SchemaContext context) {
154 final CollectionNodeBuilder<MapEntryNode, OrderedMapNode> mapBuilder = Builders.orderedMapBuilder()
155 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_SUBMODULE_LIST_QNAME));
157 for (final Submodule submodule : module.getSubmodules()) {
158 fillMapByModules(mapBuilder, IetfYangLibrary.SPECIFIC_MODULE_SUBMODULE_LIST_QNAME, true, submodule,
161 mapEntryBuilder.withChild(mapBuilder.build());
165 * Mapping deviations of specific module.
168 * module with deviations
169 * @param mapEntryBuilder
170 * mapEntryBuilder of parent for mapping children
174 private static void addDeviationList(final ModuleLike module,
175 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
176 final SchemaContext context) {
177 final CollectionNodeBuilder<MapEntryNode, MapNode> deviations = Builders.mapBuilder()
178 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_DEVIATION_LIST_QNAME));
179 for (final Deviation deviation : module.getDeviations()) {
180 final List<QName> ids = deviation.getTargetPath().getNodeIdentifiers();
181 final QName lastComponent = ids.get(ids.size() - 1);
183 deviations.withChild(newCommonLeafsMapEntryBuilder(IetfYangLibrary.SPECIFIC_MODULE_DEVIATION_LIST_QNAME,
184 context.findModule(lastComponent.getModule()).get())
187 mapEntryBuilder.withChild(deviations.build());
191 * Mapping features of specific module.
193 * @param mapEntryBuilder mapEntryBuilder of parent for mapping children
194 * @param features features of specific module
196 private static void addFeatureLeafList(
197 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
198 final Collection<? extends FeatureDefinition> features) {
199 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafSetBuilder = Builders.leafSetBuilder()
200 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_FEATURE_LEAF_LIST_QNAME));
201 for (final FeatureDefinition feature : features) {
202 final String featureName = feature.getQName().getLocalName();
203 leafSetBuilder.withChild(Builders.leafSetEntryBuilder()
205 new NodeWithValue<>(IetfYangLibrary.SPECIFIC_MODULE_FEATURE_LEAF_LIST_QNAME, featureName))
206 .withValue(featureName)
209 mapEntryBuilder.withChild(leafSetBuilder.build());
212 private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> newCommonLeafsMapEntryBuilder(
213 final QName qname, final ModuleLike module) {
214 final var name = module.getName();
215 final var revision = module.getQNameModule().getRevision().map(Revision::toString).orElse("");
216 return Builders.mapEntryBuilder()
217 .withNodeIdentifier(NodeIdentifierWithPredicates.of(qname, Map.of(
218 IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, name,
219 IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, revision)))
220 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, name))
221 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, revision));
225 * Map capabilites by ietf-restconf-monitoring.
227 * @param monitoringModule
228 * ietf-restconf-monitoring module
229 * @return mapped capabilites
231 public static ContainerNode mapCapabilites(final Module monitoringModule) {
232 final DataSchemaNode restconfState =
233 monitoringModule.getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME);
234 final DataSchemaNode capabilitesContSchema =
235 getChildOfCont((ContainerSchemaNode) restconfState, MonitoringModule.CONT_CAPABILITES_QNAME);
236 final DataContainerNodeBuilder<NodeIdentifier, ContainerNode> capabilitesContBuilder =
237 Builders.containerBuilder((ContainerSchemaNode) capabilitesContSchema);
238 final DataSchemaNode leafListCapa = getChildOfCont((ContainerSchemaNode) capabilitesContSchema,
239 MonitoringModule.LEAF_LIST_CAPABILITY_QNAME);
240 final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafListCapaBuilder =
241 Builders.orderedLeafSetBuilder((LeafListSchemaNode) leafListCapa);
242 fillLeafListCapa(leafListCapaBuilder, (LeafListSchemaNode) leafListCapa);
244 return Builders.containerBuilder()
245 .withNodeIdentifier(new NodeIdentifier(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
246 .withChild(capabilitesContBuilder.withChild(leafListCapaBuilder.build()).build())
251 * Map data to leaf-list.
254 * builder of parent for children
255 * @param leafListSchema
258 private static void fillLeafListCapa(final ListNodeBuilder<Object, LeafSetEntryNode<Object>> builder,
259 final LeafListSchemaNode leafListSchema) {
260 builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.DEPTH));
261 builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.FIELDS));
262 builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.FILTER));
263 builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.REPLAY));
264 builder.withChild(leafListEntryBuild(leafListSchema, QueryParams.WITH_DEFAULTS));
268 * Map value to leaf list entry node.
270 * @param leafListSchema
271 * leaf list schema of leaf list entry
273 * value of leaf entry
276 private static LeafSetEntryNode<Object> leafListEntryBuild(final LeafListSchemaNode leafListSchema,
277 final String value) {
278 return Builders.leafSetEntryBuilder(leafListSchema).withValue(value).build();
282 * Find specific schema node by qname in parent {@link ContainerSchemaNode}.
287 * specific qname of child
288 * @return schema node of child by qname
290 private static DataSchemaNode getChildOfCont(final ContainerSchemaNode parent, final QName childQName) {
291 for (final DataSchemaNode child : parent.getChildNodes()) {
292 if (child.getQName().equals(childQName)) {
296 throw new RestconfDocumentedException(
297 childQName.getLocalName() + " doesn't exist in container " + MonitoringModule.CONT_RESTCONF_STATE_NAME);
301 * Map data of yang notification to normalized node according to
302 * ietf-restconf-monitoring.
305 * qname of notification from listener
306 * @param notifications
307 * list of notifications for find schema of notification by
310 * start-time query parameter of notification
312 * output type of notification
314 * location of registered listener for sending data of
316 * @param monitoringModule
317 * ietf-restconf-monitoring module
318 * @return mapped data of notification - map entry node if parent exists,
319 * container streams with list and map entry node if not
321 public static MapEntryNode mapYangNotificationStreamByIetfRestconfMonitoring(final QName notifiQName,
322 final Collection<? extends NotificationDefinition> notifications, final Instant start,
323 final String outputType, final URI uri, final Module monitoringModule) {
324 for (final NotificationDefinition notificationDefinition : notifications) {
325 if (notificationDefinition.getQName().equals(notifiQName)) {
326 final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule
327 .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
328 .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME))
329 .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME);
330 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
331 Builders.mapEntryBuilder((ListSchemaNode) streamListSchema);
333 final ListSchemaNode listSchema = (ListSchemaNode) streamListSchema;
334 prepareLeafAndFillEntryBuilder(streamEntry,
335 listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME),
336 notificationDefinition.getQName().getLocalName());
338 final Optional<String> optDesc = notificationDefinition.getDescription();
339 if (optDesc.isPresent()) {
340 prepareLeafAndFillEntryBuilder(streamEntry,
341 listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME), optDesc.get());
343 prepareLeafAndFillEntryBuilder(streamEntry,
344 listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true);
346 prepareLeafAndFillEntryBuilder(streamEntry,
347 listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME),
348 DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start,
349 ZoneId.systemDefault())));
351 prepareListAndFillEntryBuilder(streamEntry,
352 (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME),
355 return streamEntry.build();
359 throw new RestconfDocumentedException(notifiQName + " doesn't exist in any modul");
362 private static void prepareListAndFillEntryBuilder(
363 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry,
364 final ListSchemaNode listSchemaNode, final String outputType, final URI uriToWebsocketServer) {
365 final CollectionNodeBuilder<MapEntryNode, MapNode> accessListBuilder = Builders.mapBuilder(listSchemaNode);
366 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> entryAccessList =
367 Builders.mapEntryBuilder(listSchemaNode);
368 prepareLeafAndFillEntryBuilder(entryAccessList,
369 listSchemaNode.getDataChildByName(MonitoringModule.LEAF_ENCODING_ACCESS_QNAME), outputType);
370 prepareLeafAndFillEntryBuilder(entryAccessList,
371 listSchemaNode.getDataChildByName(MonitoringModule.LEAF_LOCATION_ACCESS_QNAME),
372 uriToWebsocketServer.toString());
373 streamEntry.withChild(accessListBuilder.withChild(entryAccessList.build()).build());
377 * Prepare leaf and fill entry builder.
379 * @param streamEntry Stream entry
380 * @param leafSchema Leaf schema
383 private static void prepareLeafAndFillEntryBuilder(
384 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry,
385 final DataSchemaNode leafSchema, final Object value) {
386 streamEntry.withChild(Builders.leafBuilder((LeafSchemaNode) leafSchema).withValue(value).build());
390 * Map data of data change notification to normalized node according to
391 * ietf-restconf-monitoring.
394 * path of data to listen on
396 * start-time query parameter of notification
398 * output type of notification
400 * location of registered listener for sending data of
402 * @param monitoringModule
403 * ietf-restconf-monitoring module
404 * @param schemaContext
405 * schemaContext for parsing instance identifier to get schema
407 * @return mapped data of notification - map entry node if parent exists,
408 * container streams with list and map entry node if not
410 public static MapEntryNode mapDataChangeNotificationStreamByIetfRestconfMonitoring(
411 final YangInstanceIdentifier path, final Instant start, final String outputType, final URI uri,
412 final Module monitoringModule, final EffectiveModelContext schemaContext, final String streamName) {
413 final SchemaNode schemaNode = ParserIdentifier
414 .toInstanceIdentifier(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext),
415 schemaContext, Optional.empty())
417 final DataSchemaNode streamListSchema = ((ContainerSchemaNode) ((ContainerSchemaNode) monitoringModule
418 .getDataChildByName(MonitoringModule.CONT_RESTCONF_STATE_QNAME))
419 .getDataChildByName(MonitoringModule.CONT_STREAMS_QNAME))
420 .getDataChildByName(MonitoringModule.LIST_STREAM_QNAME);
421 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
422 Builders.mapEntryBuilder((ListSchemaNode) streamListSchema);
424 final ListSchemaNode listSchema = (ListSchemaNode) streamListSchema;
425 prepareLeafAndFillEntryBuilder(streamEntry,
426 listSchema.getDataChildByName(MonitoringModule.LEAF_NAME_STREAM_QNAME), streamName);
428 final Optional<String> optDesc = schemaNode.getDescription();
429 if (optDesc.isPresent()) {
430 prepareLeafAndFillEntryBuilder(streamEntry,
431 listSchema.getDataChildByName(MonitoringModule.LEAF_DESCR_STREAM_QNAME), optDesc.get());
433 prepareLeafAndFillEntryBuilder(streamEntry,
434 listSchema.getDataChildByName(MonitoringModule.LEAF_REPLAY_SUPP_STREAM_QNAME), true);
435 prepareLeafAndFillEntryBuilder(streamEntry,
436 listSchema.getDataChildByName(MonitoringModule.LEAF_START_TIME_STREAM_QNAME),
437 DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start, ZoneId.systemDefault())));
438 prepareListAndFillEntryBuilder(streamEntry,
439 (ListSchemaNode) listSchema.getDataChildByName(MonitoringModule.LIST_ACCESS_STREAM_QNAME), outputType,
442 return streamEntry.build();