blob: 41fcf391f0aa8173bba1ad51902bf315ecec6098 [file] [log] [blame]
jingan7c5bf1f2017-02-09 02:58:09 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
jingan7c5bf1f2017-02-09 02:58:09 -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 */
16
17package org.onosproject.restconf.utils;
18
19import com.fasterxml.jackson.databind.ObjectMapper;
20import com.fasterxml.jackson.databind.node.ObjectNode;
21import org.apache.commons.io.IOUtils;
22import org.onlab.osgi.DefaultServiceDirectory;
Sean Condon13b16812018-01-25 10:31:49 +000023import org.onosproject.restconf.api.RestconfError;
jingan7c5bf1f2017-02-09 02:58:09 -080024import org.onosproject.restconf.api.RestconfException;
Henry Yuc10f7fc2017-07-26 13:42:08 -040025import org.onosproject.restconf.api.RestconfRpcOutput;
Henry Yu14af7782017-03-09 19:33:36 -050026import org.onosproject.yang.model.DataNode;
Henry Yuc10f7fc2017-07-26 13:42:08 -040027import org.onosproject.yang.model.DefaultResourceData;
jingan7c5bf1f2017-02-09 02:58:09 -080028import org.onosproject.yang.model.ResourceData;
29import org.onosproject.yang.model.ResourceId;
Henry Yuc10f7fc2017-07-26 13:42:08 -040030import org.onosproject.yang.model.RpcOutput;
Henry Yu14af7782017-03-09 19:33:36 -050031import org.onosproject.yang.runtime.CompositeData;
32import org.onosproject.yang.runtime.CompositeStream;
33import org.onosproject.yang.runtime.DefaultCompositeData;
34import org.onosproject.yang.runtime.DefaultCompositeStream;
Henry Yu14af7782017-03-09 19:33:36 -050035import org.onosproject.yang.runtime.DefaultRuntimeContext;
36import org.onosproject.yang.runtime.RuntimeContext;
37import org.onosproject.yang.runtime.YangRuntimeService;
Henry Yuc10f7fc2017-07-26 13:42:08 -040038import org.slf4j.Logger;
39import org.slf4j.LoggerFactory;
Henry Yu14af7782017-03-09 19:33:36 -050040
Henry Yuc10f7fc2017-07-26 13:42:08 -040041import javax.ws.rs.core.Response;
42import javax.ws.rs.core.UriBuilder;
Henry Yu14af7782017-03-09 19:33:36 -050043import java.io.IOException;
44import java.io.InputStream;
Henry Yuc10f7fc2017-07-26 13:42:08 -040045import java.net.URI;
Sean Condon13b16812018-01-25 10:31:49 +000046import java.util.Optional;
jingan7c5bf1f2017-02-09 02:58:09 -080047
Sean Condon13b16812018-01-25 10:31:49 +000048import static javax.ws.rs.core.Response.Status.*;
jingan7c5bf1f2017-02-09 02:58:09 -080049
50/**
51 * Utilities used by the RESTCONF app.
52 */
53public final class RestconfUtils {
54 /**
55 * No instantiation.
56 */
57 private RestconfUtils() {
58 }
59
60 /**
61 * Data format required by YangRuntime Service.
62 */
jingan364cec32017-03-10 12:29:11 -080063 private static final String JSON_FORMAT = "JSON";
Henry Yuc10f7fc2017-07-26 13:42:08 -040064 private static final String SLASH = "/";
jingan7c5bf1f2017-02-09 02:58:09 -080065
66 private static final YangRuntimeService YANG_RUNTIME =
67 DefaultServiceDirectory.getService(YangRuntimeService.class);
68
Henry Yuc10f7fc2017-07-26 13:42:08 -040069 private static final Logger log = LoggerFactory.getLogger(RestconfUtils.class);
70
jingan7c5bf1f2017-02-09 02:58:09 -080071 /**
72 * Converts an input stream to JSON objectNode.
73 *
Henry Yu14af7782017-03-09 19:33:36 -050074 * @param inputStream the InputStream from Resource Data
jingan7c5bf1f2017-02-09 02:58:09 -080075 * @return JSON representation of the data resource
76 */
77 public static ObjectNode convertInputStreamToObjectNode(InputStream inputStream) {
78 ObjectNode rootNode;
79 ObjectMapper mapper = new ObjectMapper();
80 try {
81 rootNode = (ObjectNode) mapper.readTree(inputStream);
82 } catch (IOException e) {
Sean Condon13b16812018-01-25 10:31:49 +000083 throw new RestconfException("ERROR: InputStream failed to parse",
84 e, RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
85 Optional.empty());
jingan7c5bf1f2017-02-09 02:58:09 -080086 }
87 return rootNode;
88 }
89
90 /**
91 * Convert ObjectNode to InputStream.
92 *
Henry Yu14af7782017-03-09 19:33:36 -050093 * @param rootNode JSON representation of the data resource
jingan7c5bf1f2017-02-09 02:58:09 -080094 * @return the InputStream from Resource Data
95 */
96 public static InputStream convertObjectNodeToInputStream(ObjectNode rootNode) {
jingan364cec32017-03-10 12:29:11 -080097 String json = rootNode.toString();
jingan7c5bf1f2017-02-09 02:58:09 -080098 InputStream inputStream;
99 try {
100 inputStream = IOUtils.toInputStream(json);
101 } catch (Exception e) {
Sean Condon13b16812018-01-25 10:31:49 +0000102 throw new RestconfException("ERROR: Json Node failed to parse", e,
103 RestconfError.ErrorTag.MALFORMED_MESSAGE, BAD_REQUEST,
104 Optional.empty());
jingan7c5bf1f2017-02-09 02:58:09 -0800105 }
106 return inputStream;
107 }
108
109 /**
Henry Yu830b5dc2017-11-16 10:44:45 -0500110 * Convert URI to ResourceId. If the URI represents the datastore resource
111 * (i.e., the root of datastore), a null is returned.
jingan7c5bf1f2017-02-09 02:58:09 -0800112 *
Henry Yu14af7782017-03-09 19:33:36 -0500113 * @param uri URI of the data resource
jingan7c5bf1f2017-02-09 02:58:09 -0800114 * @return resource identifier
115 */
Henry Yuc10f7fc2017-07-26 13:42:08 -0400116 public static ResourceId convertUriToRid(URI uri) {
jingan7c5bf1f2017-02-09 02:58:09 -0800117 ResourceData resourceData = convertJsonToDataNode(uri, null);
118 return resourceData.resourceId();
119 }
120
121 /**
122 * Convert URI and ObjectNode to ResourceData.
123 *
Henry Yu14af7782017-03-09 19:33:36 -0500124 * @param uri URI of the data resource
125 * @param rootNode JSON representation of the data resource
jingan7c5bf1f2017-02-09 02:58:09 -0800126 * @return represents type of node in data store
127 */
Henry Yuc10f7fc2017-07-26 13:42:08 -0400128 public static ResourceData convertJsonToDataNode(URI uri,
Henry Yu14af7782017-03-09 19:33:36 -0500129 ObjectNode rootNode) {
130 RuntimeContext.Builder runtimeContextBuilder = new DefaultRuntimeContext.Builder();
jingan7c5bf1f2017-02-09 02:58:09 -0800131 runtimeContextBuilder.setDataFormat(JSON_FORMAT);
132 RuntimeContext context = runtimeContextBuilder.build();
Henry Yuc10f7fc2017-07-26 13:42:08 -0400133 ResourceData resourceData = null;
jingan7c5bf1f2017-02-09 02:58:09 -0800134 InputStream jsonData = null;
Henry Yuc10f7fc2017-07-26 13:42:08 -0400135 try {
136 if (rootNode != null) {
137 jsonData = convertObjectNodeToInputStream(rootNode);
138 }
139 String uriString = getRawUriPath(uri);
140
141 CompositeStream compositeStream = new DefaultCompositeStream(uriString, jsonData);
142 // CompositeStream --- YangRuntimeService ---> CompositeData.
143 CompositeData compositeData = YANG_RUNTIME.decode(compositeStream, context);
144 resourceData = compositeData.resourceData();
Sean Condon13b16812018-01-25 10:31:49 +0000145 } catch (RestconfException ex) {
146 throw ex;
Henry Yuc10f7fc2017-07-26 13:42:08 -0400147 } catch (Exception ex) {
148 log.error("convertJsonToDataNode failure: {}", ex.getMessage());
149 log.debug("convertJsonToDataNode failure", ex);
Sean Condon13b16812018-01-25 10:31:49 +0000150 throw new RestconfException("ERROR: JSON cannot be converted to DataNode",
151 ex, RestconfError.ErrorTag.OPERATION_FAILED, INTERNAL_SERVER_ERROR,
152 Optional.of(uri.getPath()));
jingan7c5bf1f2017-02-09 02:58:09 -0800153 }
Henry Yuc10f7fc2017-07-26 13:42:08 -0400154 if (resourceData == null) {
155 throw new RestconfException("ERROR: JSON cannot be converted to DataNode",
Sean Condon13b16812018-01-25 10:31:49 +0000156 RestconfError.ErrorTag.DATA_MISSING, CONFLICT,
157 Optional.of(uri.getPath()), Optional.empty());
Henry Yuc10f7fc2017-07-26 13:42:08 -0400158 }
jingan7c5bf1f2017-02-09 02:58:09 -0800159 return resourceData;
160 }
161
Henry Yuc10f7fc2017-07-26 13:42:08 -0400162 private static String getRawUriPath(URI uri) {
163 String path = uri.getRawPath();
sonugupta-huawei8065a3b2017-09-08 16:50:17 +0530164 if (path.equals("/onos/restconf/data")) {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400165 return null;
166 }
167
sonugupta-huawei8065a3b2017-09-08 16:50:17 +0530168 return path.replaceAll("^/onos/restconf/data/", "")
169 .replaceAll("^/onos/restconf/operations/", "");
Henry Yuc10f7fc2017-07-26 13:42:08 -0400170 }
171
jingan7c5bf1f2017-02-09 02:58:09 -0800172 /**
173 * Convert Resource Id and Data Node to Json ObjectNode.
174 *
Henry Yu14af7782017-03-09 19:33:36 -0500175 * @param rid resource identifier
176 * @param dataNode represents type of node in data store
jingan7c5bf1f2017-02-09 02:58:09 -0800177 * @return JSON representation of the data resource
178 */
179 public static ObjectNode convertDataNodeToJson(ResourceId rid, DataNode dataNode) {
Henry Yu14af7782017-03-09 19:33:36 -0500180 RuntimeContext.Builder runtimeContextBuilder = DefaultRuntimeContext.builder();
jingan7c5bf1f2017-02-09 02:58:09 -0800181 runtimeContextBuilder.setDataFormat(JSON_FORMAT);
182 RuntimeContext context = runtimeContextBuilder.build();
183 DefaultResourceData.Builder resourceDataBuilder = DefaultResourceData.builder();
184 resourceDataBuilder.addDataNode(dataNode);
185 resourceDataBuilder.resourceId(rid);
186 ResourceData resourceData = resourceDataBuilder.build();
187 DefaultCompositeData.Builder compositeDataBuilder = DefaultCompositeData.builder();
188 compositeDataBuilder.resourceData(resourceData);
189 CompositeData compositeData = compositeDataBuilder.build();
Henry Yuc10f7fc2017-07-26 13:42:08 -0400190 ObjectNode rootNode = null;
191 try {
192 // CompositeData --- YangRuntimeService ---> CompositeStream.
193 CompositeStream compositeStream = YANG_RUNTIME.encode(compositeData, context);
194 InputStream inputStream = compositeStream.resourceData();
195 rootNode = convertInputStreamToObjectNode(inputStream);
196 } catch (Exception ex) {
197 log.error("convertInputStreamToObjectNode failure: {}", ex.getMessage());
198 log.debug("convertInputStreamToObjectNode failure", ex);
199 }
jingan7c5bf1f2017-02-09 02:58:09 -0800200 if (rootNode == null) {
201 throw new RestconfException("ERROR: InputStream can not be convert to ObjectNode",
Sean Condon13b16812018-01-25 10:31:49 +0000202 null, RestconfError.ErrorTag.DATA_MISSING, CONFLICT,
203 Optional.empty());
jingan7c5bf1f2017-02-09 02:58:09 -0800204 }
205 return rootNode;
206 }
Henry Yuc10f7fc2017-07-26 13:42:08 -0400207
208 /**
209 * Removes the last path segment from the given URI. That is, returns
210 * the parent of the given URI.
211 *
212 * @param uri given URI
213 * @return parent URI
214 */
215 public static URI rmLastPathSegment(URI uri) {
216 if (uri == null) {
217 return null;
218 }
219
220 UriBuilder builder = UriBuilder.fromUri(uri);
221 String newPath = rmLastPathSegmentStr(uri.getRawPath());
222 builder.replacePath(newPath);
223
224 return builder.build();
225 }
226
227 private static String rmLastPathSegmentStr(String rawPath) {
228 if (rawPath == null) {
229 return null;
230 }
231 int pos = rawPath.lastIndexOf(SLASH);
232 if (pos <= 0) {
233 return null;
234 }
235
236 return rawPath.substring(0, pos);
237 }
238
239 /**
240 * Creates a RESTCONF RPC output object from a given YANG RPC output object.
241 *
242 * @param cmdId resource ID of the RPC
243 * @param rpcOutput given RPC output in YANG format
244 * @return RPC output in RESTCONF format
245 */
246 public static RestconfRpcOutput convertRpcOutput(ResourceId cmdId, RpcOutput rpcOutput) {
247 RestconfRpcOutput restconfRpcOutput = new RestconfRpcOutput();
248
249 restconfRpcOutput.status(convertResponseStatus(rpcOutput.status()));
250 if (rpcOutput.data() != null) {
251 restconfRpcOutput.output(convertDataNodeToJson(cmdId, rpcOutput.data()));
252 }
253
254 return restconfRpcOutput;
255 }
256
257 private static Response.Status convertResponseStatus(RpcOutput.Status status) {
258 switch (status) {
259 case RPC_SUCCESS:
260 return OK;
261 case RPC_FAILURE:
262 return EXPECTATION_FAILED;
263 case RPC_NODATA:
264 return NO_CONTENT;
265 case RPC_TIMEOUT:
266 return REQUEST_TIMEOUT;
267 default:
268 return BAD_REQUEST;
269 }
270 }
jingan7c5bf1f2017-02-09 02:58:09 -0800271}