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.exception.MissingNameSpaceException;
+import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException;
+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.w3c.dom.Document;
import org.w3c.dom.Element;
-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;
@Override
protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
- final XmlElement configElement = getConfigElement(operationElement);
+ final Datastore targetDatastore = extractTargetParameter(operationElement);
+ if (targetDatastore == Datastore.running) {
+ throw new NetconfDocumentedException("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 NormalizedNode storedNode = readStoredNode(LogicalDatastoreType.CONFIGURATION, ident);
try {
- final Optional<NormalizedNode<?,?>> newNode = modifyNode(schemaNode, element, storedNode);
+ final Optional<NormalizedNode<?, ?>> 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) {
- 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);
- }
+ throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.protocol, ErrorTag.operation_failed, ErrorSeverity.error);
}
}
final NormalizedNode node = readFuture.checkedGet().get();
return node;
} else {
- LOG.warn("Unable to read node : {} from {} datastore", path, logicalDatastoreType);
+ LOG.debug("Unable to read node : {} from {} datastore", path, logicalDatastoreType);
}
} 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);
+ LOG.debug("Unable to read stored data: {}", path, e);
}
//we can return null here since DataOperations.modify handles null as input
return null;
}
- private Optional<DataSchemaNode> getSchemaNodeFromNamespace(final String namespace, final XmlElement element){
+ private Optional<DataSchemaNode> getSchemaNodeFromNamespace(final String namespace, final XmlElement element) throws NetconfDocumentedException{
Optional<DataSchemaNode> 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 NetconfDocumentedException("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);
}
return dataSchemaNode;
}
- private Optional<NormalizedNode<?, ?>> modifyNode(final DataSchemaNode schemaNode, final XmlElement element, final NormalizedNode storedNode) throws DataModificationException{
+ private Optional<NormalizedNode<?, ?>> modifyNode(final DataSchemaNode schemaNode, final XmlElement element, final NormalizedNode storedNode, final ModifyAction defaultAction) throws DataModificationException{
if (schemaNode instanceof ContainerSchemaNode) {
final ContainerNode modifiedNode =
DomToNormalizedNodeParserFactory
.getContainerNodeParser()
.parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
- final Optional<ContainerNode> oNode = DataOperations.modify((ContainerSchemaNode) schemaNode, (ContainerNode) storedNode, modifiedNode);
+ final Optional<ContainerNode> oNode = DataOperations.modify((ContainerSchemaNode) schemaNode, (ContainerNode) storedNode, modifiedNode, defaultAction);
if (!oNode.isPresent()) {
return Optional.absent();
}
.getMapNodeParser()
.parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
- final Optional<MapNode> oNode = DataOperations.modify((ListSchemaNode) schemaNode, (MapNode) storedNode, modifiedNode);
+ final Optional<MapNode> oNode = DataOperations.modify((ListSchemaNode) schemaNode, (MapNode) storedNode, modifiedNode, defaultAction);
if (!oNode.isPresent()) {
return Optional.absent();
}
}
- private XmlElement getConfigElement(final XmlElement operationElement) throws NetconfDocumentedException{
- final Optional<XmlElement> 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 Datastore extractTargetParameter(final XmlElement operationElement) throws NetconfDocumentedException {
+ final XmlElement targetChildNode;
+ try {
+ final XmlElement targetElement = operationElement.getOnlyChildElementWithSameNamespace(TARGET_KEY);
+ targetChildNode = targetElement.getOnlyChildElementWithSameNamespace();
+ } catch (final MissingNameSpaceException | UnexpectedNamespaceException e) {
+ LOG.trace("Can't get only child element with same namespace", e);
+ throw NetconfDocumentedException.wrap(e);
+ }
+
+ return Datastore.valueOf(targetChildNode.getName());
+ }
+
+ private ModifyAction getDefaultOperation(final XmlElement operationElement) throws NetconfDocumentedException{
+ try {
+ return ModifyAction.fromXmlValue(getElement(operationElement, DEFAULT_OPERATION_KEY).getTextContent());
+ } catch (NetconfDocumentedException e) {
+ if (e.getErrorType() == ErrorType.protocol
+ && e.getErrorSeverity() == ErrorSeverity.error
+ && e.getErrorTag() == ErrorTag.missing_element) {
+ return ModifyAction.MERGE;
+ }
+ else {
+ throw e;
+ }
+ }
+ }
+
+ private XmlElement getElement(final XmlElement operationElement, String elementName) throws NetconfDocumentedException {
+ final Optional<XmlElement> childNode = operationElement.getOnlyChildElementOptionally(elementName);
+ if (!childNode.isPresent()) {
+ throw new NetconfDocumentedException(elementName + " element is missing",
+ ErrorType.protocol,
+ ErrorTag.missing_element,
ErrorSeverity.error);
}
- return configChildNode.get();
+ return childNode.get();
}
@Override
protected String getOperationName() {
return OPERATION_NAME;
}
+
}