Merge "Implement finding a primary based on the shard name and do basic wiring of...
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.java
index c0ce90e15dde780a18e40e0e216c78059c9183fc..4716a02be2f26230721be7e08eb697eaeb0224cc 100644 (file)
@@ -8,6 +8,13 @@
  */
 package org.opendaylight.controller.sal.restconf.impl;
 
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
 import java.net.URI;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
@@ -20,12 +27,10 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.Future;
-
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
-
 import org.apache.commons.lang3.StringUtils;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
@@ -49,7 +54,9 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdent
 import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
@@ -67,13 +74,6 @@ import org.opendaylight.yangtools.yang.model.util.EmptyType;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
 
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.base.Splitter;
-import com.google.common.base.Strings;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-
 public class RestconfImpl implements RestconfService {
     private final static RestconfImpl INSTANCE = new RestconfImpl();
 
@@ -252,7 +252,8 @@ public class RestconfImpl implements RestconfService {
                 operationsAsData.add(immutableSimpleNode);
 
                 String name = module.getName();
-                LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null);
+                LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName,
+                        SchemaPath.create(true, QName.create("dummy")));
                 final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder;
                 fakeRpcSchemaNode.setAugmenting(true);
 
@@ -566,7 +567,7 @@ public class RestconfImpl implements RestconfService {
     }
 
     @Override
-    public StructuredData readConfigurationData(final String identifier) {
+    public StructuredData readConfigurationData(final String identifier, UriInfo info) {
         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
         CompositeNode data = null;
         MountInstance mountPoint = iiWithData.getMountPoint();
@@ -577,11 +578,57 @@ public class RestconfImpl implements RestconfService {
             data = broker.readConfigurationData(iiWithData.getInstanceIdentifier());
         }
 
+        data = pruneDataAtDepth( data, parseDepthParameter( info ) );
         return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint());
     }
 
+    @SuppressWarnings("unchecked")
+    private <T extends Node<?>> T pruneDataAtDepth( T node, Integer depth ) {
+        if( depth == null ) {
+            return node;
+        }
+
+        if( node instanceof CompositeNode ) {
+            ImmutableList.Builder<Node<?>> newChildNodes = ImmutableList.<Node<?>> builder();
+            if( depth > 1 ) {
+                for( Node<?> childNode: ((CompositeNode)node).getValue() ) {
+                    newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) );
+                }
+            }
+
+            return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() );
+        }
+        else { // SimpleNode
+            return node;
+        }
+    }
+
+    private Integer parseDepthParameter( UriInfo info ) {
+        String param = info.getQueryParameters( false ).getFirst( "depth" );
+        if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) {
+            return null;
+        }
+
+        try {
+            Integer depth = Integer.valueOf( param );
+            if( depth < 1 ) {
+                throw new RestconfDocumentedException( new RestconfError(
+                        ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth,
+                        null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
+            }
+
+            return depth;
+        }
+        catch( NumberFormatException e ) {
+            throw new RestconfDocumentedException( new RestconfError(
+                    ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE,
+                    "Invalid depth parameter: " + e.getMessage(),
+                    null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) );
+        }
+    }
+
     @Override
-    public StructuredData readOperationalData(final String identifier) {
+    public StructuredData readOperationalData(final String identifier, UriInfo info) {
         final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier);
         CompositeNode data = null;
         MountInstance mountPoint = iiWithData.getMountPoint();
@@ -592,6 +639,7 @@ public class RestconfImpl implements RestconfService {
             data = broker.readOperationalData(iiWithData.getInstanceIdentifier());
         }
 
+        data = pruneDataAtDepth( data, parseDepthParameter( info ) );
         return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint);
     }
 
@@ -997,97 +1045,127 @@ public class RestconfImpl implements RestconfService {
             }
         }
 
-        if ((nodeBuilder instanceof CompositeNodeWrapper)) {
-            final List<NodeWrapper<?>> children = ((CompositeNodeWrapper) nodeBuilder).getValues();
-            for (final NodeWrapper<? extends Object> child : children) {
-                final List<DataSchemaNode> potentialSchemaNodes =
-                        this.controllerContext.findInstanceDataChildrenByName(
-                                             ((DataNodeContainer) schema), child.getLocalName());
+        if ( nodeBuilder instanceof CompositeNodeWrapper ) {
+            if( schema instanceof DataNodeContainer ) {
+                normalizeCompositeNode( (CompositeNodeWrapper)nodeBuilder, (DataNodeContainer)schema,
+                                        mountPoint, currentAugment );
+            }
+            else if( schema instanceof AnyXmlSchemaNode ) {
+                normalizeAnyXmlNode( (CompositeNodeWrapper)nodeBuilder, (AnyXmlSchemaNode)schema );
+            }
+        }
+        else if ( nodeBuilder instanceof SimpleNodeWrapper ) {
+            normalizeSimpleNode( (SimpleNodeWrapper) nodeBuilder, schema, mountPoint );
+        }
+        else if ((nodeBuilder instanceof EmptyNodeWrapper)) {
+            normalizeEmptyNode( (EmptyNodeWrapper) nodeBuilder, schema );
+        }
+    }
+
+    private void normalizeAnyXmlNode( CompositeNodeWrapper compositeNode, AnyXmlSchemaNode schema ) {
+        List<NodeWrapper<?>> children = compositeNode.getValues();
+        for( NodeWrapper<? extends Object> child : children ) {
+            child.setNamespace( schema.getQName().getNamespace() );
+            if( child instanceof CompositeNodeWrapper ) {
+                normalizeAnyXmlNode( (CompositeNodeWrapper)child, schema );
+            }
+        }
+    }
+
+    private void normalizeEmptyNode( EmptyNodeWrapper emptyNodeBuilder, DataSchemaNode schema ) {
+        if ((schema instanceof LeafSchemaNode)) {
+            emptyNodeBuilder.setComposite(false);
+        }
+        else {
+            if ((schema instanceof ContainerSchemaNode)) {
+                // FIXME: Add presence check
+                emptyNodeBuilder.setComposite(true);
+            }
+        }
+    }
+
+    private void normalizeSimpleNode( SimpleNodeWrapper simpleNode, DataSchemaNode schema,
+                                      MountInstance mountPoint ) {
+        final Object value = simpleNode.getValue();
+        Object inputValue = value;
+        TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
+        if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
+            if ((value instanceof String)) {
+                inputValue = new IdentityValuesDTO( simpleNode.getNamespace().toString(),
+                        (String) value, null, (String) value );
+            } // else value is already instance of IdentityValuesDTO
+        }
 
-                if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
-                    StringBuilder builder = new StringBuilder();
-                    for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
-                        builder.append("   ").append(potentialSchemaNode.getQName().getNamespace().toString())
-                               .append("\n");
-                    }
+        Object outputValue = inputValue;
 
-                    throw new RestconfDocumentedException(
-                                 "Node \"" + child.getLocalName() +
-                                 "\" is added as augment from more than one module. " +
-                                 "Therefore node must have namespace (XML format) or module name (JSON format)." +
-                                 "\nThe node is added as augment from modules with namespaces:\n" + builder,
-                                 ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
-                }
+        if( typeDefinition != null ) {
+            Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
+            outputValue = codec == null ? null : codec.deserialize(inputValue);
+        }
+
+        simpleNode.setValue(outputValue);
+    }
 
-                boolean rightNodeSchemaFound = false;
+    private void normalizeCompositeNode( CompositeNodeWrapper compositeNodeBuilder,
+                                         DataNodeContainer schema, MountInstance mountPoint,
+                                         QName currentAugment ) {
+        final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
+        for (final NodeWrapper<? extends Object> child : children) {
+            final List<DataSchemaNode> potentialSchemaNodes =
+                    this.controllerContext.findInstanceDataChildrenByName(
+                                                           schema, child.getLocalName());
+
+            if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) {
+                StringBuilder builder = new StringBuilder();
                 for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
-                    if (!rightNodeSchemaFound) {
-                        final QName potentialCurrentAugment =
-                                this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
-                        if (child.getQname() != null ) {
-                            this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
-                            rightNodeSchemaFound = true;
-                        }
-                    }
+                    builder.append("   ").append(potentialSchemaNode.getQName().getNamespace().toString())
+                           .append("\n");
                 }
 
-                if (!rightNodeSchemaFound) {
-                    throw new RestconfDocumentedException(
-                               "Schema node \"" + child.getLocalName() + "\" was not found in module.",
-                               ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
-                }
+                throw new RestconfDocumentedException(
+                             "Node \"" + child.getLocalName() +
+                             "\" is added as augment from more than one module. " +
+                             "Therefore node must have namespace (XML format) or module name (JSON format)." +
+                             "\nThe node is added as augment from modules with namespaces:\n" + builder,
+                             ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
             }
 
-            if ((schema instanceof ListSchemaNode)) {
-                final List<QName> listKeys = ((ListSchemaNode) schema).getKeyDefinition();
-                for (final QName listKey : listKeys) {
-                    boolean foundKey = false;
-                    for (final NodeWrapper<? extends Object> child : children) {
-                        if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
-                            foundKey = true;
-                        }
-                    }
-
-                    if (!foundKey) {
-                        throw new RestconfDocumentedException(
-                                       "Missing key in URI \"" + listKey.getLocalName() +
-                                       "\" of list \"" + schema.getQName().getLocalName() + "\"",
-                                       ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+            boolean rightNodeSchemaFound = false;
+            for (final DataSchemaNode potentialSchemaNode : potentialSchemaNodes) {
+                if (!rightNodeSchemaFound) {
+                    final QName potentialCurrentAugment =
+                            this.normalizeNodeName(child, potentialSchemaNode, currentAugment, mountPoint);
+                    if (child.getQname() != null ) {
+                        this.normalizeNode(child, potentialSchemaNode, potentialCurrentAugment, mountPoint);
+                        rightNodeSchemaFound = true;
                     }
                 }
             }
-        }
-        else {
-            if ((nodeBuilder instanceof SimpleNodeWrapper)) {
-                final SimpleNodeWrapper simpleNode = ((SimpleNodeWrapper) nodeBuilder);
-                final Object value = simpleNode.getValue();
-                Object inputValue = value;
-                TypeDefinition<? extends Object> typeDefinition = this.typeDefinition(schema);
-                if ((typeDefinition instanceof IdentityrefTypeDefinition)) {
-                    if ((value instanceof String)) {
-                        inputValue = new IdentityValuesDTO( nodeBuilder.getNamespace().toString(),
-                                                            (String) value, null, (String) value );
-                    } // else value is already instance of IdentityValuesDTO
-                }
-
-                Codec<Object,Object> codec = RestCodec.from(typeDefinition, mountPoint);
-                Object outputValue = codec == null ? null : codec.deserialize(inputValue);
 
-                simpleNode.setValue(outputValue);
+            if (!rightNodeSchemaFound) {
+                throw new RestconfDocumentedException(
+                           "Schema node \"" + child.getLocalName() + "\" was not found in module.",
+                           ErrorType.APPLICATION, ErrorTag.UNKNOWN_ELEMENT );
             }
-            else {
-                if ((nodeBuilder instanceof EmptyNodeWrapper)) {
-                    final EmptyNodeWrapper emptyNodeBuilder = ((EmptyNodeWrapper) nodeBuilder);
-                    if ((schema instanceof LeafSchemaNode)) {
-                        emptyNodeBuilder.setComposite(false);
-                    }
-                    else {
-                        if ((schema instanceof ContainerSchemaNode)) {
-                            // FIXME: Add presence check
-                            emptyNodeBuilder.setComposite(true);
-                        }
+        }
+
+        if ((schema instanceof ListSchemaNode)) {
+            ListSchemaNode listSchemaNode = (ListSchemaNode)schema;
+            final List<QName> listKeys = listSchemaNode.getKeyDefinition();
+            for (final QName listKey : listKeys) {
+                boolean foundKey = false;
+                for (final NodeWrapper<? extends Object> child : children) {
+                    if (Objects.equal(child.unwrap().getNodeType().getLocalName(), listKey.getLocalName())) {
+                        foundKey = true;
                     }
                 }
+
+                if (!foundKey) {
+                    throw new RestconfDocumentedException(
+                                   "Missing key in URI \"" + listKey.getLocalName() +
+                                   "\" of list \"" + listSchemaNode.getQName().getLocalName() + "\"",
+                                   ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE );
+                }
             }
         }
     }
@@ -1190,6 +1268,9 @@ public class RestconfImpl implements RestconfService {
         else if (node instanceof LeafSchemaNode) {
             return _typeDefinition((LeafSchemaNode)node);
         }
+        else if (node instanceof AnyXmlSchemaNode) {
+            return null;
+        }
         else {
             throw new IllegalArgumentException("Unhandled parameter types: " +
                     Arrays.<Object>asList(node).toString());