X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-rest-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Frestconf%2Fimpl%2FRestconfImpl.java;h=b94f6a6166c47f3b1308f7dc4696341fe5ec202b;hp=5cb3cc2bae495724c4133a57c7feb929f682ebf2;hb=51e91f6bdcc88c5aa96f956e516d31dbb5e5d5e0;hpb=86f71bd3f15ce34a9181bb01f0f71fc6979917c5 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 5cb3cc2bae..b94f6a6166 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,12 +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.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; @@ -32,9 +32,12 @@ 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; @@ -50,7 +53,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; @@ -60,12 +62,8 @@ 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.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; @@ -358,31 +356,31 @@ public class RestconfImpl implements RestconfService { private CompositeNode toStreamCompositeNode(final String streamName, final DataSchemaNode streamSchemaNode) { final List> streamNodeValues = new ArrayList>(); - List instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues - .add(NodeFactory. createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName)); + .add(NodeFactory. createImmutableSimpleNode(nameSchemaNode.getQName(), null, streamName)); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "description"); final DataSchemaNode descriptionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(descriptionSchemaNode.getQName(), null, "DESCRIPTION_PLACEHOLDER")); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "replay-support"); final DataSchemaNode replaySupportSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(replaySupportSchemaNode.getQName(), null, Boolean.valueOf(true))); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "replay-log-creation-time"); final DataSchemaNode replayLogCreationTimeSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(replayLogCreationTimeSchemaNode.getQName(), null, "")); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) streamSchemaNode), "events"); final DataSchemaNode eventsSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); streamNodeValues.add(NodeFactory. createImmutableSimpleNode(eventsSchemaNode.getQName(), null, "")); @@ -392,26 +390,26 @@ public class RestconfImpl implements RestconfService { private CompositeNode toModuleCompositeNode(final Module module, final DataSchemaNode moduleSchemaNode) { final List> moduleNodeValues = new ArrayList>(); - List instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + List instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "name"); final DataSchemaNode nameSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); moduleNodeValues.add(NodeFactory. createImmutableSimpleNode(nameSchemaNode.getQName(), null, module.getName())); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "revision"); final DataSchemaNode revisionSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); Date _revision = module.getRevision(); moduleNodeValues.add(NodeFactory. createImmutableSimpleNode(revisionSchemaNode.getQName(), null, REVISION_FORMAT.format(_revision))); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "namespace"); final DataSchemaNode namespaceSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); moduleNodeValues.add(NodeFactory. createImmutableSimpleNode(namespaceSchemaNode.getQName(), null, module.getNamespace().toString())); - instanceDataChildrenByName = this.controllerContext.findInstanceDataChildrenByName( + instanceDataChildrenByName = ControllerContext.findInstanceDataChildrenByName( ((DataNodeContainer) moduleSchemaNode), "feature"); final DataSchemaNode featureSchemaNode = Iterables.getFirst(instanceDataChildrenByName, null); for (final FeatureDefinition feature : module.getFeatures()) { @@ -611,19 +609,8 @@ 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()); } } @@ -634,7 +621,8 @@ public class RestconfImpl implements RestconfService { 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()); @@ -697,7 +685,8 @@ public class RestconfImpl implements RestconfService { 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()); @@ -729,17 +718,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(); @@ -830,7 +852,7 @@ public class RestconfImpl implements RestconfService { } String payloadName = this.getName(payload); - final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace( + final DataSchemaNode schemaNode = ControllerContext.findInstanceDataChildByNameAndNamespace( parentSchema, payloadName, module.getNamespace()); value = this.normalizeNode(payload, schemaNode, mountPoint); @@ -844,12 +866,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); } @@ -878,7 +903,7 @@ public class RestconfImpl implements RestconfService { } String payloadName = this.getName(payload); - final DataSchemaNode schemaNode = this.controllerContext.findInstanceDataChildByNameAndNamespace(module, + 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); @@ -888,13 +913,16 @@ 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); } @@ -910,7 +938,8 @@ 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.commitConfigurationDataDelete(mountPoint, normalizedII); } else { normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier()); @@ -1084,7 +1113,12 @@ 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; @@ -1095,6 +1129,34 @@ public class RestconfImpl implements RestconfService { return new InstanceIdWithSchemaNode(instance, schemaOfData, mountPoint); } + 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) { return identifier.endsWith(ControllerContext.MOUNT) || identifier.endsWith(ControllerContext.MOUNT + "/"); } @@ -1102,10 +1164,10 @@ public class RestconfImpl implements RestconfService { private boolean representsMountPointRootData(final Node data) { URI namespace = this.namespace(data); return (SchemaContext.NAME.getNamespace().equals(namespace) /* - * || MOUNT_POINT_MODULE_NAME .equals( namespace . - * toString( ) ) - */) - && SchemaContext.NAME.getLocalName().equals(this.localName(data)); + * || MOUNT_POINT_MODULE_NAME .equals( namespace . + * toString( ) ) + */) + && SchemaContext.NAME.getLocalName().equals(this.localName(data)); } private String addMountPointIdentifier(final String identifier) { @@ -1119,8 +1181,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); @@ -1239,7 +1302,7 @@ public class RestconfImpl implements RestconfService { final List> children = compositeNodeBuilder.getValues(); checkNodeMultiplicityAccordingToSchema(schema, children); for (final NodeWrapper child : children) { - final List potentialSchemaNodes = this.controllerContext.findInstanceDataChildrenByName( + final List potentialSchemaNodes = ControllerContext.findInstanceDataChildrenByName( schema, child.getLocalName()); if (potentialSchemaNodes.size() > 1 && child.getNamespace() == null) { @@ -1334,15 +1397,16 @@ public class RestconfImpl implements RestconfService { } if (nodeBuilder.getNamespace() == null || Objects.equal(nodeBuilder.getNamespace(), validQName.getNamespace()) - || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName) /* - * || Note : this check is wrong - - * can never be true as it compares - * a URI with a String not sure what - * the intention is so commented out - * ... Objects . equal ( nodeBuilder - * . getNamespace ( ) , - * MOUNT_POINT_MODULE_NAME ) - */) { + || Objects.equal(nodeBuilder.getNamespace().toString(), moduleName)) { + /* + * || Note : this check is wrong - + * can never be true as it compares + * a URI with a String not sure what + * the intention is so commented out + * ... Objects . equal ( nodeBuilder + * . getNamespace ( ) , + * MOUNT_POINT_MODULE_NAME ) + */ nodeBuilder.setQname(validQName); } @@ -1411,25 +1475,15 @@ public class RestconfImpl implements RestconfService { } private CompositeNode datastoreNormalizedNodeToCompositeNode(final NormalizedNode dataNode, final DataSchemaNode schema) { - Iterable> nodes = null; + Node 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); - } + nodes = DataNormalizer.toLegacy(dataNode); if (nodes != null) { - if (nodes.iterator().hasNext()) { - Node nodeOldStruct = nodes.iterator().next(); - return (CompositeNode) nodeOldStruct; + if (nodes instanceof CompositeNode) { + return (CompositeNode) nodes; } else { LOG.error("The node " + dataNode.getNodeType() + " couldn't be transformed to compositenode."); } @@ -1442,7 +1496,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) { @@ -1459,7 +1514,8 @@ public class RestconfImpl implements RestconfService { "It wasn't possible to translate specified data to datastore readable form.")); } - private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode(final InstanceIdWithSchemaNode iiWithSchemaNode) { + private InstanceIdWithSchemaNode normalizeInstanceIdentifierWithSchemaNode( + final InstanceIdWithSchemaNode iiWithSchemaNode) { return normalizeInstanceIdentifierWithSchemaNode(iiWithSchemaNode, false); } @@ -1470,8 +1526,8 @@ public class RestconfImpl implements RestconfService { iiWithSchemaNode.getMountPoint()); } - 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();