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;
10 import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.$YangModuleInfoImpl.qnameOf;
12 import com.google.common.annotations.VisibleForTesting;
14 import java.time.Instant;
15 import java.time.OffsetDateTime;
16 import java.time.ZoneId;
17 import java.time.format.DateTimeFormatter;
18 import java.util.Collection;
19 import java.util.List;
21 import java.util.Optional;
22 import org.opendaylight.restconf.common.errors.RestconfDocumentedException;
23 import org.opendaylight.restconf.nb.rfc8040.Rfc8040;
24 import org.opendaylight.restconf.nb.rfc8040.Rfc8040.IetfYangLibrary;
25 import org.opendaylight.restconf.nb.rfc8040.utils.parser.ParserIdentifier;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.RestconfState;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.restconf.state.Capabilities;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.restconf.state.streams.Stream;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.restconf.monitoring.rev170126.restconf.state.streams.stream.Access;
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.module.list.Module.ConformanceType;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.common.Revision;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
37 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
39 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.SystemLeafSetNode;
42 import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.builder.CollectionNodeBuilder;
45 import org.opendaylight.yangtools.yang.data.api.schema.builder.DataContainerNodeBuilder;
46 import org.opendaylight.yangtools.yang.data.api.schema.builder.ListNodeBuilder;
47 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
48 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
49 import org.opendaylight.yangtools.yang.model.api.Deviation;
50 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
51 import org.opendaylight.yangtools.yang.model.api.FeatureDefinition;
52 import org.opendaylight.yangtools.yang.model.api.Module;
53 import org.opendaylight.yangtools.yang.model.api.ModuleLike;
54 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
55 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
56 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
57 import org.opendaylight.yangtools.yang.model.api.Submodule;
60 * Util class for mapping nodes.
62 public final class RestconfMappingNodeUtil {
63 private static final QName CAPABILITY_QNAME = qnameOf("capability");
65 static final QName DESCRIPTION_QNAME = qnameOf("description");
67 static final QName ENCODING_QNAME = qnameOf("encoding");
69 static final QName LOCATION_QNAME = qnameOf("location");
71 static final QName NAME_QNAME = qnameOf("name");
73 static final QName REPLAY_SUPPORT_QNAME = qnameOf("replay-support");
75 static final QName REPLAY_LOG_CREATION_TIME = qnameOf("replay-log-creation-time");
77 private RestconfMappingNodeUtil() {
82 * Map data from modules to {@link NormalizedNode}.
84 * @param modules modules for mapping
85 * @param context schema context
86 * @param moduleSetId module-set-id of actual set
87 * @return mapped data as {@link NormalizedNode}
89 public static ContainerNode mapModulesByIetfYangLibraryYang(final Collection<? extends Module> modules,
90 final SchemaContext context, final String moduleSetId) {
91 final CollectionNodeBuilder<MapEntryNode, UserMapNode> mapBuilder = Builders.orderedMapBuilder()
92 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.MODULE_QNAME_LIST));
93 for (final Module module : context.getModules()) {
94 fillMapByModules(mapBuilder, IetfYangLibrary.MODULE_QNAME_LIST, false, module, context);
96 return Builders.containerBuilder()
97 .withNodeIdentifier(new NodeIdentifier(ModulesState.QNAME))
98 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.MODULE_SET_ID_LEAF_QNAME, moduleSetId))
99 .withChild(mapBuilder.build()).build();
103 * Map data by the specific module or submodule.
106 * ordered list builder for children
108 * QName corresponding to the list builder
110 * true if module is specified as submodule, false otherwise
112 * specific module or submodule
116 private static void fillMapByModules(final CollectionNodeBuilder<MapEntryNode, UserMapNode> mapBuilder,
117 final QName mapQName, final boolean isSubmodule, final ModuleLike module, final SchemaContext context) {
118 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder =
119 newCommonLeafsMapEntryBuilder(mapQName, module);
121 mapEntryBuilder.withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_SCHEMA_LEAF_QNAME,
122 IetfYangLibrary.BASE_URI_OF_SCHEMA + module.getName() + "/"
123 // FIXME: orElse(null) does not seem appropriate here
124 + module.getQNameModule().getRevision().map(Revision::toString).orElse(null)));
127 mapEntryBuilder.withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_NAMESPACE_LEAF_QNAME,
128 module.getNamespace().toString()));
130 // features - not mandatory
131 if (module.getFeatures() != null && !module.getFeatures().isEmpty()) {
132 addFeatureLeafList(mapEntryBuilder, module.getFeatures());
134 // deviations - not mandatory
135 final ConformanceType conformance;
136 if (module.getDeviations() != null && !module.getDeviations().isEmpty()) {
137 addDeviationList(module, mapEntryBuilder, context);
138 conformance = ConformanceType.Implement;
140 conformance = ConformanceType.Import;
142 mapEntryBuilder.withChild(
143 ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_CONFORMANCE_LEAF_QNAME, conformance.getName()));
145 // submodules - not mandatory
146 if (module.getSubmodules() != null && !module.getSubmodules().isEmpty()) {
147 addSubmodules(module, mapEntryBuilder, context);
150 mapBuilder.withChild(mapEntryBuilder.build());
154 * Mapping submodules of specific module.
157 * module with submodules
158 * @param mapEntryBuilder
159 * mapEntryBuilder of parent for mapping children
160 * @param ietfYangLibraryModule
161 * ietf-yang-library module
165 private static void addSubmodules(final ModuleLike module,
166 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
167 final SchemaContext context) {
168 final CollectionNodeBuilder<MapEntryNode, UserMapNode> mapBuilder = Builders.orderedMapBuilder()
169 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_SUBMODULE_LIST_QNAME));
171 for (final Submodule submodule : module.getSubmodules()) {
172 fillMapByModules(mapBuilder, IetfYangLibrary.SPECIFIC_MODULE_SUBMODULE_LIST_QNAME, true, submodule,
175 mapEntryBuilder.withChild(mapBuilder.build());
179 * Mapping deviations of specific module.
182 * module with deviations
183 * @param mapEntryBuilder
184 * mapEntryBuilder of parent for mapping children
188 private static void addDeviationList(final ModuleLike module,
189 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
190 final SchemaContext context) {
191 final CollectionNodeBuilder<MapEntryNode, SystemMapNode> deviations = Builders.mapBuilder()
192 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_DEVIATION_LIST_QNAME));
193 for (final Deviation deviation : module.getDeviations()) {
194 final List<QName> ids = deviation.getTargetPath().getNodeIdentifiers();
195 final QName lastComponent = ids.get(ids.size() - 1);
197 deviations.withChild(newCommonLeafsMapEntryBuilder(IetfYangLibrary.SPECIFIC_MODULE_DEVIATION_LIST_QNAME,
198 context.findModule(lastComponent.getModule()).get())
201 mapEntryBuilder.withChild(deviations.build());
205 * Mapping features of specific module.
207 * @param mapEntryBuilder mapEntryBuilder of parent for mapping children
208 * @param features features of specific module
210 private static void addFeatureLeafList(
211 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder,
212 final Collection<? extends FeatureDefinition> features) {
213 final ListNodeBuilder<String, SystemLeafSetNode<String>> leafSetBuilder = Builders.<String>leafSetBuilder()
214 .withNodeIdentifier(new NodeIdentifier(IetfYangLibrary.SPECIFIC_MODULE_FEATURE_LEAF_LIST_QNAME));
215 for (final FeatureDefinition feature : features) {
216 leafSetBuilder.withChildValue(feature.getQName().getLocalName());
218 mapEntryBuilder.withChild(leafSetBuilder.build());
221 private static DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> newCommonLeafsMapEntryBuilder(
222 final QName qname, final ModuleLike module) {
223 final var name = module.getName();
224 final var revision = module.getQNameModule().getRevision().map(Revision::toString).orElse("");
225 return Builders.mapEntryBuilder()
226 .withNodeIdentifier(NodeIdentifierWithPredicates.of(qname, Map.of(
227 IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, name,
228 IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, revision)))
229 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_NAME_LEAF_QNAME, name))
230 .withChild(ImmutableNodes.leafNode(IetfYangLibrary.SPECIFIC_MODULE_REVISION_LEAF_QNAME, revision));
234 * Map capabilites by ietf-restconf-monitoring.
236 * @param monitoringModule ietf-restconf-monitoring module
237 * @return mapped capabilites
239 public static ContainerNode mapCapabilites(final Module monitoringModule) {
240 return Builders.containerBuilder()
241 .withNodeIdentifier(new NodeIdentifier(RestconfState.QNAME))
242 .withChild(Builders.containerBuilder()
243 .withNodeIdentifier(new NodeIdentifier(Capabilities.QNAME))
244 .withChild(Builders.<String>orderedLeafSetBuilder()
245 .withNodeIdentifier(new NodeIdentifier(CAPABILITY_QNAME))
246 .withChildValue(Rfc8040.Capabilities.DEPTH)
247 .withChildValue(Rfc8040.Capabilities.FIELDS)
248 .withChildValue(Rfc8040.Capabilities.FILTER)
249 .withChildValue(Rfc8040.Capabilities.REPLAY)
250 .withChildValue(Rfc8040.Capabilities.WITH_DEFAULTS)
257 * Map data of yang notification to normalized node according to ietf-restconf-monitoring.
259 * @param notifiQName qname of notification from listener
260 * @param notifications list of notifications for find schema of notification by notifiQName
261 * @param start start-time query parameter of notification
262 * @param outputType output type of notification
263 * @param uri location of registered listener for sending data of notification
264 * @return mapped data of notification - map entry node if parent exists,
265 * container streams with list and map entry node if not
267 public static MapEntryNode mapYangNotificationStreamByIetfRestconfMonitoring(final QName notifiQName,
268 final Collection<? extends NotificationDefinition> notifications, final Instant start,
269 final String outputType, final URI uri) {
270 for (final NotificationDefinition notificationDefinition : notifications) {
271 if (notificationDefinition.getQName().equals(notifiQName)) {
272 final String streamName = notifiQName.getLocalName();
273 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
274 Builders.mapEntryBuilder()
275 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Stream.QNAME, NAME_QNAME, streamName))
276 .withChild(ImmutableNodes.leafNode(NAME_QNAME, streamName));
278 notificationDefinition.getDescription().ifPresent(
279 desc -> streamEntry.withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, desc)));
280 streamEntry.withChild(ImmutableNodes.leafNode(REPLAY_SUPPORT_QNAME, Boolean.TRUE));
282 streamEntry.withChild(ImmutableNodes.leafNode(REPLAY_LOG_CREATION_TIME,
283 DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start,
284 ZoneId.systemDefault()))));
288 .withChild(createAccessList(outputType, uri))
293 throw new RestconfDocumentedException(notifiQName + " doesn't exist in any modul");
296 private static MapNode createAccessList(final String outputType, final URI uriToWebsocketServer) {
297 return Builders.mapBuilder()
298 .withNodeIdentifier(new NodeIdentifier(Access.QNAME))
299 .withChild(Builders.mapEntryBuilder()
300 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Access.QNAME, ENCODING_QNAME, outputType))
301 .withChild(ImmutableNodes.leafNode(ENCODING_QNAME, outputType))
302 .withChild(ImmutableNodes.leafNode(LOCATION_QNAME, uriToWebsocketServer.toString()))
308 * Map data of data change notification to normalized node according to ietf-restconf-monitoring.
310 * @param path path of data to listen on
311 * @param start start-time query parameter of notification
312 * @param outputType output type of notification
313 * @param uri location of registered listener for sending data of notification
314 * @param schemaContext schemaContext for parsing instance identifier to get schema node of data
315 * @return mapped data of notification - map entry node if parent exists,
316 * container streams with list and map entry node if not
318 public static MapEntryNode mapDataChangeNotificationStreamByIetfRestconfMonitoring(
319 final YangInstanceIdentifier path, final Instant start, final String outputType, final URI uri,
320 final EffectiveModelContext schemaContext, final String streamName) {
321 final SchemaNode schemaNode = ParserIdentifier
322 .toInstanceIdentifier(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext),
323 schemaContext, Optional.empty())
325 final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> streamEntry =
326 Builders.mapEntryBuilder()
327 .withNodeIdentifier(NodeIdentifierWithPredicates.of(Stream.QNAME, NAME_QNAME, streamName))
328 .withChild(ImmutableNodes.leafNode(NAME_QNAME, streamName));
330 schemaNode.getDescription().ifPresent(desc ->
331 streamEntry.withChild(ImmutableNodes.leafNode(DESCRIPTION_QNAME, desc)));
334 .withChild(ImmutableNodes.leafNode(REPLAY_SUPPORT_QNAME, Boolean.TRUE))
335 .withChild(ImmutableNodes.leafNode(REPLAY_LOG_CREATION_TIME,
336 DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(OffsetDateTime.ofInstant(start, ZoneId.systemDefault()))))
337 .withChild(createAccessList(outputType, uri))