[ONOS-5580] Bug fix for JSON encoding of augmented node names
The submission contains:
1. Fix of ONOS-5580
2. Enable RESTCONF server to register and receive notifications from YMS
Change-Id: I8d27a7ee59679de22b7f49b370b35e48909958a4
diff --git a/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java b/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java
index 5237f50..95be9d1 100644
--- a/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java
+++ b/protocols/restconf/server/restconfmgr/src/main/java/org/onosproject/protocol/restconf/server/restconfmanager/RestconfManager.java
@@ -36,10 +36,14 @@
import org.onosproject.yms.ydt.YmsOperationExecutionStatus;
import org.onosproject.yms.ydt.YmsOperationType;
import org.onosproject.yms.ymsm.YmsService;
+import org.onosproject.yms.ynh.YangNotificationEvent;
+import org.onosproject.yms.ynh.YangNotificationListener;
+import org.onosproject.yms.ynh.YangNotificationService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
+import java.util.Map.Entry;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -48,21 +52,23 @@
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
-
-import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REQUEST;
-import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
-import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertJsonToYdt;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertUriToYdt;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertYdtToJson;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.getJsonNameFromYdtNode;
import static org.onosproject.yms.ydt.YdtContextOperationType.CREATE;
import static org.onosproject.yms.ydt.YdtContextOperationType.DELETE;
-import static org.onosproject.yms.ydt.YdtContextOperationType.REPLACE;
import static org.onosproject.yms.ydt.YdtContextOperationType.MERGE;
+import static org.onosproject.yms.ydt.YdtContextOperationType.NONE;
+import static org.onosproject.yms.ydt.YdtContextOperationType.REPLACE;
import static org.onosproject.yms.ydt.YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE;
+import static org.onosproject.yms.ydt.YmsOperationExecutionStatus.EXECUTION_EXCEPTION;
import static org.onosproject.yms.ydt.YmsOperationExecutionStatus.EXECUTION_SUCCESS;
-import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertYdtToJson;
-import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertUriToYdt;
-import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.convertJsonToYdt;
-import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
-import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onosproject.yms.ydt.YmsOperationType.EDIT_CONFIG_REQUEST;
+import static org.onosproject.yms.ydt.YmsOperationType.QUERY_REQUEST;
+
/*
* Skeletal ONOS RESTCONF Server application. The RESTCONF Manager
* implements the main logic of the RESTCONF Server.
@@ -102,6 +108,8 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected YmsService ymsService;
+ protected YangNotificationService ymsNotificationService;
+
private ListenerTracker listeners;
private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
@@ -116,8 +124,9 @@
new ThreadFactoryBuilder()
.setNameFormat("restconf-worker")
.build());
+ ymsNotificationService = ymsService.getYangNotificationService();
listeners = new ListenerTracker();
- //TODO: YMS notification
+ listeners.addListener(ymsNotificationService, new InternalYangNotificationListener());
log.info("Started");
}
@@ -145,7 +154,8 @@
YdtContext rootNode = ydtResponse.getRootNode();
YdtContext curNode = ydtBuilder.getCurNode();
- ObjectNode result = convertYdtToJson(curNode.getName(), rootNode,
+ ObjectNode result = convertYdtToJson(getJsonNameFromYdtNode(curNode),
+ rootNode,
ymsService.getYdtWalker());
//if the query URI contain a key, something like list=key
//here should only get get child with the specific key
@@ -153,17 +163,17 @@
if (child != null &&
child.getYdtType() == SINGLE_INSTANCE_LEAF_VALUE_NODE) {
- ArrayNode jsonNode = (ArrayNode) result.get(curNode.getName());
+ ArrayNode jsonNode = (ArrayNode) result.get(getJsonNameFromYdtNode(curNode));
for (JsonNode next : jsonNode) {
- if (next.findValue(child.getName())
+ if (next.findValue(getJsonNameFromYdtNode(child))
.asText().equals(child.getValue())) {
return (ObjectNode) next;
}
}
throw new RestconfException(String.format("No content for %s = %s",
- child.getName(),
+ getJsonNameFromYdtNode(child),
child.getValue()),
- INTERNAL_SERVER_ERROR);
+ INTERNAL_SERVER_ERROR);
}
return result;
}
@@ -180,9 +190,16 @@
//convert the payload json body to ydt
convertJsonToYdt(rootNode, ydtBuilder);
- return ymsService
- .executeOperation(ydtBuilder)
- .getYmsOperationResult();
+ YmsOperationExecutionStatus status = EXECUTION_EXCEPTION;
+
+ try {
+ status = ymsService.executeOperation(ydtBuilder).getYmsOperationResult();
+ } catch (Exception e) {
+ log.error("YMS operation failed: {}", e.getMessage());
+ log.debug("Exception in invokeYmsOp: ", e);
+ }
+
+ return status;
}
@Override
@@ -339,7 +356,6 @@
}
}
}
-
}
private YdtBuilder getYdtBuilder(YmsOperationType ymsOperationType) {
@@ -351,8 +367,7 @@
* queues are created by the event consumer threads and are removed when the
* threads terminate.
*/
- //TODO: YMS notification
- /*private class InternalYangNotificationListener implements YangNotificationListener {
+ private class InternalYangNotificationListener implements YangNotificationListener {
@Override
public void event(YangNotificationEvent event) {
@@ -362,28 +377,33 @@
}
if (eventQueueList.isEmpty()) {
- *//*
+ /*
* There is no consumer waiting to consume, so don't have to
* produce this event.
- *//*
+ */
return;
}
try {
- *//*
+ /*
* Put the event to every queue out there. Each queue is
* corresponding to an event stream session. The queue is
* removed when the session terminates.
- *//*
+ */
for (Entry<String, BlockingQueue<ObjectNode>> entry : eventQueueList
.entrySet()) {
- entry.getValue().put(event.subject().getData());
+ YdtContext ydtNode = event.subject().getNotificationRootContext();
+ ObjectNode jsonNode = convertYdtToJson(getJsonNameFromYdtNode(ydtNode),
+ ydtNode,
+ ymsService.getYdtWalker());
+ entry.getValue().put(jsonNode);
}
} catch (InterruptedException e) {
- Log.error("ERROR", e);
- throw new RestconfException("queue", Status.INTERNAL_SERVER_ERROR);
+ log.error("Failed to put event in queue: {}", e.getMessage());
+ log.debug("Exception trace in InternalYangNotificationListener: ", e);
+ throw new RestconfException("Failed to put event in queue",
+ INTERNAL_SERVER_ERROR);
}
}
-
- }*/
+ }
}
diff --git a/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml b/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml
index c738da9..21cc8a5 100644
--- a/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml
+++ b/protocols/restconf/server/rpp/src/main/webapp/WEB-INF/web.xml
@@ -28,6 +28,7 @@
<param-value>org.onosproject.protocol.restconf.server.rpp.RestconfProtocolProxy</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
+ <async-supported>true</async-supported>
</servlet>
<servlet-mapping>
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
index 5ede187..2bf7f74 100755
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/ParserUtils.java
@@ -335,4 +335,62 @@
String name = getLatterSegment(field, COLON);
return new NormalizedYangNode(namespace, name);
}
+
+
+ /**
+ * Extracts the node name from a YDT node and encodes it in JSON format.
+ * A JSON encoded node name has the following format:
+ * <p>
+ * module_name ":" node_name
+ * <p>
+ * where module_name is name of the YANG module in which the data
+ * resource is defined, and node_name is the name of the data resource.
+ * <p>
+ * If the YDT node is null or its node name field is null, then the function
+ * returns null. If the node name field is not null but module name field is,
+ * then the function returns only the node name.
+ *
+ * @param ydtContext YDT node of the target data resource
+ * @return JSON encoded name of the target data resource
+ */
+ public static String getJsonNameFromYdtNode(YdtContext ydtContext) {
+ if (ydtContext == null) {
+ return null;
+ }
+
+ String nodeName = ydtContext.getName();
+ if (nodeName == null) {
+ return null;
+ }
+
+ /*
+ * The namespace field in YDT node is a string which contains a list
+ * of identifiers separated by colon (:). e.g.,
+ *
+ * {identifier ":" identifier}+
+ *
+ * The last identifier in the string is the YANG module name.
+ */
+ String moduleName = getModuleNameFromNamespace(ydtContext.getNamespace());
+ if (moduleName == null) {
+ return nodeName;
+ } else {
+ return moduleName + COLON + nodeName;
+ }
+ }
+
+ private static String getModuleNameFromNamespace(String namespace) {
+ if (namespace == null) {
+ return null;
+ }
+
+ String moduleName = null;
+
+ if (namespace.contains(COLON)) {
+ String[] tokens = namespace.split(COLON);
+ moduleName = tokens[tokens.length - 1];
+ }
+
+ return moduleName;
+ }
}
diff --git a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
index 4ffd6f1..eeee124 100644
--- a/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
+++ b/protocols/restconf/server/utils/src/main/java/org/onosproject/protocol/restconf/server/utils/parser/json/YdtToJsonListener.java
@@ -23,6 +23,7 @@
import org.onosproject.yms.ydt.YdtListener;
import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.protocol.restconf.server.utils.parser.json.ParserUtils.getJsonNameFromYdtNode;
/**
* Represents implementation of codec YDT listener.
@@ -53,7 +54,7 @@
@Override
public void enterYdtNode(YdtContext ydtContext) {
- String name = ydtContext.getName();
+ String name = getJsonNameFromYdtNode(ydtContext);
if (!isBegin && name != null && name.equals(rootName)) {
isBegin = true;
@@ -69,7 +70,7 @@
break;
case MULTI_INSTANCE_NODE:
YdtContext preNode = ydtContext.getPreviousSibling();
- if (preNode == null || !preNode.getName().equals(name)) {
+ if (preNode == null || !getJsonNameFromYdtNode(preNode).equals(name)) {
jsonBuilder.addNodeTopHalf(name, JsonNodeType.ARRAY);
}
jsonBuilder.addNodeTopHalf(EMPTY, JsonNodeType.OBJECT);
@@ -94,7 +95,7 @@
return;
}
- String curName = ydtContext.getName();
+ String curName = getJsonNameFromYdtNode(ydtContext);
YdtContext nextNode = ydtContext.getNextSibling();
switch (ydtContext.getYdtType()) {
@@ -102,7 +103,7 @@
jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
break;
case MULTI_INSTANCE_NODE:
- if (nextNode == null || !nextNode.getName().equals(curName)) {
+ if (nextNode == null || !getJsonNameFromYdtNode(nextNode).equals(curName)) {
jsonBuilder.addNodeBottomHalf(JsonNodeType.OBJECT);
jsonBuilder.addNodeBottomHalf(JsonNodeType.ARRAY);
} else {
@@ -120,8 +121,8 @@
ydtContext.getYdtType());
}
if (curName.equals(rootName) &&
- (nextNode == null || !nextNode.getName().equals(rootName))) {
+ (nextNode == null || !getJsonNameFromYdtNode(nextNode).equals(rootName))) {
isBegin = false;
}
}
-}
\ No newline at end of file
+}