2 * Copyright (c) 2015 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
9 package org.opendaylight.netconf.mdsal.connector.ops.get;
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Function;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Throwables;
15 import com.google.common.collect.Iterables;
16 import com.google.common.collect.Lists;
17 import java.io.IOException;
19 import java.net.URISyntaxException;
20 import java.util.Collections;
21 import javax.xml.stream.XMLOutputFactory;
22 import javax.xml.stream.XMLStreamException;
23 import javax.xml.stream.XMLStreamWriter;
24 import javax.xml.transform.dom.DOMResult;
25 import org.opendaylight.controller.config.util.xml.DocumentedException;
26 import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorSeverity;
27 import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorTag;
28 import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorType;
29 import org.opendaylight.controller.config.util.xml.XmlElement;
30 import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
31 import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
32 import org.opendaylight.netconf.mdsal.connector.ops.Datastore;
33 import org.opendaylight.netconf.util.mapping.AbstractSingletonNetconfOperation;
34 import org.opendaylight.yangtools.yang.common.QName;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
36 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
37 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
38 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
39 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
40 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
41 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
42 import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
43 import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
44 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
45 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
46 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
47 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.Module;
51 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
52 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.w3c.dom.Document;
56 import org.w3c.dom.Element;
57 import org.w3c.dom.Node;
59 public abstract class AbstractGet extends AbstractSingletonNetconfOperation {
61 private static final Logger LOG = LoggerFactory.getLogger(AbstractGet.class);
63 protected static final String FILTER = "filter";
64 static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
65 protected final CurrentSchemaContext schemaContext;
67 public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
68 super(netconfSessionIdForReporting);
69 this.schemaContext = schemaContext;
72 private static final XMLOutputFactory XML_OUTPUT_FACTORY;
75 XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
76 XML_OUTPUT_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true);
79 protected Node transformNormalizedNode(final Document document, final NormalizedNode<?, ?> data, final YangInstanceIdentifier dataRoot) {
81 final DOMResult result = new DOMResult(document.createElement(XmlNetconfConstants.DATA_KEY));
83 final XMLStreamWriter xmlWriter = getXmlStreamWriter(result);
85 final NormalizedNodeStreamWriter nnStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter,
86 schemaContext.getCurrentContext(), getSchemaPath(dataRoot));
88 final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter, true);
90 writeRootElement(xmlWriter, nnWriter, (ContainerNode) data);
91 return result.getNode();
95 private XMLStreamWriter getXmlStreamWriter(final DOMResult result) {
97 return XML_OUTPUT_FACTORY.createXMLStreamWriter(result);
98 } catch (final XMLStreamException e) {
99 throw new RuntimeException(e);
103 private static final Function<PathArgument, QName> PATH_ARG_TO_QNAME = new Function<YangInstanceIdentifier.PathArgument, QName>() {
105 public QName apply(final YangInstanceIdentifier.PathArgument input) {
106 return input.getNodeType();
110 private SchemaPath getSchemaPath(final YangInstanceIdentifier dataRoot) {
111 return SchemaPath.create(Iterables.transform(dataRoot.getPathArguments(), PATH_ARG_TO_QNAME), dataRoot.equals(ROOT));
114 // TODO this code is located in Restconf already
115 private void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data) {
117 if (data.getNodeType().equals(SchemaContext.NAME)) {
118 for (final DataContainerChild<? extends PathArgument, ?> child : data.getValue()) {
119 nnWriter.write(child);
122 nnWriter.write(data);
126 } catch (XMLStreamException | IOException e) {
127 Throwables.propagate(e);
131 private DataSchemaNode getSchemaNodeFromNamespace(final XmlElement element) throws DocumentedException {
134 final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(new URI(element.getNamespace()), null);
135 DataSchemaNode dataSchemaNode = module.getDataChildByName(element.getName());
136 if (dataSchemaNode != null) {
137 return dataSchemaNode;
139 } catch (URISyntaxException e) {
140 LOG.debug("Error during parsing of element namespace, this should not happen since namespace of an xml " +
141 "element is valid and if the xml was parsed then the URI should be as well");
142 throw new IllegalArgumentException("Unable to parse element namespace, this should not happen since " +
143 "namespace of an xml element is valid and if the xml was parsed then the URI should be as well");
145 throw new DocumentedException("Unable to find node with namespace: " + element.getNamespace() + "in schema context: " + schemaContext.getCurrentContext().toString(),
146 ErrorType.application,
147 ErrorTag.unknown_namespace,
148 ErrorSeverity.error);
151 protected Element serializeNodeWithParentStructure(Document document, YangInstanceIdentifier dataRoot, NormalizedNode node) {
152 if (!dataRoot.equals(ROOT)) {
153 return (Element) transformNormalizedNode(document,
154 ImmutableNodes.fromInstanceId(schemaContext.getCurrentContext(), dataRoot, node),
157 return (Element) transformNormalizedNode(document, node, ROOT);
162 * @param operationElement operation element
163 * @return if Filter is present and not empty returns Optional of the InstanceIdentifier to the read location in datastore.
164 * empty filter returns Optional.absent() which should equal an empty <data/> container in the response.
165 * if filter is not present we want to read the entire datastore - return ROOT.
166 * @throws DocumentedException
168 protected Optional<YangInstanceIdentifier> getDataRootFromFilter(XmlElement operationElement) throws DocumentedException {
169 Optional<XmlElement> filterElement = operationElement.getOnlyChildElementOptionally(FILTER);
170 if (filterElement.isPresent()) {
171 if (filterElement.get().getChildElements().size() == 0) {
172 return Optional.absent();
174 return Optional.of(getInstanceIdentifierFromFilter(filterElement.get()));
176 return Optional.of(ROOT);
181 protected YangInstanceIdentifier getInstanceIdentifierFromFilter(XmlElement filterElement) throws DocumentedException {
183 if (filterElement.getChildElements().size() != 1) {
184 throw new DocumentedException("Multiple filter roots not supported yet",
185 ErrorType.application, ErrorTag.operation_not_supported, ErrorSeverity.error);
188 XmlElement element = filterElement.getOnlyChildElement();
189 DataSchemaNode schemaNode = getSchemaNodeFromNamespace(element);
191 return getReadPointFromNode(YangInstanceIdentifier.builder().build(), filterToNormalizedNode(element, schemaNode));
194 private YangInstanceIdentifier getReadPointFromNode(final YangInstanceIdentifier pathArg, final NormalizedNode nNode) {
195 final YangInstanceIdentifier path = pathArg.node(nNode.getIdentifier());
196 if (nNode instanceof DataContainerNode) {
197 DataContainerNode node = (DataContainerNode) nNode;
198 if (node.getValue().size() == 1) {
199 return getReadPointFromNode(path, (NormalizedNode) Lists.newArrayList(node.getValue()).get(0));
205 private NormalizedNode filterToNormalizedNode(XmlElement element, DataSchemaNode schemaNode) throws DocumentedException {
206 DomToNormalizedNodeParserFactory parserFactory = DomToNormalizedNodeParserFactory
207 .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext());
209 final NormalizedNode parsedNode;
211 if (schemaNode instanceof ContainerSchemaNode) {
212 parsedNode = parserFactory.getContainerNodeParser().parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
213 } else if (schemaNode instanceof ListSchemaNode) {
214 parsedNode = parserFactory.getMapNodeParser().parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
216 throw new DocumentedException("Schema node of the top level element is not an instance of container or list",
217 ErrorType.application, ErrorTag.unknown_element, ErrorSeverity.error);
222 protected static final class GetConfigExecution {
224 private final Optional<Datastore> datastore;
225 public GetConfigExecution(final Optional<Datastore> datastore) {
226 this.datastore = datastore;
229 public Optional<Datastore> getDatastore() {
233 static GetConfigExecution fromXml(final XmlElement xml, final String operationName) throws DocumentedException {
235 validateInputRpc(xml, operationName);
236 } catch (final DocumentedException e) {
237 throw new DocumentedException("Incorrect RPC: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
240 final Optional<Datastore> sourceDatastore;
242 sourceDatastore = parseSource(xml);
243 } catch (final DocumentedException e) {
244 throw new DocumentedException("Get-config source attribute error: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo());
247 return new GetConfigExecution(sourceDatastore);
250 private static Optional<Datastore> parseSource(final XmlElement xml) throws DocumentedException {
251 final Optional<XmlElement> sourceElement = xml.getOnlyChildElementOptionally(XmlNetconfConstants.SOURCE_KEY,
252 XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);
254 return sourceElement.isPresent() ?
255 Optional.of(Datastore.valueOf(sourceElement.get().getOnlyChildElement().getName())) : Optional.<Datastore>absent();
258 private static void validateInputRpc(final XmlElement xml, String operationName) throws DocumentedException{
259 xml.checkName(operationName);
260 xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0);