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=5d8c910afc31fa9d6420fc6d3a67466c34924317;hb=15fa131be8b16703089a6d8508546120cf15d45d;hp=73ca02c505f47741ca4e38c071a780a0f32e1cdf;hpb=00a9f11655358387b6048b0a427adc50e7df4672;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 73ca02c505..5d8c910afc 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 @@ -15,13 +15,12 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; - +import java.math.BigInteger; import java.net.URI; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -33,10 +32,11 @@ 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.data.AsyncDataBroker.DataChangeScope; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer; import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint; import org.opendaylight.controller.sal.rest.api.Draft02; @@ -52,7 +52,6 @@ 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; @@ -87,6 +86,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RestconfImpl implements RestconfService { + private enum UriParameters { PRETTY_PRINT("prettyPrint"), DEPTH("depth"); @@ -103,6 +103,8 @@ public class RestconfImpl implements RestconfService { } } + + private final static RestconfImpl INSTANCE = new RestconfImpl(); private static final int NOTIFICATION_PORT = 8181; @@ -205,7 +207,7 @@ public class RestconfImpl implements RestconfService { Set modules = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); modules = this.controllerContext.getAllModules(mountPoint); } else { @@ -236,7 +238,7 @@ public class RestconfImpl implements RestconfService { Module module = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); module = this.controllerContext.findModuleByNameAndRevision(mountPoint, moduleNameAndRevision); } else { @@ -267,7 +269,7 @@ public class RestconfImpl implements RestconfService { Set modules = null; DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { - InstanceIdWithSchemaNode mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointIdentifier = this.controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointIdentifier.getMountPoint(); modules = this.controllerContext.getAllModules(mountPoint); } else { @@ -518,7 +520,7 @@ public class RestconfImpl implements RestconfService { DOMMountPoint mountPoint = null; if (identifier.contains(ControllerContext.MOUNT)) { // mounted RPC call - look up mount instance. - InstanceIdWithSchemaNode mountPointId = controllerContext.toMountPointIdentifier(identifier); + InstanceIdentifierContext mountPointId = controllerContext.toMountPointIdentifier(identifier); mountPoint = mountPointId.getMountPoint(); int startOfRemoteRpcName = identifier.lastIndexOf(ControllerContext.MOUNT) @@ -609,41 +611,26 @@ public class RestconfImpl implements RestconfService { private void checkRpcSuccessAndThrowException(final RpcResult rpcResult) { if (rpcResult.isSuccessful() == false) { - Collection rpcErrors = rpcResult.getErrors(); - if (rpcErrors == null || rpcErrors.isEmpty()) { - throw new RestconfDocumentedException( - "The operation was not successful and there were no RPC errors returned", ErrorType.RPC, - ErrorTag.OPERATION_FAILED); - } - - List errorList = Lists.newArrayList(); - for (RpcError rpcError : rpcErrors) { - errorList.add(new RestconfError(rpcError)); - } - - throw new RestconfDocumentedException(errorList); + throw new RestconfDocumentedException("The operation was not successful", null, + rpcResult.getErrors()); } } @Override - public StructuredData readConfigurationData(final String identifier, final UriInfo uriInfo) { - final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier); + public NormalizedNodeContext readConfigurationData(final String identifier, final UriInfo uriInfo) { + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); DOMMountPoint mountPoint = iiWithData.getMountPoint(); NormalizedNode data = null; YangInstanceIdentifier normalizedII; if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier()); + normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData + .getInstanceIdentifier()); data = broker.readConfigurationData(mountPoint, normalizedII); } else { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); data = broker.readConfigurationData(normalizedII); } - - final CompositeNode compositeNode = datastoreNormalizedNodeToCompositeNode(data, iiWithData.getSchemaNode()); - final CompositeNode prunedCompositeNode = pruneDataAtDepth(compositeNode, parseDepthParameter(uriInfo)); - - final boolean prettyPrintMode = parsePrettyPrintParameter(uriInfo); - return new StructuredData(prunedCompositeNode, iiWithData.getSchemaNode(), mountPoint, prettyPrintMode); + return new NormalizedNodeContext(iiWithData, data); } @SuppressWarnings("unchecked") @@ -689,24 +676,21 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData readOperationalData(final String identifier, final UriInfo info) { - final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier); + public NormalizedNodeContext readOperationalData(final String identifier, final UriInfo info) { + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); DOMMountPoint mountPoint = iiWithData.getMountPoint(); NormalizedNode data = null; YangInstanceIdentifier normalizedII; if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier()); + normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData + .getInstanceIdentifier()); data = broker.readOperationalData(mountPoint, normalizedII); } else { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); data = broker.readOperationalData(normalizedII); } - 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); + return new NormalizedNodeContext(iiWithData, data); } private boolean parsePrettyPrintParameter(final UriInfo info) { @@ -716,7 +700,7 @@ public class RestconfImpl implements RestconfService { @Override public Response updateConfigurationData(final String identifier, final Node payload) { - final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); + final InstanceIdentifierContext iiWithData = this.controllerContext.toInstanceIdentifier(identifier); validateInput(iiWithData.getSchemaNode(), payload); @@ -727,17 +711,50 @@ public class RestconfImpl implements RestconfService { iiWithData.getSchemaNode()); YangInstanceIdentifier normalizedII; + if (mountPoint != null) { + normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized( + iiWithData.getInstanceIdentifier()); + } else { + normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); + } - try { - if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier()); - broker.commitConfigurationDataPut(mountPoint, normalizedII, datastoreNormalizedNode).get(); - } else { - normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); - broker.commitConfigurationDataPut(normalizedII, datastoreNormalizedNode).get(); + /* + * There is a small window where another write transaction could be updating the same data + * simultaneously and we get an OptimisticLockFailedException. This error is likely + * transient and The WriteTransaction#submit API docs state that a retry will likely + * succeed. So we'll try again if that scenario occurs. If it fails a third time then it + * probably will never succeed so we'll fail in that case. + * + * By retrying we're attempting to hide the internal implementation of the data store and + * how it handles concurrent updates from the restconf client. The client has instructed us + * to put the data and we should make every effort to do so without pushing optimistic lock + * failures back to the client and forcing them to handle it via retry (and having to + * document the behavior). + */ + int tries = 2; + while(true) { + try { + if (mountPoint != null) { + broker.commitConfigurationDataPut(mountPoint, normalizedII, + datastoreNormalizedNode).checkedGet(); + } else { + broker.commitConfigurationDataPut(normalizedII, + datastoreNormalizedNode).checkedGet(); + } + + break; + } catch (TransactionCommitFailedException e) { + if(e instanceof OptimisticLockFailedException) { + if(--tries <= 0) { + LOG.debug("Got OptimisticLockFailedException on last try - failing"); + throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); + } + + LOG.debug("Got OptimisticLockFailedException - trying again"); + } else { + throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList()); + } } - } catch (Exception e) { - throw new RestconfDocumentedException("Error updating data", e); } return Response.status(Status.OK).build(); @@ -750,7 +767,7 @@ public class RestconfImpl implements RestconfService { * if key values or key count in payload and URI isn't equal * */ - private void validateListKeysEqualityInPayloadAndUri(final InstanceIdWithSchemaNode iiWithData, + private void validateListKeysEqualityInPayloadAndUri(final InstanceIdentifierContext iiWithData, final CompositeNode payload) { if (iiWithData.getSchemaNode() instanceof ListSchemaNode) { final List keyDefinitions = ((ListSchemaNode) iiWithData.getSchemaNode()).getKeyDefinition(); @@ -801,7 +818,7 @@ public class RestconfImpl implements RestconfService { ErrorType.PROTOCOL, ErrorTag.UNKNOWN_NAMESPACE); } - InstanceIdWithSchemaNode iiWithData = null; + InstanceIdentifierContext iiWithData = null; CompositeNode value = null; if (this.representsMountPointRootData(payload)) { // payload represents mount point data and URI represents path to the mount point @@ -817,7 +834,7 @@ public class RestconfImpl implements RestconfService { value = this.normalizeNode(payload, iiWithData.getSchemaNode(), iiWithData.getMountPoint()); } else { - final InstanceIdWithSchemaNode incompleteInstIdWithData = this.controllerContext + final InstanceIdentifierContext incompleteInstIdWithData = this.controllerContext .toInstanceIdentifier(identifier); final DataNodeContainer parentSchema = (DataNodeContainer) incompleteInstIdWithData.getSchemaNode(); DOMMountPoint mountPoint = incompleteInstIdWithData.getMountPoint(); @@ -832,7 +849,7 @@ public class RestconfImpl implements RestconfService { parentSchema, payloadName, module.getNamespace()); value = this.normalizeNode(payload, schemaNode, mountPoint); - iiWithData = addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode); + iiWithData = addLastIdentifierFromData(incompleteInstIdWithData, value, schemaNode,incompleteInstIdWithData.getSchemaContext()); } final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, @@ -842,12 +859,15 @@ public class RestconfImpl implements RestconfService { try { if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier()); + normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData + .getInstanceIdentifier()); broker.commitConfigurationDataPost(mountPoint, normalizedII, datastoreNormalizedData); } else { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData); } + } catch(RestconfDocumentedException e) { + throw e; } catch (Exception e) { throw new RestconfDocumentedException("Error creating data", e); } @@ -879,20 +899,23 @@ public class RestconfImpl implements RestconfService { final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace(module, payloadName, module.getNamespace()); final CompositeNode value = this.normalizeNode(payload, schemaNode, null); - final InstanceIdWithSchemaNode iiWithData = this.addLastIdentifierFromData(null, value, schemaNode); + final InstanceIdentifierContext iiWithData = this.addLastIdentifierFromData(null, value, schemaNode,ControllerContext.getInstance().getGlobalSchema()); final NormalizedNode datastoreNormalizedData = compositeNodeToDatastoreNormalizedNode(value, schemaNode); DOMMountPoint mountPoint = iiWithData.getMountPoint(); YangInstanceIdentifier normalizedII; try { if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier()); + normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData + .getInstanceIdentifier()); broker.commitConfigurationDataPost(mountPoint, normalizedII, datastoreNormalizedData); } else { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData); } + } catch(RestconfDocumentedException e) { + throw e; } catch (Exception e) { throw new RestconfDocumentedException("Error creating data", e); } @@ -902,13 +925,14 @@ public class RestconfImpl implements RestconfService { @Override public Response deleteConfigurationData(final String identifier) { - final InstanceIdWithSchemaNode iiWithData = controllerContext.toInstanceIdentifier(identifier); + final InstanceIdentifierContext iiWithData = controllerContext.toInstanceIdentifier(identifier); DOMMountPoint mountPoint = iiWithData.getMountPoint(); YangInstanceIdentifier normalizedII; try { if (mountPoint != null) { - normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData.getInstanceIdentifier()); + normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData + .getInstanceIdentifier()); broker.commitConfigurationDataDelete(mountPoint, normalizedII); } else { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); @@ -1067,8 +1091,8 @@ public class RestconfImpl implements RestconfService { return module; } - private InstanceIdWithSchemaNode addLastIdentifierFromData(final InstanceIdWithSchemaNode identifierWithSchemaNode, - final CompositeNode data, final DataSchemaNode schemaOfData) { + private InstanceIdentifierContext addLastIdentifierFromData(final InstanceIdentifierContext identifierWithSchemaNode, + final CompositeNode data, final DataSchemaNode schemaOfData, SchemaContext schemaContext) { YangInstanceIdentifier instanceIdentifier = null; if (identifierWithSchemaNode != null) { instanceIdentifier = identifierWithSchemaNode.getInstanceIdentifier(); @@ -1082,15 +1106,49 @@ public class RestconfImpl implements RestconfService { iiBuilder = YangInstanceIdentifier.builder(iiOriginal); } - iiBuilder.node(schemaOfData.getQName()); + if ((schemaOfData instanceof ListSchemaNode)) { + HashMap keys = this.resolveKeysFromData(((ListSchemaNode) schemaOfData), data); + iiBuilder.nodeWithKey(schemaOfData.getQName(), keys); + } else { + iiBuilder.node(schemaOfData.getQName()); + } YangInstanceIdentifier instance = iiBuilder.toInstance(); DOMMountPoint mountPoint = null; + SchemaContext schemaCtx = null; if (identifierWithSchemaNode != null) { mountPoint = identifierWithSchemaNode.getMountPoint(); } - return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint); + return new InstanceIdentifierContext(instance, schemaOfData, mountPoint,schemaContext); + } + + private HashMap resolveKeysFromData(final ListSchemaNode listNode, final CompositeNode dataNode) { + final HashMap keyValues = new HashMap(); + List _keyDefinition = listNode.getKeyDefinition(); + for (final QName key : _keyDefinition) { + SimpleNode head = null; + String localName = key.getLocalName(); + List> simpleNodesByName = dataNode.getSimpleNodesByName(localName); + if (simpleNodesByName != null) { + head = Iterables.getFirst(simpleNodesByName, null); + } + + Object dataNodeKeyValueObject = null; + if (head != null) { + dataNodeKeyValueObject = head.getValue(); + } + + if (dataNodeKeyValueObject == null) { + throw new RestconfDocumentedException("Data contains list \"" + dataNode.getNodeType().getLocalName() + + "\" which does not contain key: \"" + key.getLocalName() + "\"", ErrorType.PROTOCOL, + ErrorTag.INVALID_VALUE); + } + + keyValues.put(key, dataNodeKeyValueObject); + } + + return keyValues; } private boolean endsWithMountPoint(final String identifier) { @@ -1117,8 +1175,9 @@ public class RestconfImpl implements RestconfService { 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(); + String localName = node == null ? null : + node instanceof NodeWrapper ? ((NodeWrapper)node).getLocalName() : + node.getNodeType().getLocalName(); throw new RestconfDocumentedException("Data schema node was not found for " + localName, ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE); @@ -1431,7 +1490,8 @@ public class RestconfImpl implements RestconfService { "It wasn't possible to correctly interpret data.")); } - private NormalizedNode compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode, final DataSchemaNode schema) { + private NormalizedNode compositeNodeToDatastoreNormalizedNode(final CompositeNode compNode, + final DataSchemaNode schema) { List> lst = new ArrayList>(); lst.add(compNode); if (schema instanceof ContainerSchemaNode) { @@ -1448,19 +1508,20 @@ public class RestconfImpl implements RestconfService { "It wasn't possible to translate specified data to datastore readable form.")); } - private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode(final InstanceIdWithSchemaNode iiWithSchemaNode) { + private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode( + final InstanceIdentifierContext iiWithSchemaNode) { return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false); } - private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode( - final InstanceIdWithSchemaNode iiWithSchemaNode, final boolean unwrapLastListNode) { - return new InstanceIdWithSchemaNode(instanceIdentifierToReadableFormForNormalizeNode( + private InstanceIdentifierContext normalizeInstanceIdentifierWithSchemaNode( + final InstanceIdentifierContext iiWithSchemaNode, final boolean unwrapLastListNode) { + return new InstanceIdentifierContext(instanceIdentifierToReadableFormForNormalizeNode( iiWithSchemaNode.getInstanceIdentifier(), unwrapLastListNode), iiWithSchemaNode.getSchemaNode(), - iiWithSchemaNode.getMountPoint()); + iiWithSchemaNode.getMountPoint(),iiWithSchemaNode.getSchemaContext()); } - private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode(final YangInstanceIdentifier instIdentifier, - final boolean unwrapLastListNode) { + private YangInstanceIdentifier instanceIdentifierToReadableFormForNormalizeNode( + final YangInstanceIdentifier instIdentifier, final boolean unwrapLastListNode) { Preconditions.checkNotNull(instIdentifier, "Instance identifier can't be null"); final List result = new ArrayList(); final Iterator iter = instIdentifier.getPathArguments().iterator(); @@ -1493,4 +1554,9 @@ public class RestconfImpl implements RestconfService { } return false; } + + public BigInteger getOperationalReceived() { + // TODO Auto-generated method stub + return null; + } }