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=09be4163df8ad8d5247fd23e64f34a5b4e0c5b16;hpb=e3998d55e33da9f6ecb69da75ecc71a047b6362b;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 09be4163df..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,33 +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.util.mapping.AbstractLastNetconfOperation; -import org.opendaylight.controller.netconf.util.xml.XmlElement; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.controller.netconf.mdsal.connector.ops.DataTreeChangeTracker.DataTreeChange; +import org.opendaylight.controller.netconf.util.mapping.AbstractSingletonNetconfOperation; +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; @@ -44,14 +41,16 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.NodeList; -public class EditConfig extends AbstractLastNetconfOperation { +public class EditConfig extends AbstractSingletonNetconfOperation { private static final Logger LOG = LoggerFactory.getLogger(EditConfig.class); private static final String OPERATION_NAME = "edit-config"; 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; @@ -62,63 +61,119 @@ public class EditConfig extends AbstractLastNetconfOperation { } @Override - protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { - final XmlElement configElement = getConfigElement(operationElement); + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws DocumentedException { + final Datastore targetDatastore = extractTargetParameter(operationElement); + if (targetDatastore == Datastore.running) { + throw new DocumentedException("edit-config on running datastore is not supported", + ErrorType.protocol, + ErrorTag.operation_not_supported, + ErrorSeverity.error); + } + + final ModifyAction defaultAction = getDefaultOperation(operationElement); + + final XmlElement configElement = getElement(operationElement, CONFIG_KEY); 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); - final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction(); - if (newNode.isPresent()) { - rwTx.put(LogicalDatastoreType.CONFIGURATION, ident, newNode.get()); - } else { - rwTx.delete(LogicalDatastoreType.CONFIGURATION, ident); - } - } catch (final DataModificationException e) { - if (e instanceof DataExistsException) { - throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_exists, ErrorSeverity.error); - } else if (e instanceof DataMissingException) { - throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error); - } else { - //should never happen, since in edit-config only the 2 previous cases can happen - 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.warn("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); } - } catch (final ReadFailedException e) { - //only log this since DataOperations.modify will handle throwing an exception or writing the node. - LOG.warn("Unable to read stored data: {}", path, 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); + } + break; + case REMOVE: + rwtx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())); + break; + default: + LOG.warn("Unknown/not implemented operation, not executing"); } + } - //we can return null here since DataOperations.modify handles null as input - return null; + private NormalizedNode parseIntoNormalizedNode(final DataSchemaNode schemaNode, final XmlElement element, + final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider) { + + + 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){ + 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 final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(new URI(namespace), null); - dataSchemaNode = Optional.of(module.getDataChildByName(element.getName())); + DataSchemaNode schemaNode = module.getDataChildByName(element.getName()); + if (schemaNode != null) { + dataSchemaNode = Optional.of(module.getDataChildByName(element.getName())); + } else { + 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); } @@ -126,57 +181,47 @@ public class EditConfig extends AbstractLastNetconfOperation { return dataSchemaNode; } - private Optional> modifyNode(final DataSchemaNode schemaNode, final XmlElement element, final NormalizedNode storedNode) 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); - 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); - if (!oNode.isPresent()) { - return Optional.absent(); - } + 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 DocumentedException("Missing target element", ErrorType.rpc, ErrorTag.missing_attribute, ErrorSeverity.error); + } else if (elementsByTagName.getLength() > 1) { + 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()); + } + } - final NormalizedNode node = oNode.get(); - return Optional.>of(node); + 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 DocumentedException("Multiple " + DEFAULT_OPERATION_KEY + " elements", + ErrorType.rpc, ErrorTag.unknown_attribute, ErrorSeverity.error); } 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(); + return ModifyAction.fromXmlValue(elementsByTagName.item(0).getTextContent()); } } - private XmlElement getConfigElement(final XmlElement operationElement) throws NetconfDocumentedException{ - final Optional configChildNode = operationElement.getOnlyChildElementOptionally(CONFIG_KEY); - if (!configChildNode.isPresent()) { - throw new NetconfDocumentedException("Can't get child element with name: " + CONFIG_KEY, - ErrorType.application, - ErrorTag.unknown_element, + private XmlElement getElement(final XmlElement operationElement, String elementName) throws DocumentedException { + final Optional childNode = operationElement.getOnlyChildElementOptionally(elementName); + if (!childNode.isPresent()) { + throw new DocumentedException(elementName + " element is missing", + ErrorType.protocol, + ErrorTag.missing_element, ErrorSeverity.error); } - return configChildNode.get(); + return childNode.get(); } @Override protected String getOperationName() { return OPERATION_NAME; } + }