blob: 0ad6b254dbc1f7744deaf6cd0bcc7d9990eb90e5 [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;
Sean Condon13b16812018-01-25 10:31:49 +000031import org.onosproject.restconf.api.RestconfError;
Henry Yu14af7782017-03-09 19:33:36 -050032import org.onosproject.restconf.api.RestconfException;
Henry Yuc10f7fc2017-07-26 13:42:08 -040033import org.onosproject.restconf.api.RestconfRpcOutput;
Henry Yu14af7782017-03-09 19:33:36 -050034import org.onosproject.restconf.api.RestconfService;
Henry Yuc10f7fc2017-07-26 13:42:08 -040035import org.onosproject.restconf.utils.RestconfUtils;
Henry Yu14af7782017-03-09 19:33:36 -050036import org.onosproject.yang.model.DataNode;
Henry Yuc10f7fc2017-07-26 13:42:08 -040037import org.onosproject.yang.model.DefaultResourceData;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +053038import org.onosproject.yang.model.InnerNode;
39import org.onosproject.yang.model.KeyLeaf;
40import org.onosproject.yang.model.ListKey;
41import org.onosproject.yang.model.NodeKey;
Henry Yu14af7782017-03-09 19:33:36 -050042import org.onosproject.yang.model.ResourceData;
43import org.onosproject.yang.model.ResourceId;
Henry Yuc10f7fc2017-07-26 13:42:08 -040044import org.onosproject.yang.model.RpcInput;
45import org.onosproject.yang.model.RpcOutput;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +053046import org.onosproject.yang.model.SchemaId;
Jin Gan79f75372017-01-05 15:08:11 -080047import org.slf4j.Logger;
48import org.slf4j.LoggerFactory;
49
Sean Condon13b16812018-01-25 10:31:49 +000050import javax.ws.rs.core.Response;
Henry Yuc10f7fc2017-07-26 13:42:08 -040051import java.net.URI;
Sean Condon13b16812018-01-25 10:31:49 +000052import java.util.Arrays;
jingan7c5bf1f2017-02-09 02:58:09 -080053import java.util.List;
Henry Yuc10f7fc2017-07-26 13:42:08 -040054import java.util.Map;
Sean Condon13b16812018-01-25 10:31:49 +000055import java.util.Optional;
Henry Yuc10f7fc2017-07-26 13:42:08 -040056import java.util.concurrent.CompletableFuture;
Jin Gan79f75372017-01-05 15:08:11 -080057import java.util.concurrent.ExecutorService;
58import java.util.concurrent.Executors;
Henry Yu14af7782017-03-09 19:33:36 -050059
Sean Condon13b16812018-01-25 10:31:49 +000060import static javax.ws.rs.core.Response.Status.CONFLICT;
Jin Gan79f75372017-01-05 15:08:11 -080061import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
Henry Yu830b5dc2017-11-16 10:44:45 -050062import static org.onosproject.d.config.ResourceIds.parentOf;
jingan7c5bf1f2017-02-09 02:58:09 -080063import static org.onosproject.restconf.utils.RestconfUtils.convertDataNodeToJson;
Henry Yu14af7782017-03-09 19:33:36 -050064import static org.onosproject.restconf.utils.RestconfUtils.convertJsonToDataNode;
Henry Yuc10f7fc2017-07-26 13:42:08 -040065import static org.onosproject.restconf.utils.RestconfUtils.rmLastPathSegment;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +053066import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
67import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
68import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_NODE;
jingan7c5bf1f2017-02-09 02:58:09 -080069
Jin Gan79f75372017-01-05 15:08:11 -080070/*
jingan7c5bf1f2017-02-09 02:58:09 -080071 * ONOS RESTCONF application. The RESTCONF Manager
72 * implements the main logic of the RESTCONF application.
Jin Gan79f75372017-01-05 15:08:11 -080073 *
74 * The design of the RESTCONF subsystem contains 2 major bundles:
jingan7c5bf1f2017-02-09 02:58:09 -080075 * This bundle module is the back-end of the server.
Jin Gan79f75372017-01-05 15:08:11 -080076 * It provides the main logic of the RESTCONF server. It interacts with
jingan7c5bf1f2017-02-09 02:58:09 -080077 * the Dynamic Config Service and yang runtime service to run operations
78 * on the YANG data objects (i.e., resource id, yang data node).
Jin Gan79f75372017-01-05 15:08:11 -080079 */
80
jingan8a773322017-03-21 16:12:48 -070081@Component(immediate = true)
Jin Gan79f75372017-01-05 15:08:11 -080082@Service
83public class RestconfManager implements RestconfService {
84
85 private static final String RESTCONF_ROOT = "/onos/restconf";
Jin Gan79f75372017-01-05 15:08:11 -080086
87 private final int maxNumOfWorkerThreads = 5;
88
89 private final Logger log = LoggerFactory.getLogger(getClass());
90
jingan7c5bf1f2017-02-09 02:58:09 -080091 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Henry Yu14af7782017-03-09 19:33:36 -050092 protected DynamicConfigService dynamicConfigService;
Jin Gan79f75372017-01-05 15:08:11 -080093
Jin Gan79f75372017-01-05 15:08:11 -080094 private ExecutorService workerThreadPool;
95
96 @Activate
97 protected void activate() {
98 workerThreadPool = Executors
99 .newFixedThreadPool(maxNumOfWorkerThreads,
100 new ThreadFactoryBuilder()
101 .setNameFormat("restconf-worker")
102 .build());
103 log.info("Started");
104 }
105
106 @Deactivate
107 protected void deactivate() {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400108 workerThreadPool.shutdownNow();
Jin Gan79f75372017-01-05 15:08:11 -0800109 log.info("Stopped");
110 }
111
112 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400113 public ObjectNode runGetOperationOnDataResource(URI uri)
Jin Gan79f75372017-01-05 15:08:11 -0800114 throws RestconfException {
Henry Yu830b5dc2017-11-16 10:44:45 -0500115 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
jingan7c5bf1f2017-02-09 02:58:09 -0800116 // TODO: define Filter (if there is any requirement).
Yuta HIGUCHIac85ee12017-08-03 20:07:35 -0700117 Filter filter = Filter.builder().build();
jingan7c5bf1f2017-02-09 02:58:09 -0800118 DataNode dataNode;
Henry Yuc10f7fc2017-07-26 13:42:08 -0400119
jingan7c5bf1f2017-02-09 02:58:09 -0800120 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500121 if (!dynamicConfigService.nodeExist(rl.ridForDynConfig())) {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400122 return null;
123 }
Henry Yu830b5dc2017-11-16 10:44:45 -0500124 dataNode = dynamicConfigService.readNode(rl.ridForDynConfig(), filter);
jingan7c5bf1f2017-02-09 02:58:09 -0800125 } catch (FailedException e) {
126 log.error("ERROR: DynamicConfigService: ", e);
Sean Condon13b16812018-01-25 10:31:49 +0000127 throw new RestconfException("ERROR: DynamicConfigService", e,
128 RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
129 Optional.of(uri.getPath()));
jingan7c5bf1f2017-02-09 02:58:09 -0800130 }
Henry Yu830b5dc2017-11-16 10:44:45 -0500131 ObjectNode rootNode = convertDataNodeToJson(rl.ridForYangRuntime(), dataNode);
jingan7c5bf1f2017-02-09 02:58:09 -0800132 return rootNode;
Jin Gan79f75372017-01-05 15:08:11 -0800133 }
134
135 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400136 public void runPostOperationOnDataResource(URI uri, ObjectNode rootNode)
Jin Gan79f75372017-01-05 15:08:11 -0800137 throws RestconfException {
Henry Yu830b5dc2017-11-16 10:44:45 -0500138 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
139 ResourceData receivedData = convertJsonToDataNode(rl.uriForYangRuntime(), rootNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400140 ResourceId rid = receivedData.resourceId();
141 List<DataNode> dataNodeList = receivedData.dataNodes();
jingan7c5bf1f2017-02-09 02:58:09 -0800142 if (dataNodeList.size() > 1) {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400143 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
jingan7c5bf1f2017-02-09 02:58:09 -0800144 }
145 DataNode dataNode = dataNodeList.get(0);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400146
147 if (rid == null) {
148 rid = ResourceId.builder().addBranchPointSchema("/", null).build();
149 dataNode = removeTopNode(dataNode);
150 }
151
jingan7c5bf1f2017-02-09 02:58:09 -0800152 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500153 dynamicConfigService.createNode(rl.ridForDynConfig(), dataNode);
jingan7c5bf1f2017-02-09 02:58:09 -0800154 } catch (FailedException e) {
Sean Condon13b16812018-01-25 10:31:49 +0000155 if (e.getMessage().startsWith("Requested node already present")) {
156 throw new RestconfException("Already exists", e,
157 RestconfError.ErrorTag.DATA_EXISTS, CONFLICT,
158 Optional.of(uri.getPath()));
159 } else {
160 log.error("ERROR: DynamicConfigService: ", e);
161 throw new RestconfException("ERROR: DynamicConfigService", e,
162 RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
163 Optional.of(uri.getPath()));
164 }
jingan7c5bf1f2017-02-09 02:58:09 -0800165 }
Jin Gan79f75372017-01-05 15:08:11 -0800166 }
167
168 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400169 public void runPutOperationOnDataResource(URI uri, ObjectNode rootNode)
Jin Gan79f75372017-01-05 15:08:11 -0800170 throws RestconfException {
Henry Yu830b5dc2017-11-16 10:44:45 -0500171 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
172 ResourceData receivedData = convertJsonToDataNode(rmLastPathSegment(rl.uriForYangRuntime()), rootNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400173 List<DataNode> dataNodeList = receivedData.dataNodes();
174 if (dataNodeList.size() > 1) {
175 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
176 }
177 DataNode dataNode = dataNodeList.get(0);
178
jingan7c5bf1f2017-02-09 02:58:09 -0800179 try {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400180 /*
181 * If the data node already exists, then replace it.
182 * Otherwise, create it.
183 */
Henry Yu830b5dc2017-11-16 10:44:45 -0500184 if (dynamicConfigService.nodeExist(rl.ridForDynConfig())) {
185 dynamicConfigService.replaceNode(parentOf(rl.ridForDynConfig()), dataNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400186 } else {
Henry Yu830b5dc2017-11-16 10:44:45 -0500187 dynamicConfigService.createNode(parentOf(rl.ridForDynConfig()), dataNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400188 }
189
jingan7c5bf1f2017-02-09 02:58:09 -0800190 } catch (FailedException e) {
191 log.error("ERROR: DynamicConfigService: ", e);
Sean Condon13b16812018-01-25 10:31:49 +0000192 throw new RestconfException("ERROR: DynamicConfigService", e,
193 RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
194 Optional.of(uri.getPath()));
jingan7c5bf1f2017-02-09 02:58:09 -0800195 }
Jin Gan79f75372017-01-05 15:08:11 -0800196 }
197
198 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400199 public void runDeleteOperationOnDataResource(URI uri)
Jin Gan79f75372017-01-05 15:08:11 -0800200 throws RestconfException {
Henry Yu830b5dc2017-11-16 10:44:45 -0500201 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400202 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500203 if (dynamicConfigService.nodeExist(rl.ridForDynConfig())) {
204 dynamicConfigService.deleteNode(rl.ridForDynConfig());
Henry Yuc10f7fc2017-07-26 13:42:08 -0400205 }
206 } catch (FailedException e) {
207 log.error("ERROR: DynamicConfigService: ", e);
Sean Condon13b16812018-01-25 10:31:49 +0000208 throw new RestconfException("ERROR: DynamicConfigService", e,
209 RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
210 Optional.of(uri.getPath()));
Henry Yuc10f7fc2017-07-26 13:42:08 -0400211 }
212 }
213
214 @Override
215 public void runPatchOperationOnDataResource(URI uri, ObjectNode rootNode)
216 throws RestconfException {
Henry Yu830b5dc2017-11-16 10:44:45 -0500217 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
218 ResourceData receivedData = convertJsonToDataNode(rmLastPathSegment(rl.uriForYangRuntime()), rootNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400219 ResourceId rid = receivedData.resourceId();
220 List<DataNode> dataNodeList = receivedData.dataNodes();
221 if (dataNodeList.size() > 1) {
222 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
223 }
224 DataNode dataNode = dataNodeList.get(0);
225
226 if (rid == null) {
227 rid = ResourceId.builder().addBranchPointSchema("/", null).build();
228 dataNode = removeTopNode(dataNode);
229 }
230
231 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500232 dynamicConfigService.updateNode(parentOf(rl.ridForDynConfig()), dataNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400233 } catch (FailedException e) {
234 log.error("ERROR: DynamicConfigService: ", e);
Sean Condon13b16812018-01-25 10:31:49 +0000235 throw new RestconfException("ERROR: DynamicConfigService", e,
236 RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
237 Optional.of(uri.getPath()));
Henry Yuc10f7fc2017-07-26 13:42:08 -0400238 }
239 }
240
241 private DataNode removeTopNode(DataNode dataNode) {
242 if (dataNode instanceof InnerNode && dataNode.key().schemaId().name().equals("/")) {
243 Map.Entry<NodeKey, DataNode> entry = ((InnerNode) dataNode).childNodes().entrySet().iterator().next();
244 dataNode = entry.getValue();
245 }
246 return dataNode;
Jin Gan79f75372017-01-05 15:08:11 -0800247 }
248
249 @Override
250 public String getRestconfRootPath() {
251 return RESTCONF_ROOT;
252 }
253
Jin Gan79f75372017-01-05 15:08:11 -0800254 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400255 public void subscribeEventStream(String streamId, String clientIpAddr,
Jin Gan79f75372017-01-05 15:08:11 -0800256 ChunkedOutput<String> output)
257 throws RestconfException {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400258 //TODO: to be completed
Sean Condon13b16812018-01-25 10:31:49 +0000259 throw new RestconfException("Not implemented",
260 RestconfError.ErrorTag.OPERATION_NOT_SUPPORTED,
261 Response.Status.NOT_IMPLEMENTED,
262 Optional.empty(), Optional.of("subscribeEventStream not yet implemented"));
Henry Yuc10f7fc2017-07-26 13:42:08 -0400263 }
264
265 @Override
266 public CompletableFuture<RestconfRpcOutput> runRpc(URI uri,
267 ObjectNode input,
268 String clientIpAddress) {
269 CompletableFuture<RestconfRpcOutput> result =
270 CompletableFuture.supplyAsync(() -> executeRpc(uri, input, clientIpAddress));
271 return result;
272 }
273
274 private RestconfRpcOutput executeRpc(URI uri, ObjectNode input, String clientIpAddress) {
275 ResourceData rpcInputNode = convertJsonToDataNode(uri, input);
276 ResourceId resourceId = rpcInputNode.resourceId();
277 List<DataNode> inputDataNodeList = rpcInputNode.dataNodes();
278 DataNode inputDataNode = inputDataNodeList.get(0);
Yi Tseng33375992018-02-16 16:35:52 -0800279 RpcInput rpcInput = new RpcInput(inputDataNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400280
281 RestconfRpcOutput restconfOutput = null;
282 try {
283 CompletableFuture<RpcOutput> rpcFuture =
Yi Tseng33375992018-02-16 16:35:52 -0800284 dynamicConfigService.invokeRpc(resourceId, rpcInput);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400285 RpcOutput rpcOutput = rpcFuture.get();
286 restconfOutput = RestconfUtils.convertRpcOutput(resourceId, rpcOutput);
287 } catch (InterruptedException e) {
288 log.error("ERROR: computeResultQ.take() has been interrupted.");
289 log.debug("executeRpc Exception:", e);
Sean Condon13b16812018-01-25 10:31:49 +0000290 RestconfError error =
291 RestconfError.builder(RestconfError.ErrorType.RPC,
292 RestconfError.ErrorTag.OPERATION_FAILED)
293 .errorMessage("RPC execution has been interrupted")
294 .errorPath(uri.getPath())
295 .build();
296 restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR,
297 RestconfError.wrapErrorAsJson(Arrays.asList(error)));
Henry Yuc10f7fc2017-07-26 13:42:08 -0400298 restconfOutput.reason("RPC execution has been interrupted");
299 } catch (Exception e) {
300 log.error("ERROR: executeRpc: {}", e.getMessage());
301 log.debug("executeRpc Exception:", e);
Sean Condon13b16812018-01-25 10:31:49 +0000302 RestconfError error =
303 RestconfError.builder(RestconfError.ErrorType.RPC,
304 RestconfError.ErrorTag.OPERATION_FAILED)
305 .errorMessage(e.getMessage())
306 .errorPath(uri.getPath())
307 .build();
308 restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR,
309 RestconfError.wrapErrorAsJson(Arrays.asList(error)));
Henry Yuc10f7fc2017-07-26 13:42:08 -0400310 restconfOutput.reason(e.getMessage());
Jin Gan79f75372017-01-05 15:08:11 -0800311 }
312
Henry Yuc10f7fc2017-07-26 13:42:08 -0400313 return restconfOutput;
Jin Gan79f75372017-01-05 15:08:11 -0800314 }
315
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530316 private ResourceData getDataForStore(ResourceData resourceData) {
317 List<DataNode> nodes = resourceData.dataNodes();
318 ResourceId rid = resourceData.resourceId();
319 DataNode.Builder dbr = null;
320 ResourceId parentId = null;
321 try {
322 NodeKey lastKey = rid.nodeKeys().get(rid.nodeKeys().size() - 1);
323 SchemaId sid = lastKey.schemaId();
324 if (lastKey instanceof ListKey) {
325 dbr = InnerNode.builder(
326 sid.name(), sid.namespace()).type(MULTI_INSTANCE_NODE);
327 for (KeyLeaf keyLeaf : ((ListKey) lastKey).keyLeafs()) {
328 Object val = keyLeaf.leafValue();
329 dbr = dbr.addKeyLeaf(keyLeaf.leafSchema().name(),
330 sid.namespace(), val);
331 dbr = dbr.createChildBuilder(keyLeaf.leafSchema().name(),
332 sid.namespace(), val)
333 .type(SINGLE_INSTANCE_LEAF_VALUE_NODE);
sonugupta-huawei6119ac72017-03-21 16:25:40 +0530334 //Exit for key leaf node
335 dbr = dbr.exitNode();
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530336 }
337 } else {
338 dbr = InnerNode.builder(
339 sid.name(), sid.namespace()).type(SINGLE_INSTANCE_NODE);
340 }
341 if (nodes != null && !nodes.isEmpty()) {
342 // adding the parent node for given list of nodes
343 for (DataNode node : nodes) {
344 dbr = ((InnerNode.Builder) dbr).addNode(node);
345 }
346 }
347 parentId = rid.copyBuilder().removeLastKey().build();
348 } catch (CloneNotSupportedException e) {
Ray Milkey74e59132018-01-17 15:24:52 -0800349 log.error("getDataForStore()", e);
350 return null;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530351 }
352 ResourceData.Builder resData = DefaultResourceData.builder();
353 resData.addDataNode(dbr.build());
354 resData.resourceId(parentId);
355 return resData.build();
356 }
Jin Gan79f75372017-01-05 15:08:11 -0800357}