diff --git a/apps/config/src/main/java/org/onosproject/d/config/ResourceIds.java b/apps/config/src/main/java/org/onosproject/d/config/ResourceIds.java index a08c04a81a..95c8276818 100644 --- a/apps/config/src/main/java/org/onosproject/d/config/ResourceIds.java +++ b/apps/config/src/main/java/org/onosproject/d/config/ResourceIds.java @@ -15,12 +15,7 @@ */ package org.onosproject.d.config; -import static com.google.common.base.Preconditions.checkArgument; -import static org.slf4j.LoggerFactory.getLogger; - -import java.util.Iterator; -import java.util.Objects; - +import com.google.common.annotations.Beta; import org.onosproject.yang.model.DataNode; import org.onosproject.yang.model.KeyLeaf; import org.onosproject.yang.model.LeafListKey; @@ -30,7 +25,11 @@ import org.onosproject.yang.model.ResourceId; import org.onosproject.yang.model.SchemaId; import org.slf4j.Logger; -import com.google.common.annotations.Beta; +import java.util.Iterator; +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkArgument; +import static org.slf4j.LoggerFactory.getLogger; /** * Utility related to ResourceId. @@ -52,7 +51,7 @@ public abstract class ResourceIds { * Builds the ResourceId of specified {@code node}. * * @param parent ResourceId of {@code node} parent - * @param node to create ResourceId. + * @param node to create ResourceId. * @return ResourceId of {@code node} */ public static ResourceId resourceId(ResourceId parent, DataNode node) { @@ -69,27 +68,27 @@ public abstract class ResourceIds { SchemaId sid = node.key().schemaId(); switch (node.type()) { - case MULTI_INSTANCE_LEAF_VALUE_NODE: - builder.addLeafListBranchPoint(sid.name(), sid.namespace(), - ((LeafListKey) node.key()).asString()); - break; + case MULTI_INSTANCE_LEAF_VALUE_NODE: + builder.addLeafListBranchPoint(sid.name(), sid.namespace(), + ((LeafListKey) node.key()).asString()); + break; - case MULTI_INSTANCE_NODE: - builder.addBranchPointSchema(sid.name(), sid.namespace()); - for (KeyLeaf keyLeaf : ((ListKey) node.key()).keyLeafs()) { - builder.addKeyLeaf(keyLeaf.leafSchema().name(), - keyLeaf.leafSchema().namespace(), - keyLeaf.leafValAsString()); - } - break; + case MULTI_INSTANCE_NODE: + builder.addBranchPointSchema(sid.name(), sid.namespace()); + for (KeyLeaf keyLeaf : ((ListKey) node.key()).keyLeafs()) { + builder.addKeyLeaf(keyLeaf.leafSchema().name(), + keyLeaf.leafSchema().namespace(), + keyLeaf.leafValAsString()); + } + break; - case SINGLE_INSTANCE_LEAF_VALUE_NODE: - case SINGLE_INSTANCE_NODE: - builder.addBranchPointSchema(sid.name(), sid.namespace()); - break; + case SINGLE_INSTANCE_LEAF_VALUE_NODE: + case SINGLE_INSTANCE_NODE: + builder.addBranchPointSchema(sid.name(), sid.namespace()); + break; - default: - throw new IllegalArgumentException("Unknown type " + node); + default: + throw new IllegalArgumentException("Unknown type " + node); } @@ -100,7 +99,7 @@ public abstract class ResourceIds { * Concats {@code path} after {@code prefix}. * * @param prefix path - * @param path to append after {@code path} + * @param path to append after {@code path} * @return concatenated ResouceId */ public static ResourceId concat(ResourceId prefix, ResourceId path) { @@ -118,7 +117,7 @@ public abstract class ResourceIds { /** * Returns {@code child} as relative ResourceId against {@code base}. * - * @param base ResourceId + * @param base ResourceId * @param child ResourceId to relativize * @return relative ResourceId */ @@ -135,7 +134,7 @@ public abstract class ResourceIds { checkArgument(Objects.equals(b, c), "%s is not a prefix of %s.\n" + - "b:%s != c:%s", + "b:%s != c:%s", base, child, b, c); } @@ -144,17 +143,47 @@ public abstract class ResourceIds { child.nodeKeys().size())).build(); } + /** + * Removes the root node from {@code path}. + * + * @param path given resource ID + * @return resource ID without root node + */ + public static ResourceId removeRootNode(ResourceId path) { + if (!startsWithRootNode(path)) { + return path; + } + + return ResourceId.builder().append(path.nodeKeys().subList(1, + path.nodeKeys().size())).build(); + } + + /** + * Returns the resource ID of the parent data node pointed by {@code path}. + * + * @param path resource ID of the given data node + * @return resource ID of the parent data node + */ + public static ResourceId parentOf(ResourceId path) { + try { + return path.copyBuilder().removeLastKey().build(); + } catch (CloneNotSupportedException e) { + log.error("Could not copy {}", path, e); + throw new IllegalArgumentException("Could not copy " + path, e); + } + } + /** * Tests if {@code child} starts with {@code prefix}. * * @param prefix expected - * @param child to test + * @param child to test * @return true if {@code child} starts with {@code prefix} */ public static boolean isPrefix(ResourceId prefix, ResourceId child) { return child.nodeKeys().size() >= prefix.nodeKeys().size() && - prefix.nodeKeys().equals(child.nodeKeys().subList(0, prefix.nodeKeys().size())); + prefix.nodeKeys().equals(child.nodeKeys().subList(0, prefix.nodeKeys().size())); } public static boolean startsWithRootNode(ResourceId path) { diff --git a/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/DataResourceLocator.java b/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/DataResourceLocator.java new file mode 100644 index 0000000000..ce4c5cd9a4 --- /dev/null +++ b/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/DataResourceLocator.java @@ -0,0 +1,188 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.restconf.restconfmanager; + +import org.onosproject.net.DeviceId; +import org.onosproject.yang.model.ResourceId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.core.UriBuilder; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; + +import static org.onosproject.d.config.DeviceResourceIds.toResourceId; +import static org.onosproject.d.config.ResourceIds.concat; +import static org.onosproject.d.config.ResourceIds.removeRootNode; +import static org.onosproject.restconf.utils.RestconfUtils.convertUriToRid; + +/** + * Representation of the data resource identifiers used by the RESTCONF manager. + *
+ * For a data resource under the device hierarchy, the restconf manager needs
+ * to maintain 2 separate resource IDs, one used by the
+ * the Dynamic Config, and the other by the Yang
+ * Runtime. (i.e., The resource IDs used by the dyn-config contain the
+ * "/devices/device" prefix, whereas the ones used by Yang Runtime do not.)
+ * This class provides the interface for the RESTCONF manager to use these
+ * 2 resource IDs.
+ */
+public final class DataResourceLocator {
+
+ private static final Logger log = LoggerFactory.getLogger(DataResourceLocator.class);
+
+ private static final String DATA_ROOT_DIR = "/onos/restconf/data";
+ private static final String DEVICE_REGEX = "/devices/device=[^/]+";
+ private static final String DEVICE_URI_PREFIX = DATA_ROOT_DIR + "/devices/device=";
+
+ /**
+ * The resource ID used by Yang Runtime to refer to
+ * a data node.
+ */
+ private ResourceId yrtResourceId;
+
+ /**
+ * The resource ID used by the Dynamic Config to refer
+ * to a data node.
+ */
+ private ResourceId dcsResourceId;
+
+ /**
+ * URI used by RESTCONF to refer to a data node.
+ */
+ private URI uriForRestconf;
+
+ /**
+ * URI used by Yang Runtime to refer to a data node.
+ */
+ private URI uriForYangRuntime;
+
+ // Suppresses default constructor, ensuring non-instantiability.
+ private DataResourceLocator() {
+ }
+
+ private DataResourceLocator(ResourceId yrtResourceId,
+ ResourceId dcsResourceId,
+ URI uriForRestconf,
+ URI uriForYangRuntime) {
+ this.yrtResourceId = yrtResourceId;
+ this.dcsResourceId = dcsResourceId;
+ this.uriForRestconf = uriForRestconf;
+ this.uriForYangRuntime = uriForYangRuntime;
+ }
+
+ public ResourceId ridForDynConfig() {
+ return dcsResourceId;
+ }
+
+ public ResourceId ridForYangRuntime() {
+ return yrtResourceId;
+ }
+
+ public URI uriForRestconf() {
+ return uriForRestconf;
+ }
+
+ public URI uriForYangRuntime() {
+ return uriForYangRuntime;
+ }
+
+ /**
+ * Creates a DataResourceLocator object based on a given URI.
+ *
+ * @param uri given URI
+ * @return instantiated DataResourceLocator object
+ */
+ public static DataResourceLocator newInstance(URI uri) {
+ URI uriForYangRuntime = uriForYangRuntime(uri);
+ ResourceId yrtResourceId = ridForYangRuntime(uriForYangRuntime);
+ /*
+ * If the given URI starts with "devices/device" prefix, then form the
+ * resource ID used by dyn-config by adding the prefix to the resource ID
+ * used by YANG runtime. Otherwise the two resource IDs are the same.
+ */
+ ResourceId dcsResourceId = isDeviceResource(uri) ?
+ addDevicePrefix(yrtResourceId, getDeviceId(uri)) : yrtResourceId;
+
+ return new DataResourceLocator(yrtResourceId, dcsResourceId,
+ uri, uriForYangRuntime);
+ }
+
+ private static URI uriForYangRuntime(URI uriForRestconf) {
+ return isDeviceResource(uriForRestconf) ?
+ removeDeviceProxyPrefix(uriForRestconf) : uriForRestconf;
+ }
+
+ private static ResourceId ridForYangRuntime(URI uriForYangRuntime) {
+ ResourceId yrtResourceId = convertUriToRid(uriForYangRuntime);
+ if (yrtResourceId == null) {
+ yrtResourceId = ResourceId.builder().addBranchPointSchema("/", null).build();
+ }
+
+ return yrtResourceId;
+ }
+
+ private static URI removeDeviceProxyPrefix(URI uri) {
+ if (uri == null) {
+ return null;
+ }
+ UriBuilder builder = UriBuilder.fromUri(uri);
+ String newPath = rmDeviceStr(uri.getRawPath());
+ builder.replacePath(newPath);
+
+ return builder.build();
+ }
+
+ private static String rmDeviceStr(String uriStr) {
+ if (uriStr == null) {
+ return null;
+
+ }
+ return uriStr.replaceFirst(DEVICE_REGEX, "");
+ }
+
+ private static DeviceId getDeviceId(URI uri) {
+ return DeviceId.deviceId(deviceIdStr(uri.getRawPath()));
+ }
+
+ private static String deviceIdStr(String rawPath) {
+ String[] segments = rawPath.split("/");
+ try {
+ for (String s : segments) {
+ if (s.startsWith("device=")) {
+ return URLDecoder.decode(s.substring("device=".length()), "utf-8");
+ }
+ }
+ } catch (UnsupportedEncodingException e) {
+ log.error("deviceIdStr: caught UnsupportedEncodingException");
+ log.debug("deviceIdStr: ", e);
+ }
+ return null;
+ }
+
+ private static ResourceId addDevicePrefix(ResourceId rid, DeviceId did) {
+ return concat(toResourceId(did), removeRootNode(rid));
+ }
+
+ private static boolean isDeviceResource(URI uri) {
+ if (uri == null) {
+ return false;
+ }
+ return uri.getRawPath().startsWith(DEVICE_URI_PREFIX);
+ }
+}
diff --git a/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java b/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java
index 356306db20..5334cd058e 100644
--- a/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java
+++ b/apps/restconf/restconfmgr/src/main/java/org/onosproject/restconf/restconfmanager/RestconfManager.java
@@ -54,9 +54,9 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
+import static org.onosproject.d.config.ResourceIds.parentOf;
import static org.onosproject.restconf.utils.RestconfUtils.convertDataNodeToJson;
import static org.onosproject.restconf.utils.RestconfUtils.convertJsonToDataNode;
-import static org.onosproject.restconf.utils.RestconfUtils.convertUriToRid;
import static org.onosproject.restconf.utils.RestconfUtils.rmLastPathSegment;
import static org.onosproject.yang.model.DataNode.Type.MULTI_INSTANCE_NODE;
import static org.onosproject.yang.model.DataNode.Type.SINGLE_INSTANCE_LEAF_VALUE_NODE;
@@ -107,29 +107,30 @@ public class RestconfManager implements RestconfService {
@Override
public ObjectNode runGetOperationOnDataResource(URI uri)
throws RestconfException {
- ResourceId rid = convertUriToRid(uri);
+ DataResourceLocator rl = DataResourceLocator.newInstance(uri);
// TODO: define Filter (if there is any requirement).
Filter filter = Filter.builder().build();
DataNode dataNode;
try {
- if (!dynamicConfigService.nodeExist(rid)) {
+ if (!dynamicConfigService.nodeExist(rl.ridForDynConfig())) {
return null;
}
- dataNode = dynamicConfigService.readNode(rid, filter);
+ dataNode = dynamicConfigService.readNode(rl.ridForDynConfig(), filter);
} catch (FailedException e) {
log.error("ERROR: DynamicConfigService: ", e);
throw new RestconfException("ERROR: DynamicConfigService",
INTERNAL_SERVER_ERROR);
}
- ObjectNode rootNode = convertDataNodeToJson(rid, dataNode);
+ ObjectNode rootNode = convertDataNodeToJson(rl.ridForYangRuntime(), dataNode);
return rootNode;
}
@Override
public void runPostOperationOnDataResource(URI uri, ObjectNode rootNode)
throws RestconfException {
- ResourceData receivedData = convertJsonToDataNode(uri, rootNode);
+ DataResourceLocator rl = DataResourceLocator.newInstance(uri);
+ ResourceData receivedData = convertJsonToDataNode(rl.uriForYangRuntime(), rootNode);
ResourceId rid = receivedData.resourceId();
List