Bug 6947 / Bug 6948 - implement point and insert query params
[netconf.git] / restconf / sal-rest-connector / src / main / java / org / opendaylight / netconf / sal / restconf / impl / BrokerFacade.java
index b915987285d4f20d4e669fa215c9f3306611b3df..f0fa715f0f1567beb3fee759a9b950739354517e 100644 (file)
@@ -9,16 +9,15 @@ package org.opendaylight.netconf.sal.restconf.impl;
 
 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
-
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import javax.annotation.Nullable;
@@ -43,13 +42,22 @@ import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
 import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
 import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
 import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
-import org.opendaylight.restconf.restful.utils.TransactionUtil;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 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.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.slf4j.Logger;
@@ -132,10 +140,13 @@ public class BrokerFacade {
      *            - path of node
      * @param payload
      *            - input data
+     * @param point
+     * @param insert
      * @return wrapper of status and future of PUT
      */
     public PutResult commitConfigurationDataPut(
-            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         Preconditions.checkNotNull(globalSchema);
         Preconditions.checkNotNull(path);
         Preconditions.checkNotNull(payload);
@@ -146,7 +157,7 @@ public class BrokerFacade {
         final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null ? Status.OK
                 : Status.CREATED;
         final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
-                newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema);
+                newReadWriteTransaction, CONFIGURATION, path, payload, globalSchema, insert, point);
         return new PutResult(status, future);
     }
 
@@ -163,10 +174,13 @@ public class BrokerFacade {
      *            - path of node
      * @param payload
      *            - input data
+     * @param point
+     * @param insert
      * @return wrapper of status and future of PUT
      */
     public PutResult commitMountPointDataPut(
-            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         Preconditions.checkNotNull(mountPoint);
         Preconditions.checkNotNull(path);
         Preconditions.checkNotNull(payload);
@@ -177,8 +191,8 @@ public class BrokerFacade {
             final Status status = readDataViaTransaction(newReadWriteTransaction, CONFIGURATION, path) != null
                     ? Status.OK : Status.CREATED;
             final CheckedFuture<Void, TransactionCommitFailedException> future = putDataViaTransaction(
-                    newReadWriteTransaction, CONFIGURATION, path,
-                    payload, mountPoint.getSchemaContext());
+                    newReadWriteTransaction, CONFIGURATION, path, payload, mountPoint.getSchemaContext(), insert,
+                    point);
             return new PutResult(status, future);
         }
         final String errMsg = "DOM data broker service isn't available for mount point " + path;
@@ -186,94 +200,149 @@ public class BrokerFacade {
         throw new RestconfDocumentedException(errMsg);
     }
 
-    public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext context,
-                                                                      final SchemaContext globalSchema)
-            throws InterruptedException {
-        final DOMDataReadWriteTransaction patchTransaction = this.domDataBroker.newReadWriteTransaction();
-        final List<PATCHStatusEntity> editCollection = new ArrayList<>();
+    public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext patchContext) throws Exception {
+        final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
+
+        // get new transaction and schema context on server or on mounted device
+        final SchemaContext schemaContext;
+        final DOMDataReadWriteTransaction patchTransaction;
+        if (mountPoint == null) {
+            schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
+            patchTransaction = this.domDataBroker.newReadWriteTransaction();
+        } else {
+            schemaContext = mountPoint.getSchemaContext();
+
+            final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
+
+            if (optional.isPresent()) {
+                patchTransaction = optional.get().newReadWriteTransaction();
+            } else {
+                // if mount point does not have broker it is not possible to continue and global error is reported
+                LOG.error("Http PATCH {} has failed - device {} does not support broker service",
+                        patchContext.getPatchId(), mountPoint.getIdentifier());
+                return new PATCHStatusContext(
+                        patchContext.getPatchId(),
+                        null,
+                        false,
+                        ImmutableList.of(new RestconfError(
+                                ErrorType.APPLICATION,
+                                ErrorTag.OPERATION_FAILED,
+                                "DOM data broker service isn't available for mount point "
+                                        + mountPoint.getIdentifier()))
+                );
+            }
+        }
 
+        final List<PATCHStatusEntity> editCollection = new ArrayList<>();
         List<RestconfError> editErrors;
-        int errorCounter = 0;
+        boolean withoutError = true;
 
-        for (final PATCHEntity patchEntity : context.getData()) {
+        for (final PATCHEntity patchEntity : patchContext.getData()) {
             final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
 
             switch (operation) {
                 case CREATE:
-                    if (errorCounter == 0) {
+                    if (withoutError) {
                         try {
                             postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
-                                    patchEntity.getNode(), globalSchema);
+                                    patchEntity.getNode(), schemaContext);
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
+                            LOG.error("Error call http PATCH operation {} on target {}",
+                                    operation,
+                                    patchEntity.getTargetNode().toString());
+
                             editErrors = new ArrayList<>();
                             editErrors.addAll(e.getErrors());
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
-                            errorCounter++;
+                            withoutError = false;
                         }
                     }
                     break;
                 case REPLACE:
-                    if (errorCounter == 0) {
+                    if (withoutError) {
                         try {
                             putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
-                                    .getTargetNode(), patchEntity.getNode(), globalSchema);
+                                    .getTargetNode(), patchEntity.getNode(), schemaContext);
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
+                            LOG.error("Error call http PATCH operation {} on target {}",
+                                    operation,
+                                    patchEntity.getTargetNode().toString());
+
                             editErrors = new ArrayList<>();
                             editErrors.addAll(e.getErrors());
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
-                            errorCounter++;
+                            withoutError = false;
                         }
                     }
                     break;
                 case DELETE:
-                    if (errorCounter == 0) {
+                    if (withoutError) {
                         try {
                             deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
                                     .getTargetNode());
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
+                            LOG.error("Error call http PATCH operation {} on target {}",
+                                    operation,
+                                    patchEntity.getTargetNode().toString());
+
                             editErrors = new ArrayList<>();
                             editErrors.addAll(e.getErrors());
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
-                            errorCounter++;
+                            withoutError = false;
                         }
                     }
                     break;
                 case REMOVE:
-                    if (errorCounter == 0) {
+                    if (withoutError) {
                         try {
                             deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
                                     .getTargetNode());
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
-                            LOG.error("Error removing {} by {} operation", patchEntity.getTargetNode().toString(),
-                                    patchEntity.getEditId(), e);
+                            LOG.error("Error call http PATCH operation {} on target {}",
+                                    operation,
+                                    patchEntity.getTargetNode().toString());
+
+                            editErrors = new ArrayList<>();
+                            editErrors.addAll(e.getErrors());
+                            editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+                            withoutError = false;
                         }
                     }
                     break;
                 case MERGE:
-                    if (errorCounter == 0) {
+                    if (withoutError) {
                         try {
                             mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
-                                    patchEntity.getNode(), globalSchema);
+                                    patchEntity.getNode(), schemaContext);
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
                         } catch (final RestconfDocumentedException e) {
+                            LOG.error("Error call http PATCH operation {} on target {}",
+                                    operation,
+                                    patchEntity.getTargetNode().toString());
+
                             editErrors = new ArrayList<>();
                             editErrors.addAll(e.getErrors());
                             editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
-                            errorCounter++;
+                            withoutError = false;
                         }
                     }
                     break;
+                default:
+                    LOG.error("Unsupported http PATCH operation {} on target {}",
+                            operation,
+                            patchEntity.getTargetNode().toString());
+                    break;
             }
         }
 
         // if errors then cancel transaction and return error status
-        if (errorCounter != 0) {
+        if (!withoutError) {
             patchTransaction.cancel();
-            return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
+            return new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
         }
 
         // if no errors commit transaction
@@ -284,7 +353,7 @@ public class BrokerFacade {
         Futures.addCallback(future, new FutureCallback<Void>() {
             @Override
             public void onSuccess(@Nullable final Void result) {
-                status.setStatus(new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
+                status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
                         true, null));
                 waiter.countDown();
             }
@@ -292,8 +361,9 @@ public class BrokerFacade {
             @Override
             public void onFailure(final Throwable t) {
                 // if commit failed it is global error
-                status.setStatus(new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
-                        false, Lists.newArrayList(
+                LOG.error("Http PATCH {} transaction commit has failed", patchContext.getPatchId());
+                status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
+                        false, ImmutableList.of(
                         new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, t.getMessage()))));
                 waiter.countDown();
             }
@@ -305,17 +375,20 @@ public class BrokerFacade {
 
     // POST configuration
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
-            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         checkPreconditions();
-        return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
+        return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload,
+                globalSchema, insert, point);
     }
 
     public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
-            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
+            final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final String insert, final String point) {
         final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
         if (domDataBrokerService.isPresent()) {
             return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
-                    payload, mountPoint.getSchemaContext());
+                    payload, mountPoint.getSchemaContext(), insert, point);
         }
         final String errMsg = "DOM data broker service isn't available for mount point " + path;
         LOG.warn(errMsg);
@@ -376,8 +449,8 @@ public class BrokerFacade {
 
             @Override
             public void onSuccess(final Optional<NormalizedNode<?, ?>> result) {
-                responseWaiter.countDown();
                 handlingCallback(null, datastore, path, result, readData);
+                responseWaiter.countDown();
             }
 
             @Override
@@ -389,7 +462,7 @@ public class BrokerFacade {
 
         try {
             responseWaiter.await();
-        } catch (final InterruptedException e) {
+        } catch (final Exception e) {
             final String msg = "Problem while waiting for response";
             LOG.warn(msg);
             throw new RestconfDocumentedException(msg, e);
@@ -397,51 +470,237 @@ public class BrokerFacade {
         return readData.getResult();
     }
 
+    /**
+     * POST data and submit transaction {@link DOMDataReadWriteTransaction}
+     */
     private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
+            final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String insert, final String point) {
+        LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
+        postData(rWTransaction, datastore, path, payload, schemaContext, insert, point);
+        return rWTransaction.submit();
+    }
+
+    /**
+     * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}
+     */
+    private void postDataWithinTransaction(
             final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
-        // FIXME: This is doing correct post for container and list children
-        //        not sure if this will work for choice case
-        if(payload instanceof MapNode) {
-            LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
+        LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
+        postData(rWTransaction, datastore, path, payload, schemaContext, null, null);
+    }
+
+    private void postData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+                          final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String insert, final String point) {
+        if (insert == null) {
+            makeNormalPost(rWTransaction, datastore, path, payload, schemaContext);
+        } else {
+            final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+            checkItemDoesNotExists(rWTransaction, datastore, path);
+            switch (insert) {
+                case "first":
+                    if(schemaNode instanceof ListSchemaNode){
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent().getParent());
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                            makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readList,
+                                    schemaContext);
+                        }
+                    } else {
+                        final OrderedLeafSetNode readLeafList =
+                                (OrderedLeafSetNode) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent());
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                            makeNormalPost(rWTransaction, datastore, path.getParent().getParent(), readLeafList,
+                                    schemaContext);
+                        }
+                    }
+                    break;
+                case "last":
+                    simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                    break;
+                case "before":
+                    if(schemaNode instanceof ListSchemaNode){
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList,
+                                    true);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, true);
+                        }
+                    }
+                    break;
+                case "after":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent().getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList,
+                                    false);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+                        } else {
+                            insertWithPointLeafListPost(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, false);
+                        }
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException(
+                            "Used bad value of insert parameter. Possible values are first, last, before or after, "
+                                    + "but was: " + insert);
+            }
+        }
+    }
+
+    private void insertWithPointLeafListPost(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+            final boolean before) {
+        rWTransaction.delete(datastore, path.getParent().getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (h == p) {
+                checkItemDoesNotExists(rWTransaction, datastore, path);
+                simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().getParent().node(nodeChild.getIdentifier());
+            checkItemDoesNotExists(rWTransaction, datastore, childPath);
+            rWTransaction.put(datastore, childPath, nodeChild);
+            h++;
+        }
+    }
+
+    private void insertWithPointListPost(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String point, final MapNode readList, final boolean before) {
+        rWTransaction.delete(datastore, path.getParent().getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier()
+                    .equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent().getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (h == p) {
+                checkItemDoesNotExists(rWTransaction, datastore, path);
+                simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().getParent().node(mapEntryNode.getIdentifier());
+            checkItemDoesNotExists(rWTransaction, datastore, childPath);
+            rWTransaction.put(datastore, childPath, mapEntryNode);
+            h++;
+        }
+    }
+
+    private DataSchemaNode checkListAndOrderedType(final SchemaContext ctx,
+            final YangInstanceIdentifier path) {
+        final YangInstanceIdentifier parent = path.getParent();
+        final DataSchemaContextNode<?> node = DataSchemaContextTree.from(ctx).getChild(parent);
+        final DataSchemaNode dataSchemaNode = node.getDataSchemaNode();
+
+        if (dataSchemaNode instanceof ListSchemaNode) {
+            if(!((ListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user list.");
+            }
+            return dataSchemaNode;
+        }
+        if (dataSchemaNode instanceof LeafListSchemaNode) {
+            if(!((LeafListSchemaNode) dataSchemaNode).isUserOrdered()) {
+                throw new RestconfDocumentedException("Insert parameter can be used only with ordered-by user leaf-list.");
+            }
+            return dataSchemaNode;
+        }
+        throw new RestconfDocumentedException("Insert parameter can be used only with list or leaf-list");
+    }
+
+    private void makeNormalPost(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+        if (payload instanceof MapNode) {
             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
-            for(final MapEntryNode child : ((MapNode) payload).getValue()) {
+            ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+            for (final MapEntryNode child : ((MapNode) payload).getValue()) {
                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
                 checkItemDoesNotExists(rWTransaction, datastore, childPath);
                 rWTransaction.put(datastore, childPath, child);
             }
-        } else {
-            checkItemDoesNotExists(rWTransaction,datastore, path);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
-            rWTransaction.put(datastore, path, payload);
-        }
-        return rWTransaction.submit();
-    }
-
-    private void postDataWithinTransaction(
-            final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
-        // FIXME: This is doing correct post for container and list children
-        //        not sure if this will work for choice case
-        if(payload instanceof MapNode) {
-            LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
+        } else if (payload instanceof LeafSetNode) {
             final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
             rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
-            for(final MapEntryNode child : ((MapNode) payload).getValue()) {
+            ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+            for (final LeafSetEntryNode<?> child : ((LeafSetNode<?>) payload).getValue()) {
                 final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
                 checkItemDoesNotExists(rWTransaction, datastore, childPath);
                 rWTransaction.put(datastore, childPath, child);
             }
         } else {
-            checkItemDoesNotExists(rWTransaction,datastore, path);
-            TransactionUtil.ensureParentsByMerge(path, schemaContext, rWTransaction);
-            rWTransaction.put(datastore, path, payload);
+            simplePostPut(rWTransaction, datastore, path, payload, schemaContext);
         }
     }
 
+    private void simplePostPut(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+        checkItemDoesNotExists(rWTransaction, datastore, path);
+        ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+        rWTransaction.put(datastore, path, payload);
+    }
+
     /**
      * Check if item already exists. Throws error if it does NOT already exist.
      * @param rWTransaction Current transaction
@@ -457,8 +716,8 @@ public class BrokerFacade {
         Futures.addCallback(future, new FutureCallback<Boolean>() {
             @Override
             public void onSuccess(@Nullable final Boolean result) {
-                responseWaiter.countDown();
                 handlingCallback(null, store, path, Optional.of(result), readData);
+                responseWaiter.countDown();
             }
 
             @Override
@@ -470,13 +729,13 @@ public class BrokerFacade {
 
         try {
             responseWaiter.await();
-        } catch (final InterruptedException e) {
+        } catch (final Exception e) {
             final String msg = "Problem while waiting for response";
             LOG.warn(msg);
             throw new RestconfDocumentedException(msg, e);
         }
 
-        if (!readData.getResult()) {
+        if ((readData.getResult() == null) || !readData.getResult()) {
             final String errMsg = "Operation via Restconf was not executed because data does not exist";
             LOG.trace("{}:{}", errMsg, path);
             rWTransaction.cancel();
@@ -500,8 +759,8 @@ public class BrokerFacade {
         Futures.addCallback(future, new FutureCallback<Boolean>() {
             @Override
             public void onSuccess(@Nullable final Boolean result) {
-                responseWaiter.countDown();
                 handlingCallback(null, store, path, Optional.of(result), readData);
+                responseWaiter.countDown();
             }
 
             @Override
@@ -513,7 +772,7 @@ public class BrokerFacade {
 
         try {
             responseWaiter.await();
-        } catch (final InterruptedException e) {
+        } catch (final Exception e) {
             final String msg = "Problem while waiting for response";
             LOG.warn(msg);
             throw new RestconfDocumentedException(msg, e);
@@ -528,20 +787,202 @@ public class BrokerFacade {
         }
     }
 
+    /**
+     * PUT data and submit {@link DOMDataReadWriteTransaction}
+     *
+     * @param point
+     * @param insert
+     */
     private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
-            final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
-            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+            final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String insert, final String point) {
         LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
-        writeTransaction.put(datastore, path, payload);
-        return writeTransaction.submit();
+        putData(readWriteTransaction, datastore, path, payload, schemaContext, insert, point);
+        return readWriteTransaction.submit();
     }
 
+    /**
+     * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}
+     *
+     * @param insert
+     * @param point
+     */
     private void putDataWithinTransaction(
             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
+        putData(writeTransaction, datastore, path, payload, schemaContext, null, null);
+    }
+
+    // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
+    private void putData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext,
+            final String insert, final String point) {
+        if (insert == null) {
+            makePut(rWTransaction, datastore, path, payload, schemaContext);
+        } else {
+            final DataSchemaNode schemaNode = checkListAndOrderedType(schemaContext, path);
+            checkItemDoesNotExists(rWTransaction, datastore, path);
+            switch (insert) {
+                case "first":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent());
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                            makePut(rWTransaction, datastore, path.getParent(), readList, schemaContext);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            rWTransaction.delete(datastore, path.getParent());
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                            makePut(rWTransaction, datastore, path.getParent(), readLeafList,
+                                    schemaContext);
+                        }
+                    }
+                    break;
+                case "last":
+                    simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                    break;
+                case "before":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList, true);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, true);
+                        }
+                    }
+                    break;
+                case "after":
+                    if (schemaNode instanceof ListSchemaNode) {
+                        final OrderedMapNode readList =
+                                (OrderedMapNode) this.readConfigurationData(path.getParent());
+                        if ((readList == null) || readList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readList, false);
+                        }
+                    } else {
+                        final OrderedLeafSetNode<?> readLeafList =
+                                (OrderedLeafSetNode<?>) readConfigurationData(path.getParent());
+                        if ((readLeafList == null) || readLeafList.getValue().isEmpty()) {
+                            simplePut(datastore, path, rWTransaction, schemaContext, payload);
+                        } else {
+                            insertWithPointLeafListPut(rWTransaction, datastore, path, payload, schemaContext, point,
+                                    readLeafList, false);
+                        }
+                    }
+                    break;
+                default:
+                    throw new RestconfDocumentedException(
+                            "Used bad value of insert parameter. Possible values are first, last, before or after, "
+                                    + "but was: " + insert);
+            }
+        }
+    }
+
+    private void insertWithPointLeafListPut(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final OrderedLeafSetNode<?> readLeafList,
+            final boolean before) {
+        rWTransaction.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (nodeChild.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final LeafSetEntryNode<?> nodeChild : readLeafList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, rWTransaction, schemaContext, payload);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(nodeChild.getIdentifier());
+            rWTransaction.put(datastore, childPath, nodeChild);
+            h++;
+        }
+    }
+
+    private void insertWithPointListPut(final DOMDataReadWriteTransaction rWTransaction,
+            final LogicalDatastoreType datastore, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+            final SchemaContext schemaContext, final String point, final OrderedMapNode readList,
+            final boolean before) {
+        rWTransaction.delete(datastore, path.getParent());
+        final InstanceIdentifierContext<?> instanceIdentifier =
+                ControllerContext.getInstance().toInstanceIdentifier(point);
+        int p = 0;
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (mapEntryNode.getIdentifier().equals(instanceIdentifier.getInstanceIdentifier().getLastPathArgument())) {
+                break;
+            }
+            p++;
+        }
+        if (!before) {
+            p++;
+        }
+        int h = 0;
+        final NormalizedNode<?, ?> emptySubtree =
+                ImmutableNodes.fromInstanceId(schemaContext, path.getParent());
+        rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+        for (final MapEntryNode mapEntryNode : readList.getValue()) {
+            if (h == p) {
+                simplePut(datastore, path, rWTransaction, schemaContext, payload);
+            }
+            final YangInstanceIdentifier childPath = path.getParent().node(mapEntryNode.getIdentifier());
+            rWTransaction.put(datastore, childPath, mapEntryNode);
+            h++;
+        }
+    }
+
+    private void makePut(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+            final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+        if (payload instanceof MapNode) {
+            final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+            writeTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+            ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
+            for (final MapEntryNode child : ((MapNode) payload).getValue()) {
+                final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+                writeTransaction.put(datastore, childPath, child);
+            }
+        } else {
+            simplePut(datastore, path, writeTransaction, schemaContext, payload);
+        }
+    }
+
+    private void simplePut(final LogicalDatastoreType datastore, final YangInstanceIdentifier path,
+            final DOMDataReadWriteTransaction writeTransaction, final SchemaContext schemaContext,
+            final NormalizedNode<?, ?> payload) {
+        ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
         writeTransaction.put(datastore, path, payload);
     }
 
@@ -565,7 +1006,7 @@ public class BrokerFacade {
             final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
             final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
         LOG.trace("Merge {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
-        TransactionUtil.ensureParentsByMerge(path, schemaContext, writeTransaction);
+        ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
 
         // merging is necessary only for lists otherwise we can call put method
         if (payload instanceof MapNode) {
@@ -641,8 +1082,37 @@ public class BrokerFacade {
             return this.status;
         }
 
-        public void setStatus(PATCHStatusContext status) {
+        public void setStatus(final PATCHStatusContext status) {
             this.status = status;
         }
     }
+
+    private void ensureParentsByMerge(final LogicalDatastoreType store, final YangInstanceIdentifier normalizedPath,
+            final DOMDataReadWriteTransaction rwTx, final SchemaContext schemaContext) {
+        final List<PathArgument> normalizedPathWithoutChildArgs = new ArrayList<>();
+        YangInstanceIdentifier rootNormalizedPath = null;
+
+        final Iterator<PathArgument> it = normalizedPath.getPathArguments().iterator();
+
+        while (it.hasNext()) {
+            final PathArgument pathArgument = it.next();
+            if (rootNormalizedPath == null) {
+                rootNormalizedPath = YangInstanceIdentifier.create(pathArgument);
+            }
+
+            if (it.hasNext()) {
+                normalizedPathWithoutChildArgs.add(pathArgument);
+            }
+        }
+
+        if (normalizedPathWithoutChildArgs.isEmpty()) {
+            return;
+        }
+
+        Preconditions.checkArgument(rootNormalizedPath != null, "Empty path received");
+
+        final NormalizedNode<?, ?> parentStructure = ImmutableNodes.fromInstanceId(schemaContext,
+                YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
+        rwTx.merge(store, rootNormalizedPath, parentStructure);
+    }
 }