Merge "Model dom-broker statistics"
[controller.git] / opendaylight / md-sal / sal-rest-connector / src / main / java / org / opendaylight / controller / sal / restconf / impl / RestconfImpl.java
index 5d8c910afc31fa9d6420fc6d3a67466c34924317..e24500a76ce57ff5a7b607dec7a7e208990c35c3 100644 (file)
@@ -9,14 +9,18 @@
 package org.opendaylight.controller.sal.restconf.impl;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.base.Predicates;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
+import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import java.math.BigInteger;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
@@ -62,6 +66,7 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceI
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModifiedNodeDoesNotExistException;
 import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.NodeFactory;
@@ -79,7 +84,10 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
 import org.opendaylight.yangtools.yang.model.util.EmptyType;
+import org.opendaylight.yangtools.yang.model.util.ExtendedType;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
 import org.slf4j.Logger;
@@ -103,7 +111,15 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
+    private static class TypeDef {
+        public final TypeDefinition<? extends Object> typedef;
+        public final QName qName;
 
+        TypeDef(final TypeDefinition<? extends Object> typedef, final QName qName) {
+            this.typedef = typedef;
+            this.qName = qName;
+        }
+    }
 
     private final static RestconfImpl INSTANCE = new RestconfImpl();
 
@@ -137,13 +153,24 @@ public class RestconfImpl implements RestconfService {
 
     private static final String SCOPE_PARAM_NAME = "scope";
 
+    private static final String NETCONF_BASE = "urn:ietf:params:xml:ns:netconf:base:1.0";
+
+    private static final String NETCONF_BASE_PAYLOAD_NAME = "data";
+
+    private static final QName NETCONF_BASE_QNAME;
+
     static {
         try {
             EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
+            NETCONF_BASE_QNAME = QName.create(QNameModule.create(new URI(NETCONF_BASE), null), NETCONF_BASE_PAYLOAD_NAME );
         } catch (ParseException e) {
             throw new RestconfDocumentedException(
                     "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION,
                     ErrorTag.OPERATION_FAILED);
+        } catch (URISyntaxException e) {
+            throw new RestconfDocumentedException(
+                    "It wasn't possible to create instance of URI class with "+NETCONF_BASE+" URI", ErrorType.APPLICATION,
+                    ErrorTag.OPERATION_FAILED);
         }
     }
 
@@ -705,11 +732,13 @@ public class RestconfImpl implements RestconfService {
         validateInput(iiWithData.getSchemaNode(), payload);
 
         DOMMountPoint mountPoint = iiWithData.getMountPoint();
+        validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier());
         final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint);
         validateListKeysEqualityInPayloadAndUri(iiWithData, value);
         final NormalizedNode<?, ?> datastoreNormalizedNode = compositeNodeToDatastoreNormalizedNode(value,
                 iiWithData.getSchemaNode());
 
+
         YangInstanceIdentifier normalizedII;
         if (mountPoint != null) {
             normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(
@@ -760,6 +789,29 @@ public class RestconfImpl implements RestconfService {
         return Response.status(Status.OK).build();
     }
 
+    private void validateTopLevelNodeName(final Node<?> node,
+            final YangInstanceIdentifier identifier) {
+        final String payloadName = getName(node);
+        final Iterator<PathArgument> pathArguments = identifier.getReversePathArguments().iterator();
+
+        //no arguments
+        if (!pathArguments.hasNext()) {
+            //no "data" payload
+            if (!node.getNodeType().equals(NETCONF_BASE_QNAME)) {
+                throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument",
+                        ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE);
+            }
+        //any arguments
+        } else {
+            final String identifierName = pathArguments.next().getNodeType().getLocalName();
+            if (!payloadName.equals(identifierName)) {
+                throw new RestconfDocumentedException("Payload name (" + payloadName
+                        + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL,
+                        ErrorTag.MALFORMED_MESSAGE);
+            }
+        }
+    }
+
     /**
      * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node
      *
@@ -939,9 +991,13 @@ public class RestconfImpl implements RestconfService {
                 broker.commitConfigurationDataDelete(normalizedII).get();
             }
         } catch (Exception e) {
-            throw new RestconfDocumentedException("Error creating data", e);
+            final Optional<Throwable> searchedException = Iterables.tryFind(Throwables.getCausalChain(e),
+                    Predicates.instanceOf(ModifiedNodeDoesNotExistException.class));
+            if (searchedException.isPresent()) {
+                throw new RestconfDocumentedException("Data specified for deleting doesn't exist.", ErrorType.APPLICATION, ErrorTag.DATA_MISSING);
+            }
+            throw new RestconfDocumentedException("Error while deleting data", e);
         }
-
         return Response.status(Status.OK).build();
     }
 
@@ -1196,7 +1252,9 @@ public class RestconfImpl implements RestconfService {
                 try {
                     this.normalizeNode(nodeWrap, schema, null, mountPoint);
                 } catch (IllegalArgumentException e) {
-                    throw new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+                    RestconfDocumentedException restconfDocumentedException = new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+                    restconfDocumentedException.addSuppressed(e);
+                    throw restconfDocumentedException;
                 }
                 if (nodeWrap instanceof CompositeNodeWrapper) {
                     return ((CompositeNodeWrapper) nodeWrap).unwrap();
@@ -1273,12 +1331,20 @@ public class RestconfImpl implements RestconfService {
             final DOMMountPoint 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
+        TypeDef typeDef = this.typeDefinition(schema);
+        TypeDefinition<? extends Object> typeDefinition = typeDef != null ? typeDef.typedef : null;
+
+        // For leafrefs, extract the type it is pointing to
+        if(typeDefinition instanceof LeafrefTypeDefinition) {
+            if (schema.getQName().equals(typeDef.qName)) {
+                typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? this.controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), schema);
+            } else {
+                typeDefinition = SchemaContextUtil.getBaseTypeForLeafRef(((LeafrefTypeDefinition) typeDefinition), mountPoint == null ? this.controllerContext.getGlobalSchema() : mountPoint.getSchemaContext(), typeDef.qName);
+            }
+        }
+
+        if (typeDefinition instanceof IdentityrefTypeDefinition) {
+            inputValue = parseToIdentityValuesDTO(simpleNode, value, inputValue);
         }
 
         Object outputValue = inputValue;
@@ -1291,6 +1357,14 @@ public class RestconfImpl implements RestconfService {
         simpleNode.setValue(outputValue);
     }
 
+    private Object parseToIdentityValuesDTO(final SimpleNodeWrapper simpleNode, final Object value, Object inputValue) {
+        if ((value instanceof String)) {
+            inputValue = new IdentityValuesDTO(simpleNode.getNamespace().toString(), (String) value, null,
+                    (String) value);
+        } // else value is already instance of IdentityValuesDTO
+        return inputValue;
+    }
+
     private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder,
             final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) {
         final List<NodeWrapper<?>> children = compositeNodeBuilder.getValues();
@@ -1438,29 +1512,25 @@ public class RestconfImpl implements RestconfService {
         }
     }
 
-    private TypeDefinition<? extends Object> _typeDefinition(final LeafSchemaNode node) {
-        TypeDefinition<?> baseType = node.getType();
+    private TypeDef typeDefinition(final TypeDefinition<?> type, final QName nodeQName) {
+        TypeDefinition<?> baseType = type;
+        QName qName = nodeQName;
         while (baseType.getBaseType() != null) {
+            if (baseType instanceof ExtendedType) {
+                qName = baseType.getQName();
+            }
             baseType = baseType.getBaseType();
         }
 
-        return baseType;
-    }
-
-    private TypeDefinition<? extends Object> typeDefinition(final LeafListSchemaNode node) {
-        TypeDefinition<?> baseType = node.getType();
-        while (baseType.getBaseType() != null) {
-            baseType = baseType.getBaseType();
-        }
+        return new TypeDef(baseType, qName);
 
-        return baseType;
     }
 
-    private TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
+    private TypeDef typeDefinition(final DataSchemaNode node) {
         if (node instanceof LeafListSchemaNode) {
-            return typeDefinition((LeafListSchemaNode) node);
+            return typeDefinition(((LeafListSchemaNode)node).getType(), node.getQName());
         } else if (node instanceof LeafSchemaNode) {
-            return _typeDefinition((LeafSchemaNode) node);
+            return typeDefinition(((LeafSchemaNode)node).getType(), node.getQName());
         } else if (node instanceof AnyXmlSchemaNode) {
             return null;
         } else {