[ONOS-5012] implement RESTconf server
- fix javadoc longer than 80 char limit
- fix javadoc that missing @params
- chain calls to StringBuilder.append()
- combine constant strings in place
Change-Id: Ie2ef4fd4c19e955ad2d5a5584f5017a842abb790
diff --git a/protocols/restconf/server/restconfmgr/BUCK b/protocols/restconf/server/restconfmgr/BUCK
index 100f922..26d8076 100644
--- a/protocols/restconf/server/restconfmgr/BUCK
+++ b/protocols/restconf/server/restconfmgr/BUCK
@@ -6,6 +6,8 @@
'//utils/rest:onlab-rest',
'//core/store/serializers:onos-core-serializers',
'//protocols/restconf/server/api:onos-protocols-restconf-server-api',
+ '//protocols/restconf/server/utils:onos-protocols-restconf-server-utils',
+ '//apps/yms/api:onos-apps-yms-api',
]
osgi_jar_with_tests (
diff --git a/protocols/restconf/server/restconfmgr/pom.xml b/protocols/restconf/server/restconfmgr/pom.xml
index 4c8b8c5..3fb62e3 100644
--- a/protocols/restconf/server/restconfmgr/pom.xml
+++ b/protocols/restconf/server/restconfmgr/pom.xml
@@ -58,6 +58,16 @@
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.scr.annotations</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-restconf-server-utils</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-yms-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
<build>
<plugins>
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 ee8506f..5237f50 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
@@ -15,20 +15,30 @@
*/
package org.onosproject.protocol.restconf.server.restconfmanager;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.glassfish.jersey.server.ChunkedOutput;
import org.onosproject.event.ListenerTracker;
import org.onosproject.protocol.restconf.server.api.RestconfException;
import org.onosproject.protocol.restconf.server.api.RestconfService;
+import org.onosproject.yms.ydt.YdtBuilder;
+import org.onosproject.yms.ydt.YdtContext;
+import org.onosproject.yms.ydt.YdtContextOperationType;
+import org.onosproject.yms.ydt.YdtResponse;
+import org.onosproject.yms.ydt.YmsOperationExecutionStatus;
+import org.onosproject.yms.ydt.YmsOperationType;
+import org.onosproject.yms.ymsm.YmsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.ws.rs.core.Response;
import java.io.IOException;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
@@ -37,21 +47,37 @@
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
+
+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 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.YdtType.SINGLE_INSTANCE_LEAF_VALUE_NODE;
+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;
/*
* Skeletal ONOS RESTCONF Server application. The RESTCONF Manager
* implements the main logic of the RESTCONF Server.
*
* The design of the RESTCONF subsystem contains 2 major bundles:
*
- * 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a JAX-RS application.
- * It acts as the frond-end of the the RESTCONF server. It handles
- * HTTP requests that are sent to the RESTCONF Root Path. It then calls the RESTCONF Manager
- * to process the requests.
+ * 1. RESTCONF Protocol Proxy (RPP). This bundle is implemented as a
+ * JAX-RS application. It acts as the frond-end of the RESTCONF server.
+ * It intercepts/handles HTTP requests that are sent to the RESTCONF
+ * Root Path. It then calls the RESTCONF Manager to process the requests.
*
- * 2. RESTCONF Manager. This is the back-end. It provides the main logic of the RESTCONF server.
- * It calls the YMS (YANG Management System) to operate on the YANG data objects.
+ * 2. RESTCONF Manager. This bundle module is the back-end of the server.
+ * It provides the main logic of the RESTCONF server. It interacts with
+ * the YMS (YANG Management System) to run operations on the YANG data
+ * objects (i.e., data resources).
*/
/**
@@ -73,26 +99,25 @@
private final Logger log = LoggerFactory.getLogger(getClass());
- //TODO: YMS service
- //@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- //protected YmsService ymsService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected YmsService ymsService;
private ListenerTracker listeners;
private ConcurrentMap<String, BlockingQueue<ObjectNode>> eventQueueList =
- new ConcurrentHashMap<String, BlockingQueue<ObjectNode>>();
+ new ConcurrentHashMap<>();
private ExecutorService workerThreadPool;
@Activate
protected void activate() {
- workerThreadPool = Executors.newFixedThreadPool(maxNumOfWorkerThreads,
- new ThreadFactoryBuilder()
- .setNameFormat("restconf-worker")
- .build());
+ workerThreadPool = Executors
+ .newFixedThreadPool(maxNumOfWorkerThreads,
+ new ThreadFactoryBuilder()
+ .setNameFormat("restconf-worker")
+ .build());
listeners = new ListenerTracker();
//TODO: YMS notification
- //listeners.addListener(ymsService, new InternalYangNotificationListener());
log.info("Started");
}
@@ -104,34 +129,117 @@
}
@Override
- public ObjectNode runGetOperationOnDataResource(String uri) throws RestconfException {
- //TODO: YMS integration
- return null;
+ public ObjectNode runGetOperationOnDataResource(String uri)
+ throws RestconfException {
+ YdtBuilder ydtBuilder = getYdtBuilder(QUERY_REQUEST);
+ //Convert the URI to ydtBuilder
+ convertUriToYdt(uri, ydtBuilder, NONE);
+ YdtResponse ydtResponse = ymsService.executeOperation(ydtBuilder);
+ YmsOperationExecutionStatus status = ydtResponse
+ .getYmsOperationResult();
+ if (status != EXECUTION_SUCCESS) {
+ throw new RestconfException("YMS GET operation failed",
+ INTERNAL_SERVER_ERROR);
+ }
+
+ YdtContext rootNode = ydtResponse.getRootNode();
+ YdtContext curNode = ydtBuilder.getCurNode();
+
+ ObjectNode result = convertYdtToJson(curNode.getName(), rootNode,
+ ymsService.getYdtWalker());
+ //if the query URI contain a key, something like list=key
+ //here should only get get child with the specific key
+ YdtContext child = curNode.getFirstChild();
+ if (child != null &&
+ child.getYdtType() == SINGLE_INSTANCE_LEAF_VALUE_NODE) {
+
+ ArrayNode jsonNode = (ArrayNode) result.get(curNode.getName());
+ for (JsonNode next : jsonNode) {
+ if (next.findValue(child.getName())
+ .asText().equals(child.getValue())) {
+ return (ObjectNode) next;
+ }
+ }
+ throw new RestconfException(String.format("No content for %s = %s",
+ child.getName(),
+ child.getValue()),
+ INTERNAL_SERVER_ERROR);
+ }
+ return result;
+ }
+
+ private YmsOperationExecutionStatus
+ invokeYmsOp(String uri, ObjectNode rootNode,
+ YdtContextOperationType opType) {
+ YdtBuilder ydtBuilder = getYdtBuilder(EDIT_CONFIG_REQUEST);
+ //Convert the URI to ydtBuilder
+ convertUriToYdt(uri, ydtBuilder, opType);
+
+ //set default operation type for the payload node
+ ydtBuilder.setDefaultEditOperationType(opType);
+ //convert the payload json body to ydt
+ convertJsonToYdt(rootNode, ydtBuilder);
+
+ return ymsService
+ .executeOperation(ydtBuilder)
+ .getYmsOperationResult();
}
@Override
- public void runPostOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
- //TODO: YMS integration
+ public void runPostOperationOnDataResource(String uri, ObjectNode rootNode)
+ throws RestconfException {
+ YmsOperationExecutionStatus status =
+ invokeYmsOp(uri, rootNode, CREATE);
+
+ if (status != EXECUTION_SUCCESS) {
+ throw new RestconfException("YMS post operation failed.",
+ INTERNAL_SERVER_ERROR);
+ }
}
@Override
- public void runPutOperationOnDataResource(String uri, ObjectNode rootNode) throws RestconfException {
- //TODO: YMS integration
+ public void runPutOperationOnDataResource(String uri, ObjectNode rootNode)
+ throws RestconfException {
+ YmsOperationExecutionStatus status =
+ invokeYmsOp(uri, rootNode, REPLACE);
+
+ if (status != EXECUTION_SUCCESS) {
+ throw new RestconfException("YMS put operation failed.",
+ INTERNAL_SERVER_ERROR);
+ }
}
- /**
- * Process the delete operation on a data resource.
- *
- * @param uri URI of the data resource to be deleted.
- */
@Override
- public void runDeleteOperationOnDataResource(String uri) throws RestconfException {
- //TODO: YMS integration
+ public void runDeleteOperationOnDataResource(String uri)
+ throws RestconfException {
+ //Get a root ydtBuilder
+ YdtBuilder ydtBuilder = getYdtBuilder(EDIT_CONFIG_REQUEST);
+ //Convert the URI to ydtBuilder
+ convertUriToYdt(uri, ydtBuilder, DELETE);
+ //Execute the delete operation
+ YmsOperationExecutionStatus status = ymsService
+ .executeOperation(ydtBuilder)
+ .getYmsOperationResult();
+ if (status != EXECUTION_SUCCESS) {
+ throw new RestconfException("YMS delete operation failed.",
+ INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ @Override
+ public void runPatchOperationOnDataResource(String uri, ObjectNode rootNode)
+ throws RestconfException {
+ YmsOperationExecutionStatus status = invokeYmsOp(uri, rootNode, MERGE);
+
+ if (status != EXECUTION_SUCCESS) {
+ throw new RestconfException("YMS patch operation failed.",
+ INTERNAL_SERVER_ERROR);
+ }
}
@Override
public String getRestconfRootPath() {
- return this.RESTCONF_ROOT;
+ return RESTCONF_ROOT;
}
/**
@@ -143,16 +251,21 @@
* @throws RestconfException if the worker thread fails to create
*/
@Override
- public void subscribeEventStream(String streamId, ChunkedOutput<String> output) throws RestconfException {
- BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<ObjectNode>();
+ public void subscribeEventStream(String streamId,
+ ChunkedOutput<String> output)
+ throws RestconfException {
+ BlockingQueue<ObjectNode> eventQueue = new LinkedBlockingQueue<>();
if (workerThreadPool instanceof ThreadPoolExecutor) {
- if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >= maxNumOfWorkerThreads) {
- throw new RestconfException("no more work threads left to handle event subscription",
- Response.Status.INTERNAL_SERVER_ERROR);
+ if (((ThreadPoolExecutor) workerThreadPool).getActiveCount() >=
+ maxNumOfWorkerThreads) {
+ throw new RestconfException("no more work threads left to " +
+ "handle event subscription",
+ INTERNAL_SERVER_ERROR);
}
} else {
- throw new RestconfException("Server ERROR: workerThreadPool NOT instanceof ThreadPoolExecutor",
- Response.Status.INTERNAL_SERVER_ERROR);
+ throw new RestconfException("Server ERROR: workerThreadPool NOT " +
+ "instanceof ThreadPoolExecutor",
+ INTERNAL_SERVER_ERROR);
}
@@ -169,10 +282,11 @@
pool.shutdown(); // Disable new tasks from being submitted
try {
// Wait a while for existing tasks to terminate
- if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) {
+ if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// Wait a while for tasks to respond to being cancelled
- if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT, TimeUnit.SECONDS)) {
+ if (!pool.awaitTermination(THREAD_TERMINATION_TIMEOUT,
+ SECONDS)) {
log.error("Pool did not terminate");
}
}
@@ -190,7 +304,8 @@
private final ChunkedOutput<String> output;
private final BlockingQueue<ObjectNode> bqueue;
- public EventConsumer(ChunkedOutput<String> output, BlockingQueue<ObjectNode> q) {
+ public EventConsumer(ChunkedOutput<String> output,
+ BlockingQueue<ObjectNode> q) {
this.queueId = Thread.currentThread().getName();
this.output = output;
this.bqueue = q;
@@ -212,7 +327,8 @@
*/
eventQueueList.remove(this.queueId);
} catch (InterruptedException e) {
- log.error("ERROR: EventConsumer: bqueue.take() has been interrupted.");
+ log.error("ERROR: EventConsumer: bqueue.take() " +
+ "has been interrupted.");
log.debug("EventConsumer Exception:", e);
} finally {
try {
@@ -226,6 +342,10 @@
}
+ private YdtBuilder getYdtBuilder(YmsOperationType ymsOperationType) {
+ return ymsService.getYdtBuilder(RESTCONF_ROOT, null, ymsOperationType);
+ }
+
/**
* The listener class acts as the event producer for the event queues. The
* queues are created by the event consumer threads and are removed when the