blob: 1b6a5894ddabf50e65c48c049990cf68d53a0a26 [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;
Henry Yu830b5dc2017-11-16 10:44:45 -050057import static org.onosproject.d.config.ResourceIds.parentOf;
jingan7c5bf1f2017-02-09 02:58:09 -080058import static org.onosproject.restconf.utils.RestconfUtils.convertDataNodeToJson;
Henry Yu14af7782017-03-09 19:33:36 -050059import static org.onosproject.restconf.utils.RestconfUtils.convertJsonToDataNode;
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 {
Henry Yu830b5dc2017-11-16 10:44:45 -0500110 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
jingan7c5bf1f2017-02-09 02:58:09 -0800111 // TODO: define Filter (if there is any requirement).
Yuta HIGUCHIac85ee12017-08-03 20:07:35 -0700112 Filter filter = Filter.builder().build();
jingan7c5bf1f2017-02-09 02:58:09 -0800113 DataNode dataNode;
Henry Yuc10f7fc2017-07-26 13:42:08 -0400114
jingan7c5bf1f2017-02-09 02:58:09 -0800115 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500116 if (!dynamicConfigService.nodeExist(rl.ridForDynConfig())) {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400117 return null;
118 }
Henry Yu830b5dc2017-11-16 10:44:45 -0500119 dataNode = dynamicConfigService.readNode(rl.ridForDynConfig(), 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 }
Henry Yu830b5dc2017-11-16 10:44:45 -0500125 ObjectNode rootNode = convertDataNodeToJson(rl.ridForYangRuntime(), dataNode);
jingan7c5bf1f2017-02-09 02:58:09 -0800126 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 {
Henry Yu830b5dc2017-11-16 10:44:45 -0500132 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
133 ResourceData receivedData = convertJsonToDataNode(rl.uriForYangRuntime(), rootNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400134 ResourceId rid = receivedData.resourceId();
135 List<DataNode> dataNodeList = receivedData.dataNodes();
jingan7c5bf1f2017-02-09 02:58:09 -0800136 if (dataNodeList.size() > 1) {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400137 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
jingan7c5bf1f2017-02-09 02:58:09 -0800138 }
139 DataNode dataNode = dataNodeList.get(0);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400140
141 if (rid == null) {
142 rid = ResourceId.builder().addBranchPointSchema("/", null).build();
143 dataNode = removeTopNode(dataNode);
144 }
145
jingan7c5bf1f2017-02-09 02:58:09 -0800146 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500147 dynamicConfigService.createNode(rl.ridForDynConfig(), dataNode);
jingan7c5bf1f2017-02-09 02:58:09 -0800148 } catch (FailedException e) {
149 log.error("ERROR: DynamicConfigService: ", e);
150 throw new RestconfException("ERROR: DynamicConfigService",
151 INTERNAL_SERVER_ERROR);
152 }
Jin Gan79f75372017-01-05 15:08:11 -0800153 }
154
155 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400156 public void runPutOperationOnDataResource(URI uri, ObjectNode rootNode)
Jin Gan79f75372017-01-05 15:08:11 -0800157 throws RestconfException {
Henry Yu830b5dc2017-11-16 10:44:45 -0500158 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
159 ResourceData receivedData = convertJsonToDataNode(rmLastPathSegment(rl.uriForYangRuntime()), rootNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400160 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 */
Henry Yu830b5dc2017-11-16 10:44:45 -0500171 if (dynamicConfigService.nodeExist(rl.ridForDynConfig())) {
172 dynamicConfigService.replaceNode(parentOf(rl.ridForDynConfig()), dataNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400173 } else {
Henry Yu830b5dc2017-11-16 10:44:45 -0500174 dynamicConfigService.createNode(parentOf(rl.ridForDynConfig()), dataNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400175 }
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 Yu830b5dc2017-11-16 10:44:45 -0500187 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400188 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500189 if (dynamicConfigService.nodeExist(rl.ridForDynConfig())) {
190 dynamicConfigService.deleteNode(rl.ridForDynConfig());
Henry Yuc10f7fc2017-07-26 13:42:08 -0400191 }
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 {
Henry Yu830b5dc2017-11-16 10:44:45 -0500202 DataResourceLocator rl = DataResourceLocator.newInstance(uri);
203 ResourceData receivedData = convertJsonToDataNode(rmLastPathSegment(rl.uriForYangRuntime()), rootNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400204 ResourceId rid = receivedData.resourceId();
205 List<DataNode> dataNodeList = receivedData.dataNodes();
206 if (dataNodeList.size() > 1) {
207 log.warn("There are more than one Data Node can be proceed: {}", dataNodeList.size());
208 }
209 DataNode dataNode = dataNodeList.get(0);
210
211 if (rid == null) {
212 rid = ResourceId.builder().addBranchPointSchema("/", null).build();
213 dataNode = removeTopNode(dataNode);
214 }
215
216 try {
Henry Yu830b5dc2017-11-16 10:44:45 -0500217 dynamicConfigService.updateNode(parentOf(rl.ridForDynConfig()), dataNode);
Henry Yuc10f7fc2017-07-26 13:42:08 -0400218 } catch (FailedException e) {
219 log.error("ERROR: DynamicConfigService: ", e);
220 throw new RestconfException("ERROR: DynamicConfigService",
221 INTERNAL_SERVER_ERROR);
222 }
223 }
224
225 private DataNode removeTopNode(DataNode dataNode) {
226 if (dataNode instanceof InnerNode && dataNode.key().schemaId().name().equals("/")) {
227 Map.Entry<NodeKey, DataNode> entry = ((InnerNode) dataNode).childNodes().entrySet().iterator().next();
228 dataNode = entry.getValue();
229 }
230 return dataNode;
Jin Gan79f75372017-01-05 15:08:11 -0800231 }
232
233 @Override
234 public String getRestconfRootPath() {
235 return RESTCONF_ROOT;
236 }
237
Jin Gan79f75372017-01-05 15:08:11 -0800238 @Override
Henry Yuc10f7fc2017-07-26 13:42:08 -0400239 public void subscribeEventStream(String streamId, String clientIpAddr,
Jin Gan79f75372017-01-05 15:08:11 -0800240 ChunkedOutput<String> output)
241 throws RestconfException {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400242 //TODO: to be completed
243 }
244
245 @Override
246 public CompletableFuture<RestconfRpcOutput> runRpc(URI uri,
247 ObjectNode input,
248 String clientIpAddress) {
249 CompletableFuture<RestconfRpcOutput> result =
250 CompletableFuture.supplyAsync(() -> executeRpc(uri, input, clientIpAddress));
251 return result;
252 }
253
254 private RestconfRpcOutput executeRpc(URI uri, ObjectNode input, String clientIpAddress) {
255 ResourceData rpcInputNode = convertJsonToDataNode(uri, input);
256 ResourceId resourceId = rpcInputNode.resourceId();
257 List<DataNode> inputDataNodeList = rpcInputNode.dataNodes();
258 DataNode inputDataNode = inputDataNodeList.get(0);
259 RpcInput rpcInput = new RpcInput(inputDataNode);
260
261 RestconfRpcOutput restconfOutput = null;
262 try {
263 CompletableFuture<RpcOutput> rpcFuture =
264 dynamicConfigService.invokeRpc(resourceId, rpcInput);
265 RpcOutput rpcOutput = rpcFuture.get();
266 restconfOutput = RestconfUtils.convertRpcOutput(resourceId, rpcOutput);
267 } catch (InterruptedException e) {
268 log.error("ERROR: computeResultQ.take() has been interrupted.");
269 log.debug("executeRpc Exception:", e);
270 restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR, null);
271 restconfOutput.reason("RPC execution has been interrupted");
272 } catch (Exception e) {
273 log.error("ERROR: executeRpc: {}", e.getMessage());
274 log.debug("executeRpc Exception:", e);
275 restconfOutput = new RestconfRpcOutput(INTERNAL_SERVER_ERROR, null);
276 restconfOutput.reason(e.getMessage());
Jin Gan79f75372017-01-05 15:08:11 -0800277 }
278
Henry Yuc10f7fc2017-07-26 13:42:08 -0400279 return restconfOutput;
Jin Gan79f75372017-01-05 15:08:11 -0800280 }
281
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530282 private ResourceData getDataForStore(ResourceData resourceData) {
283 List<DataNode> nodes = resourceData.dataNodes();
284 ResourceId rid = resourceData.resourceId();
285 DataNode.Builder dbr = null;
286 ResourceId parentId = null;
287 try {
288 NodeKey lastKey = rid.nodeKeys().get(rid.nodeKeys().size() - 1);
289 SchemaId sid = lastKey.schemaId();
290 if (lastKey instanceof ListKey) {
291 dbr = InnerNode.builder(
292 sid.name(), sid.namespace()).type(MULTI_INSTANCE_NODE);
293 for (KeyLeaf keyLeaf : ((ListKey) lastKey).keyLeafs()) {
294 Object val = keyLeaf.leafValue();
295 dbr = dbr.addKeyLeaf(keyLeaf.leafSchema().name(),
296 sid.namespace(), val);
297 dbr = dbr.createChildBuilder(keyLeaf.leafSchema().name(),
298 sid.namespace(), val)
299 .type(SINGLE_INSTANCE_LEAF_VALUE_NODE);
sonugupta-huawei6119ac72017-03-21 16:25:40 +0530300 //Exit for key leaf node
301 dbr = dbr.exitNode();
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530302 }
303 } else {
304 dbr = InnerNode.builder(
305 sid.name(), sid.namespace()).type(SINGLE_INSTANCE_NODE);
306 }
307 if (nodes != null && !nodes.isEmpty()) {
308 // adding the parent node for given list of nodes
309 for (DataNode node : nodes) {
310 dbr = ((InnerNode.Builder) dbr).addNode(node);
311 }
312 }
313 parentId = rid.copyBuilder().removeLastKey().build();
314 } catch (CloneNotSupportedException e) {
Ray Milkey74e59132018-01-17 15:24:52 -0800315 log.error("getDataForStore()", e);
316 return null;
sonugupta-huaweif0af7aa2017-03-17 00:54:52 +0530317 }
318 ResourceData.Builder resData = DefaultResourceData.builder();
319 resData.addDataNode(dbr.build());
320 resData.resourceId(parentId);
321 return resData.build();
322 }
Jin Gan79f75372017-01-05 15:08:11 -0800323}