X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Frestconf%2Fimpl%2FRestconfImpl.java;h=b68bee2f0c9cc30e04a7c6ae62288f6cf6f4bdb6;hb=48daeb11697dbb83f554b64fe8702e21de3e0a12;hp=4c005c6ae5e9e109604a1890cd5ee0c5f81d264a;hpb=6d73d16b194435ea1ea783a37d1b51fc1f558a1f;p=controller.git diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index 4c005c6ae5..b68bee2f0c 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -24,17 +24,19 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.Future; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; -import org.opendaylight.controller.sal.core.api.mount.MountInstance; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.controller.sal.rest.api.Draft02; import org.opendaylight.controller.sal.rest.api.RestconfService; import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag; @@ -47,16 +49,23 @@ import org.opendaylight.controller.sal.streams.listeners.Notificator; import org.opendaylight.controller.sal.streams.websockets.WebSocketServer; import org.opendaylight.yangtools.concepts.Codec; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.SimpleNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode; -import org.opendaylight.yangtools.yang.data.api.Node; -import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.parser.CnSnToNormalizedNodeParserFactory; +import org.opendaylight.yangtools.yang.data.composite.node.schema.cnsn.serializer.CnSnFromNormalizedNodeSerializerFactory; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; @@ -76,6 +85,8 @@ import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; import org.opendaylight.yangtools.yang.model.util.EmptyType; import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder; import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RestconfImpl implements RestconfService { private enum UriParameters { @@ -96,6 +107,8 @@ public class RestconfImpl implements RestconfService { private final static RestconfImpl INSTANCE = new RestconfImpl(); + private static final int NOTIFICATION_PORT = 8181; + private static final int CHAR_NOT_FOUND = -1; private final static String MOUNT_POINT_MODULE_NAME = "ietf-netconf"; @@ -110,6 +123,30 @@ public class RestconfImpl implements RestconfService { private ControllerContext controllerContext; + private static final Logger LOG = LoggerFactory.getLogger(RestconfImpl.class); + + private static final DataChangeScope DEFAULT_SCOPE = DataChangeScope.BASE; + + private static final LogicalDatastoreType DEFAULT_DATASTORE = LogicalDatastoreType.CONFIGURATION; + + private static final URI NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT = URI.create("urn:sal:restconf:event:subscription"); + + private static final Date EVENT_SUBSCRIPTION_AUGMENT_REVISION; + + private static final String DATASTORE_PARAM_NAME = "datastore"; + + private static final String SCOPE_PARAM_NAME = "scope"; + + static { + try { + EVENT_SUBSCRIPTION_AUGMENT_REVISION = new SimpleDateFormat("yyyy-MM-dd").parse("2014-07-08"); + } catch (ParseException e) { + throw new RestconfDocumentedException( + "It wasn't possible to convert revision date of sal-remote-augment to date", ErrorType.APPLICATION, + ErrorTag.OPERATION_FAILED); + } + } + public void setBroker(final BrokerFacade broker) { this.broker = broker; } @@ -168,7 +205,7 @@ public class RestconfImpl implements RestconfService { @Override public StructuredData getModules(final String identifier, final UriInfo uriInfo) { Set modules = null; - MountInstance mountPoint = null; + DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); @@ -199,7 +236,7 @@ public class RestconfImpl implements RestconfService { public StructuredData getModule(final String identifier, final UriInfo uriInfo) { final QName moduleNameAndRevision = this.getModuleNameAndRevision(identifier); Module module = null; - MountInstance mountPoint = null; + DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); @@ -230,7 +267,7 @@ public class RestconfImpl implements RestconfService { @Override public StructuredData getOperations(final String identifier, final UriInfo uriInfo) { Set modules = null; - MountInstance mountPoint = null; + DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); @@ -245,7 +282,7 @@ public class RestconfImpl implements RestconfService { } private StructuredData operationsFromModulesToStructuredData(final Set modules, - final MountInstance mountPoint, boolean prettyPrint) { + final DOMMountPoint mountPoint, boolean prettyPrint) { final List> operationsAsData = new ArrayList>(); Module restconfModule = this.getRestconfModule(); final DataSchemaNode operationsSchemaNode = controllerContext.getRestconfModuleRestConfSchemaNode( @@ -405,7 +442,7 @@ public class RestconfImpl implements RestconfService { return callRpc(rpc, payload, parsePrettyPrintParameter(uriInfo)); } - private void validateInput(final DataSchemaNode inputSchema, final CompositeNode payload) { + private void validateInput(final DataSchemaNode inputSchema, final Node payload) { if (inputSchema != null && payload == null) { // expected a non null payload throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); @@ -416,6 +453,7 @@ public class RestconfImpl implements RestconfService { // else // { // TODO: Validate "mandatory" and "config" values here??? Or should those be + // those be // validate in a more central location inside MD-SAL core. // } } @@ -436,7 +474,15 @@ public class RestconfImpl implements RestconfService { String streamName = null; if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) { String fullRestconfIdentifier = this.controllerContext.toFullRestconfIdentifier(pathIdentifier); - streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier); + + LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME); + datastore = datastore == null ? DEFAULT_DATASTORE : datastore; + + DataChangeScope scope = parseEnumTypeParameter(value, DataChangeScope.class, SCOPE_PARAM_NAME); + scope = scope == null ? DEFAULT_SCOPE : scope; + + streamName = Notificator.createStreamNameFromUri(fullRestconfIdentifier + "/datastore=" + datastore + + "/scope=" + scope); } if (Strings.isNullOrEmpty(streamName)) { @@ -453,7 +499,7 @@ public class RestconfImpl implements RestconfService { final MutableCompositeNode responseData = NodeFactory.createMutableCompositeNode(rpc.getOutput().getQName(), null, output, null, null); - if (!Notificator.existListenerFor(pathIdentifier)) { + if (!Notificator.existListenerFor(streamName)) { Notificator.createListener(pathIdentifier, streamName); } @@ -470,7 +516,7 @@ public class RestconfImpl implements RestconfService { private RpcExecutor resolveIdentifierInInvokeRpc(final String identifier) { String identifierEncoded = null; - MountInstance mountPoint = null; + DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { // mounted RPC call - look up mount instance. InstanceIdWithSchemaNode mountPointId = controllerContext.toMountPointIdentifier(identifier); @@ -582,18 +628,20 @@ public class RestconfImpl implements RestconfService { @Override public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) { - final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); - CompositeNode data = null; - MountInstance mountPoint = iiWithData.getMountPoint(); + final InstanceIdWithSchemaNode iiWithData = normalizeInstanceIdentifierWithSchemaNode( + this.controllerContext.toInstanceIdentifier(identifier), true); + DOMMountPoint mountPoint = iiWithData.getMountPoint(); + NormalizedNode data = null; if (mountPoint != null) { - data = broker.readConfigurationDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier()); + data = broker.readConfigurationData(mountPoint, iiWithData.getInstanceIdentifier()); } else { data = broker.readConfigurationData(iiWithData.getInstanceIdentifier()); } + CompositeNode compositeNode = datastoreNormalizedNodeToCompositeNode(data, iiWithData.getSchemaNode()); - data = pruneDataAtDepth(data, parseDepthParameter(uriInfo)); + compositeNode = pruneDataAtDepth(compositeNode, parseDepthParameter(uriInfo)); boolean prettyPrintMode = parsePrettyPrintParameter(uriInfo); - return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint(), prettyPrintMode); + return new StructuredData(compositeNode, iiWithData.getSchemaNode(), iiWithData.getMountPoint(), prettyPrintMode); } @SuppressWarnings("unchecked") @@ -640,18 +688,21 @@ public class RestconfImpl implements RestconfService { @Override public StructuredData readOperationalData(final String identifier, final UriInfo info) { - final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); - CompositeNode data = null; - MountInstance mountPoint = iiWithData.getMountPoint(); + final InstanceIdWithSchemaNode iiWithData = normalizeInstanceIdentifierWithSchemaNode( + this.controllerContext.toInstanceIdentifier(identifier), true); + NormalizedNode data = null; + + DOMMountPoint mountPoint = iiWithData.getMountPoint(); if (mountPoint != null) { - data = broker.readOperationalDataBehindMountPoint(mountPoint, iiWithData.getInstanceIdentifier()); + data = broker.readOperationalData(mountPoint, iiWithData.getInstanceIdentifier()); } else { data = broker.readOperationalData(iiWithData.getInstanceIdentifier()); } - data = pruneDataAtDepth(data, parseDepthParameter(info)); - boolean prettyPrintMode = parsePrettyPrintParameter(info); - return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint, prettyPrintMode); + final CompositeNode compositeNode = datastoreNormalizedNodeToCompositeNode(data, iiWithData.getSchemaNode()); + final CompositeNode prunedCompositeNode = pruneDataAtDepth(compositeNode, parseDepthParameter(info)); + final boolean prettyPrintMode = parsePrettyPrintParameter(info); + return new StructuredData(prunedCompositeNode, iiWithData.getSchemaNode(), mountPoint,prettyPrintMode); } private boolean parsePrettyPrintParameter(UriInfo info) { @@ -660,32 +711,31 @@ public class RestconfImpl implements RestconfService { } @Override - public Response updateConfigurationData(final String identifier, final CompositeNode payload) { - final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); + public Response updateConfigurationData(final String identifier, final Node payload) { + final InstanceIdWithSchemaNode iiWithData = normalizeInstanceIdentifierWithSchemaNode(this.controllerContext + .toInstanceIdentifier(identifier)); validateInput(iiWithData.getSchemaNode(), payload); - MountInstance mountPoint = iiWithData.getMountPoint(); + DOMMountPoint mountPoint = iiWithData.getMountPoint(); final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint); - validateListKeysEqualityInPayloadAndUri(iiWithData, payload); - RpcResult status = null; + validateListKeysEqualityInPayloadAndUri(iiWithData, value); + final NormalizedNode datastoreNormalizedNode = compositeNodeToDatastoreNormalizedNode(value, + iiWithData.getSchemaNode()); try { if (mountPoint != null) { - status = broker.commitConfigurationDataPutBehindMountPoint(mountPoint, - iiWithData.getInstanceIdentifier(), value).get(); + broker.commitConfigurationDataPut(mountPoint, iiWithData.getInstanceIdentifier(), + datastoreNormalizedNode).get(); } else { - status = broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), value).get(); + broker.commitConfigurationDataPut(iiWithData.getInstanceIdentifier(), datastoreNormalizedNode) + .get(); } } catch (Exception e) { throw new RestconfDocumentedException("Error updating data", e); } - if (status.getResult() == TransactionStatus.COMMITED) { - return Response.status(Status.OK).build(); - } - - return Response.status(Status.INTERNAL_SERVER_ERROR).build(); + return Response.status(Status.OK).build(); } /** @@ -734,7 +784,7 @@ public class RestconfImpl implements RestconfService { } @Override - public Response createConfigurationData(final String identifier, final CompositeNode payload) { + public Response createConfigurationData(final String identifier, final Node payload) { if (payload == null) { throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } @@ -765,8 +815,8 @@ public class RestconfImpl implements RestconfService { final InstanceIdWithSchemaNode incompleteInstIdWithData = this.controllerContext .toInstanceIdentifier(identifier); final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode(); - MountInstance mountPoint = incompleteInstIdWithData.getMountPoint(); - final Module module = this.findModule(mountPoint, payload); + DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint(); + final Module module = findModule(mountPoint, payload); if (module == null) { throw new RestconfDocumentedException("Module was not found for \"" + payloadNS + "\"", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); @@ -777,38 +827,30 @@ public class RestconfImpl implements RestconfService { parentSchema, payloadName, module.getNamespace()); value = this.normalizeNode(payload, schemaNode, mountPoint); - iiWithData = this.addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode); + iiWithData = normalizeInstanceIdentifierWithSchemaNode(this.addLastIdentifierFromData( + incompleteInstIdWithData, value, schemaNode)); } - RpcResult status = null; - MountInstance mountPoint = iiWithData.getMountPoint(); + final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, + iiWithData.getSchemaNode()); + DOMMountPoint mountPoint = iiWithData.getMountPoint(); try { if (mountPoint != null) { - Future> future = broker.commitConfigurationDataPostBehindMountPoint( - mountPoint, iiWithData.getInstanceIdentifier(), value); - status = future == null ? null : future.get(); + broker.commitConfigurationDataPost(mountPoint, + iiWithData.getInstanceIdentifier(), datastoreNormalizedData); } else { - Future> future = broker.commitConfigurationDataPost( - iiWithData.getInstanceIdentifier(), value); - status = future == null ? null : future.get(); + broker.commitConfigurationDataPost( + iiWithData.getInstanceIdentifier(), datastoreNormalizedData); } } catch (Exception e) { throw new RestconfDocumentedException("Error creating data", e); } - if (status == null) { - return Response.status(Status.ACCEPTED).build(); - } - - if (status.getResult() == TransactionStatus.COMMITED) { - return Response.status(Status.NO_CONTENT).build(); - } - - return Response.status(Status.INTERNAL_SERVER_ERROR).build(); + return Response.status(Status.NO_CONTENT).build(); } @Override - public Response createConfigurationData(final CompositeNode payload) { + public Response createConfigurationData(final Node payload) { if (payload == null) { throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } @@ -833,57 +875,56 @@ public class RestconfImpl implements RestconfService { final CompositeNode value = this.normalizeNode(payload, schemaNode, null); final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode); RpcResult status = null; - MountInstance mountPoint = iiWithData.getMountPoint(); + final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, schemaNode); + DOMMountPoint mountPoint = iiWithData.getMountPoint(); try { if (mountPoint != null) { - Future> future = broker.commitConfigurationDataPostBehindMountPoint( - mountPoint, iiWithData.getInstanceIdentifier(), value); - status = future == null ? null : future.get(); + broker.commitConfigurationDataPost(mountPoint, + iiWithData.getInstanceIdentifier(), datastoreNormalizedData); } else { - Future> future = broker.commitConfigurationDataPost( - iiWithData.getInstanceIdentifier(), value); - status = future == null ? null : future.get(); + broker.commitConfigurationDataPost( + iiWithData.getInstanceIdentifier(), datastoreNormalizedData); } } catch (Exception e) { throw new RestconfDocumentedException("Error creating data", e); } - if (status == null) { - return Response.status(Status.ACCEPTED).build(); - } - - if (status.getResult() == TransactionStatus.COMMITED) { - return Response.status(Status.NO_CONTENT).build(); - } - - return Response.status(Status.INTERNAL_SERVER_ERROR).build(); + return Response.status(Status.NO_CONTENT).build(); } @Override public Response deleteConfigurationData(final String identifier) { - final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); + final InstanceIdWithSchemaNode iiWithData = normalizeInstanceIdentifierWithSchemaNode(this.controllerContext + .toInstanceIdentifier(identifier)); RpcResult status = null; - MountInstance mountPoint = iiWithData.getMountPoint(); + DOMMountPoint mountPoint = iiWithData.getMountPoint(); try { if (mountPoint != null) { - status = broker.commitConfigurationDataDeleteBehindMountPoint(mountPoint, - iiWithData.getInstanceIdentifier()).get(); + broker.commitConfigurationDataDelete(mountPoint, iiWithData.getInstanceIdentifier()).get(); } else { - status = broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get(); + broker.commitConfigurationDataDelete(iiWithData.getInstanceIdentifier()).get(); } } catch (Exception e) { throw new RestconfDocumentedException("Error creating data", e); } - if (status.getResult() == TransactionStatus.COMMITED) { - return Response.status(Status.OK).build(); - } - - return Response.status(Status.INTERNAL_SERVER_ERROR).build(); + return Response.status(Status.OK).build(); } + /** + * Subscribes to some path in schema context (stream) to listen on changes + * on this stream. + * + * Additional parameters for subscribing to stream are loaded via rpc input + * parameters: + *
    + *
  • datastore
  • - default CONFIGURATION (other values of + * {@link LogicalDatastoreType} enum type) + *
  • scope
  • - default BASE (other values of {@link DataChangeScope}) + *
+ */ @Override public Response subscribeToStream(final String identifier, final UriInfo uriInfo) { final String streamName = Notificator.createStreamNameFromUri(identifier); @@ -896,18 +937,100 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Stream was not found.", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_ELEMENT); } - broker.registerToListenDataChanges(listener); + Map paramToValues = resolveValuesFromUri(identifier); + LogicalDatastoreType datastore = parserURIEnumParameter(LogicalDatastoreType.class, + paramToValues.get(DATASTORE_PARAM_NAME)); + if (datastore == null) { + throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /datastore=)", + ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); + } + DataChangeScope scope = parserURIEnumParameter(DataChangeScope.class, paramToValues.get(SCOPE_PARAM_NAME)); + if (scope == null) { + throw new RestconfDocumentedException("Stream name doesn't contains datastore value (pattern /scope=)", + ErrorType.APPLICATION, ErrorTag.MISSING_ATTRIBUTE); + } + + broker.registerToListenDataChanges(datastore, scope, listener); final UriBuilder uriBuilder = uriInfo.getAbsolutePathBuilder(); - UriBuilder port = uriBuilder.port(WebSocketServer.getInstance().getPort()); + int notificationPort = NOTIFICATION_PORT; + try { + WebSocketServer webSocketServerInstance = WebSocketServer.getInstance(); + notificationPort = webSocketServerInstance.getPort(); + } catch (NullPointerException e) { + WebSocketServer.createInstance(NOTIFICATION_PORT); + } + UriBuilder port = uriBuilder.port(notificationPort); final URI uriToWebsocketServer = port.replacePath(streamName).build(); return Response.status(Status.OK).location(uriToWebsocketServer).build(); } - private Module findModule(final MountInstance mountPoint, final CompositeNode data) { - if (data instanceof CompositeNodeWrapper) { - return findModule(mountPoint, (CompositeNodeWrapper) data); + /** + * Load parameter for subscribing to stream from input composite node + * + * @param compNode + * contains value + * @return enum object if its string value is equal to {@code paramName}. In + * other cases null. + */ + private T parseEnumTypeParameter(final CompositeNode compNode, final Class classDescriptor, + final String paramName) { + QNameModule salRemoteAugment = QNameModule.create(NAMESPACE_EVENT_SUBSCRIPTION_AUGMENT, + EVENT_SUBSCRIPTION_AUGMENT_REVISION); + SimpleNode simpleNode = compNode.getFirstSimpleByName(QName.create(salRemoteAugment, paramName)); + if (simpleNode == null) { + return null; + } + Object rawValue = simpleNode.getValue(); + if (!(rawValue instanceof String)) { + return null; + } + + return resolveAsEnum(classDescriptor, (String) rawValue); + } + + /** + * Checks whether {@code value} is one of the string representation of + * enumeration {@code classDescriptor} + * + * @return enum object if string value of {@code classDescriptor} + * enumeration is equal to {@code value}. Other cases null. + */ + private T parserURIEnumParameter(final Class classDescriptor, final String value) { + if (Strings.isNullOrEmpty(value)) { + return null; + } + return resolveAsEnum(classDescriptor, value); + } + + private T resolveAsEnum(Class classDescriptor, String value) { + T[] enumConstants = classDescriptor.getEnumConstants(); + if (enumConstants != null) { + for (T enm : classDescriptor.getEnumConstants()) { + if (((Enum) enm).name().equals(value)) { + return enm; + } + } + } + return null; + } + + private Map resolveValuesFromUri(String uri) { + Map result = new HashMap<>(); + String[] tokens = uri.split("/"); + for (int i = 1; i < tokens.length; i++) { + String[] parameterTokens = tokens[i].split("="); + if (parameterTokens.length == 2) { + result.put(parameterTokens[0], parameterTokens[1]); + } + } + return result; + } + + private Module findModule(final DOMMountPoint mountPoint, final Node data) { + if (data instanceof NodeWrapper) { + return findModule(mountPoint, (NodeWrapper) data); } else if (data != null) { URI namespace = data.getNodeType().getNamespace(); if (mountPoint != null) { @@ -921,7 +1044,7 @@ public class RestconfImpl implements RestconfService { } } - private Module findModule(final MountInstance mountPoint, final CompositeNodeWrapper data) { + private Module findModule(final DOMMountPoint mountPoint, final NodeWrapper data) { URI namespace = data.getNamespace(); Preconditions. checkNotNull(namespace); @@ -956,15 +1079,10 @@ public class RestconfImpl implements RestconfService { iiBuilder = YangInstanceIdentifier.builder(iiOriginal); } - if ((schemaOfData instanceof ListSchemaNode)) { - HashMap keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data); - iiBuilder.nodeWithKey(schemaOfData.getQName(), keys); - } else { - iiBuilder.node(schemaOfData.getQName()); - } + iiBuilder.node(schemaOfData.getQName()); YangInstanceIdentifier instance = iiBuilder.toInstance(); - MountInstance mountPoint = null; + DOMMountPoint mountPoint = null; if (identifierWithSchemaNode != null) { mountPoint = identifierWithSchemaNode.getMountPoint(); } @@ -1004,7 +1122,7 @@ public class RestconfImpl implements RestconfService { return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"); } - private boolean representsMountPointRootData(final CompositeNode data) { + private boolean representsMountPointRootData(final Node data) { URI namespace = this.namespace(data); return (SchemaContext.NAME.getNamespace().equals(namespace) /* * || MOUNT_POINT_MODULE_NAME .equals( namespace . @@ -1022,8 +1140,7 @@ public class RestconfImpl implements RestconfService { return identifier + "/" + ControllerContext.MOUNT; } - private CompositeNode normalizeNode(final CompositeNode node, final DataSchemaNode schema, - final MountInstance mountPoint) { + private CompositeNode normalizeNode(final Node node, final DataSchemaNode schema, final DOMMountPoint mountPoint) { if (schema == null) { QName nodeType = node == null ? null : node.getNodeType(); String localName = nodeType == null ? null : nodeType.getLocalName(); @@ -1037,24 +1154,32 @@ public class RestconfImpl implements RestconfService { ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } - if ((node instanceof CompositeNodeWrapper)) { - boolean isChangeAllowed = ((CompositeNodeWrapper) node).isChangeAllowed(); + if ((node instanceof NodeWrapper)) { + NodeWrapper nodeWrap = (NodeWrapper) node; + boolean isChangeAllowed = ((NodeWrapper) node).isChangeAllowed(); if (isChangeAllowed) { + nodeWrap = topLevelElementAsCompositeNodeWrapper((NodeWrapper) node, schema); try { - this.normalizeNode(((CompositeNodeWrapper) node), schema, null, mountPoint); + this.normalizeNode(nodeWrap, schema, null, mountPoint); } catch (IllegalArgumentException e) { throw new RestconfDocumentedException(e.getMessage(), ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } + if (nodeWrap instanceof CompositeNodeWrapper) { + return ((CompositeNodeWrapper) nodeWrap).unwrap(); + } } + } - return ((CompositeNodeWrapper) node).unwrap(); + if (node instanceof CompositeNode) { + return (CompositeNode) node; } - return node; + throw new RestconfDocumentedException("Top level element is not interpreted as composite node.", + ErrorType.APPLICATION, ErrorTag.INVALID_VALUE); } private void normalizeNode(final NodeWrapper nodeBuilder, final DataSchemaNode schema, - final QName previousAugment, final MountInstance mountPoint) { + final QName previousAugment, final DOMMountPoint mountPoint) { if (schema == null) { throw new RestconfDocumentedException("Data has bad format.\n\"" + nodeBuilder.getLocalName() + "\" does not exist in yang schema.", ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -1111,7 +1236,7 @@ public class RestconfImpl implements RestconfService { } private void normalizeSimpleNode(final SimpleNodeWrapper simpleNode, final DataSchemaNode schema, - final MountInstance mountPoint) { + final DOMMountPoint mountPoint) { final Object value = simpleNode.getValue(); Object inputValue = value; TypeDefinition typeDefinition = this.typeDefinition(schema); @@ -1133,7 +1258,7 @@ public class RestconfImpl implements RestconfService { } private void normalizeCompositeNode(final CompositeNodeWrapper compositeNodeBuilder, - final DataNodeContainer schema, final MountInstance mountPoint, final QName currentAugment) { + final DataNodeContainer schema, final DOMMountPoint mountPoint, final QName currentAugment) { final List> children = compositeNodeBuilder.getValues(); checkNodeMultiplicityAccordingToSchema(schema, children); for (final NodeWrapper child : children) { @@ -1214,7 +1339,7 @@ public class RestconfImpl implements RestconfService { } private QName normalizeNodeName(final NodeWrapper nodeBuilder, final DataSchemaNode schema, - final QName previousAugment, final MountInstance mountPoint) { + final QName previousAugment, final DOMMountPoint mountPoint) { QName validQName = schema.getQName(); QName currentAugment = previousAugment; if (schema.isAugmenting()) { @@ -1248,9 +1373,9 @@ public class RestconfImpl implements RestconfService { return currentAugment; } - private URI namespace(final CompositeNode data) { - if (data instanceof CompositeNodeWrapper) { - return ((CompositeNodeWrapper) data).getNamespace(); + private URI namespace(final Node data) { + if (data instanceof NodeWrapper) { + return ((NodeWrapper) data).getNamespace(); } else if (data != null) { return data.getNodeType().getNamespace(); } else { @@ -1258,9 +1383,9 @@ public class RestconfImpl implements RestconfService { } } - private String localName(final CompositeNode data) { - if (data instanceof CompositeNodeWrapper) { - return ((CompositeNodeWrapper) data).getLocalName(); + private String localName(final Node data) { + if (data instanceof NodeWrapper) { + return ((NodeWrapper) data).getLocalName(); } else if (data != null) { return data.getNodeType().getLocalName(); } else { @@ -1268,9 +1393,9 @@ public class RestconfImpl implements RestconfService { } } - private String getName(final CompositeNode data) { - if (data instanceof CompositeNodeWrapper) { - return ((CompositeNodeWrapper) data).getLocalName(); + private String getName(final Node data) { + if (data instanceof NodeWrapper) { + return ((NodeWrapper) data).getLocalName(); } else if (data != null) { return data.getNodeType().getLocalName(); } else { @@ -1307,4 +1432,99 @@ public class RestconfImpl implements RestconfService { throw new IllegalArgumentException("Unhandled parameter types: " + Arrays. asList(node).toString()); } } + + private CompositeNode datastoreNormalizedNodeToCompositeNode(NormalizedNode dataNode, DataSchemaNode schema) { + Iterable> nodes = null; + if (dataNode == null) { + throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.DATA_MISSING, + "No data was found.")); + } + if (schema instanceof ContainerSchemaNode && dataNode instanceof ContainerNode) { + nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getContainerNodeSerializer() + .serialize((ContainerSchemaNode) schema, (ContainerNode) dataNode); + } else if (schema instanceof ListSchemaNode && dataNode instanceof MapNode) { + nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getMapNodeSerializer() + .serialize((ListSchemaNode) schema, (MapNode) dataNode); + } else if (schema instanceof ListSchemaNode && dataNode instanceof MapEntryNode) { + nodes = CnSnFromNormalizedNodeSerializerFactory.getInstance().getMapEntryNodeSerializer() + .serialize((ListSchemaNode) schema, (MapEntryNode) dataNode); + } + if (nodes != null) { + if (nodes.iterator().hasNext()) { + Node nodeOldStruct = nodes.iterator().next(); + return (CompositeNode) nodeOldStruct; + } else { + LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode."); + } + } else { + LOG.error("Top level node isn't of type Container or List schema node but " + + schema.getClass().getSimpleName()); + } + + throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, + "It wasn't possible to correctly interpret data.")); + } + + private NormalizedNode compositeNodeToDatastoreNormalizedNode(CompositeNode compNode, DataSchemaNode schema) { + List> lst = new ArrayList>(); + lst.add(compNode); + if (schema instanceof ContainerSchemaNode) { + return CnSnToNormalizedNodeParserFactory.getInstance().getContainerNodeParser() + .parse(lst, (ContainerSchemaNode) schema); + } else if (schema instanceof ListSchemaNode) { + return CnSnToNormalizedNodeParserFactory.getInstance().getMapNodeParser() + .parse(lst, (ListSchemaNode) schema); + } + + LOG.error("Top level isn't of type container, list, leaf schema node but " + schema.getClass().getSimpleName()); + + throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, + "It wasn't possible to translate specified data to datastore readable form.")); + } + + private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode(InstanceIdWithSchemaNode iiWithSchemaNode) { + return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false); + } + + private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode( + InstanceIdWithSchemaNode iiWithSchemaNode, boolean unwrapLastListNode) { + return new InstanceIdWithSchemaNode(instanceIdentifierToReadableFormForNormalizeNode( + iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(), + iiWithSchemaNode.getMountPoint()); + } + + private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(YangInstanceIdentifier instIdentifier, + boolean unwrapLastListNode) { + Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null"); + final List result = new ArrayList(); + final Iterator iter = instIdentifier.getPathArguments().iterator(); + while (iter.hasNext()) { + final PathArgument pathArgument = iter.next(); + if (pathArgument instanceof NodeIdentifierWithPredicates && (iter.hasNext() || unwrapLastListNode)) { + result.add(new YangInstanceIdentifier.NodeIdentifier(pathArgument.getNodeType())); + } + result.add(pathArgument); + } + return YangInstanceIdentifier.create(result); + } + + private CompositeNodeWrapper topLevelElementAsCompositeNodeWrapper(final NodeWrapper node, + final DataSchemaNode schemaNode) { + if (node instanceof CompositeNodeWrapper) { + return (CompositeNodeWrapper) node; + } else if (node instanceof SimpleNodeWrapper && isDataContainerNode(schemaNode)) { + final SimpleNodeWrapper simpleNodeWrapper = (SimpleNodeWrapper) node; + return new CompositeNodeWrapper(namespace(simpleNodeWrapper), localName(simpleNodeWrapper)); + } + + throw new RestconfDocumentedException(new RestconfError(ErrorType.APPLICATION, ErrorTag.INVALID_VALUE, + "Top level element has to be composite node or has to represent data container node.")); + } + + private boolean isDataContainerNode(final DataSchemaNode schemaNode) { + if (schemaNode instanceof ContainerSchemaNode || schemaNode instanceof ListSchemaNode) { + return true; + } + return false; + } }