2 * Copyright (c) 2014 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.sal.connect.netconf.schema.mapping;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME;
12 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.IETF_NETCONF_NOTIFICATIONS;
13 import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI;
15 import com.google.common.annotations.Beta;
16 import com.google.common.annotations.VisibleForTesting;
17 import com.google.common.base.Preconditions;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableMap;
20 import com.google.common.collect.ImmutableSet;
21 import com.google.common.collect.Iterables;
22 import com.google.common.collect.Maps;
23 import com.google.common.collect.Multimap;
24 import com.google.common.collect.Multimaps;
25 import com.google.common.collect.Streams;
26 import java.io.IOException;
27 import java.net.URISyntaxException;
28 import java.time.Instant;
29 import java.util.AbstractMap;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
36 import java.util.Map.Entry;
37 import java.util.Optional;
39 import java.util.function.Function;
40 import java.util.stream.Collectors;
41 import javax.xml.stream.XMLStreamException;
42 import javax.xml.transform.dom.DOMResult;
43 import javax.xml.transform.dom.DOMSource;
44 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
45 import org.opendaylight.mdsal.dom.api.DOMActionResult;
46 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
47 import org.opendaylight.mdsal.dom.api.DOMEvent;
48 import org.opendaylight.mdsal.dom.api.DOMNotification;
49 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
50 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
51 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
52 import org.opendaylight.netconf.api.NetconfMessage;
53 import org.opendaylight.netconf.api.xml.MissingNameSpaceException;
54 import org.opendaylight.netconf.api.xml.XmlElement;
55 import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
56 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
57 import org.opendaylight.netconf.sal.connect.util.MessageCounter;
58 import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
59 import org.opendaylight.yangtools.yang.common.QName;
60 import org.opendaylight.yangtools.yang.common.QNameModule;
61 import org.opendaylight.yangtools.yang.common.Revision;
62 import org.opendaylight.yangtools.yang.common.XMLNamespace;
63 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
64 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
65 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
66 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
69 import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
70 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
71 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
72 import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
73 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
74 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
75 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
76 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
77 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
78 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
79 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
80 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
81 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
82 import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
83 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
84 import org.opendaylight.yangtools.yang.model.api.Module;
85 import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
86 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
87 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
88 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
89 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
90 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute;
91 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
92 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference;
93 import org.slf4j.Logger;
94 import org.slf4j.LoggerFactory;
95 import org.w3c.dom.Document;
96 import org.w3c.dom.Element;
97 import org.w3c.dom.Node;
98 import org.w3c.dom.NodeList;
99 import org.xml.sax.SAXException;
101 public class NetconfMessageTransformer implements MessageTransformer<NetconfMessage> {
102 private static final Logger LOG = LoggerFactory.getLogger(NetconfMessageTransformer.class);
104 private static final ImmutableSet<XMLNamespace> BASE_OR_NOTIFICATION_NS = ImmutableSet.of(
106 IETF_NETCONF_NOTIFICATIONS.getNamespace(),
107 CREATE_SUBSCRIPTION_RPC_QNAME.getNamespace());
109 private final MountPointContext mountContext;
110 private final DataSchemaContextTree contextTree;
111 private final BaseSchema baseSchema;
112 private final MessageCounter counter;
113 private final ImmutableMap<QName, ? extends RpcDefinition> mappedRpcs;
114 private final Multimap<QName, ? extends NotificationDefinition> mappedNotifications;
115 private final boolean strictParsing;
116 private final ImmutableMap<Absolute, ActionDefinition> actions;
118 public NetconfMessageTransformer(final MountPointContext mountContext, final boolean strictParsing,
119 final BaseSchema baseSchema) {
120 counter = new MessageCounter();
121 this.mountContext = requireNonNull(mountContext);
123 final EffectiveModelContext schemaContext = mountContext.getEffectiveModelContext();
124 contextTree = DataSchemaContextTree.from(schemaContext);
126 mappedRpcs = Maps.uniqueIndex(schemaContext.getOperations(), SchemaNode::getQName);
127 actions = getActions(schemaContext);
129 // RFC6020 normal notifications
130 mappedNotifications = Multimaps.index(schemaContext.getNotifications(),
131 node -> node.getQName().withoutRevision());
132 this.baseSchema = baseSchema;
133 this.strictParsing = strictParsing;
137 static ImmutableMap<Absolute, ActionDefinition> getActions(final SchemaContext schemaContext) {
138 final Map<Absolute, ActionDefinition> values = new HashMap<>();
139 final SchemaInferenceStack stack = SchemaInferenceStack.of((EffectiveModelContext) schemaContext);
140 findAction(schemaContext, values, stack);
141 return ImmutableMap.copyOf(values);
144 private static void findAction(final DataSchemaNode dataSchemaNode, final Map<Absolute, ActionDefinition> builder,
145 final SchemaInferenceStack stack) {
146 if (dataSchemaNode instanceof ActionNodeContainer) {
147 for (ActionDefinition actionDefinition : ((ActionNodeContainer) dataSchemaNode).getActions()) {
148 stack.enterSchemaTree(actionDefinition.getQName());
149 builder.put(stack.toSchemaNodeIdentifier(), actionDefinition);
153 if (dataSchemaNode instanceof DataNodeContainer) {
154 for (DataSchemaNode innerDataSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
155 stack.enterSchemaTree(innerDataSchemaNode.getQName());
156 findAction(innerDataSchemaNode, builder, stack);
159 } else if (dataSchemaNode instanceof ChoiceSchemaNode) {
160 for (CaseSchemaNode caze : ((ChoiceSchemaNode) dataSchemaNode).getCases()) {
161 stack.enterSchemaTree(caze.getQName());
162 findAction(caze, builder, stack);
169 public synchronized DOMNotification toNotification(final NetconfMessage message) {
170 final Entry<Instant, XmlElement> stripped = NetconfMessageTransformUtil.stripNotification(message);
171 final QName notificationNoRev;
173 notificationNoRev = QName.create(
174 stripped.getValue().getNamespace(), stripped.getValue().getName()).withoutRevision();
175 } catch (final MissingNameSpaceException e) {
176 throw new IllegalArgumentException(
177 "Unable to parse notification " + message + ", cannot find namespace", e);
180 Collection<? extends NotificationDefinition> notificationDefinitions =
181 mappedNotifications.get(notificationNoRev);
182 Element element = stripped.getValue().getDomElement();
184 NestedNotificationInfo nestedNotificationInfo = null;
185 if (notificationDefinitions.isEmpty()) {
186 // check if notification is nested notification
187 Optional<NestedNotificationInfo> nestedNotificationOptional = findNestedNotification(message, element);
188 if (nestedNotificationOptional.isPresent()) {
189 nestedNotificationInfo = nestedNotificationOptional.get();
190 notificationDefinitions = List.of(nestedNotificationInfo.notificationDefinition);
191 element = (Element) nestedNotificationInfo.notificationNode;
194 Preconditions.checkArgument(notificationDefinitions.size() > 0,
195 "Unable to parse notification %s, unknown notification. Available notifications: %s",
196 notificationDefinitions, mappedNotifications.keySet());
198 final NotificationDefinition mostRecentNotification = getMostRecentNotification(notificationDefinitions);
200 final SchemaInferenceStack stack = SchemaInferenceStack.of(mountContext.getEffectiveModelContext());
201 if (nestedNotificationInfo != null) {
202 final QNameModule targetModule = mostRecentNotification.getQName().getModule();
203 nestedNotificationInfo.domDataTreeIdentifier.getRootIdentifier().getPathArguments().stream()
204 .filter(arg -> !(arg instanceof NodeIdentifierWithPredicates))
205 .filter(arg -> !(arg instanceof AugmentationIdentifier))
206 .map(arg -> arg.getNodeType().bindTo(targetModule))
207 .forEach(stack::enterSchemaTree);
209 stack.enterSchemaTree(mostRecentNotification.getQName());
212 final ContainerNode content;
214 final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
215 final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
216 final XmlParserStream xmlParser = XmlParserStream.create(writer, mountContext, stack.toInference(),
218 xmlParser.traverse(new DOMSource(element));
219 content = (ContainerNode) resultHolder.getResult();
220 } catch (XMLStreamException | URISyntaxException | IOException | SAXException
221 | UnsupportedOperationException e) {
222 throw new IllegalArgumentException(String.format("Failed to parse notification %s", element), e);
225 if (nestedNotificationInfo != null) {
226 return new NetconfDeviceTreeNotification(content,
227 stack.toSchemaNodeIdentifier(),
228 stripped.getKey(), nestedNotificationInfo.domDataTreeIdentifier);
231 return new NetconfDeviceNotification(content, stripped.getKey());
234 private Optional<NestedNotificationInfo> findNestedNotification(final NetconfMessage message,
235 final Element element) {
236 final Iterator<? extends Module> modules = mountContext.getEffectiveModelContext()
237 .findModules(XMLNamespace.of(element.getNamespaceURI())).iterator();
238 if (!modules.hasNext()) {
239 throw new IllegalArgumentException(
240 "Unable to parse notification " + message + ", cannot find top level module");
242 final Module module = modules.next();
243 final QName topLevelNodeQName = QName.create(element.getNamespaceURI(), element.getLocalName());
244 for (DataSchemaNode childNode : module.getChildNodes()) {
245 if (topLevelNodeQName.isEqualWithoutRevision(childNode.getQName())) {
246 return Optional.of(traverseXmlNodeContainingNotification(element, childNode,
247 YangInstanceIdentifier.builder()));
250 return Optional.empty();
253 private NestedNotificationInfo traverseXmlNodeContainingNotification(final Node xmlNode,
254 final SchemaNode schemaNode, final YangInstanceIdentifier.InstanceIdentifierBuilder builder) {
255 if (schemaNode instanceof ContainerSchemaNode) {
256 ContainerSchemaNode dataContainerNode = (ContainerSchemaNode) schemaNode;
257 builder.node(QName.create(xmlNode.getNamespaceURI(), xmlNode.getLocalName()));
259 Entry<Node, SchemaNode> xmlContainerChildPair = findXmlContainerChildPair(xmlNode, dataContainerNode);
260 return traverseXmlNodeContainingNotification(xmlContainerChildPair.getKey(),
261 xmlContainerChildPair.getValue(), builder);
262 } else if (schemaNode instanceof ListSchemaNode) {
263 ListSchemaNode listSchemaNode = (ListSchemaNode) schemaNode;
264 builder.node(QName.create(xmlNode.getNamespaceURI(), xmlNode.getLocalName()));
266 Map<QName, Object> listKeys = findXmlListKeys(xmlNode, listSchemaNode);
267 builder.nodeWithKey(QName.create(xmlNode.getNamespaceURI(), xmlNode.getLocalName()), listKeys);
269 Entry<Node, SchemaNode> xmlListChildPair = findXmlListChildPair(xmlNode, listSchemaNode);
270 return traverseXmlNodeContainingNotification(xmlListChildPair.getKey(),
271 xmlListChildPair.getValue(), builder);
272 } else if (schemaNode instanceof NotificationDefinition) {
273 builder.node(QName.create(xmlNode.getNamespaceURI(), xmlNode.getLocalName()));
275 NotificationDefinition notificationDefinition = (NotificationDefinition) schemaNode;
276 return new NestedNotificationInfo(notificationDefinition,
277 new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, builder.build()), xmlNode);
279 throw new IllegalStateException("No notification found");
282 private static Entry<Node, SchemaNode> findXmlContainerChildPair(final Node xmlNode,
283 final ContainerSchemaNode container) {
284 final NodeList nodeList = xmlNode.getChildNodes();
285 final Map<QName, SchemaNode> childrenWithoutRevision =
286 Streams.concat(container.getChildNodes().stream(), container.getNotifications().stream())
287 .collect(Collectors.toMap(child -> child.getQName().withoutRevision(), Function.identity()));
289 for (int i = 0; i < nodeList.getLength(); i++) {
290 Node currentNode = nodeList.item(i);
291 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
292 QName currentNodeQName = QName.create(currentNode.getNamespaceURI(), currentNode.getLocalName());
293 SchemaNode schemaChildNode = childrenWithoutRevision.get(currentNodeQName);
294 if (schemaChildNode != null) {
295 return new AbstractMap.SimpleEntry<>(currentNode, schemaChildNode);
299 throw new IllegalStateException("No container child found.");
302 private static Map<QName, Object> findXmlListKeys(final Node xmlNode, final ListSchemaNode listSchemaNode) {
303 Map<QName, Object> listKeys = new HashMap<>();
304 NodeList nodeList = xmlNode.getChildNodes();
305 Set<QName> keyDefinitionsWithoutRevision = listSchemaNode.getKeyDefinition().stream()
306 .map(QName::withoutRevision).collect(Collectors.toSet());
307 for (int i = 0; i < nodeList.getLength(); i++) {
308 Node currentNode = nodeList.item(i);
309 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
310 QName currentNodeQName = QName.create(currentNode.getNamespaceURI(), currentNode.getLocalName());
311 if (keyDefinitionsWithoutRevision.contains(currentNodeQName)) {
312 listKeys.put(currentNodeQName, currentNode.getFirstChild().getNodeValue());
316 if (listKeys.isEmpty()) {
317 throw new IllegalStateException("Notification cannot be contained in list without key statement.");
322 private static Entry<Node, SchemaNode> findXmlListChildPair(final Node xmlNode, final ListSchemaNode list) {
323 final NodeList nodeList = xmlNode.getChildNodes();
324 for (int i = 0; i < nodeList.getLength(); i++) {
325 Node currentNode = nodeList.item(i);
326 if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
327 QName currentNodeQName = QName.create(currentNode.getNamespaceURI(), currentNode.getLocalName());
328 for (SchemaNode childNode : Iterables.concat(list.getChildNodes(), list.getNotifications())) {
329 if (!list.getKeyDefinition().contains(childNode.getQName())
330 && currentNodeQName.isEqualWithoutRevision(childNode.getQName())) {
331 return new AbstractMap.SimpleEntry<>(currentNode, childNode);
336 throw new IllegalStateException("No list child found.");
339 private static NotificationDefinition getMostRecentNotification(
340 final Collection<? extends NotificationDefinition> notificationDefinitions) {
341 return Collections.max(notificationDefinitions, (o1, o2) ->
342 Revision.compare(o1.getQName().getRevision(), o2.getQName().getRevision()));
346 public NetconfMessage toRpcRequest(final QName rpc, final NormalizedNode payload) {
347 // In case no input for rpc is defined, we can simply construct the payload here
349 // Determine whether a base netconf operation is being invoked
350 // and also check if the device exposed model for base netconf.
351 // If no, use pre built base netconf operations model
352 final boolean needToUseBaseCtx = mappedRpcs.get(rpc) == null && isBaseOrNotificationRpc(rpc);
353 final ImmutableMap<QName, ? extends RpcDefinition> currentMappedRpcs;
354 if (needToUseBaseCtx) {
355 currentMappedRpcs = baseSchema.getMappedRpcs();
357 currentMappedRpcs = mappedRpcs;
360 final RpcDefinition mappedRpc = Preconditions.checkNotNull(currentMappedRpcs.get(rpc),
361 "Unknown rpc %s, available rpcs: %s", rpc, currentMappedRpcs.keySet());
362 if (mappedRpc.getInput().getChildNodes().isEmpty()) {
363 return new NetconfMessage(NetconfMessageTransformUtil.prepareDomResultForRpcRequest(rpc, counter)
364 .getNode().getOwnerDocument());
367 Preconditions.checkNotNull(payload, "Transforming an rpc with input: %s, payload cannot be null", rpc);
369 Preconditions.checkArgument(payload instanceof ContainerNode,
370 "Transforming an rpc with input: %s, payload has to be a container, but was: %s", rpc, payload);
371 final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForRpcRequest(rpc, counter);
373 // If the schema context for netconf device does not contain model for base netconf operations,
374 // use default pre build context with just the base model
375 // This way operations like lock/unlock are supported even if the source for base model was not provided
376 final EffectiveModelContext ctx = needToUseBaseCtx ? baseSchema.getEffectiveModelContext()
377 : mountContext.getEffectiveModelContext();
378 NetconfMessageTransformUtil.writeNormalizedOperationInput((ContainerNode) payload, result, Absolute.of(rpc),
380 } catch (final XMLStreamException | IOException | IllegalStateException e) {
381 throw new IllegalStateException("Unable to serialize input of " + rpc, e);
384 final Document node = result.getNode().getOwnerDocument();
386 return new NetconfMessage(node);
390 public NetconfMessage toActionRequest(final Absolute action, final DOMDataTreeIdentifier domDataTreeIdentifier,
391 final NormalizedNode payload) {
392 final ActionDefinition actionDef = actions.get(action);
393 Preconditions.checkArgument(actionDef != null, "Action does not exist: %s", action);
395 final InputSchemaNode inputDef = actionDef.getInput();
396 if (inputDef.getChildNodes().isEmpty()) {
397 return new NetconfMessage(NetconfMessageTransformUtil.prepareDomResultForActionRequest(contextTree,
398 domDataTreeIdentifier, counter, actionDef.getQName()).getNode().getOwnerDocument());
401 Preconditions.checkNotNull(payload, "Transforming an action with input: %s, payload cannot be null", action);
402 Preconditions.checkArgument(payload instanceof ContainerNode,
403 "Transforming an action with input: %s, payload has to be a container, but was: %s", action, payload);
405 final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForActionRequest(contextTree,
406 domDataTreeIdentifier, counter, actionDef.getQName());
408 NetconfMessageTransformUtil.writeNormalizedOperationInput((ContainerNode) payload, result, action,
409 mountContext.getEffectiveModelContext());
410 } catch (final XMLStreamException | IOException | IllegalStateException e) {
411 throw new IllegalStateException("Unable to serialize input of " + action, e);
414 return new NetconfMessage(result.getNode().getOwnerDocument());
417 private static boolean isBaseOrNotificationRpc(final QName rpc) {
418 return BASE_OR_NOTIFICATION_NS.contains(rpc.getNamespace());
422 public synchronized DOMRpcResult toRpcResult(final NetconfMessage message, final QName rpc) {
423 final NormalizedNode normalizedNode;
424 if (NetconfMessageTransformUtil.isDataRetrievalOperation(rpc)) {
425 normalizedNode = Builders.containerBuilder()
426 .withNodeIdentifier(NetconfMessageTransformUtil.NETCONF_RPC_REPLY_NODEID)
427 .withChild(Builders.anyXmlBuilder()
428 .withNodeIdentifier(NetconfMessageTransformUtil.NETCONF_DATA_NODEID)
429 .withValue(new DOMSource(NetconfMessageTransformUtil.getDataSubtree(message.getDocument())))
433 // Determine whether a base netconf operation is being invoked
434 // and also check if the device exposed model for base netconf.
435 // If no, use pre built base netconf operations model
436 final ImmutableMap<QName, ? extends RpcDefinition> currentMappedRpcs;
437 if (mappedRpcs.get(rpc) == null && isBaseOrNotificationRpc(rpc)) {
438 currentMappedRpcs = baseSchema.getMappedRpcs();
440 currentMappedRpcs = mappedRpcs;
443 final RpcDefinition rpcDefinition = currentMappedRpcs.get(rpc);
444 Preconditions.checkArgument(rpcDefinition != null,
445 "Unable to parse response of %s, the rpc is unknown", rpc);
447 // In case no input for rpc is defined, we can simply construct the payload here
448 normalizedNode = parseResult(message, rpcDefinition,
449 Absolute.of(rpcDefinition.getQName(), rpcDefinition.getOutput().getQName()));
451 return new DefaultDOMRpcResult(normalizedNode);
455 public DOMActionResult toActionResult(final Absolute action, final NetconfMessage message) {
456 final ActionDefinition actionDefinition = actions.get(action);
457 Preconditions.checkArgument(actionDefinition != null, "Action does not exist: %s", action);
459 final List<QName> actionIds = action.getNodeIdentifiers();
460 final ContainerNode normalizedNode = (ContainerNode) parseResult(message, actionDefinition,
461 Absolute.of(ImmutableList.<QName>builderWithExpectedSize(actionIds.size() + 1)
462 .addAll(actionIds).add(actionDefinition.getOutput().getQName())
464 if (normalizedNode == null) {
465 return new SimpleDOMActionResult(List.of());
467 return new SimpleDOMActionResult(normalizedNode, List.of());
471 private NormalizedNode parseResult(final NetconfMessage message, final OperationDefinition operationDefinition,
472 final Absolute operationPath) {
473 final Optional<XmlElement> okResponseElement = XmlElement.fromDomDocument(message.getDocument())
474 .getOnlyChildElementWithSameNamespaceOptionally("ok");
475 if (operationDefinition.getOutput().getChildNodes().isEmpty()) {
476 Preconditions.checkArgument(okResponseElement.isPresent(),
477 "Unexpected content in response of rpc: %s, %s", operationDefinition.getQName(), message);
480 if (okResponseElement.isPresent()) {
481 LOG.debug("Received response <ok/> for RPC with defined Output");
485 final Element element = message.getDocument().getDocumentElement();
486 final Inference inference = SchemaInferenceStack.of(mountContext.getEffectiveModelContext(),
487 operationPath).toInference();
489 final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
490 final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder);
491 final XmlParserStream xmlParser = XmlParserStream.create(writer, mountContext,
492 inference, strictParsing);
493 xmlParser.traverse(new DOMSource(element));
494 return resultHolder.getResult();
495 } catch (XMLStreamException | URISyntaxException | IOException | SAXException e) {
496 throw new IllegalArgumentException(String.format("Failed to parse RPC response %s", element), e);
502 public static class NetconfDeviceNotification implements DOMNotification, DOMEvent {
503 private final ContainerNode content;
504 private final Absolute schemaPath;
505 private final Instant eventTime;
507 NetconfDeviceNotification(final ContainerNode content, final Instant eventTime) {
508 this.content = content;
509 this.eventTime = eventTime;
510 schemaPath = Absolute.of(content.getIdentifier().getNodeType());
513 NetconfDeviceNotification(final ContainerNode content, final Absolute schemaPath, final Instant eventTime) {
514 this.content = content;
515 this.eventTime = eventTime;
516 this.schemaPath = schemaPath;
520 public Absolute getType() {
525 public ContainerNode getBody() {
530 public Instant getEventInstant() {
536 public static class NetconfDeviceTreeNotification extends NetconfDeviceNotification {
537 private final DOMDataTreeIdentifier domDataTreeIdentifier;
539 NetconfDeviceTreeNotification(final ContainerNode content, final Absolute schemaPath, final Instant eventTime,
540 final DOMDataTreeIdentifier domDataTreeIdentifier) {
541 super(content, schemaPath, eventTime);
542 this.domDataTreeIdentifier = domDataTreeIdentifier;
545 public DOMDataTreeIdentifier getDomDataTreeIdentifier() {
546 return domDataTreeIdentifier;
550 private static final class NestedNotificationInfo {
551 private final NotificationDefinition notificationDefinition;
552 private final DOMDataTreeIdentifier domDataTreeIdentifier;
553 private final Node notificationNode;
555 NestedNotificationInfo(final NotificationDefinition notificationDefinition,
556 final DOMDataTreeIdentifier domDataTreeIdentifier, final Node notificationNode) {
557 this.notificationDefinition = notificationDefinition;
558 this.domDataTreeIdentifier = domDataTreeIdentifier;
559 this.notificationNode = notificationNode;