Bug 5528 - Create stream for DS 55/40055/14
authorJakub Toth <jatoth@cisco.com>
Wed, 8 Jun 2016 16:37:16 +0000 (18:37 +0200)
committerJakub Toth <jatoth@cisco.com>
Tue, 28 Jun 2016 12:24:27 +0000 (12:24 +0000)
Change-Id: I2bdd16302aebd70c8929bbedfa99672358a96c91
Signed-off-by: Jakub Toth <jatoth@cisco.com>
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/services/impl/RestconfInvokeOperationsServiceImpl.java
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java [new file with mode: 0644]
restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java [new file with mode: 0644]

index e31939323d1c610951ab77b95ebd88dd3ed52181..1322e73ea96e09bfc8f9948ea3ea837e14c02e06 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.restconf.restful.services.impl;
 
+import java.net.URI;
 import javax.ws.rs.core.UriInfo;
 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
@@ -16,7 +17,9 @@ import org.opendaylight.restconf.common.references.SchemaContextRef;
 import org.opendaylight.restconf.handlers.RpcServiceHandler;
 import org.opendaylight.restconf.handlers.SchemaContextHandler;
 import org.opendaylight.restconf.restful.services.api.RestconfInvokeOperationsService;
+import org.opendaylight.restconf.restful.utils.CreateStreamUtil;
 import org.opendaylight.restconf.restful.utils.RestconfInvokeOperationsUtil;
+import org.opendaylight.restconf.restful.utils.RestconfStreamsConstants;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
@@ -32,17 +35,27 @@ public class RestconfInvokeOperationsServiceImpl implements RestconfInvokeOperat
 
     @Override
     public NormalizedNodeContext invokeRpc(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) {
+        final SchemaContextRef refSchemaCtx = new SchemaContextRef(this.schemaContextHandler.get());
         final SchemaPath schemaPath = payload.getInstanceIdentifierContext().getSchemaNode().getPath();
         final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint();
+        final URI namespace = payload.getInstanceIdentifierContext().getSchemaNode().getQName().getNamespace();
         DOMRpcResult response;
+
         SchemaContextRef schemaContextRef;
+
         if (mountPoint == null) {
-            response = RestconfInvokeOperationsUtil.invokeRpc(payload.getData(), schemaPath, this.rpcServiceHandler);
+            if (namespace.toString().equals(RestconfStreamsConstants.SAL_REMOTE_NAMESPACE)) {
+                response = CreateStreamUtil.createStream(payload, refSchemaCtx);
+            } else {
+                response = RestconfInvokeOperationsUtil.invokeRpc(payload.getData(), schemaPath,
+                        this.rpcServiceHandler);
+            }
             schemaContextRef = new SchemaContextRef(this.schemaContextHandler.get());
         } else {
             response = RestconfInvokeOperationsUtil.invokeRpcViaMountPoint(mountPoint, payload.getData(), schemaPath);
             schemaContextRef = new SchemaContextRef(mountPoint.getSchemaContext());
         }
+
         final DOMRpcResult result = RestconfInvokeOperationsUtil.checkResponse(response);
 
         RpcDefinition resultNodeSchema = null;
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/CreateStreamUtil.java
new file mode 100644 (file)
index 0000000..2a6cd37
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.restful.utils;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+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.Notificator;
+import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.restconf.utils.parser.ParserIdentifier;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Util class for streams
+ *
+ * <ul>
+ * <li>create stream
+ * <li>subscribe
+ * </ul>
+ *
+ */
+public final class CreateStreamUtil {
+
+    private static final Logger LOG = LoggerFactory.getLogger(CreateStreamUtil.class);
+
+    private CreateStreamUtil() {
+        throw new UnsupportedOperationException("Util class");
+    }
+
+    /**
+     * Create stream with POST operation via RPC
+     *
+     * @param payload
+     *            - input of rpc - example in JSON:
+     *
+     *            <pre>
+     *            {@code
+     *            {
+     *                "input": {
+     *                    "path": "/toaster:toaster/toaster:toasterStatus",
+     *                    "sal-remote-augment:datastore": "OPERATIONAL",
+     *                    "sal-remote-augment:scope": "ONE"
+     *                }
+     *            }
+     *            }
+     *            </pre>
+     *
+     * @param refSchemaCtx
+     *            - reference to {@link SchemaContext} -
+     *            {@link SchemaContextRef}
+     * @return {@link CheckedFuture} with {@link DOMRpcResult} - This mean
+     *         output of RPC - example in JSON:
+     *
+     *         <pre>
+     *         {@code
+     *         {
+     *             "output": {
+     *                 "stream-name": "toaster:toaster/toaster:toasterStatus/datastore=OPERATIONAL/scope=ONE"
+     *             }
+     *         }
+     *         }
+     *         </pre>
+     *
+     */
+    public static DOMRpcResult createStream(final NormalizedNodeContext payload,
+            final SchemaContextRef refSchemaCtx) {
+        final ContainerNode data = (ContainerNode) payload.getData();
+        final QName qname = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
+        final YangInstanceIdentifier path = preparePath(payload, data, qname);
+        final String streamName = prepareStream(path, refSchemaCtx.get(), data);
+
+        final QName outputQname = QName.create(qname, "output");
+        final QName streamNameQname = QName.create(qname, "stream-name");
+
+        final ContainerNode output = ImmutableContainerNodeBuilder.create()
+                .withNodeIdentifier(new NodeIdentifier(outputQname))
+                .withChild(ImmutableNodes.leafNode(streamNameQname, streamName)).build();
+
+        if (!Notificator.existListenerFor(streamName)) {
+            Notificator.createListener(path, streamName);
+        }
+
+        return new DefaultDOMRpcResult(output);
+    }
+
+    private static String prepareStream(final YangInstanceIdentifier path, final SchemaContext schemaContext,
+            final ContainerNode data) {
+        LogicalDatastoreType ds = parseEnum(data, LogicalDatastoreType.class,
+                RestconfStreamsConstants.DATASTORE_PARAM_NAME);
+        ds = ds == null ? RestconfStreamsConstants.DEFAULT_DS : ds;
+
+        DataChangeScope scope = parseEnum(data, DataChangeScope.class, RestconfStreamsConstants.SCOPE_PARAM_NAME);
+        scope = scope == null ? RestconfStreamsConstants.DEFAULT_SCOPE : scope;
+
+        final String streamName = Notificator
+                .createStreamNameFromUri(ParserIdentifier.stringFromYangInstanceIdentifier(path, schemaContext)
+                + RestconfStreamsConstants.DS_URI + ds + RestconfStreamsConstants.SCOPE_URI + scope);
+        if((streamName == null) || streamName.equals("")){
+            final String errMsg = "Path is empty or contains value node which is not Container or List build-in type.";
+            LOG.debug(errMsg + path);
+            throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
+        }
+        return streamName;
+    }
+
+    private static <T> T parseEnum(final ContainerNode data, final Class<T> clazz, final String paramName) {
+        final Optional<DataContainerChild<? extends PathArgument, ?>> augNode = data
+                .getChild(RestconfStreamsConstants.SAL_REMOTE_AUG_IDENTIFIER);
+        if (!augNode.isPresent() && !(augNode instanceof AugmentationNode)) {
+            return null;
+        }
+        final Optional<DataContainerChild<? extends PathArgument, ?>> enumNode =
+                ((AugmentationNode) augNode.get()).getChild(
+                        new NodeIdentifier(QName.create(RestconfStreamsConstants.SAL_REMOTE_AUGMENT, paramName)));
+        if (!enumNode.isPresent()) {
+            return null;
+        }
+        final Object value = enumNode.get().getValue();
+        if (!(value instanceof String)) {
+            return null;
+        }
+
+        return resolveEnum(clazz, (String) value);
+    }
+
+    private static <T> T resolveEnum(final Class<T> clazz, final String value) {
+        for (final T t : clazz.getEnumConstants()) {
+            if (((Enum<?>) t).name().equals(value)) {
+                return t;
+            }
+        }
+        return null;
+    }
+
+    private static YangInstanceIdentifier preparePath(final NormalizedNodeContext payload, final ContainerNode data,
+            final QName qName) {
+        final Optional<DataContainerChild<? extends PathArgument, ?>> path = data
+                .getChild(new YangInstanceIdentifier.NodeIdentifier(QName.create(qName, "path")));
+        Object pathValue = null;
+        if (path.isPresent()) {
+            pathValue = path.get().getValue();
+        }
+        if (!(pathValue instanceof YangInstanceIdentifier)) {
+            final String errMsg = "Instance identifier was not normalized correctly ";
+            LOG.debug(errMsg + qName);
+            throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
+        }
+        return (YangInstanceIdentifier) pathValue;
+    }
+}
diff --git a/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java b/restconf/sal-rest-connector/src/main/java/org/opendaylight/restconf/restful/utils/RestconfStreamsConstants.java
new file mode 100644 (file)
index 0000000..3cab59b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.restconf.restful.utils;
+
+import com.google.common.collect.Sets;
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.restconf.utils.RestconfConstants;
+import org.opendaylight.restconf.utils.parser.builder.ParserBuilderConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Constants for streams
+ *
+ */
+public final class RestconfStreamsConstants {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestconfStreamsConstants.class);
+
+    public static final String SAL_REMOTE_NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
+
+    public static final String DATASTORE_PARAM_NAME = "datastore";
+
+    private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription");
+
+    public static final QNameModule SAL_REMOTE_AUGMENT;
+
+    public static final YangInstanceIdentifier.AugmentationIdentifier SAL_REMOTE_AUG_IDENTIFIER;
+
+    public static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE;
+
+    public static final LogicalDatastoreType DEFAULT_DS = LogicalDatastoreType.CONFIGURATION;
+
+    public static final String SCOPE_PARAM_NAME = "scope";
+
+    public static final String DS_URI = RestconfConstants.SLASH + DATASTORE_PARAM_NAME
+            + ParserBuilderConstants.Deserializer.EQUAL;
+
+    public static final String SCOPE_URI = RestconfConstants.SLASH + SCOPE_PARAM_NAME
+            + ParserBuilderConstants.Deserializer.EQUAL;
+
+    static {
+        Date eventSubscriptionAugRevision;
+        try {
+            eventSubscriptionAugRevision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08");
+        } catch (final ParseException e) {
+            final String errMsg = "It wasn't possible to convert revision date of sal-remote-augment to date";
+            LOG.debug(errMsg);
+            throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED);
+        }
+        SAL_REMOTE_AUGMENT = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, eventSubscriptionAugRevision);
+        SAL_REMOTE_AUG_IDENTIFIER = new YangInstanceIdentifier.AugmentationIdentifier(Sets
+                .newHashSet(QName.create(SAL_REMOTE_AUGMENT, "scope"), QName.create(SAL_REMOTE_AUGMENT, "datastore")));
+    }
+
+    private RestconfStreamsConstants() {
+        throw new UnsupportedOperationException("Util class.");
+    }
+
+}