X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Fconnect%2Fnetconf%2Fschema%2Fmapping%2FNetconfMessageTransformer.java;h=0df0e1aa74bda1b3c95df680fcfe0c787fd9a138;hb=1d91e1bbe29d0da6ea427a5d0837064c8a3d5134;hp=89d9c19850ec9c51121677d4843bccb9983dfe89;hpb=21588aadb96699b6f4f87e2cbbbb08fe1e38ed78;p=netconf.git diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java index 89d9c19850..0df0e1aa74 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/schema/mapping/NetconfMessageTransformer.java @@ -11,33 +11,40 @@ import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTr import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_URI; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; import java.io.IOException; -import java.io.StringReader; +import java.net.URISyntaxException; +import java.time.Instant; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; -import java.util.Date; import java.util.Map; -import javax.annotation.Nonnull; +import java.util.Set; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLStreamException; import javax.xml.transform.dom.DOMResult; -import org.opendaylight.controller.config.util.xml.MissingNameSpaceException; -import org.opendaylight.controller.config.util.xml.XmlElement; -import org.opendaylight.controller.config.util.xml.XmlUtil; -import org.opendaylight.controller.md.sal.dom.api.DOMEvent; -import org.opendaylight.controller.md.sal.dom.api.DOMNotification; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; -import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; +import javax.xml.transform.dom.DOMSource; +import org.opendaylight.mdsal.dom.api.DOMActionResult; +import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier; +import org.opendaylight.mdsal.dom.api.DOMEvent; +import org.opendaylight.mdsal.dom.api.DOMNotification; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult; +import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult; import org.opendaylight.netconf.api.NetconfMessage; +import org.opendaylight.netconf.api.xml.MissingNameSpaceException; +import org.opendaylight.netconf.api.xml.XmlElement; import org.opendaylight.netconf.sal.connect.api.MessageTransformer; import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; import org.opendaylight.netconf.sal.connect.util.MessageCounter; -import org.opendaylight.yangtools.util.xml.UntrustedXML; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.Revision; +import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -46,8 +53,13 @@ import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; +import org.opendaylight.yangtools.yang.model.api.ActionDefinition; +import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; +import org.opendaylight.yangtools.yang.model.api.OperationDefinition; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaNode; @@ -56,6 +68,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.xml.sax.SAXException; public class NetconfMessageTransformer implements MessageTransformer { @@ -66,8 +79,8 @@ public class NetconfMessageTransformer implements MessageTransformer mappedRpcs; private final Multimap mappedNotifications; - private final boolean strictParsing; + private final Set actions; public NetconfMessageTransformer(final SchemaContext schemaContext, final boolean strictParsing) { this(schemaContext, strictParsing, BaseSchema.BASE_NETCONF_CTX); @@ -77,17 +90,42 @@ public class NetconfMessageTransformer implements MessageTransformer node.getQName().withoutRevision()); this.baseSchema = baseSchema; this.strictParsing = strictParsing; } - @SuppressWarnings("checkstyle:IllegalCatch") + @VisibleForTesting + Set getActions() { + Builder builder = ImmutableSet.builder(); + for (DataSchemaNode dataSchemaNode : schemaContext.getChildNodes()) { + if (dataSchemaNode instanceof ActionNodeContainer) { + findAction(dataSchemaNode, builder); + } + } + return builder.build(); + } + + private void findAction(final DataSchemaNode dataSchemaNode, final Builder builder) { + if (dataSchemaNode instanceof ActionNodeContainer) { + final ActionNodeContainer containerSchemaNode = (ActionNodeContainer) dataSchemaNode; + for (ActionDefinition actionDefinition : containerSchemaNode.getActions()) { + builder.add(actionDefinition); + } + } + if (dataSchemaNode instanceof DataNodeContainer) { + for (DataSchemaNode innerDataSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) { + findAction(innerDataSchemaNode, builder); + } + } + } + @Override public synchronized DOMNotification toNotification(final NetconfMessage message) { - final Map.Entry stripped = NetconfMessageTransformUtil.stripNotification(message); + final Map.Entry stripped = NetconfMessageTransformUtil.stripNotification(message); final QName notificationNoRev; try { notificationNoRev = QName.create( @@ -113,10 +151,10 @@ public class NetconfMessageTransformer implements MessageTransformer notificationDefinitions) { - Comparator cmp = (o1, o2) -> - o1.getQName().getRevision().compareTo(o2.getQName().getRevision()); - - return Collections.max(notificationDefinitions, cmp); + return Collections.max(notificationDefinitions, (o1, o2) -> + Revision.compare(o1.getQName().getRevision(), o2.getQName().getRevision())); } @Override @@ -152,9 +188,9 @@ public class NetconfMessageTransformer implements MessageTransformer payload) { + ActionDefinition actionDefinition = null; + SchemaPath schemaPath = action; + for (ActionDefinition actionDef : actions) { + if (actionDef.getPath().getLastComponent().equals(action.getLastComponent())) { + actionDefinition = actionDef; + schemaPath = actionDef.getPath(); + } + } + Preconditions.checkNotNull(actionDefinition, "Action does not exist: %s", action.getLastComponent()); + + if (actionDefinition.getInput().getChildNodes().isEmpty()) { + return new NetconfMessage(NetconfMessageTransformUtil.prepareDomResultForActionRequest( + domDataTreeIdentifier, action, counter, actionDefinition.getQName().getLocalName()) + .getNode().getOwnerDocument()); + } + + Preconditions.checkNotNull(payload, "Transforming an action with input: %s, payload cannot be null", + action.getLastComponent()); + Preconditions.checkArgument(payload instanceof ContainerNode, + "Transforming an rpc with input: %s, payload has to be a container, but was: %s", + action.getLastComponent(), payload); + // Set the path to the input of rpc for the node stream writer + action = action.createChild(QName.create(action.getLastComponent(), "input").intern()); + final DOMResult result = NetconfMessageTransformUtil.prepareDomResultForActionRequest( + domDataTreeIdentifier, action, counter, actionDefinition.getQName().getLocalName()); + + try { + NetconfMessageTransformUtil.writeNormalizedRpc((ContainerNode) payload, result, + schemaPath.createChild(QName.create(action.getLastComponent(), "input").intern()), schemaContext); + } catch (final XMLStreamException | IOException | IllegalStateException e) { + throw new IllegalStateException("Unable to serialize " + action, e); + } + + final Document node = result.getNode().getOwnerDocument(); + + return new NetconfMessage(node); + } + private static boolean isBaseOrNotificationRpc(final QName rpc) { return rpc.getNamespace().equals(NETCONF_URI) || rpc.getNamespace().equals(IETF_NETCONF_NOTIFICATIONS.getNamespace()) || rpc.getNamespace().equals(NetconfMessageTransformUtil.CREATE_SUBSCRIPTION_RPC_QNAME.getNamespace()); } - @SuppressWarnings("checkstyle:IllegalCatch") @Override public synchronized DOMRpcResult toRpcResult(final NetconfMessage message, final SchemaPath rpc) { final NormalizedNode normalizedNode; @@ -196,10 +272,10 @@ public class NetconfMessageTransformer implements MessageTransformeremptyList()); + } + + private NormalizedNode parseResult(final NetconfMessage message, + final OperationDefinition operationDefinition) { + if (operationDefinition.getOutput().getChildNodes().isEmpty()) { + Preconditions.checkArgument(XmlElement.fromDomDocument( + message.getDocument()).getOnlyChildElementWithSameNamespaceOptionally("ok").isPresent(), + "Unexpected content in response of rpc: %s, %s", operationDefinition.getQName(), message); + return null; + } else { + final Element element = message.getDocument().getDocumentElement(); + try { + final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); + final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); + final XmlParserStream xmlParser = XmlParserStream.create(writer, schemaContext, + operationDefinition.getOutput(), strictParsing); + xmlParser.traverse(new DOMSource(element)); + return resultHolder.getResult(); + } catch (XMLStreamException | URISyntaxException | IOException | ParserConfigurationException + | SAXException e) { + throw new IllegalArgumentException(String.format("Failed to parse RPC response %s", element), e); + } + } + } + static class NetconfDeviceNotification implements DOMNotification, DOMEvent { private final ContainerNode content; private final SchemaPath schemaPath; - private final Date eventTime; + private final Instant eventTime; - NetconfDeviceNotification(final ContainerNode content, final Date eventTime) { + NetconfDeviceNotification(final ContainerNode content, final Instant eventTime) { this.content = content; this.eventTime = eventTime; this.schemaPath = toPath(content.getNodeType()); } - @Nonnull @Override public SchemaPath getType() { return schemaPath; - } - @Nonnull @Override public ContainerNode getBody() { return content; } @Override - public Date getEventTime() { + public Instant getEventInstant() { return eventTime; } }