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.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorSeverity;
+import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorTag;
+import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorType;
+import org.opendaylight.controller.config.util.xml.XmlElement;
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.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation;
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.ImmutableNodes;
+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;
}
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));
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);
// 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<? extends PathArgument, ?> child : data.getValue()) {
- nnWriter.write(child);
+ if (data.getNodeType().equals(SchemaContext.NAME)) {
+ for (final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
+ nnWriter.write(child);
+ }
+ } else {
+ nnWriter.write(data);
}
nnWriter.flush();
xmlWriter.flush();
}
}
+ private DataSchemaNode getSchemaNodeFromNamespace(final XmlElement element) throws DocumentedException {
+
+ 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 DocumentedException("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,
+ ImmutableNodes.fromInstanceId(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 <data/> container in the response.
+ * if filter is not present we want to read the entire datastore - return ROOT.
+ * @throws DocumentedException
+ */
+ protected Optional<YangInstanceIdentifier> getDataRootFromFilter(XmlElement operationElement) throws DocumentedException {
+ Optional<XmlElement> 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 DocumentedException {
+
+ if (filterElement.getChildElements().size() != 1) {
+ throw new DocumentedException("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 DocumentedException {
+ 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 DocumentedException("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> datastore;
+ private final Optional<Datastore> datastore;
public GetConfigExecution(final Optional<Datastore> datastore) {
this.datastore = datastore;
}
return datastore;
}
- static GetConfigExecution fromXml(final XmlElement xml, final String operationName) throws NetconfDocumentedException {
+ static GetConfigExecution fromXml(final XmlElement xml, final String operationName) throws DocumentedException {
try {
validateInputRpc(xml, operationName);
- } catch (final NetconfDocumentedException e) {
- throw new NetconfDocumentedException("Incorrect RPC: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
+ } catch (final DocumentedException e) {
+ throw new DocumentedException("Incorrect RPC: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
}
final Optional<Datastore> sourceDatastore;
try {
sourceDatastore = parseSource(xml);
- } catch (final NetconfDocumentedException e) {
- throw new NetconfDocumentedException("Get-config source attribute error: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
+ } catch (final DocumentedException e) {
+ throw new DocumentedException("Get-config source attribute error: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
}
- // Add filter
-
return new GetConfigExecution(sourceDatastore);
}
- private static Optional<Datastore> parseSource(final XmlElement xml) throws NetconfDocumentedException {
+ private static Optional<Datastore> parseSource(final XmlElement xml) throws DocumentedException {
final Optional<XmlElement> sourceElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SOURCE_KEY,
XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
Optional.of(Datastore.valueOf(sourceElement.get().getOnlyChildElement().getName())) : Optional.<Datastore>absent();
}
- private static void validateInputRpc(final XmlElement xml, String operationName) throws NetconfDocumentedException{
+ private static void validateInputRpc(final XmlElement xml, String operationName) throws DocumentedException{
xml.checkName(operationName);
xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
}
+
}
}