X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fmdsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fmdsal%2Fconnector%2Fops%2Fget%2FAbstractGet.java;h=9a66ceb5bcd0d8dc66fc77cfa2f215a37e77d223;hp=8f6ff417d6499d47dc594bc5ab5fb2de03e937d0;hb=3b78a463e85c496cba2a68eddb027d9432b8d530;hpb=3927509ec3ecfa32a51b725d2b7155d425f5b877 diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java index 8f6ff417d6..9a66ceb5bc 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java @@ -8,42 +8,62 @@ package org.opendaylight.controller.netconf.mdsal.connector.ops.get; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Throwables; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Collections; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.dom.DOMResult; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext; import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore; -import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation; +import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation; import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.controller.sal.connect.netconf.util.InstanceIdToNodes; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.Node; -public abstract class AbstractGet extends AbstractLastNetconfOperation { +public abstract class AbstractGet extends AbstractSingletonNetconfOperation { - protected static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build(); + private static final Logger LOG = LoggerFactory.getLogger(AbstractGet.class); + protected static final String FILTER = "filter"; + static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build(); protected final CurrentSchemaContext schemaContext; - public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) { super(netconfSessionIdForReporting); this.schemaContext = schemaContext; @@ -57,7 +77,6 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { } protected Node transformNormalizedNode(final Document document, final NormalizedNode data, final YangInstanceIdentifier dataRoot) { -// boolean isDataRoot = true; final DOMResult result = new DOMResult(document.createElement(XmlNetconfConstants.DATA_KEY)); @@ -68,20 +87,11 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter); -// if (isDataRoot) { writeRootElement(xmlWriter, nnWriter, (ContainerNode) data); -// } else { -// if (data instanceof MapEntryNode) { -// // Restconf allows returning one list item. We need to wrap it -// // in map node in order to serialize it properly -// data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).addChild((MapEntryNode) data).build(); -// } -// nnWriter.write(data); -// nnWriter.flush(); -// } return result.getNode(); } + private XMLStreamWriter getXmlStreamWriter(final DOMResult result) { try { return XML_OUTPUT_FACTORY.createXMLStreamWriter(result); @@ -104,9 +114,12 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { // TODO this code is located in Restconf already private void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data) { try { - final QName name = SchemaContext.NAME; - for (final DataContainerChild child : data.getValue()) { - nnWriter.write(child); + if (data.getNodeType().equals(SchemaContext.NAME)) { + for (final DataContainerChild child : data.getValue()) { + nnWriter.write(child); + } + } else { + nnWriter.write(data); } nnWriter.flush(); xmlWriter.flush(); @@ -115,9 +128,100 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { } } + private DataSchemaNode getSchemaNodeFromNamespace(final XmlElement element) throws NetconfDocumentedException { + + try { + final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(new URI(element.getNamespace()), null); + DataSchemaNode dataSchemaNode = module.getDataChildByName(element.getName()); + if (dataSchemaNode != null) { + return dataSchemaNode; + } + } catch (URISyntaxException e) { + LOG.debug("Error during parsing of element namespace, this should not happen since namespace of an xml " + + "element is valid and if the xml was parsed then the URI should be as well"); + throw new IllegalArgumentException("Unable to parse element namespace, this should not happen since " + + "namespace of an xml element is valid and if the xml was parsed then the URI should be as well"); + } + throw new NetconfDocumentedException("Unable to find node with namespace: " + element.getNamespace() + "in schema context: " + schemaContext.getCurrentContext().toString(), + ErrorType.application, + ErrorTag.unknown_namespace, + ErrorSeverity.error); + } + + protected Element serializeNodeWithParentStructure(Document document, YangInstanceIdentifier dataRoot, NormalizedNode node) { + if (!dataRoot.equals(ROOT)) { + return (Element) transformNormalizedNode(document, + InstanceIdToNodes.serialize(schemaContext.getCurrentContext(), dataRoot, node), + ROOT); + } + return (Element) transformNormalizedNode(document, node, ROOT); + } + + /** + * + * @param operationElement operation element + * @return if Filter is present and not empty returns Optional of the InstanceIdentifier to the read location in datastore. + * empty filter returns Optional.absent() which should equal an empty container in the response. + * if filter is not present we want to read the entire datastore - return ROOT. + * @throws NetconfDocumentedException + */ + protected Optional getDataRootFromFilter(XmlElement operationElement) throws NetconfDocumentedException { + Optional filterElement = operationElement.getOnlyChildElementOptionally(FILTER); + if (filterElement.isPresent()) { + if (filterElement.get().getChildElements().size() == 0) { + return Optional.absent(); + } + return Optional.of(getInstanceIdentifierFromFilter(filterElement.get())); + } else { + return Optional.of(ROOT); + } + } + + @VisibleForTesting + protected YangInstanceIdentifier getInstanceIdentifierFromFilter(XmlElement filterElement) throws NetconfDocumentedException { + + if (filterElement.getChildElements().size() != 1) { + throw new NetconfDocumentedException("Multiple filter roots not supported yet", + ErrorType.application, ErrorTag.operation_not_supported, ErrorSeverity.error); + } + + XmlElement element = filterElement.getOnlyChildElement(); + DataSchemaNode schemaNode = getSchemaNodeFromNamespace(element); + + return getReadPointFromNode(YangInstanceIdentifier.builder().build(), filterToNormalizedNode(element, schemaNode)); + } + + private YangInstanceIdentifier getReadPointFromNode(final YangInstanceIdentifier pathArg, final NormalizedNode nNode) { + final YangInstanceIdentifier path = pathArg.node(nNode.getIdentifier()); + if (nNode instanceof DataContainerNode) { + DataContainerNode node = (DataContainerNode) nNode; + if (node.getValue().size() == 1) { + return getReadPointFromNode(path, (NormalizedNode) Lists.newArrayList(node.getValue()).get(0)); + } + } + return path; + } + + private NormalizedNode filterToNormalizedNode(XmlElement element, DataSchemaNode schemaNode) throws NetconfDocumentedException { + DomToNormalizedNodeParserFactory parserFactory = DomToNormalizedNodeParserFactory + .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext()); + + final NormalizedNode parsedNode; + + if (schemaNode instanceof ContainerSchemaNode) { + parsedNode = parserFactory.getContainerNodeParser().parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode); + } else if (schemaNode instanceof ListSchemaNode) { + parsedNode = parserFactory.getMapNodeParser().parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode); + } else { + throw new NetconfDocumentedException("Schema node of the top level element is not an instance of container or list", + ErrorType.application, ErrorTag.unknown_element, ErrorSeverity.error); + } + return parsedNode; + } + protected static final class GetConfigExecution { - private final Optional datastore; + private final Optional datastore; public GetConfigExecution(final Optional datastore) { this.datastore = datastore; } @@ -140,8 +244,6 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { throw new NetconfDocumentedException("Get-config source attribute error: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo()); } - // Add filter - return new GetConfigExecution(sourceDatastore); } @@ -157,6 +259,7 @@ public abstract class AbstractGet extends AbstractLastNetconfOperation { xml.checkName(operationName); xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); } + } }