From 8d17b7aca17f10005f5f3da1b80d2e442fd0168c Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Mon, 16 Feb 2015 10:36:48 +0100 Subject: [PATCH] BUG-2633 - Netconf northbound mapping. Supported operations for now are: get, get-config - candidate and running datastore, without filtering edit-config - only on candidate datastore discard-changes lock, unlock - supported only for compatibility reasons since we don't support locking of running datastore, and each session has it's own candidate datastore. commit Change-Id: Idb0bad09dc1d8925674576290fecb47ccd1e5dbd Signed-off-by: Tomas Cere Signed-off-by: Maros Marsalek --- .../netconf/mdsal-netconf-connector/pom.xml | 117 +++++++++++ .../mdsal/connector/CurrentSchemaContext.java | 41 ++++ .../MdsalNetconfOperationService.java | 158 +++++++++++++++ .../MdsalNetconfOperationServiceFactory.java | 35 ++++ .../mdsal/connector/OperationProvider.java | 50 +++++ .../mdsal/connector/TransactionProvider.java | 131 +++++++++++++ .../netconf/mdsal/connector/ops/Commit.java | 50 +++++ .../mdsal/connector/ops/Datastore.java | 13 ++ .../mdsal/connector/ops/DiscardChanges.java | 63 ++++++ .../mdsal/connector/ops/EditConfig.java | 182 ++++++++++++++++++ .../netconf/mdsal/connector/ops/Lock.java | 64 ++++++ .../netconf/mdsal/connector/ops/Unlock.java | 50 +++++ .../mdsal/connector/ops/get/AbstractGet.java | 163 ++++++++++++++++ .../netconf/mdsal/connector/ops/get/Get.java | 82 ++++++++ .../mdsal/connector/ops/get/GetConfig.java | 83 ++++++++ opendaylight/netconf/netconf-impl/pom.xml | 36 ++++ .../netconf/impl/CommitNotifier.java | 32 +++ .../DefaultCommitNotificationProducer.java | 3 +- .../NetconfServerSessionListenerFactory.java | 4 +- ...NetconfServerSessionNegotiatorFactory.java | 6 +- .../mapping/operations/DefaultCommit.java | 6 +- .../impl/osgi/NetconfOperationRouterImpl.java | 4 +- opendaylight/netconf/pom.xml | 1 + 23 files changed, 1363 insertions(+), 11 deletions(-) create mode 100644 opendaylight/netconf/mdsal-netconf-connector/pom.xml create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/OperationProvider.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/TransactionProvider.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Commit.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Datastore.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/DiscardChanges.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/EditConfig.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Lock.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Unlock.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java create mode 100644 opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java create mode 100644 opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CommitNotifier.java diff --git a/opendaylight/netconf/mdsal-netconf-connector/pom.xml b/opendaylight/netconf/mdsal-netconf-connector/pom.xml new file mode 100644 index 0000000000..2808672ca2 --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/pom.xml @@ -0,0 +1,117 @@ + + + 4.0.0 + + + org.opendaylight.controller + netconf-subsystem + 0.3.0-SNAPSHOT + + mdsal-netconf-connector + bundle + ${project.artifactId} + + + + ${project.groupId} + netconf-api + + + ${project.groupId} + netconf-mapping-api + + + ${project.groupId} + netconf-util + + + com.google.guava + guava + + + org.opendaylight.yangtools + yang-data-impl + + + org.opendaylight.controller + sal-core-api + + + org.opendaylight.controller + commons.logback_settings + + + org.opendaylight.yangtools + mockito-configuration + + + org.slf4j + slf4j-api + + + org.opendaylight.controller + sal-core-api + + + org.opendaylight.controller + config-util + + + org.opendaylight.yangtools + yang-data-operations + 0.7.0-SNAPSHOT + + + + + + + + org.apache.felix + maven-bundle-plugin + + + * + + + + + + org.opendaylight.yangtools + yang-maven-plugin + + + config + + generate-sources + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + ${jmxGeneratorPath} + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + ${salGeneratorPath} + + + true + + + + + + org.opendaylight.controller + yang-jmx-generator-plugin + ${config.version} + + + + + + + diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java new file mode 100644 index 0000000000..df671e8f4f --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/CurrentSchemaContext.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector; + +import com.google.common.base.Preconditions; +import java.util.concurrent.atomic.AtomicReference; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; + +public class CurrentSchemaContext implements SchemaContextListener, AutoCloseable { + final AtomicReference currentContext = new AtomicReference(); + private final ListenerRegistration schemaContextListenerListenerRegistration; + + public SchemaContext getCurrentContext() { + Preconditions.checkState(currentContext.get() != null, "Current context not received"); + return currentContext.get(); + } + + public CurrentSchemaContext(final SchemaService schemaService) { + schemaContextListenerListenerRegistration = schemaService.registerSchemaContextListener(this); + } + + @Override + public void onGlobalContextUpdated(final SchemaContext schemaContext) { + currentContext.set(schemaContext); + } + + @Override + public void close() throws Exception { + schemaContextListenerListenerRegistration.close(); + currentContext.set(null); + } +} \ No newline at end of file diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java new file mode 100644 index 0000000000..f54c5e9838 --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationService.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector; + +import com.google.common.base.Optional; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.netconf.mapping.api.Capability; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MdsalNetconfOperationService implements NetconfOperationService { + + private static final Logger LOG = LoggerFactory.getLogger(MdsalNetconfOperationService.class); + + private final CurrentSchemaContext schemaContext; + private final String netconfSessionIdForReporting; + private final OperationProvider operationProvider; + + public MdsalNetconfOperationService(final CurrentSchemaContext schemaContext, final String netconfSessionIdForReporting, + final DOMDataBroker dataBroker) { + this.schemaContext = schemaContext; + // TODO schema contexts are different in data broker and the one we receive here ... the one received here should be updated same way as broker is + this.netconfSessionIdForReporting = netconfSessionIdForReporting; + this.operationProvider = new OperationProvider(netconfSessionIdForReporting, schemaContext, dataBroker); + } + + @Override + public void close() { + + } + + // TODO does this get called dynamically ? + @Override + public Set getCapabilities() { + final Set capabilities = new HashSet<>(); + // [RFC6241] 8.3. Candidate Configuration Capability + capabilities.add(new BasicCapability("urn:ietf:params:netconf:capability:candidate:1.0")); + + final SchemaContext currentContext = schemaContext.getCurrentContext(); + final Set modules = currentContext.getModules(); + for (final Module module : modules) { + if(currentContext.getModuleSource(module).isPresent()) { + capabilities.add(new YangStoreCapability(module, currentContext.getModuleSource(module).get())); + } else { + LOG.warn("Missing source for module {}. This module will not be available from netconf server for session {}", + module, netconfSessionIdForReporting); + } + } + + return capabilities; + } + + @Override + public Set getNetconfOperations() { + return operationProvider.getOperations(); + } + + // TODO reuse from netconf impl + private static class BasicCapability implements Capability { + + private final String capability; + + private BasicCapability(final String capability) { + this.capability = capability; + } + + @Override + public String getCapabilityUri() { + return capability; + } + + @Override + public Optional getModuleNamespace() { + return Optional.absent(); + } + + @Override + public Optional getModuleName() { + return Optional.absent(); + } + + @Override + public Optional getRevision() { + return Optional.absent(); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.absent(); + } + + @Override + public Collection getLocation() { + return Collections.emptyList(); + } + + @Override + public String toString() { + return capability; + } + } + + private static final class YangStoreCapability extends BasicCapability { + + private final String content; + private final String revision; + private final String moduleName; + private final String moduleNamespace; + + public YangStoreCapability(final Module module, final String moduleContent) { + super(toCapabilityURI(module)); + this.content = moduleContent; + this.moduleName = module.getName(); + this.moduleNamespace = module.getNamespace().toString(); + this.revision = SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision()); + } + + @Override + public Optional getCapabilitySchema() { + return Optional.of(content); + } + + private static String toCapabilityURI(final Module module) { + return String.valueOf(module.getNamespace()) + "?module=" + + module.getName() + "&revision=" + SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision()); + } + + @Override + public Optional getModuleName() { + return Optional.of(moduleName); + } + + @Override + public Optional getModuleNamespace() { + return Optional.of(moduleNamespace); + } + + @Override + public Optional getRevision() { + return Optional.of(revision); + } + } +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java new file mode 100644 index 0000000000..098f25bf4a --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/MdsalNetconfOperationServiceFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector; + +import com.google.common.base.Preconditions; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; +import org.opendaylight.controller.sal.core.api.model.SchemaService; + +public class MdsalNetconfOperationServiceFactory implements NetconfOperationServiceFactory, AutoCloseable { + + private final DOMDataBroker dataBroker; + private final CurrentSchemaContext currentSchemaContext; + + public MdsalNetconfOperationServiceFactory(final SchemaService schemaService, final DOMDataBroker domDataBroker) { + this.currentSchemaContext = new CurrentSchemaContext(Preconditions.checkNotNull(schemaService)); + this.dataBroker = Preconditions.checkNotNull(domDataBroker); + } + + @Override + public MdsalNetconfOperationService createService(final String netconfSessionIdForReporting) { + return new MdsalNetconfOperationService(currentSchemaContext, netconfSessionIdForReporting, dataBroker); + } + + @Override + public void close() throws Exception { + currentSchemaContext.close(); + } +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/OperationProvider.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/OperationProvider.java new file mode 100644 index 0000000000..c881ae2e4e --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/OperationProvider.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector; + +import com.google.common.collect.Sets; +import java.util.Set; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.mdsal.connector.ops.Commit; +import org.opendaylight.controller.netconf.mdsal.connector.ops.DiscardChanges; +import org.opendaylight.controller.netconf.mdsal.connector.ops.EditConfig; +import org.opendaylight.controller.netconf.mdsal.connector.ops.Lock; +import org.opendaylight.controller.netconf.mdsal.connector.ops.Unlock; +import org.opendaylight.controller.netconf.mdsal.connector.ops.get.Get; +import org.opendaylight.controller.netconf.mdsal.connector.ops.get.GetConfig; + +final class OperationProvider { + + private final String netconfSessionIdForReporting; + private final CurrentSchemaContext schemaContext; + private final DOMDataBroker dataBroker; + private final TransactionProvider transactionProvider; + + public OperationProvider(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final DOMDataBroker dataBroker) { + this.netconfSessionIdForReporting = netconfSessionIdForReporting; + this.schemaContext = schemaContext; + this.dataBroker = dataBroker; + this.transactionProvider = new TransactionProvider(dataBroker, netconfSessionIdForReporting); + + } + + Set getOperations() { + return Sets.newHashSet( + new Commit(netconfSessionIdForReporting, transactionProvider), + new DiscardChanges(netconfSessionIdForReporting, transactionProvider), + new EditConfig(netconfSessionIdForReporting, schemaContext, transactionProvider), + new Get(netconfSessionIdForReporting, schemaContext, transactionProvider), + new GetConfig(netconfSessionIdForReporting, schemaContext, transactionProvider), + new Lock(netconfSessionIdForReporting), + new Unlock(netconfSessionIdForReporting) + ); + } + +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/TransactionProvider.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/TransactionProvider.java new file mode 100644 index 0000000000..f1b214b83e --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/TransactionProvider.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.CheckedFuture; +import java.util.ArrayList; +import java.util.List; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +//TODO make a global TransactionProvider for all Netconf sessions instead of each session having one. +public class TransactionProvider implements AutoCloseable{ + + private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class); + + private final DOMDataBroker dataBroker; + + private DOMDataReadWriteTransaction candidateTransaction = null; + private DOMDataReadWriteTransaction runningTransaction = null; + private final List allOpenReadWriteTransactions = new ArrayList<>(); + + private final String netconfSessionIdForReporting; + + private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No candidateTransaction found for session "; + + + public TransactionProvider(DOMDataBroker dataBroker, String netconfSessionIdForReporting) { + this.dataBroker = dataBroker; + this.netconfSessionIdForReporting = netconfSessionIdForReporting; + } + + @Override + public synchronized void close() throws Exception { + for (DOMDataReadWriteTransaction rwt : allOpenReadWriteTransactions) { + rwt.cancel(); + } + + allOpenReadWriteTransactions.clear(); + } + + public synchronized Optional getCandidateTransaction() { + if (candidateTransaction == null) { + return Optional.absent(); + } + + return Optional.of(candidateTransaction); + } + + public synchronized DOMDataReadWriteTransaction getOrCreateTransaction() { + if (getCandidateTransaction().isPresent()) { + return getCandidateTransaction().get(); + } + + candidateTransaction = dataBroker.newReadWriteTransaction(); + allOpenReadWriteTransactions.add(candidateTransaction); + return candidateTransaction; + } + + public synchronized boolean commitTransaction() throws NetconfDocumentedException { + if (!getCandidateTransaction().isPresent()) { + throw new NetconfDocumentedException(NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting, + ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error); + } + + CheckedFuture future = candidateTransaction.submit(); + try { + future.checkedGet(); + } catch (TransactionCommitFailedException e) { + LOG.debug("Transaction {} failed on", candidateTransaction, e); + throw new NetconfDocumentedException("Transaction commit failed on " + e.getMessage() + " " + netconfSessionIdForReporting, + ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error); + } + allOpenReadWriteTransactions.remove(candidateTransaction); + candidateTransaction = null; + + return true; + } + + public synchronized void abortTransaction() { + LOG.debug("Aborting current candidateTransaction"); + Optional otx = getCandidateTransaction(); + Preconditions.checkState(otx.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting); + candidateTransaction.cancel(); + allOpenReadWriteTransactions.remove(candidateTransaction); + candidateTransaction = null; + } + + public synchronized DOMDataReadWriteTransaction createRunningTransaction() { + runningTransaction = dataBroker.newReadWriteTransaction(); + allOpenReadWriteTransactions.add(runningTransaction); + return runningTransaction; + } + + public synchronized boolean commitRunningTransaction(DOMDataReadWriteTransaction tx) throws NetconfDocumentedException { + allOpenReadWriteTransactions.remove(tx); + + CheckedFuture future = tx.submit(); + try { + future.checkedGet(); + } catch (TransactionCommitFailedException e) { + LOG.debug("Transaction {} failed on", tx, e); + throw new NetconfDocumentedException("Transaction commit failed on " + e.getMessage() + " " + netconfSessionIdForReporting, + ErrorType.application, ErrorTag.operation_failed, ErrorSeverity.error); + } + + return true; + } + + public synchronized void abortRunningTransaction(DOMDataReadWriteTransaction tx) { + LOG.debug("Aborting current running Transaction"); + Preconditions.checkState(runningTransaction != null, NO_TRANSACTION_FOUND_FOR_SESSION + netconfSessionIdForReporting); + tx.cancel(); + allOpenReadWriteTransactions.remove(tx); + } + +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Commit.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Commit.java new file mode 100644 index 0000000000..15396cfbf8 --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Commit.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class Commit extends AbstractLastNetconfOperation{ + + private static final Logger LOG = LoggerFactory.getLogger(Commit.class); + + private static final String OPERATION_NAME = "commit"; + private final TransactionProvider transactionProvider; + + public Commit(final String netconfSessionIdForReporting, final TransactionProvider transactionProvider) { + super(netconfSessionIdForReporting); + this.transactionProvider = transactionProvider; + + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + + boolean commitStatus = transactionProvider.commitTransaction(); + LOG.trace("Transaction commited succesfuly", commitStatus); + + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + @Override + protected String getOperationName() { + return OPERATION_NAME; + } + +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Datastore.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Datastore.java new file mode 100644 index 0000000000..0f86c5a1d1 --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Datastore.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops; + +public enum Datastore { + candidate, running +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/DiscardChanges.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/DiscardChanges.java new file mode 100644 index 0000000000..36f6d8e3cf --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/DiscardChanges.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops; + +import com.google.common.base.Optional; +import java.util.HashMap; +import java.util.Map; +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.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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class DiscardChanges extends AbstractLastNetconfOperation{ + + private static final Logger LOG = LoggerFactory.getLogger(DiscardChanges.class); + + private static final String OPERATION_NAME = "discard-changes"; + + private final TransactionProvider transactionProvider; + + public DiscardChanges(final String netconfSessionIdForReporting, final TransactionProvider transactionProvider) { + super(netconfSessionIdForReporting); + this.transactionProvider = transactionProvider; + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + operationElement.getOnlyChildElement(OPERATION_NAME); + + try { + transactionProvider.abortTransaction(); + } catch (IllegalStateException e) { + LOG.warn("Abort failed ", e); + final Map errorInfo = new HashMap<>(); + errorInfo + .put(ErrorTag.operation_failed.name(), + "Operation failed. Use 'get-config' or 'edit-config' before triggering 'discard-changes' operation"); + throw new NetconfDocumentedException(e.getMessage(), e, ErrorType.application, ErrorTag.operation_failed, + ErrorSeverity.error, errorInfo); + } + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + @Override + protected String getOperationName() { + return OPERATION_NAME; + } +} 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 new file mode 100644 index 0000000000..09be4163df --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/EditConfig.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +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 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.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; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class EditConfig extends AbstractLastNetconfOperation { + + 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 final CurrentSchemaContext schemaContext; + private final TransactionProvider transactionProvider; + + public EditConfig(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final TransactionProvider transactionProvider) { + super(netconfSessionIdForReporting); + this.schemaContext = schemaContext; + this.transactionProvider = transactionProvider; + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + final XmlElement configElement = getConfigElement(operationElement); + + 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); + } + } + } + + 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); + } + } 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); + } + + //we can return null here since DataOperations.modify handles null as input + return null; + } + + private Optional getSchemaNodeFromNamespace(final String namespace, final XmlElement element){ + 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())); + } catch (URISyntaxException e) { + LOG.debug("Unable to create URI for namespace : {}", namespace); + } + + 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(); + } + + 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 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, + ErrorSeverity.error); + } + + return configChildNode.get(); + } + + @Override + protected String getOperationName() { + return OPERATION_NAME; + } +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Lock.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Lock.java new file mode 100644 index 0000000000..db912c5fc0 --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Lock.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.util.exception.MissingNameSpaceException; +import org.opendaylight.controller.netconf.util.exception.UnexpectedNamespaceException; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class Lock extends AbstractLastNetconfOperation{ + + private static final Logger LOG = LoggerFactory.getLogger(Lock.class); + + private static final String OPERATION_NAME = "lock"; + private static final String TARGET_KEY = "target"; + + public Lock(final String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + final Datastore targetDatastore = extractTargetParameter(operationElement); + if (targetDatastore == Datastore.candidate) { + LOG.debug("Locking candidate datastore on session: {}", getNetconfSessionIdForReporting()); + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + throw new NetconfDocumentedException("Unable to lock " + targetDatastore + " datastore", NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error); + } + + static 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()); + } + + @Override + protected String getOperationName() { + return OPERATION_NAME; + } +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Unlock.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Unlock.java new file mode 100644 index 0000000000..2dd26633dd --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/Unlock.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops; + +import com.google.common.base.Optional; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class Unlock extends AbstractLastNetconfOperation{ + + private static final Logger LOG = LoggerFactory.getLogger(Unlock.class); + + private static final String OPERATION_NAME = "unlock"; + private static final String TARGET_KEY = "target"; + + public Unlock(final String netconfSessionIdForReporting) { + super(netconfSessionIdForReporting); + } + + @Override + protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException { + final Datastore targetDatastore = Lock.extractTargetParameter(operationElement); + if (targetDatastore == Datastore.candidate) { + LOG.debug("Unlocking candidate datastore on session: {}", getNetconfSessionIdForReporting()); + return XmlUtil.createElement(document, XmlNetconfConstants.OK, Optional.absent()); + } + + throw new NetconfDocumentedException("Unable to unlock " + targetDatastore + " datastore", NetconfDocumentedException.ErrorType.application, + NetconfDocumentedException.ErrorTag.operation_not_supported, NetconfDocumentedException.ErrorSeverity.error); + } + + @Override + protected String getOperationName() { + return OPERATION_NAME; + } + +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java new file mode 100644 index 0000000000..e0c004461c --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/AbstractGet.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops.get; + +import com.google.common.base.Function; +import com.google.common.base.Throwables; +import com.google.common.collect.Iterables; +import java.io.IOException; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.dom.DOMResult; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.mdsal.connector.CurrentSchemaContext; +import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore; +import org.opendaylight.controller.netconf.util.mapping.AbstractLastNetconfOperation; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +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.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +public abstract class AbstractGet extends AbstractLastNetconfOperation { + + protected static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build(); + + protected final CurrentSchemaContext schemaContext; + + + public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) { + super(netconfSessionIdForReporting); + this.schemaContext = schemaContext; + } + + private static final XMLOutputFactory XML_OUTPUT_FACTORY; + + static { + XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory(); + XML_OUTPUT_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, true); + } + + protected Node transformNormalizedNode(final Document document, final NormalizedNode data, final YangInstanceIdentifier dataRoot) { +// boolean isDataRoot = true; + + final DOMResult result = new DOMResult(document.createElement(XmlNetconfConstants.DATA_KEY)); + + final XMLStreamWriter xmlWriter = getXmlStreamWriter(result); + + final NormalizedNodeStreamWriter nnStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(xmlWriter, + schemaContext.getCurrentContext(), getSchemaPath(dataRoot)); + + final NormalizedNodeWriter nnWriter = NormalizedNodeWriter.forStreamWriter(nnStreamWriter); + +// if (isDataRoot) { + writeRootElement(xmlWriter, nnWriter, (ContainerNode) data); +// } else { +// if (data instanceof MapEntryNode) { +// // Restconf allows returning one list item. We need to wrap it +// // in map node in order to serialize it properly +// data = ImmutableNodes.mapNodeBuilder(data.getNodeType()).addChild((MapEntryNode) data).build(); +// } +// nnWriter.write(data); +// nnWriter.flush(); +// } + return result.getNode(); + } + + private XMLStreamWriter getXmlStreamWriter(final DOMResult result) { + try { + return XML_OUTPUT_FACTORY.createXMLStreamWriter(result); + } catch (final XMLStreamException e) { + throw new RuntimeException(e); + } + } + + private static final Function PATH_ARG_TO_QNAME = new Function() { + @Override + public QName apply(final YangInstanceIdentifier.PathArgument input) { + return input.getNodeType(); + } + }; + + private SchemaPath getSchemaPath(final YangInstanceIdentifier dataRoot) { + return SchemaPath.create(Iterables.transform(dataRoot.getPathArguments(), PATH_ARG_TO_QNAME), dataRoot.equals(ROOT)); + } + + // TODO this code is located in Restconf already + private void writeRootElement(final XMLStreamWriter xmlWriter, final NormalizedNodeWriter nnWriter, final ContainerNode data) { + try { + final QName name = SchemaContext.NAME; + for (final DataContainerChild child : data.getValue()) { + nnWriter.write(child); + } + nnWriter.flush(); + xmlWriter.flush(); + } catch (XMLStreamException | IOException e) { + Throwables.propagate(e); + } + } + + protected static final class GetConfigExecution { + private final Datastore datastore; + + public GetConfigExecution(final Datastore datastore) { + this.datastore = datastore; + } + + public Datastore getDatastore() { + return datastore; + } + + static GetConfigExecution fromXml(final XmlElement xml, final String operationName) throws NetconfDocumentedException { + try { + validateInputRpc(xml, operationName); + } catch (final NetconfDocumentedException e) { + throw new NetconfDocumentedException("Incorrect RPC: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo()); + } + + final Datastore sourceDatastore; + try { + sourceDatastore = parseSource(xml); + } catch (final NetconfDocumentedException e) { + throw new NetconfDocumentedException("Get-config source attribute error: " + e.getMessage(), e.getErrorType(), e.getErrorTag(), e.getErrorSeverity(), e.getErrorInfo()); + } + + // Add filter + + return new GetConfigExecution(sourceDatastore); + } + + private static Datastore parseSource(final XmlElement xml) throws NetconfDocumentedException { + final Datastore sourceDatastore; + final XmlElement sourceElement = xml.getOnlyChildElement(XmlNetconfConstants.SOURCE_KEY, + XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + + final String sourceParsed = sourceElement.getOnlyChildElement().getName(); + sourceDatastore = Datastore.valueOf(sourceParsed); + return sourceDatastore; + } + + private static void validateInputRpc(final XmlElement xml, String operationName) throws NetconfDocumentedException{ + xml.checkName(operationName); + xml.checkNamespace(XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0); + } + } + +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java new file mode 100644 index 0000000000..a2b2fbb0db --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/Get.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops.get; + +import com.google.common.base.Optional; +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.mdsal.connector.CurrentSchemaContext; +import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider; +import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class Get extends AbstractGet { + + private static final Logger LOG = LoggerFactory.getLogger(Get.class); + + private static final String OPERATION_NAME = "get"; + + private final TransactionProvider transactionProvider; + + public Get(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final TransactionProvider transactionProvider) { + super(netconfSessionIdForReporting, schemaContext); + this.transactionProvider = transactionProvider; + } + + @Override + protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement) throws NetconfDocumentedException { + GetConfigExecution getConfigExecution = null; + try { + getConfigExecution = GetConfigExecution.fromXml(operationElement, OPERATION_NAME); + + } catch (final NetconfDocumentedException e) { + LOG.warn("Get request processing failed on session: {}", getNetconfSessionIdForReporting(), e); + throw e; + } + + final YangInstanceIdentifier dataRoot = ROOT; + DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore()); + try { + final Optional> normalizedNodeOptional = rwTx.read(LogicalDatastoreType.OPERATIONAL, dataRoot).checkedGet(); + if (getConfigExecution.getDatastore() == Datastore.running) { + transactionProvider.abortRunningTransaction(rwTx); + rwTx = null; + } + return (Element) transformNormalizedNode(document, normalizedNodeOptional.get(), dataRoot); + } catch (ReadFailedException e) { + LOG.warn("Unable to read data: {}", dataRoot, e); + throw new IllegalStateException("Unable to read data " + dataRoot, e); + } + } + + private DOMDataReadWriteTransaction getTransaction(Datastore datastore) throws NetconfDocumentedException{ + if (datastore == Datastore.candidate) { + return transactionProvider.getOrCreateTransaction(); + } else if (datastore == Datastore.running) { + return transactionProvider.createRunningTransaction(); + } + throw new NetconfDocumentedException("Incorrect Datastore: ", ErrorType.protocol, ErrorTag.bad_element, ErrorSeverity.error); + } + + @Override + protected String getOperationName() { + return OPERATION_NAME; + } +} diff --git a/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java new file mode 100644 index 0000000000..b56bcc795c --- /dev/null +++ b/opendaylight/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/controller/netconf/mdsal/connector/ops/get/GetConfig.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.mdsal.connector.ops.get; + +import com.google.common.base.Optional; +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.mdsal.connector.CurrentSchemaContext; +import org.opendaylight.controller.netconf.mdsal.connector.TransactionProvider; +import org.opendaylight.controller.netconf.mdsal.connector.ops.Datastore; +import org.opendaylight.controller.netconf.util.xml.XmlElement; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +public class GetConfig extends AbstractGet { + + private static final Logger LOG = LoggerFactory.getLogger(GetConfig.class); + + private static final String OPERATION_NAME = "get-config"; + + private final TransactionProvider transactionProvider; + + public GetConfig(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext, final TransactionProvider transactionProvider) { + super(netconfSessionIdForReporting, schemaContext); + this.transactionProvider = transactionProvider; + } + + @Override + protected Element handleWithNoSubsequentOperations(Document document, XmlElement operationElement) throws NetconfDocumentedException { + GetConfigExecution getConfigExecution = null; + try { + getConfigExecution = GetConfigExecution.fromXml(operationElement, OPERATION_NAME); + + } catch (final NetconfDocumentedException e) { + LOG.warn("Get request processing failed on session: {}", getNetconfSessionIdForReporting(), e); + throw e; + } + + final YangInstanceIdentifier dataRoot = ROOT; + DOMDataReadWriteTransaction rwTx = getTransaction(getConfigExecution.getDatastore()); + try { + final Optional> normalizedNodeOptional = rwTx.read(LogicalDatastoreType.CONFIGURATION, dataRoot).checkedGet(); + if (getConfigExecution.getDatastore() == Datastore.running) { + transactionProvider.abortRunningTransaction(rwTx); + rwTx = null; + } + return (Element) transformNormalizedNode(document, normalizedNodeOptional.get(), dataRoot); + } catch (ReadFailedException e) { + LOG.warn("Unable to read data: {}", dataRoot, e); + throw new IllegalStateException("Unable to read data " + dataRoot, e); + } + } + + private DOMDataReadWriteTransaction getTransaction(Datastore datastore) throws NetconfDocumentedException{ + if (datastore == Datastore.candidate) { + return transactionProvider.getOrCreateTransaction(); + } else if (datastore == Datastore.running) { + return transactionProvider.createRunningTransaction(); + } + throw new NetconfDocumentedException("Incorrect Datastore: ", ErrorType.protocol, ErrorTag.bad_element, ErrorSeverity.error); + } + + @Override + protected String getOperationName() { + return OPERATION_NAME; + } + +} diff --git a/opendaylight/netconf/netconf-impl/pom.xml b/opendaylight/netconf/netconf-impl/pom.xml index 310073fc72..c4a00aa4ee 100644 --- a/opendaylight/netconf/netconf-impl/pom.xml +++ b/opendaylight/netconf/netconf-impl/pom.xml @@ -110,6 +110,7 @@ org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator + * @@ -125,6 +126,41 @@ + + org.opendaylight.yangtools + yang-maven-plugin + + + config + + generate-sources + + + + + org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + ${jmxGeneratorPath} + + urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + ${salGeneratorPath} + + + true + + + + + + org.opendaylight.controller + yang-jmx-generator-plugin + ${config.version} + + + diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CommitNotifier.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CommitNotifier.java new file mode 100644 index 0000000000..d9f8e34da9 --- /dev/null +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/CommitNotifier.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.netconf.impl; + +import java.util.Set; +import org.w3c.dom.Element; + +public interface CommitNotifier { + void sendCommitNotification(String message, Element cfgSnapshot, Set capabilities); + + public static final class NoopCommitNotifier implements CommitNotifier { + + private static final CommitNotifier INSTANCE = new NoopCommitNotifier(); + + private NoopCommitNotifier() {} + + public static CommitNotifier getInstance() { + return INSTANCE; + } + + @Override + public void sendCommitNotification(final String message, final Element cfgSnapshot, final Set capabilities) { + // NOOP + } + } +} diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java index ab37bac683..88ff928c51 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/DefaultCommitNotificationProducer.java @@ -24,7 +24,7 @@ import org.slf4j.LoggerFactory; import org.w3c.dom.Element; public class DefaultCommitNotificationProducer extends NotificationBroadcasterSupport implements - DefaultCommitOperationMXBean, AutoCloseable { + DefaultCommitOperationMXBean, AutoCloseable, CommitNotifier { private static final Logger LOG = LoggerFactory.getLogger(DefaultCommitNotificationProducer.class); @@ -46,6 +46,7 @@ public class DefaultCommitNotificationProducer extends NotificationBroadcasterSu } } + @Override public void sendCommitNotification(String message, Element cfgSnapshot, Set capabilities) { CommitJMXNotification notif = NetconfJMXNotification.afterCommit(this, message, cfgSnapshot, capabilities); LOG.debug("Notification about commit {} sent", notif); diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java index 0537942487..604cc5f55b 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionListenerFactory.java @@ -17,12 +17,12 @@ import org.opendaylight.protocol.framework.SessionListenerFactory; public class NetconfServerSessionListenerFactory implements SessionListenerFactory { - private final DefaultCommitNotificationProducer commitNotifier; + private final CommitNotifier commitNotifier; private final SessionMonitoringService monitor; private final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot; private final CapabilityProvider capabilityProvider; - public NetconfServerSessionListenerFactory(final DefaultCommitNotificationProducer commitNotifier, + public NetconfServerSessionListenerFactory(final CommitNotifier commitNotifier, final SessionMonitoringService monitor, final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot, final CapabilityProvider capabilityProvider) { diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java index 34f4f0e653..451c066b77 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/NetconfServerSessionNegotiatorFactory.java @@ -44,7 +44,7 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF private final SessionIdProvider idProvider; private final NetconfOperationProvider netconfOperationProvider; private final long connectionTimeoutMillis; - private final DefaultCommitNotificationProducer commitNotificationProducer; + private final CommitNotifier commitNotificationProducer; private final SessionMonitoringService monitoringService; private static final Logger LOG = LoggerFactory.getLogger(NetconfServerSessionNegotiatorFactory.class); private final Set baseCapabilities; @@ -52,7 +52,7 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF // TODO too many params, refactor public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider, SessionIdProvider idProvider, long connectionTimeoutMillis, - DefaultCommitNotificationProducer commitNot, + CommitNotifier commitNot, SessionMonitoringService monitoringService) { this(timer, netconfOperationProvider, idProvider, connectionTimeoutMillis, commitNot, monitoringService, DEFAULT_BASE_CAPABILITIES); } @@ -60,7 +60,7 @@ public class NetconfServerSessionNegotiatorFactory implements SessionNegotiatorF // TODO too many params, refactor public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider, SessionIdProvider idProvider, long connectionTimeoutMillis, - DefaultCommitNotificationProducer commitNot, + CommitNotifier commitNot, SessionMonitoringService monitoringService, Set baseCapabilities) { this.timer = timer; this.netconfOperationProvider = netconfOperationProvider; diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java index fbe855f8be..742255f973 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/mapping/operations/DefaultCommit.java @@ -12,7 +12,7 @@ import com.google.common.base.Preconditions; import java.io.InputStream; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; -import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; +import org.opendaylight.controller.netconf.impl.CommitNotifier; import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouter; import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; @@ -31,11 +31,11 @@ public class DefaultCommit extends AbstractNetconfOperation { private static final String NOTIFY_ATTR = "notify"; - private final DefaultCommitNotificationProducer notificationProducer; + private final CommitNotifier notificationProducer; private final CapabilityProvider cap; private final NetconfOperationRouter operationRouter; - public DefaultCommit(DefaultCommitNotificationProducer notifier, CapabilityProvider cap, + public DefaultCommit(CommitNotifier notifier, CapabilityProvider cap, String netconfSessionIdForReporting, NetconfOperationRouter netconfOperationRouter) { super(netconfSessionIdForReporting); this.notificationProducer = notifier; diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java index 2178d4eedf..c8fa341747 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java @@ -17,7 +17,7 @@ import java.util.NavigableMap; import java.util.Set; import java.util.TreeMap; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; -import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; +import org.opendaylight.controller.netconf.impl.CommitNotifier; import org.opendaylight.controller.netconf.impl.NetconfServerSession; import org.opendaylight.controller.netconf.impl.mapping.CapabilityProvider; import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession; @@ -44,7 +44,7 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter { private final Collection allNetconfOperations; public NetconfOperationRouterImpl(final NetconfOperationServiceSnapshot netconfOperationServiceSnapshot, final CapabilityProvider capabilityProvider, - final DefaultCommitNotificationProducer commitNotifier) { + final CommitNotifier commitNotifier) { this.netconfOperationServiceSnapshot = Preconditions.checkNotNull(netconfOperationServiceSnapshot); final String sessionId = netconfOperationServiceSnapshot.getNetconfSessionIdForReporting(); diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index 653dd70b29..0f14239686 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -20,6 +20,7 @@ netconf-config netconf-impl config-netconf-connector + mdsal-netconf-connector netconf-util netconf-netty-util config-persister-impl -- 2.36.6