blob: cd0b42803e7df9aa6a5f8826a69dfb74a5385136 [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;
jingan7c5bf1f2017-02-09 02:58:09 -080023import org.onosproject.restconf.api.RestconfException;
Henry Yuc10f7fc2017-07-26 13:42:08 -040024import org.onosproject.restconf.api.RestconfRpcOutput;
Henry Yu14af7782017-03-09 19:33:36 -050025import org.onosproject.restconf.utils.exceptions.RestconfUtilsException;
26import 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;
jingan7c5bf1f2017-02-09 02:58:09 -080046
Henry Yuc10f7fc2017-07-26 13:42:08 -040047import static javax.ws.rs.core.Response.Status.BAD_REQUEST;
48import static javax.ws.rs.core.Response.Status.EXPECTATION_FAILED;
jingan7c5bf1f2017-02-09 02:58:09 -080049import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
Henry Yuc10f7fc2017-07-26 13:42:08 -040050import static javax.ws.rs.core.Response.Status.NO_CONTENT;
51import static javax.ws.rs.core.Response.Status.OK;
52import static javax.ws.rs.core.Response.Status.REQUEST_TIMEOUT;
jingan7c5bf1f2017-02-09 02:58:09 -080053
54/**
55 * Utilities used by the RESTCONF app.
56 */
57public final class RestconfUtils {
58 /**
59 * No instantiation.
60 */
61 private RestconfUtils() {
62 }
63
64 /**
65 * Data format required by YangRuntime Service.
66 */
jingan364cec32017-03-10 12:29:11 -080067 private static final String JSON_FORMAT = "JSON";
Henry Yuc10f7fc2017-07-26 13:42:08 -040068 private static final String SLASH = "/";
jingan7c5bf1f2017-02-09 02:58:09 -080069
70 private static final YangRuntimeService YANG_RUNTIME =
71 DefaultServiceDirectory.getService(YangRuntimeService.class);
72
Henry Yuc10f7fc2017-07-26 13:42:08 -040073 private static final Logger log = LoggerFactory.getLogger(RestconfUtils.class);
74
jingan7c5bf1f2017-02-09 02:58:09 -080075 /**
76 * Converts an input stream to JSON objectNode.
77 *
Henry Yu14af7782017-03-09 19:33:36 -050078 * @param inputStream the InputStream from Resource Data
jingan7c5bf1f2017-02-09 02:58:09 -080079 * @return JSON representation of the data resource
80 */
81 public static ObjectNode convertInputStreamToObjectNode(InputStream inputStream) {
82 ObjectNode rootNode;
83 ObjectMapper mapper = new ObjectMapper();
84 try {
85 rootNode = (ObjectNode) mapper.readTree(inputStream);
86 } catch (IOException e) {
87 throw new RestconfUtilsException("ERROR: InputStream failed to parse");
88 }
89 return rootNode;
90 }
91
92 /**
93 * Convert ObjectNode to InputStream.
94 *
Henry Yu14af7782017-03-09 19:33:36 -050095 * @param rootNode JSON representation of the data resource
jingan7c5bf1f2017-02-09 02:58:09 -080096 * @return the InputStream from Resource Data
97 */
98 public static InputStream convertObjectNodeToInputStream(ObjectNode rootNode) {
jingan364cec32017-03-10 12:29:11 -080099 String json = rootNode.toString();
jingan7c5bf1f2017-02-09 02:58:09 -0800100 InputStream inputStream;
101 try {
102 inputStream = IOUtils.toInputStream(json);
103 } catch (Exception e) {
104 throw new RestconfUtilsException("ERROR: Json Node failed to parse");
105 }
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();
145 } catch (Exception ex) {
146 log.error("convertJsonToDataNode failure: {}", ex.getMessage());
147 log.debug("convertJsonToDataNode failure", ex);
jingan7c5bf1f2017-02-09 02:58:09 -0800148 }
Henry Yuc10f7fc2017-07-26 13:42:08 -0400149 if (resourceData == null) {
150 throw new RestconfException("ERROR: JSON cannot be converted to DataNode",
151 INTERNAL_SERVER_ERROR);
152 }
jingan7c5bf1f2017-02-09 02:58:09 -0800153 return resourceData;
154 }
155
Henry Yuc10f7fc2017-07-26 13:42:08 -0400156 private static String getRawUriPath(URI uri) {
157 String path = uri.getRawPath();
sonugupta-huawei8065a3b2017-09-08 16:50:17 +0530158 if (path.equals("/onos/restconf/data")) {
Henry Yuc10f7fc2017-07-26 13:42:08 -0400159 return null;
160 }
161
sonugupta-huawei8065a3b2017-09-08 16:50:17 +0530162 return path.replaceAll("^/onos/restconf/data/", "")
163 .replaceAll("^/onos/restconf/operations/", "");
Henry Yuc10f7fc2017-07-26 13:42:08 -0400164 }
165
jingan7c5bf1f2017-02-09 02:58:09 -0800166 /**
167 * Convert Resource Id and Data Node to Json ObjectNode.
168 *
Henry Yu14af7782017-03-09 19:33:36 -0500169 * @param rid resource identifier
170 * @param dataNode represents type of node in data store
jingan7c5bf1f2017-02-09 02:58:09 -0800171 * @return JSON representation of the data resource
172 */
173 public static ObjectNode convertDataNodeToJson(ResourceId rid, DataNode dataNode) {
Henry Yu14af7782017-03-09 19:33:36 -0500174 RuntimeContext.Builder runtimeContextBuilder = DefaultRuntimeContext.builder();
jingan7c5bf1f2017-02-09 02:58:09 -0800175 runtimeContextBuilder.setDataFormat(JSON_FORMAT);
176 RuntimeContext context = runtimeContextBuilder.build();
177 DefaultResourceData.Builder resourceDataBuilder = DefaultResourceData.builder();
178 resourceDataBuilder.addDataNode(dataNode);
179 resourceDataBuilder.resourceId(rid);
180 ResourceData resourceData = resourceDataBuilder.build();
181 DefaultCompositeData.Builder compositeDataBuilder = DefaultCompositeData.builder();
182 compositeDataBuilder.resourceData(resourceData);
183 CompositeData compositeData = compositeDataBuilder.build();
Henry Yuc10f7fc2017-07-26 13:42:08 -0400184 ObjectNode rootNode = null;
185 try {
186 // CompositeData --- YangRuntimeService ---> CompositeStream.
187 CompositeStream compositeStream = YANG_RUNTIME.encode(compositeData, context);
188 InputStream inputStream = compositeStream.resourceData();
189 rootNode = convertInputStreamToObjectNode(inputStream);
190 } catch (Exception ex) {
191 log.error("convertInputStreamToObjectNode failure: {}", ex.getMessage());
192 log.debug("convertInputStreamToObjectNode failure", ex);
193 }
jingan7c5bf1f2017-02-09 02:58:09 -0800194 if (rootNode == null) {
195 throw new RestconfException("ERROR: InputStream can not be convert to ObjectNode",
196 INTERNAL_SERVER_ERROR);
197 }
198 return rootNode;
199 }
Henry Yuc10f7fc2017-07-26 13:42:08 -0400200
201 /**
202 * Removes the last path segment from the given URI. That is, returns
203 * the parent of the given URI.
204 *
205 * @param uri given URI
206 * @return parent URI
207 */
208 public static URI rmLastPathSegment(URI uri) {
209 if (uri == null) {
210 return null;
211 }
212
213 UriBuilder builder = UriBuilder.fromUri(uri);
214 String newPath = rmLastPathSegmentStr(uri.getRawPath());
215 builder.replacePath(newPath);
216
217 return builder.build();
218 }
219
220 private static String rmLastPathSegmentStr(String rawPath) {
221 if (rawPath == null) {
222 return null;
223 }
224 int pos = rawPath.lastIndexOf(SLASH);
225 if (pos <= 0) {
226 return null;
227 }
228
229 return rawPath.substring(0, pos);
230 }
231
232 /**
233 * Creates a RESTCONF RPC output object from a given YANG RPC output object.
234 *
235 * @param cmdId resource ID of the RPC
236 * @param rpcOutput given RPC output in YANG format
237 * @return RPC output in RESTCONF format
238 */
239 public static RestconfRpcOutput convertRpcOutput(ResourceId cmdId, RpcOutput rpcOutput) {
240 RestconfRpcOutput restconfRpcOutput = new RestconfRpcOutput();
241
242 restconfRpcOutput.status(convertResponseStatus(rpcOutput.status()));
243 if (rpcOutput.data() != null) {
244 restconfRpcOutput.output(convertDataNodeToJson(cmdId, rpcOutput.data()));
245 }
246
247 return restconfRpcOutput;
248 }
249
250 private static Response.Status convertResponseStatus(RpcOutput.Status status) {
251 switch (status) {
252 case RPC_SUCCESS:
253 return OK;
254 case RPC_FAILURE:
255 return EXPECTATION_FAILED;
256 case RPC_NODATA:
257 return NO_CONTENT;
258 case RPC_TIMEOUT:
259 return REQUEST_TIMEOUT;
260 default:
261 return BAD_REQUEST;
262 }
263 }
jingan7c5bf1f2017-02-09 02:58:09 -0800264}