final SchemaContextRef refSchemaCtx) {
final ContainerNode data = (ContainerNode) payload.getData();
final QName qname = payload.getInstanceIdentifierContext().getSchemaNode().getQName();
- final YangInstanceIdentifier path = preparePath(payload, data, qname);
+ final YangInstanceIdentifier path = preparePath(data, qname);
final String streamName = prepareStream(path, refSchemaCtx.get(), data);
final QName outputQname = QName.create(qname, "output");
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;
}
return StreamUtil.resolveEnum(clazz, (String) value);
}
- private static YangInstanceIdentifier preparePath(final NormalizedNodeContext payload, final ContainerNode data,
- final QName qName) {
+ private static YangInstanceIdentifier preparePath(final ContainerNode data, final QName qName) {
final Optional<DataContainerChild<? extends PathArgument, ?>> path = data
.getChild(new YangInstanceIdentifier.NodeIdentifier(QName.create(qName, "path")));
Object pathValue = null;
--- /dev/null
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
+import org.opendaylight.controller.md.sal.rest.common.TestRestconfUtils;
+import org.opendaylight.netconf.sal.restconf.impl.InstanceIdentifierContext;
+import org.opendaylight.netconf.sal.restconf.impl.NormalizedNodeContext;
+import org.opendaylight.netconf.sal.restconf.impl.RestconfDocumentedException;
+import org.opendaylight.restconf.common.references.SchemaContextRef;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
+
+public class CreateStreamUtilTest {
+
+ private static final String PATH_FOR_NEW_SCHEMA_CONTEXT = "/streams";
+
+ private NormalizedNodeContext payload;
+ private SchemaContextRef refSchemaCtx;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ refSchemaCtx = new SchemaContextRef(TestRestconfUtils.loadSchemaContext(PATH_FOR_NEW_SCHEMA_CONTEXT));
+ }
+
+ @Test
+ public void createStreamTest() {
+ payload = prepareDomPayload("create-data-change-event-subscription", "input", "toaster", "path");
+ final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+ assertEquals(result.getErrors(), Collections.emptyList());
+ final NormalizedNode<?, ?> testedNn = result.getResult();
+ assertNotNull(testedNn);
+ final NormalizedNodeContext contextRef = prepareDomPayload("create-data-change-event-subscription", "output", "toaster:toaster/datastore=CONFIGURATION/scope=BASE", "stream-name");
+ assertEquals(contextRef.getData(), testedNn);
+ }
+
+ @Test(expected = RestconfDocumentedException.class)
+ public void createStreamWrongValueTest() {
+ payload = prepareDomPayload("create-data-change-event-subscription", "input", "String value", "path");
+ final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+ assertEquals(result.getErrors(), Collections.emptyList());
+ }
+
+ @Test(expected = RestconfDocumentedException.class)
+ public void createStreamWrongInputRpcTest() {
+ payload = prepareDomPayload("create-data-change-event-subscription2", "input", "toaster", "path2");
+ final DOMRpcResult result = CreateStreamUtil.createStream(payload, refSchemaCtx);
+ assertEquals(result.getErrors(), Collections.emptyList());
+ }
+
+ private NormalizedNodeContext prepareDomPayload(final String rpcName, final String inputOutput, final String toasterValue, final String inputOutputName) {
+ final SchemaContext schema = refSchemaCtx.get();
+ final Module rpcModule = schema.findModuleByName("sal-remote", null);
+ assertNotNull(rpcModule);
+ final QName rpcQName = QName.create(rpcModule.getQNameModule(), rpcName);
+ final QName rpcInputQName = QName.create(rpcModule.getQNameModule(), inputOutput);
+ final Set<RpcDefinition> setRpcs = rpcModule.getRpcs();
+ ContainerSchemaNode rpcInputSchemaNode = null;
+ for (final RpcDefinition rpc : setRpcs) {
+ if (rpcQName.isEqualWithoutRevision(rpc.getQName())) {
+ rpcInputSchemaNode = SchemaNodeUtils.getRpcDataSchema(rpc, rpcInputQName);
+ break;
+ }
+ }
+ assertNotNull(rpcInputSchemaNode);
+
+ final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> container = Builders.containerBuilder(rpcInputSchemaNode);
+
+ final QName lfQName = QName.create(rpcModule.getQNameModule(), inputOutputName);
+ final DataSchemaNode lfSchemaNode = rpcInputSchemaNode.getDataChildByName(lfQName);
+
+ assertTrue(lfSchemaNode instanceof LeafSchemaNode);
+
+ final QName rpcQname = QName.create("http://netconfcentral.org/ns/toaster", "2009-11-20", toasterValue);
+ final Object o;
+ if ("toaster".equals(toasterValue)) {
+ o = YangInstanceIdentifier.builder().node(rpcQname).build();
+ } else {
+ o = toasterValue;
+ }
+ final LeafNode<Object> lfNode = (Builders.leafBuilder((LeafSchemaNode) lfSchemaNode)
+ .withValue(o)).build();
+ container.withChild(lfNode);
+
+ return new NormalizedNodeContext(new InstanceIdentifierContext<>(null, rpcInputSchemaNode, null, schema), container.build());
+ }
+}
--- /dev/null
+module sal-remote {
+
+ yang-version 1;
+ namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote";
+ prefix "sal-remote";
+
+ organization "Cisco Systems, Inc.";
+ contact "Martin Bobak <mbobak@cisco.com>";
+
+ description
+ "This module contains the definition of methods related to
+ sal remote model.
+
+ Copyright (c)2013 Cisco Systems, Inc. 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";
+
+ revision "2014-01-14" {
+ description
+ "Initial revision";
+ }
+
+
+ typedef q-name {
+ type string;
+ reference
+ "http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#QName";
+ }
+
+ rpc create-data-change-event-subscription {
+ input {
+ leaf path {
+ type instance-identifier;
+ description "Subtree path. ";
+ }
+ }
+ output {
+ leaf stream-name {
+ type string;
+ description "Notification stream name.";
+ }
+ }
+ }
+
+ rpc create-data-change-event-subscription2 {
+ input {
+ leaf path2 {
+ type instance-identifier;
+ description "Subtree path. ";
+ }
+ }
+ output {
+ leaf stream-name2 {
+ type string;
+ description "Notification stream name.";
+ }
+ }
+ }
+
+ notification data-changed-notification {
+ description "Data change notification.";
+ list data-change-event {
+ key path;
+ leaf path {
+ type instance-identifier;
+ }
+ leaf store {
+ type enumeration {
+ enum config;
+ enum operation;
+ }
+ }
+ leaf operation {
+ type enumeration {
+ enum created;
+ enum updated;
+ enum deleted;
+ }
+ }
+ anyxml data{
+ description "DataObject ";
+ }
+ }
+ }
+
+ rpc create-notification-stream {
+ input {
+ leaf-list notifications {
+ type q-name;
+ description "Notification QNames";
+ }
+ }
+ output {
+ leaf notification-stream-identifier {
+ type string;
+ description "Unique notification stream identifier, in which notifications will be propagated";
+ }
+ }
+ }
+
+ rpc begin-transaction{
+ output{
+ anyxml data-modification-transaction{
+ description "DataModificationTransaction xml";
+ }
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+ module toaster {
+
+ yang-version 1;
+
+ namespace
+ "http://netconfcentral.org/ns/toaster";
+
+ prefix toast;
+
+ organization "Netconf Central";
+
+ contact
+ "Andy Bierman <andy@netconfcentral.org>";
+
+ description
+ "YANG version of the TOASTER-MIB.";
+
+ revision "2009-11-20" {
+ description
+ "Toaster module in progress.";
+ }
+
+
+ identity toast-type {
+ description
+ "Base for all bread types supported by the toaster.
+ New bread types not listed here nay be added in the
+ future.";
+ }
+
+ identity white-bread {
+ base toast:toast-type;
+ description "White bread.";
+ }
+
+ identity wheat-bread {
+ base toast-type;
+ description "Wheat bread.";
+ }
+
+ identity wonder-bread {
+ base toast-type;
+ description "Wonder bread.";
+ }
+
+ identity frozen-waffle {
+ base toast-type;
+ description "Frozen waffle.";
+ }
+
+ identity frozen-bagel {
+ base toast-type;
+ description "Frozen bagel.";
+ }
+
+ identity hash-brown {
+ base toast-type;
+ description "Hash browned potatos.";
+ }
+
+ typedef DisplayString {
+ type string {
+ length "0 .. 255";
+ }
+ description
+ "YANG version of the SMIv2 DisplayString TEXTUAL-CONVENTION.";
+ reference
+ "RFC 2579, section 2.";
+
+ }
+
+ container toaster {
+ presence
+ "Indicates the toaster service is available";
+ description
+ "Top-level container for all toaster database objects.";
+ leaf toasterManufacturer {
+ type DisplayString;
+ config false;
+ mandatory true;
+ description
+ "The name of the toaster's manufacturer. For instance,
+ Microsoft Toaster.";
+ }
+
+ leaf toasterModelNumber {
+ type DisplayString;
+ config false;
+ mandatory true;
+ description
+ "The name of the toaster's model. For instance,
+ Radiant Automatic.";
+ }
+
+ leaf toasterStatus {
+ type enumeration {
+ enum "up" {
+ value 1;
+ description
+ "The toaster knob position is up.
+ No toast is being made now.";
+ }
+ enum "down" {
+ value 2;
+ description
+ "The toaster knob position is down.
+ Toast is being made now.";
+ }
+ }
+ config false;
+ mandatory true;
+ description
+ "This variable indicates the current state of
+ the toaster.";
+ }
+ } // container toaster
+
+ rpc make-toast {
+ description
+ "Make some toast.
+ The toastDone notification will be sent when
+ the toast is finished.
+ An 'in-use' error will be returned if toast
+ is already being made.
+ A 'resource-denied' error will be returned
+ if the toaster service is disabled.";
+ input {
+ leaf toasterDoneness {
+ type uint32 {
+ range "1 .. 10";
+ }
+ default '5';
+ description
+ "This variable controls how well-done is the
+ ensuing toast. It should be on a scale of 1 to 10.
+ Toast made at 10 generally is considered unfit
+ for human consumption; toast made at 1 is warmed
+ lightly.";
+ }
+
+ leaf toasterToastType {
+ type identityref {
+ base toast:toast-type;
+ }
+ default 'wheat-bread';
+ description
+ "This variable informs the toaster of the type of
+ material that is being toasted. The toaster
+ uses this information, combined with
+ toasterDoneness, to compute for how
+ long the material must be toasted to achieve
+ the required doneness.";
+ }
+ }
+ } // rpc make-toast
+
+ rpc testOutput {
+ output {
+ leaf textOut {
+ type string;
+ }
+ }
+ }
+
+ rpc cancel-toast {
+ description
+ "Stop making toast, if any is being made.
+ A 'resource-denied' error will be returned
+ if the toaster service is disabled.";
+ } // rpc cancel-toast
+
+ notification toastDone {
+ description
+ "Indicates that the toast in progress has completed.";
+ leaf toastStatus {
+ type enumeration {
+ enum "done" {
+ value 0;
+ description "The toast is done.";
+ }
+ enum "cancelled" {
+ value 1;
+ description
+ "The toast was cancelled.";
+ }
+ enum "error" {
+ value 2;
+ description
+ "The toaster service was disabled or
+ the toaster is broken.";
+ }
+ }
+ description
+ "Indicates the final toast status";
+ }
+ } // notification toastDone
+ } // module toaster