--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.3.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>mdsal-netconf-connector</artifactId>
+ <packaging>bundle</packaging>
+ <name>${project.artifactId}</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.logback_settings</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-operations</artifactId>
+ <version>0.7.0-SNAPSHOT</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Import-Package>*</Import-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <!--FIXME extract yang plugin definition into parent-->
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * 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<SchemaContext> currentContext = new AtomicReference<SchemaContext>();
+ private final ListenerRegistration<SchemaContextListener> 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
--- /dev/null
+/*
+ * 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<Capability> getCapabilities() {
+ final Set<Capability> 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<Module> 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<NetconfOperation> 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<String> getModuleNamespace() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return Optional.absent();
+ }
+
+ @Override
+ public Collection<String> 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<String> 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<String> getModuleName() {
+ return Optional.of(moduleName);
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(moduleNamespace);
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(revision);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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<NetconfOperation> getOperations() {
+ return Sets.<NetconfOperation>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)
+ );
+ }
+
+}
--- /dev/null
+/*
+ * 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<DOMDataReadWriteTransaction> 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<DOMDataReadWriteTransaction> 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<Void, TransactionCommitFailedException> 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<DOMDataReadWriteTransaction> 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<Void, TransactionCommitFailedException> 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);
+ }
+
+}
--- /dev/null
+/*
+ * 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.<String>absent());
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+
+}
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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<String, String> 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.<String>absent());
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+}
--- /dev/null
+/*
+ * 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<NormalizedNode<?,?>> 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.<String>absent());
+ }
+
+ private NormalizedNode readStoredNode(final LogicalDatastoreType logicalDatastoreType, final YangInstanceIdentifier path) throws NetconfDocumentedException{
+ final DOMDataReadWriteTransaction rwTx = transactionProvider.getOrCreateTransaction();
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, 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<DataSchemaNode> getSchemaNodeFromNamespace(final String namespace, final XmlElement element){
+ 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()));
+ } 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{
+ if (schemaNode instanceof ContainerSchemaNode) {
+ final ContainerNode modifiedNode =
+ DomToNormalizedNodeParserFactory
+ .getInstance(DomUtils.defaultValueCodecProvider())
+ .getContainerNodeParser()
+ .parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
+
+ final Optional<ContainerNode> oNode = DataOperations.modify((ContainerSchemaNode) schemaNode, (ContainerNode) storedNode, modifiedNode);
+ if (!oNode.isPresent()) {
+ return Optional.absent();
+ }
+
+ final NormalizedNode<?,?> node = oNode.get();
+ return Optional.<NormalizedNode<?,?>>of(node);
+ } else if (schemaNode instanceof ListSchemaNode) {
+ final MapNode modifiedNode =
+ DomToNormalizedNodeParserFactory
+ .getInstance(DomUtils.defaultValueCodecProvider())
+ .getMapNodeParser()
+ .parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
+
+ final Optional<MapNode> oNode = DataOperations.modify((ListSchemaNode) schemaNode, (MapNode) storedNode, modifiedNode);
+ if (!oNode.isPresent()) {
+ return Optional.absent();
+ }
+
+ final NormalizedNode<?, ?> node = oNode.get();
+ return Optional.<NormalizedNode<?,?>>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<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,
+ ErrorSeverity.error);
+ }
+
+ return configChildNode.get();
+ }
+
+ @Override
+ protected String getOperationName() {
+ return OPERATION_NAME;
+ }
+}
--- /dev/null
+/*
+ * 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.<String>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;
+ }
+}
--- /dev/null
+/*
+ * 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.<String>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;
+ }
+
+}
--- /dev/null
+/*
+ * 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<PathArgument, QName> PATH_ARG_TO_QNAME = new Function<YangInstanceIdentifier.PathArgument, QName>() {
+ @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<? extends PathArgument, ?> 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);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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<NormalizedNode<?, ?>> 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;
+ }
+}
--- /dev/null
+/*
+ * 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<NormalizedNode<?, ?>> 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;
+ }
+
+}
<configuration>
<instructions>
<Bundle-Activator>org.opendaylight.controller.netconf.impl.osgi.NetconfImplActivator</Bundle-Activator>
+ <Import-Package>*</Import-Package>
</instructions>
</configuration>
</plugin>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>config</id>
+ <goals>
+ <goal>generate-sources</goal>
+ </goals>
+ <configuration>
+ <codeGenerators>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
+ <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
+ <additionalConfiguration>
+ <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
+ </additionalConfiguration>
+ </generator>
+ <generator>
+ <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+ <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+ </generator>
+ </codeGenerators>
+ <inspectDependencies>true</inspectDependencies>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>yang-jmx-generator-plugin</artifactId>
+ <version>${config.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
</plugins>
</build>
--- /dev/null
+/*
+ * 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<String> 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<String> capabilities) {
+ // NOOP
+ }
+ }
+}
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);
}
}
+ @Override
public void sendCommitNotification(String message, Element cfgSnapshot, Set<String> capabilities) {
CommitJMXNotification notif = NetconfJMXNotification.afterCommit(this, message, cfgSnapshot, capabilities);
LOG.debug("Notification about commit {} sent", notif);
public class NetconfServerSessionListenerFactory implements SessionListenerFactory<NetconfServerSessionListener> {
- 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) {
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<String> baseCapabilities;
// 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);
}
// TODO too many params, refactor
public NetconfServerSessionNegotiatorFactory(Timer timer, NetconfOperationProvider netconfOperationProvider,
SessionIdProvider idProvider, long connectionTimeoutMillis,
- DefaultCommitNotificationProducer commitNot,
+ CommitNotifier commitNot,
SessionMonitoringService monitoringService, Set<String> baseCapabilities) {
this.timer = timer;
this.netconfOperationProvider = netconfOperationProvider;
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;
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;
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;
private final Collection<NetconfOperation> 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();
<module>netconf-config</module>
<module>netconf-impl</module>
<module>config-netconf-connector</module>
+ <module>mdsal-netconf-connector</module>
<module>netconf-util</module>
<module>netconf-netty-util</module>
<module>config-persister-impl</module>