blob: 2b7763d99fb610fe31945e5d84bf1df4eb26b4c9 [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 /**
110 * Convert URI to ResourceId.
111 *
Henry Yu14af7782017-03-09 19:33:36 -0500112 * @param uri URI of the data resource
jingan7c5bf1f2017-02-09 02:58:09 -0800113 * @return resource identifier
114 */
Henry Yuc10f7fc2017-07-26 13:42:08 -0400115 public static ResourceId convertUriToRid(URI uri) {
jingan7c5bf1f2017-02-09 02:58:09 -0800116 ResourceData resourceData = convertJsonToDataNode(uri, null);
117 return resourceData.resourceId();
118 }
119
120 /**
121 * Convert URI and ObjectNode to ResourceData.
122 *
Henry Yu14af7782017-03-09 19:33:36 -0500123 * @param uri URI of the data resource
124 * @param rootNode JSON representation of the data resource
jingan7c5bf1f2017-02-09 02:58:09 -0800125 * @return represents type of node in data store
126 */
Henry Yuc10f7fc2017-07-26 13:42:08 -0400127 public static ResourceData convertJsonToDataNode(URI uri,
Henry Yu14af7782017-03-09 19:33:36 -0500128 ObjectNode rootNode) {
129 RuntimeContext.Builder runtimeContextBuilder = new DefaultRuntimeContext.Builder();
jingan7c5bf1f2017-02-09 02:58:09 -0800130 runtimeContextBuilder.setDataFormat(JSON_FORMAT);
131 RuntimeContext context = runtimeContextBuilder.build();
Henry Yuc10f7fc2017-07-26 13:42:08 -0400132 ResourceData resourceData = null;
jingan7c5bf1f2017-02-09 02:58:09 -0800133 InputStream jsonData = null;
Henry Yuc10f7fc2017-07-26 13:42:08 -0400134 try {
135 if (rootNode != null) {
136 jsonData = convertObjectNodeToInputStream(rootNode);
137 }
138 String uriString = getRawUriPath(uri);
139
140 CompositeStream compositeStream = new DefaultCompositeStream(uriString, jsonData);
141 // CompositeStream --- YangRuntimeService ---> CompositeData.
142 CompositeData compositeData = YANG_RUNTIME.decode(compositeStream, context);
143 resourceData = compositeData.resourceData();
144 } catch (Exception ex) {
145 log.error("convertJsonToDataNode failure: {}", ex.getMessage());
146 log.debug("convertJsonToDataNode failure", ex);
jingan7c5bf1f2017-02-09 02:58:09 -0800147 }
Henry Yuc10f7fc2017-07-26 13:42:08 -0400148 if (resourceData == null) {
149 throw new RestconfException("ERROR: JSON cannot be converted to DataNode",
150 INTERNAL_SERVER_ERROR);
151 }
jingan7c5bf1f2017-02-09 02:58:09 -0800152 return resourceData;
153 }
154
Henry Yuc10f7fc2017-07-26 13:42:08 -0400155 private static String getRawUriPath(URI uri) {
156 String path = uri.getRawPath();
157 if (path.equals("/restconf/data")) {
158 return null;
159 }
160
161
162 return path.replaceAll("^/restconf/data/", "").replaceAll("^/restconf/operations/", "");
163 }
164
jingan7c5bf1f2017-02-09 02:58:09 -0800165 /**
166 * Convert Resource Id and Data Node to Json ObjectNode.
167 *
Henry Yu14af7782017-03-09 19:33:36 -0500168 * @param rid resource identifier
169 * @param dataNode represents type of node in data store
jingan7c5bf1f2017-02-09 02:58:09 -0800170 * @return JSON representation of the data resource
171 */
172 public static ObjectNode convertDataNodeToJson(ResourceId rid, DataNode dataNode) {
Henry Yu14af7782017-03-09 19:33:36 -0500173 RuntimeContext.Builder runtimeContextBuilder = DefaultRuntimeContext.builder();
jingan7c5bf1f2017-02-09 02:58:09 -0800174 runtimeContextBuilder.setDataFormat(JSON_FORMAT);
175 RuntimeContext context = runtimeContextBuilder.build();
176 DefaultResourceData.Builder resourceDataBuilder = DefaultResourceData.builder();
177 resourceDataBuilder.addDataNode(dataNode);
178 resourceDataBuilder.resourceId(rid);
179 ResourceData resourceData = resourceDataBuilder.build();
180 DefaultCompositeData.Builder compositeDataBuilder = DefaultCompositeData.builder();
181 compositeDataBuilder.resourceData(resourceData);
182 CompositeData compositeData = compositeDataBuilder.build();
Henry Yuc10f7fc2017-07-26 13:42:08 -0400183 ObjectNode rootNode = null;
184 try {
185 // CompositeData --- YangRuntimeService ---> CompositeStream.
186 CompositeStream compositeStream = YANG_RUNTIME.encode(compositeData, context);
187 InputStream inputStream = compositeStream.resourceData();
188 rootNode = convertInputStreamToObjectNode(inputStream);
189 } catch (Exception ex) {
190 log.error("convertInputStreamToObjectNode failure: {}", ex.getMessage());
191 log.debug("convertInputStreamToObjectNode failure", ex);
192 }
jingan7c5bf1f2017-02-09 02:58:09 -0800193 if (rootNode == null) {
194 throw new RestconfException("ERROR: InputStream can not be convert to ObjectNode",
195 INTERNAL_SERVER_ERROR);
196 }
197 return rootNode;
198 }
Henry Yuc10f7fc2017-07-26 13:42:08 -0400199
200 /**
201 * Removes the last path segment from the given URI. That is, returns
202 * the parent of the given URI.
203 *
204 * @param uri given URI
205 * @return parent URI
206 */
207 public static URI rmLastPathSegment(URI uri) {
208 if (uri == null) {
209 return null;
210 }
211
212 UriBuilder builder = UriBuilder.fromUri(uri);
213 String newPath = rmLastPathSegmentStr(uri.getRawPath());
214 builder.replacePath(newPath);
215
216 return builder.build();
217 }
218
219 private static String rmLastPathSegmentStr(String rawPath) {
220 if (rawPath == null) {
221 return null;
222 }
223 int pos = rawPath.lastIndexOf(SLASH);
224 if (pos <= 0) {
225 return null;
226 }
227
228 return rawPath.substring(0, pos);
229 }
230
231 /**
232 * Creates a RESTCONF RPC output object from a given YANG RPC output object.
233 *
234 * @param cmdId resource ID of the RPC
235 * @param rpcOutput given RPC output in YANG format
236 * @return RPC output in RESTCONF format
237 */
238 public static RestconfRpcOutput convertRpcOutput(ResourceId cmdId, RpcOutput rpcOutput) {
239 RestconfRpcOutput restconfRpcOutput = new RestconfRpcOutput();
240
241 restconfRpcOutput.status(convertResponseStatus(rpcOutput.status()));
242 if (rpcOutput.data() != null) {
243 restconfRpcOutput.output(convertDataNodeToJson(cmdId, rpcOutput.data()));
244 }
245
246 return restconfRpcOutput;
247 }
248
249 private static Response.Status convertResponseStatus(RpcOutput.Status status) {
250 switch (status) {
251 case RPC_SUCCESS:
252 return OK;
253 case RPC_FAILURE:
254 return EXPECTATION_FAILED;
255 case RPC_NODATA:
256 return NO_CONTENT;
257 case RPC_TIMEOUT:
258 return REQUEST_TIMEOUT;
259 default:
260 return BAD_REQUEST;
261 }
262 }
jingan7c5bf1f2017-02-09 02:58:09 -0800263}