X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fnetconf%2Fmdsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fnetconf%2Fmdsal%2Fconnector%2Fops%2FEditConfig.java;h=889068940eebe626d3b60d97ac8d66fd7b10e7c3;hb=refs%2Fchanges%2F13%2F23413%2F26;hp=a2aa0e20679473951cacd1191097f1368e76bac6;hpb=228af4aa1ef1a802fd24e7e010f3bba959ee03dd;p=controller.git diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/EditConfig.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/EditConfig.java index a2aa0e2067..889068940e 100644 --- a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/EditConfig.java +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/EditConfig.java @@ -9,34 +9,30 @@ package org.opendaylight.controller.netconf.mdsal.connector.ops; import com.google.common.base.Optional; -import com.google.common.util.concurrent.CheckedFuture; import java.net.URI; import java.net.URISyntaxException; import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import org.opendaylight.controller.config.util.xml.DocumentedException; +import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorSeverity; +import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorTag; +import org.opendaylight.controller.config.util.xml.DocumentedException.ErrorType; +import org.opendaylight.controller.config.util.xml.XmlElement; +import org.opendaylight.controller.config.util.xml.XmlUtil; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; -import org.opendaylight.controller.netconf.api.NetconfDocumentedException; -import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorSeverity; -import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorTag; -import org.opendaylight.controller.netconf.api.NetconfDocumentedException.ErrorType; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext; import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider; +import org.opendaylight.controller.netconf.mdsal.connector.ops.DataTreeChangeTracker.DataTreeChange; import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation; -import org.opendaylight.controller.netconf.util.xml.XmlElement; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; -import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils; import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; -import org.opendaylight.yangtools.yang.data.operations.DataModificationException; -import org.opendaylight.yangtools.yang.data.operations.DataModificationException.DataExistsException; -import org.opendaylight.yangtools.yang.data.operations.DataModificationException.DataMissingException; -import org.opendaylight.yangtools.yang.data.operations.DataOperations; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; @@ -55,8 +51,6 @@ public class EditConfig extends AbstractSingletonNetconfOperation { private static final String CONFIG_KEY = "config"; private static final String TARGET_KEY = "target"; private static final String DEFAULT_OPERATION_KEY = "default-operation"; - - private final CurrentSchemaContext schemaContext; private final TransactionProvider transactionProvider; @@ -67,10 +61,10 @@ public class EditConfig extends AbstractSingletonNetconfOperation { } @Override - protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws DocumentedException { final Datastore targetDatastore = extractTargetParameter(operationElement); if (targetDatastore == Datastore.running) { - throw new NetconfDocumentedException("edit-config on running datastore is not supported", + throw new DocumentedException("edit-config on running datastore is not supported", ErrorType.protocol, ErrorTag.operation_not_supported, ErrorSeverity.error); @@ -83,49 +77,90 @@ public class EditConfig extends AbstractSingletonNetconfOperation { for (XmlElement element : configElement.getChildElements()) { final String ns = element.getNamespace(); final DataSchemaNode schemaNode = getSchemaNodeFromNamespace(ns, element).get(); - YangInstanceIdentifier ident = YangInstanceIdentifier.of(schemaNode.getQName()); - final NormalizedNode storedNode = readStoredNode(LogicalDatastoreType.CONFIGURATION, ident); - try { - final Optional> newNode = modifyNode(schemaNode, element, storedNode, defaultAction); - final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction(); - if (newNode.isPresent()) { - rwTx.put(LogicalDatastoreType.CONFIGURATION, ident, newNode.get()); - } else { - rwTx.delete(LogicalDatastoreType.CONFIGURATION, ident); - } - } catch (final DataExistsException e) { - throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_exists, ErrorSeverity.error); - } catch (final DataMissingException e) { - throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error); - } catch (final DataModificationException e) { - throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.operation_failed, ErrorSeverity.error); - } + final DataTreeChangeTracker changeTracker = new DataTreeChangeTracker(defaultAction); + final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider = new EditOperationStrategyProvider(changeTracker); + + parseIntoNormalizedNode(schemaNode, element, editOperationStrategyProvider); + executeOperations(changeTracker); } return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); } - private NormalizedNode readStoredNode(final LogicalDatastoreType logicalDatastoreType, final YangInstanceIdentifier path) throws NetconfDocumentedException{ - final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction(); - final CheckedFuture>, ReadFailedException> readFuture = rwTx.read(logicalDatastoreType, path); - try { - if (readFuture.checkedGet().isPresent()) { - final NormalizedNode node = readFuture.checkedGet().get(); - return node; - } else { - LOG.debug("Unable to read node : {} from {} datastore", path, logicalDatastoreType); + private void executeOperations(final DataTreeChangeTracker changeTracker) throws DocumentedException { + final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction(); + final List aa = changeTracker.getDataTreeChanges(); + final ListIterator iterator = aa.listIterator(aa.size()); + + while (iterator.hasPrevious()) { + final DataTreeChange dtc = iterator.previous(); + executeChange(rwTx, dtc); + } + } + + private void executeChange(final DOMDataReadWriteTransaction rwtx, final DataTreeChange change) throws DocumentedException { + switch (change.getAction()) { + case NONE: + return; + case MERGE: + rwtx.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot()); + break; + case CREATE: + try { + final Optional> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())).checkedGet(); + if (readResult.isPresent()) { + throw new DocumentedException("Data already exists, cannot execute CREATE operation", ErrorType.protocol, ErrorTag.data_exists, ErrorSeverity.error); + } + rwtx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot()); + } catch (ReadFailedException e) { + LOG.warn("Read from datastore failed when trying to read data for create operation", change, e); + } + break; + case REPLACE: + rwtx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot()); + break; + case DELETE: + try { + final Optional> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())).checkedGet(); + if (!readResult.isPresent()) { + throw new DocumentedException("Data is missing, cannot execute DELETE operation", ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error); + } + rwtx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())); + } catch (ReadFailedException e) { + LOG.warn("Read from datastore failed when trying to read data for delete operation", change, e); } - } catch (final ReadFailedException e) { - //only log this since DataOperations.modify will handle throwing an exception or writing the node. - LOG.debug("Unable to read stored data: {}", path, e); + break; + case REMOVE: + rwtx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())); + break; + default: + LOG.warn("Unknown/not implemented operation, not executing"); } + } + + private NormalizedNode parseIntoNormalizedNode(final DataSchemaNode schemaNode, final XmlElement element, + final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider) { + - //we can return null here since DataOperations.modify handles null as input - return null; + if (schemaNode instanceof ContainerSchemaNode) { + return DomToNormalizedNodeParserFactory + .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext(), editOperationStrategyProvider) + .getContainerNodeParser() + .parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode); + } else if (schemaNode instanceof ListSchemaNode) { + return DomToNormalizedNodeParserFactory + .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext(), editOperationStrategyProvider) + .getMapNodeParser() + .parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode); + } else { + //this should never happen since edit-config on any other node type should not be possible nor makes sense + LOG.debug("DataNode from module is not ContainerSchemaNode nor ListSchemaNode, aborting.."); + } + throw new UnsupportedOperationException("implement exception if parse fails"); } - private Optional getSchemaNodeFromNamespace(final String namespace, final XmlElement element) throws NetconfDocumentedException{ + private Optional getSchemaNodeFromNamespace(final String namespace, final XmlElement element) throws DocumentedException{ Optional dataSchemaNode = Optional.absent(); try { //returns module with newest revision since findModuleByNamespace returns a set of modules and we only need the newest one @@ -134,12 +169,11 @@ public class EditConfig extends AbstractSingletonNetconfOperation { if (schemaNode != null) { dataSchemaNode = Optional.of(module.getDataChildByName(element.getName())); } else { - throw new NetconfDocumentedException("Unable to find node with namespace: " + namespace + "in module: " + module.toString(), + throw new DocumentedException("Unable to find node with namespace: " + namespace + "in module: " + module.toString(), ErrorType.application, ErrorTag.unknown_namespace, ErrorSeverity.error); } - } catch (URISyntaxException e) { LOG.debug("Unable to create URI for namespace : {}", namespace); } @@ -147,61 +181,25 @@ public class EditConfig extends AbstractSingletonNetconfOperation { return dataSchemaNode; } - private Optional> modifyNode(final DataSchemaNode schemaNode, final XmlElement element, final NormalizedNode storedNode, final ModifyAction defaultAction) throws DataModificationException{ - if (schemaNode instanceof ContainerSchemaNode) { - final ContainerNode modifiedNode = - DomToNormalizedNodeParserFactory - .getInstance(DomUtils.defaultValueCodecProvider()) - .getContainerNodeParser() - .parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode); - - final Optional oNode = DataOperations.modify((ContainerSchemaNode) schemaNode, (ContainerNode) storedNode, modifiedNode, defaultAction); - if (!oNode.isPresent()) { - return Optional.absent(); - } - - final NormalizedNode node = oNode.get(); - return Optional.>of(node); - } else if (schemaNode instanceof ListSchemaNode) { - final MapNode modifiedNode = - DomToNormalizedNodeParserFactory - .getInstance(DomUtils.defaultValueCodecProvider()) - .getMapNodeParser() - .parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode); - - final Optional oNode = DataOperations.modify((ListSchemaNode) schemaNode, (MapNode) storedNode, modifiedNode, defaultAction); - if (!oNode.isPresent()) { - return Optional.absent(); - } - - final NormalizedNode node = oNode.get(); - return Optional.>of(node); - } else { - //this should never happen since edit-config on any other node type should not be possible nor makes sense - LOG.debug("DataNode from module is not ContainerSchemaNode nor ListSchemaNode, aborting.."); - return Optional.absent(); - } - } - - private Datastore extractTargetParameter(final XmlElement operationElement) throws NetconfDocumentedException { + private Datastore extractTargetParameter(final XmlElement operationElement) throws DocumentedException { final NodeList elementsByTagName = operationElement.getDomElement().getElementsByTagName(TARGET_KEY); // Direct lookup instead of using XmlElement class due to performance if (elementsByTagName.getLength() == 0) { - throw new NetconfDocumentedException("Missing target element", ErrorType.rpc, ErrorTag.missing_attribute, ErrorSeverity.error); + throw new DocumentedException("Missing target element", ErrorType.rpc, ErrorTag.missing_attribute, ErrorSeverity.error); } else if (elementsByTagName.getLength() > 1) { - throw new NetconfDocumentedException("Multiple target elements", ErrorType.rpc, ErrorTag.unknown_attribute, ErrorSeverity.error); + throw new DocumentedException("Multiple target elements", ErrorType.rpc, ErrorTag.unknown_attribute, ErrorSeverity.error); } else { final XmlElement targetChildNode = XmlElement.fromDomElement((Element) elementsByTagName.item(0)).getOnlyChildElement(); return Datastore.valueOf(targetChildNode.getName()); } } - private ModifyAction getDefaultOperation(final XmlElement operationElement) throws NetconfDocumentedException { + private ModifyAction getDefaultOperation(final XmlElement operationElement) throws DocumentedException { final NodeList elementsByTagName = operationElement.getDomElement().getElementsByTagName(DEFAULT_OPERATION_KEY); if(elementsByTagName.getLength() == 0) { return ModifyAction.MERGE; } else if(elementsByTagName.getLength() > 1) { - throw new NetconfDocumentedException("Multiple " + DEFAULT_OPERATION_KEY + " elements", + throw new DocumentedException("Multiple " + DEFAULT_OPERATION_KEY + " elements", ErrorType.rpc, ErrorTag.unknown_attribute, ErrorSeverity.error); } else { return ModifyAction.fromXmlValue(elementsByTagName.item(0).getTextContent()); @@ -209,10 +207,10 @@ public class EditConfig extends AbstractSingletonNetconfOperation { } - private XmlElement getElement(final XmlElement operationElement, String elementName) throws NetconfDocumentedException { + private XmlElement getElement(final XmlElement operationElement, String elementName) throws DocumentedException { final Optional childNode = operationElement.getOnlyChildElementOptionally(elementName); if (!childNode.isPresent()) { - throw new NetconfDocumentedException(elementName + " element is missing", + throw new DocumentedException(elementName + " element is missing", ErrorType.protocol, ErrorTag.missing_element, ErrorSeverity.error);