blob: c1660500f1da14b92565b72e9d59c06cbb948a2a [file] [log] [blame]
Jin Gan79f75372017-01-05 15:08:11 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Jin Gan79f75372017-01-05 15:08:11 -08003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
jingan7c5bf1f2017-02-09 02:58:09 -080016
Jin Gan79f75372017-01-05 15:08:11 -080017package org.onosproject.restconf.restconfmanager;
18
19import com.fasterxml.jackson.databind.node.ObjectNode;
20import com.google.common.util.concurrent.ThreadFactoryBuilder;
21import org.apache.felix.scr.annotations.Activate;
22import org.apache.felix.scr.annotations.Component;
23import org.apache.felix.scr.annotations.Deactivate;
jingan7c5bf1f2017-02-09 02:58:09 -080024import org.apache.felix.scr.annotations.Reference;
25import org.apache.felix.scr.annotations.ReferenceCardinality;
Jin Gan79f75372017-01-05 15:08:11 -080026import org.apache.felix.scr.annotations.Service;
27import org.glassfish.jersey.server.ChunkedOutput;
Henry Yu14af7782017-03-09 19:33:36 -050028import org.onosproject.config.DynamicConfigService;
29import org.onosproject.config.FailedException;
30import org.onosproject.config.Filter;
31import org.onosproject.restconf.api.RestconfException;
Henry Yuc10f7fc2017-07-26 13:42:08 -040032import org.onosproject.restconf.api.RestconfRpcOutput;
Henry Yu14af7782017-03-09 19:33:36 -050033import org.onosproject.restconf.api.RestconfService;
Henry Yuc10f7fc2017-07-26 13:42:08 -040034import org.onosproject.restconf.utils.RestconfUtils;
Henry Yu14af7782017-03-09 19:33:36 -050035import org.onosproject.yang.model.DataNode;
Henry Yuc10f7fc2017-07-26 13:42:08 -040036import org.onosproject.yang.model.DefaultResourceData;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +053037import org.onosproject.yang.model.InnerNode;
38import org.onosproject.yang.model.KeyLeaf;
39import org.onosproject.yang.model.ListKey;
40import org.onosproject.yang.model.NodeKey;
Henry Yu14af7782017-03-09 19:33:36 -050041import org.onosproject.yang.model.ResourceData;
42import org.onosproject.yang.model.ResourceId;
Henry Yuc10f7fc2017-07-26 13:42:08 -040043import org.onosproject.yang.model.RpcInput;
44import org.onosproject.yang.model.RpcOutput;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +053045import org.onosproject.yang.model.SchemaId;
Jin Gan79f75372017-01-05 15:08:11 -080046import org.slf4j.Logger;
47import org.slf4j.LoggerFactory;
48
Henry Yuc10f7fc2017-07-26 13:42:08 -040049import java.net.URI;
jingan7c5bf1f2017-02-09 02:58:09 -080050import java.util.List;
Henry Yuc10f7fc2017-07-26 13:42:08 -040051import java.util.Map;
52import java.util.concurrent.CompletableFuture;
Jin Gan79f75372017-01-05 15:08:11 -080053import java.util.concurrent.ExecutorService;
54import java.util.concurrent.Executors;
Henry Yu14af7782017-03-09 19:33:36 -050055
Jin Gan79f75372017-01-05 15:08:11 -080056import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
jingan7c5bf1f2017-02-09 02:58:09 -080057import static org.onosproject.restconf.utils.RestconfUtils.convertDataNodeToJson;
Henry Yu14af7782017-03-09 19:33:36 -050058import static org.onosproject.restconf.utils.RestconfUtils.convertJsonToDataNode;
59import static org.onosproject.restconf.utils.RestconfUtils.convertUriToRid;
Henry Yuc10f7fc2017-07-26 13:42:08 -040060import static org.onosproject.restconf.utils.RestconfUtils.rmLastPathSegment;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +053061import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
62import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
63import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
jingan7c5bf1f2017-02-09 02:58:09 -080064
Jin Gan79f75372017-01-05 15:08:11 -080065/*
jingan7c5bf1f2017-02-09 02:58:09 -080066 * ONOS RESTCONF application. The RESTCONF Manager
67 * implements the main logic of the RESTCONF application.
Jin Gan79f75372017-01-05 15:08:11 -080068 *
69 * The design of the RESTCONF subsystem contains 2 major bundles:
jingan7c5bf1f2017-02-09 02:58:09 -080070 * This bundle module is the back-end of the server.
Jin Gan79f75372017-01-05 15:08:11 -080071 * It provides the main logic of the RESTCONF server. It interacts with
jingan7c5bf1f2017-02-09 02:58:09 -080072 * the Dynamic Config Service and yang runtime service to run operations
73 * on the YANG data objects (i.e., resource id, yang data node).
Jin Gan79f75372017-01-05 15:08:11 -080074 */
75
jingan8a773322017-03-21 16:12:48 -070076@Component(immediate = true)
Jin Gan79f75372017-01-05 15:08:11 -080077@Service
78public class RestconfManager implements RestconfService {
79
80 private static final String RESTCONF_ROOT = "/onos/restconf";
Jin Gan79f75372017-01-05 15:08:11 -080081
82 private final int maxNumOfWorkerThreads = 5;
83
84 private final Logger log = LoggerFactory.getLogger(getClass());
85
jingan7c5bf1f2017-02-09 02:58:09 -080086 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Henry Yu14af7782017-03-09 19:33:36 -050087 protected DynamicConfigService dynamicConfigService;
Jin Gan79f75372017-01-05 15:08:11 -080088
Jin Gan79f75372017-01-05 15:08:11 -080089 private ExecutorService workerThreadPool;
90
91 @Activate
92 protected void activate() {
93 workerThreadPool = Executors
94 .newFixedThreadPool(maxNumOfWorkerThreads,
95 new ThreadFactoryBuilder()
96 .setNameFormat("restconf-worker")
97 .build());
98 log.info("Started");
99 }
100
101 @Deactivate
102 protected void deactivate() {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400103 workerThreadPool.shutdownNow();
Jin Gan79f75372017-01-05 15:08:11 -0800104 log.info("Stopped");
105 }
106
107 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400108 public ObjectNode runGetOperationOnDataResource(URI uri)
Jin Gan79f75372017-01-05 15:08:11 -0800109 throws RestconfException {
jingan7c5bf1f2017-02-09 02:58:09 -0800110 ResourceId rid = convertUriToRid(uri);
111 // TODO: define Filter (if there is any requirement).
112 Filter filter = new Filter();
113 DataNode dataNode;
Henry Yuc10f7fc2017-07-26 13:42:08 -0400114
jingan7c5bf1f2017-02-09 02:58:09 -0800115 try {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400116 if (!dynamicConfigService.nodeExist(rid)) {
117 return null;
118 }
Henry Yu14af7782017-03-09 19:33:36 -0500119 dataNode = dynamicConfigService.readNode(rid, filter);
jingan7c5bf1f2017-02-09 02:58:09 -0800120 } catch (FailedException e) {
121 log.error("ERROR: DynamicConfigService: ", e);
122 throw new RestconfException("ERROR: DynamicConfigService",
123 INTERNAL_SERVER_ERROR);
124 }
125 ObjectNode rootNode = convertDataNodeToJson(rid, dataNode);
126 return rootNode;
Jin Gan79f75372017-01-05 15:08:11 -0800127 }
128
129 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400130 public void runPostOperationOnDataResource(URI uri, ObjectNode rootNode)
Jin Gan79f75372017-01-05 15:08:11 -0800131 throws RestconfException {
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530132 ResourceData receivedData = convertJsonToDataNode(uri, rootNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400133 ResourceId rid = receivedData.resourceId();
134 List<DataNode> dataNodeList = receivedData.dataNodes();
jingan7c5bf1f2017-02-09 02:58:09 -0800135 if (dataNodeList.size() > 1) {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400136 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
jingan7c5bf1f2017-02-09 02:58:09 -0800137 }
138 DataNode dataNode = dataNodeList.get(0);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400139
140 if (rid == null) {
141 rid = ResourceId.builder().addBranchPointSchema("/", null).build();
142 dataNode = removeTopNode(dataNode);
143 }
144
jingan7c5bf1f2017-02-09 02:58:09 -0800145 try {
Sithara Punnassery7b0c15e2017-07-07 15:08:19 -0700146 dynamicConfigService.createNode(rid, dataNode);
jingan7c5bf1f2017-02-09 02:58:09 -0800147 } catch (FailedException e) {
148 log.error("ERROR: DynamicConfigService: ", e);
149 throw new RestconfException("ERROR: DynamicConfigService",
150 INTERNAL_SERVER_ERROR);
151 }
Jin Gan79f75372017-01-05 15:08:11 -0800152 }
153
154 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400155 public void runPutOperationOnDataResource(URI uri, ObjectNode rootNode)
Jin Gan79f75372017-01-05 15:08:11 -0800156 throws RestconfException {
jingan7c5bf1f2017-02-09 02:58:09 -0800157 ResourceId rid = convertUriToRid(uri);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400158 ResourceData receivedData = convertJsonToDataNode(rmLastPathSegment(uri), rootNode);
159 ResourceId parentRid = receivedData.resourceId();
160 List<DataNode> dataNodeList = receivedData.dataNodes();
161 if (dataNodeList.size() > 1) {
162 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
163 }
164 DataNode dataNode = dataNodeList.get(0);
165
jingan7c5bf1f2017-02-09 02:58:09 -0800166 try {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400167 /*
168 * If the data node already exists, then replace it.
169 * Otherwise, create it.
170 */
171 if (dynamicConfigService.nodeExist(rid)) {
172 dynamicConfigService.replaceNode(parentRid, dataNode);
173 } else {
174 dynamicConfigService.createNode(parentRid, dataNode);
175 }
176
jingan7c5bf1f2017-02-09 02:58:09 -0800177 } catch (FailedException e) {
178 log.error("ERROR: DynamicConfigService: ", e);
179 throw new RestconfException("ERROR: DynamicConfigService",
180 INTERNAL_SERVER_ERROR);
181 }
Jin Gan79f75372017-01-05 15:08:11 -0800182 }
183
184 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400185 public void runDeleteOperationOnDataResource(URI uri)
Jin Gan79f75372017-01-05 15:08:11 -0800186 throws RestconfException {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400187 ResourceId rid = convertUriToRid(uri);
188 try {
189 if (dynamicConfigService.nodeExist(rid)) {
190 dynamicConfigService.deleteNode(rid);
191 }
192 } catch (FailedException e) {
193 log.error("ERROR: DynamicConfigService: ", e);
194 throw new RestconfException("ERROR: DynamicConfigService",
195 INTERNAL_SERVER_ERROR);
196 }
197 }
198
199 @Override
200 public void runPatchOperationOnDataResource(URI uri, ObjectNode rootNode)
201 throws RestconfException {
202 ResourceData receivedData = convertJsonToDataNode(rmLastPathSegment(uri), rootNode);
203 ResourceId rid = receivedData.resourceId();
204 List<DataNode> dataNodeList = receivedData.dataNodes();
205 if (dataNodeList.size() > 1) {
206 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
207 }
208 DataNode dataNode = dataNodeList.get(0);
209
210 if (rid == null) {
211 rid = ResourceId.builder().addBranchPointSchema("/", null).build();
212 dataNode = removeTopNode(dataNode);
213 }
214
215 try {
216 dynamicConfigService.updateNode(rid, dataNode);
217 } catch (FailedException e) {
218 log.error("ERROR: DynamicConfigService: ", e);
219 throw new RestconfException("ERROR: DynamicConfigService",
220 INTERNAL_SERVER_ERROR);
221 }
222 }
223
224 private DataNode removeTopNode(DataNode dataNode) {
225 if (dataNode instanceof InnerNode && dataNode.key().schemaId().name().equals("/")) {
226 Map.Entry<NodeKey, DataNode> entry = ((InnerNode) dataNode).childNodes().entrySet().iterator().next();
227 dataNode = entry.getValue();
228 }
229 return dataNode;
Jin Gan79f75372017-01-05 15:08:11 -0800230 }
231
232 @Override
233 public String getRestconfRootPath() {
234 return RESTCONF_ROOT;
235 }
236
Jin Gan79f75372017-01-05 15:08:11 -0800237 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400238 public void subscribeEventStream(String streamId, String clientIpAddr,
Jin Gan79f75372017-01-05 15:08:11 -0800239 ChunkedOutput<String> output)
240 throws RestconfException {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400241 //TODO: to be completed
242 }
243
244 @Override
245 public CompletableFuture<RestconfRpcOutput> runRpc(URI uri,
246 ObjectNode input,
247 String clientIpAddress) {
248 CompletableFuture<RestconfRpcOutput> result =
249 CompletableFuture.supplyAsync(() -> executeRpc(uri, input, clientIpAddress));
250 return result;
251 }
252
253 private RestconfRpcOutput executeRpc(URI uri, ObjectNode input, String clientIpAddress) {
254 ResourceData rpcInputNode = convertJsonToDataNode(uri, input);
255 ResourceId resourceId = rpcInputNode.resourceId();
256 List<DataNode> inputDataNodeList = rpcInputNode.dataNodes();
257 DataNode inputDataNode = inputDataNodeList.get(0);
258 RpcInput rpcInput = new RpcInput(inputDataNode);
259
260 RestconfRpcOutput restconfOutput = null;
261 try {
262 CompletableFuture<RpcOutput> rpcFuture =
263 dynamicConfigService.invokeRpc(resourceId, rpcInput);
264 RpcOutput rpcOutput = rpcFuture.get();
265 restconfOutput = RestconfUtils.convertRpcOutput(resourceId, rpcOutput);
266 } catch (InterruptedException e) {
267 log.error("ERROR: computeResultQ.take() has been interrupted.");
268 log.debug("executeRpc Exception:", e);
269 restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR, null);
270 restconfOutput.reason("RPC execution has been interrupted");
271 } catch (Exception e) {
272 log.error("ERROR: executeRpc: {}", e.getMessage());
273 log.debug("executeRpc Exception:", e);
274 restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR, null);
275 restconfOutput.reason(e.getMessage());
Jin Gan79f75372017-01-05 15:08:11 -0800276 }
277
Henry Yuc10f7fc2017-07-26 13:42:08 -0400278 return restconfOutput;
Jin Gan79f75372017-01-05 15:08:11 -0800279 }
280
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530281 private ResourceData getDataForStore(ResourceData resourceData) {
282 List<DataNode> nodes = resourceData.dataNodes();
283 ResourceId rid = resourceData.resourceId();
284 DataNode.Builder dbr = null;
285 ResourceId parentId = null;
286 try {
287 NodeKey lastKey = rid.nodeKeys().get(rid.nodeKeys().size() - 1);
288 SchemaId sid = lastKey.schemaId();
289 if (lastKey instanceof ListKey) {
290 dbr = InnerNode.builder(
291 sid.name(), sid.namespace()).type(MULTI_INSTANCE_NODE);
292 for (KeyLeaf keyLeaf : ((ListKey) lastKey).keyLeafs()) {
293 Object val = keyLeaf.leafValue();
294 dbr = dbr.addKeyLeaf(keyLeaf.leafSchema().name(),
295 sid.namespace(), val);
296 dbr = dbr.createChildBuilder(keyLeaf.leafSchema().name(),
297 sid.namespace(), val)
298 .type(SINGLE_INSTANCE_LEAF_VALUE_NODE);
sonugupta-huawei6119ac72017-03-21 16:25:40 +0530299 //Exit for key leaf node
300 dbr = dbr.exitNode();
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530301 }
302 } else {
303 dbr = InnerNode.builder(
304 sid.name(), sid.namespace()).type(SINGLE_INSTANCE_NODE);
305 }
306 if (nodes != null && !nodes.isEmpty()) {
307 // adding the parent node for given list of nodes
308 for (DataNode node : nodes) {
309 dbr = ((InnerNode.Builder) dbr).addNode(node);
310 }
311 }
312 parentId = rid.copyBuilder().removeLastKey().build();
313 } catch (CloneNotSupportedException e) {
314 e.printStackTrace();
315 }
316 ResourceData.Builder resData = DefaultResourceData.builder();
317 resData.addDataNode(dbr.build());
318 resData.resourceId(parentId);
319 return resData.build();
320 }
Jin Gan79f75372017-01-05 15:08:11 -0800321}