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=b24420fe8a5cd9183016f00aacd3657f64568082;hb=3a46b99bbad87937775f42ba881e5613293cb5dd;hp=c82cf8e0bc5050b682d9b99896f786f68381cb58;hpb=4ebc1ca89310e67b4fbcea2e03f7f7e1b38f67cc;p=netconf.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 c82cf8e0bc..b24420fe8a 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 @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; @@ -314,28 +315,40 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData getOperations(final UriInfo uriInfo) { + public NormalizedNodeContext getOperations(final UriInfo uriInfo) { final Set allModules = controllerContext.getAllModules(); - return operationsFromModulesToStructuredData(allModules, null, parsePrettyPrintParameter(uriInfo)); + return operationsFromModulesToNormalizedContext(allModules, null); } @Override - public StructuredData getOperations(final String identifier, final UriInfo uriInfo) { + public NormalizedNodeContext getOperations(final String identifier, final UriInfo uriInfo) { Set modules = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { final InstanceIdentifierContext mountPointIdentifier = controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); modules = controllerContext.getAllModules(mountPoint); + } else { - throw new RestconfDocumentedException( - "URI has bad format. If operations behind mount point should be showed, URI has to end with " - + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); + final String errMsg = "URI has bad format. If operations behind mount point should be showed, URI has to end with "; + throw new RestconfDocumentedException(errMsg + ControllerContext.MOUNT, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); } - return operationsFromModulesToStructuredData(modules, mountPoint, parsePrettyPrintParameter(uriInfo)); + return operationsFromModulesToNormalizedContext(modules, mountPoint); + } + + private NormalizedNodeContext operationsFromModulesToNormalizedContext(final Set modules, + final DOMMountPoint mountPoint) { + + // FIXME find best way to change restconf-netconf yang schema for provide this functionality + final String errMsg = "We are not able support view operations functionality yet."; + throw new RestconfDocumentedException(errMsg, ErrorType.APPLICATION, ErrorTag.OPERATION_NOT_SUPPORTED); } + /** + * @deprecated method will be removed in Lithium release + */ + @Deprecated private StructuredData operationsFromModulesToStructuredData(final Set modules, final DOMMountPoint mountPoint, final boolean prettyPrint) { final List> operationsAsData = new ArrayList>(); @@ -411,6 +424,15 @@ public class RestconfImpl implements RestconfService { } } + /** + * @deprecated method will be removed for Lithium release + * so, please use toStreamEntryNode method + * + * @param streamName + * @param streamSchemaNode + * @return + */ + @Deprecated private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) { final List> streamNodeValues = new ArrayList>(); List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( @@ -445,6 +467,15 @@ public class RestconfImpl implements RestconfService { return NodeFactory.createImmutableCompositeNode(streamSchemaNode.getQName(), null, streamNodeValues); } + /** + * @deprecated method will be removed for Lithium release + * so, please use toModuleEntryNode method + * + * @param module + * @param moduleSchemaNode + * @return + */ + @Deprecated private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) { final List> moduleNodeValues = new ArrayList>(); List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( @@ -497,6 +528,29 @@ public class RestconfImpl implements RestconfService { return callRpc(rpc, payload, parsePrettyPrintParameter(uriInfo)); } + private void validateInput(final DataSchemaNode inputSchema, final NormalizedNodeContext payload) { + if (inputSchema != null && payload.getData() == null) { + // expected a non null payload + throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } else if (inputSchema == null && payload.getData() != null) { + // did not expect any input + throw new RestconfDocumentedException("No input expected.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + // else + // { + // TODO: Validate "mandatory" and "config" values here??? Or should those be + // those be + // validate in a more central location inside MD-SAL core. + // } + } + + /** + * @deprecated method will be removed for Lithium release + * + * @param inputSchema + * @param payload + */ + @Deprecated private void validateInput(final DataSchemaNode inputSchema, final Node payload) { if (inputSchema != null && payload == null) { // expected a non null payload @@ -528,7 +582,7 @@ public class RestconfImpl implements RestconfService { final YangInstanceIdentifier pathIdentifier = ((YangInstanceIdentifier) pathValue); String streamName = null; if (!Iterables.isEmpty(pathIdentifier.getPathArguments())) { - final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier); + final String fullRestconfIdentifier = controllerContext.toFullRestconfIdentifier(pathIdentifier, null); LogicalDatastoreType datastore = parseEnumTypeParameter(value, LogicalDatastoreType.class, DATASTORE_PARAM_NAME); @@ -754,26 +808,16 @@ public class RestconfImpl implements RestconfService { } @Override - public Response updateConfigurationData(final String identifier, final Node payload) { + public Response updateConfigurationData(final String identifier, final NormalizedNodeContext payload) { + Preconditions.checkNotNull(identifier); final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); validateInput(iiWithData.getSchemaNode(), payload); - - final DOMMountPoint mountPoint = iiWithData.getMountPoint(); validateTopLevelNodeName(payload, iiWithData.getInstanceIdentifier()); - final CompositeNode value = this.normalizeNode(payload, iiWithData.getSchemaNode(), mountPoint); - validateListKeysEqualityInPayloadAndUri(iiWithData, value); - final NormalizedNode datastoreNormalizedNode = compositeNodeToDatastoreNormalizedNode(value, - iiWithData.getSchemaNode()); - + validateListKeysEqualityInPayloadAndUri(iiWithData, payload.getData()); - YangInstanceIdentifier normalizedII; - if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized( - iiWithData.getInstanceIdentifier()); - } else { - normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); - } + final DOMMountPoint mountPoint = iiWithData.getMountPoint(); + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); /* * There is a small window where another write transaction could be updating the same data @@ -792,11 +836,9 @@ public class RestconfImpl implements RestconfService { while(true) { try { if (mountPoint != null) { - broker.commitConfigurationDataPut(mountPoint, normalizedII, - datastoreNormalizedNode).checkedGet(); + broker.commitConfigurationDataPut(mountPoint, normalizedII, payload.getData()).checkedGet(); } else { - broker.commitConfigurationDataPut(normalizedII, - datastoreNormalizedNode).checkedGet(); + broker.commitConfigurationDataPut(normalizedII, payload.getData()).checkedGet(); } break; @@ -817,6 +859,37 @@ public class RestconfImpl implements RestconfService { return Response.status(Status.OK).build(); } + private void validateTopLevelNodeName(final NormalizedNodeContext node, + final YangInstanceIdentifier identifier) { + + final String payloadName = node.getData().getNodeType().getLocalName(); + final Iterator pathArguments = identifier.getReversePathArguments().iterator(); + + //no arguments + if ( ! pathArguments.hasNext()) { + //no "data" payload + if ( ! node.getData().getNodeType().equals(NETCONF_BASE_QNAME)) { + throw new RestconfDocumentedException("Instance identifier has to contain at least one path argument", + ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); + } + //any arguments + } else { + final String identifierName = pathArguments.next().getNodeType().getLocalName(); + if ( ! payloadName.equals(identifierName)) { + throw new RestconfDocumentedException("Payload name (" + payloadName + + ") is different from identifier name (" + identifierName + ")", ErrorType.PROTOCOL, + ErrorTag.MALFORMED_MESSAGE); + } + } + } + + /** + * @deprecated method will be removed for Lithium release + * + * @param node + * @param identifier + */ + @Deprecated private void validateTopLevelNodeName(final Node node, final YangInstanceIdentifier identifier) { final String payloadName = getName(node); @@ -847,6 +920,29 @@ public class RestconfImpl implements RestconfService { * if key values or key count in payload and URI isn't equal * */ + private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData, + final NormalizedNode payload) { + if (iiWithData.getSchemaNode() instanceof ListSchemaNode) { + final List keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition(); + final PathArgument lastPathArgument = iiWithData.getInstanceIdentifier().getLastPathArgument(); + if (lastPathArgument instanceof NodeIdentifierWithPredicates) { + final Map uriKeyValues = ((NodeIdentifierWithPredicates) lastPathArgument) + .getKeyValues(); + isEqualUriAndPayloadKeyValues(uriKeyValues, payload, keyDefinitions); + } + } + } + + /** + * @deprecated method will be removed for Lithium release + * + * Validates whether keys in {@code payload} are equal to values of keys in {@code iiWithData} for list schema node + * + * @throws RestconfDocumentedException + * if key values or key count in payload and URI isn't equal + * + */ + @Deprecated private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData, final CompositeNode payload) { if (iiWithData.getSchemaNode() instanceof ListSchemaNode) { @@ -860,6 +956,39 @@ public class RestconfImpl implements RestconfService { } } + private void isEqualUriAndPayloadKeyValues(final Map uriKeyValues, final NormalizedNode payload, + final List keyDefinitions) { + for (final QName keyDefinition : keyDefinitions) { + final Object uriKeyValue = uriKeyValues.get(keyDefinition); + // should be caught during parsing URI to InstanceIdentifier + if (uriKeyValue == null) { + final String errMsg = "Missing key " + keyDefinition + " in URI."; + throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); + } + // TODO thing about the possibility to fix +// final List> payloadKeyValues = payload.getSimpleNodesByName(keyDefinition.getLocalName()); +// if (payloadKeyValues.isEmpty()) { +// final String errMsg = "Missing key " + keyDefinition.getLocalName() + " in the message body."; +// throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING); +// } +// +// final Object payloadKeyValue = payloadKeyValues.iterator().next().getValue(); +// if (!uriKeyValue.equals(payloadKeyValue)) { +// final String errMsg = "The value '" + uriKeyValue + "' for key '" + keyDefinition.getLocalName() + +// "' specified in the URI doesn't match the value '" + payloadKeyValue + "' specified in the message body. "; +// throw new RestconfDocumentedException(errMsg, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); +// } + } + } + + /** + * @deprecated method will be removed for Lithium release + * + * @param uriKeyValues + * @param payload + * @param keyDefinitions + */ + @Deprecated private void isEqualUriAndPayloadKeyValues(final Map uriKeyValues, final CompositeNode payload, final List keyDefinitions) { for (final QName keyDefinition : keyDefinitions) { @@ -886,61 +1015,30 @@ public class RestconfImpl implements RestconfService { } @Override - public Response createConfigurationData(final String identifier, final Node payload) { + public Response createConfigurationData(final String identifier, final NormalizedNodeContext payload, final UriInfo uriInfo) { if (payload == null) { throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - final URI payloadNS = namespace(payload); + final URI payloadNS = payload.getData().getNodeType().getNamespace(); if (payloadNS == null) { throw new RestconfDocumentedException( "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE); } - InstanceIdentifierContext iiWithData = null; - CompositeNode value = null; - if (representsMountPointRootData(payload)) { - // payload represents mount point data and URI represents path to the mount point - - if (endsWithMountPoint(identifier)) { - throw new RestconfDocumentedException("URI has bad format. URI should be without \"" - + ControllerContext.MOUNT + "\" for POST operation.", ErrorType.PROTOCOL, - ErrorTag.INVALID_VALUE); - } - - final String completeIdentifier = addMountPointIdentifier(identifier); - iiWithData = controllerContext.toInstanceIdentifier(completeIdentifier); - - value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint()); - } else { - final InstanceIdentifierContext incompleteInstIdWithData = controllerContext - .toInstanceIdentifier(identifier); - final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode(); - final DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint(); - final Module module = findModule(mountPoint, payload); - - final String payloadName = getName(payload); - final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace( - parentSchema, payloadName, module.getNamespace()); - value = this.normalizeNode(payload, schemaNode, mountPoint); - - iiWithData = addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode,incompleteInstIdWithData.getSchemaContext()); - } + final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); - final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, - iiWithData.getSchemaNode()); - final DOMMountPoint mountPoint = iiWithData.getMountPoint(); - YangInstanceIdentifier normalizedII; + final InstanceIdentifierContext iiWithData = mountPoint != null + ? controllerContext.toMountPointIdentifier(identifier) + : controllerContext.toInstanceIdentifier(identifier); + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); try { if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData - .getInstanceIdentifier()); - broker.commitConfigurationDataPost(mountPoint, normalizedII, datastoreNormalizedData); + broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()); } else { - normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); - broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData); + broker.commitConfigurationDataPost(normalizedII, payload.getData()); } } catch(final RestconfDocumentedException e) { throw e; @@ -948,42 +1046,38 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Error creating data", e); } - return Response.status(Status.NO_CONTENT).build(); + + final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT); + final URI location = resolveLocation(uriInfo, "config", mountPoint, normalizedII); + if (location != null) { + responseBuilder.location(location); + } + return responseBuilder.build(); } @Override - public Response createConfigurationData(final Node payload) { + public Response createConfigurationData(final NormalizedNodeContext payload, final UriInfo uriInfo) { if (payload == null) { throw new RestconfDocumentedException("Input is required.", ErrorType.PROTOCOL, ErrorTag.MALFORMED_MESSAGE); } - final URI payloadNS = namespace(payload); + final URI payloadNS = payload.getData().getNodeType().getNamespace(); if (payloadNS == null) { throw new RestconfDocumentedException( "Data has bad format. Root element node must have namespace (XML format) or module name(JSON format)", ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE); } - final Module module = this.findModule(null, payload); - - final String payloadName = getName(payload); - final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace(module, - payloadName, module.getNamespace()); - final CompositeNode value = this.normalizeNode(payload, schemaNode, null); - final InstanceIdentifierContext iiWithData = addLastIdentifierFromData(null, value, schemaNode,ControllerContext.getInstance().getGlobalSchema()); - final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, schemaNode); - final DOMMountPoint mountPoint = iiWithData.getMountPoint(); - YangInstanceIdentifier normalizedII; + final DOMMountPoint mountPoint = payload.getInstanceIdentifierContext().getMountPoint(); + final InstanceIdentifierContext iiWithData = payload.getInstanceIdentifierContext(); + final YangInstanceIdentifier normalizedII = iiWithData.getInstanceIdentifier(); try { if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData - .getInstanceIdentifier()); - broker.commitConfigurationDataPost(mountPoint, normalizedII, datastoreNormalizedData); + broker.commitConfigurationDataPost(mountPoint, normalizedII, payload.getData()); } else { - normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); - broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData); + broker.commitConfigurationDataPost(normalizedII, payload.getData()); } } catch(final RestconfDocumentedException e) { throw e; @@ -991,7 +1085,24 @@ public class RestconfImpl implements RestconfService { throw new RestconfDocumentedException("Error creating data", e); } - return Response.status(Status.NO_CONTENT).build(); + final ResponseBuilder responseBuilder = Response.status(Status.NO_CONTENT); + final URI location = resolveLocation(uriInfo, "", mountPoint, normalizedII); + if (location != null) { + responseBuilder.location(location); + } + return responseBuilder.build(); + } + + private URI resolveLocation(final UriInfo uriInfo, final String uriBehindBase, final DOMMountPoint mountPoint, final YangInstanceIdentifier normalizedII) { + final UriBuilder uriBuilder = uriInfo.getBaseUriBuilder(); + uriBuilder.path("config"); + try { + uriBuilder.path(controllerContext.toFullRestconfIdentifier(normalizedII, mountPoint)); + } catch (final Exception e) { + LOG.debug("Location for instance identifier"+normalizedII+"wasn't created", e); + return null; + } + return uriBuilder.build(); } @Override @@ -1252,7 +1363,12 @@ public class RestconfImpl implements RestconfService { return identifier + "/" + ControllerContext.MOUNT; } - private CompositeNode normalizeNode(final Node node, final DataSchemaNode schema, final DOMMountPoint mountPoint) { + /** + * @deprecated method will be removed in Lithium release + * we don't wish to use Node and CompositeNode anywhere + */ + @Deprecated + public CompositeNode normalizeNode(final Node node, final DataSchemaNode schema, final DOMMountPoint mountPoint) { if (schema == null) { final String localName = node == null ? null : node instanceof NodeWrapper ? ((NodeWrapper)node).getLocalName() : @@ -1525,6 +1641,13 @@ public class RestconfImpl implements RestconfService { } } + /** + * @deprecated method will be removed for Lithium release + * + * @param data + * @return + */ + @Deprecated private String getName(final Node data) { if (data instanceof NodeWrapper) { return ((NodeWrapper) data).getLocalName();