Updated implementation of Netconf, fixed DOM Mountpoint 66/4266/2
authorTony Tkacik <ttkacik@cisco.com>
Wed, 15 Jan 2014 13:04:02 +0000 (14:04 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Wed, 15 Jan 2014 18:03:25 +0000 (19:03 +0100)
Change-Id: Ia0d4f92a65174e891a8111571ced24182d9f47c6
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
18 files changed:
opendaylight/md-sal/sal-common-util/pom.xml
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Arguments.java
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/Futures.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/RpcProvisionRegistry.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountInstance.java
opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/mount/MountProvisionService.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/$ModuleInfo.java [new file with mode: 0644]
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/DataBrokerImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointImpl.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointManagerImpl.xtend
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/osgi/MountProviderServiceProxy.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java [deleted file]

index ff15e72..adbe3d2 100644 (file)
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    
   </dependencies>
 
   <packaging>bundle</packaging>
index 2d10fba..902665d 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * 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.sal.common.util;
 
 public class Arguments {
diff --git a/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java b/opendaylight/md-sal/sal-common-util/src/main/java/org/opendaylight/controller/sal/common/util/CommitHandlerTransactions.java
new file mode 100644 (file)
index 0000000..bffeb59
--- /dev/null
@@ -0,0 +1,38 @@
+package org.opendaylight.controller.sal.common.util;
+
+import java.util.Collections;
+
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.yangtools.concepts.Path;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+
+public class CommitHandlerTransactions {
+
+    private static class AllwaysSuccessfulTransaction<P extends Path<P>,D> implements DataCommitTransaction<P, D> {
+        
+        private final  DataModification<P, D> modification;
+
+        public AllwaysSuccessfulTransaction(DataModification<P, D> modification) {
+            this.modification = modification;
+        }
+        @Override
+        public RpcResult<Void> rollback() throws IllegalStateException {
+            return Rpcs.<Void>getRpcResult(true, null, Collections.<RpcError>emptyList());
+        }
+        @Override
+        public RpcResult<Void> finish() throws IllegalStateException {
+            return Rpcs.<Void>getRpcResult(true, null, Collections.<RpcError>emptyList());
+        }
+        
+        @Override
+        public DataModification<P, D> getModification() {
+            return modification;
+        }
+    }
+    
+    public static final <P extends Path<P>,D> AllwaysSuccessfulTransaction<P, D> allwaysSuccessfulTransaction(DataModification<P, D> modification)  {
+        return new AllwaysSuccessfulTransaction<>(modification);
+    }
+}
index d9cf5cc..42b00ba 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * 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.sal.common.util;
 
 import java.util.concurrent.ExecutionException;
index c326bab..951d5b1 100644 (file)
@@ -4,7 +4,7 @@ import org.opendaylight.controller.sal.core.api.Broker.RoutedRpcRegistration;
 import org.opendaylight.controller.sal.core.api.Broker.RpcRegistration;
 import org.opendaylight.yangtools.yang.common.QName;
 
-public interface RpcProvisionRegistry {
+public interface RpcProvisionRegistry extends BrokerService {
 
     /**
      * Registers an implementation of the rpc.
index 910c7cb..18c8546 100644 (file)
@@ -6,7 +6,6 @@
  * and is available at http://www.eclipse.org/legal/epl-v10.html
  */
 
-
 package org.opendaylight.controller.sal.core.api.mount;
 
 import java.util.concurrent.Future;
@@ -18,9 +17,11 @@ import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
-public interface MountInstance extends NotificationService, DataBrokerService {
+public interface MountInstance extends //
+        NotificationService, //
+        DataBrokerService {
 
     Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
-    
+
     SchemaContext getSchemaContext();
 }
index fade7d3..c1f873c 100644 (file)
@@ -1,5 +1,8 @@
 package org.opendaylight.controller.sal.core.api.mount;
 
+import java.util.EventListener;
+
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 
 public interface MountProvisionService extends MountService {
@@ -10,4 +13,14 @@ public interface MountProvisionService extends MountService {
     MountProvisionInstance createMountPoint(InstanceIdentifier path);
     
     MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path);
+    
+    ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener);
+    
+    public  interface MountProvisionListener extends EventListener {
+        
+        void onMountPointCreated(InstanceIdentifier path);
+        
+        void onMountPointRemoved(InstanceIdentifier path);
+        
+    }
 }
diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/$ModuleInfo.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/$ModuleInfo.java
new file mode 100644 (file)
index 0000000..3cc5a61
--- /dev/null
@@ -0,0 +1,6 @@
+package org.opendaylight.controller.sal.dom.broker;
+
+public class $ModuleInfo {
+
+    
+}
index 56eae97..8f62be9 100644 (file)
@@ -13,6 +13,8 @@ import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 
+import com.google.common.util.concurrent.MoreExecutors;
+
 public class DataBrokerImpl extends AbstractDataBroker<InstanceIdentifier, CompositeNode, DataChangeListener> implements
         DataProviderService, AutoCloseable {
 
@@ -21,6 +23,7 @@ public class DataBrokerImpl extends AbstractDataBroker<InstanceIdentifier, Compo
     
     public DataBrokerImpl() {
         setDataReadRouter(new DataReaderRouter());
+        setExecutor(MoreExecutors.sameThreadExecutor());
     }
     
     public AtomicLong getCreatedTransactionsCount() {
index ab5b145..b4fccff 100644 (file)
@@ -36,7 +36,7 @@ import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 public class MountPointImpl implements MountProvisionInstance {
 
     private final RpcRouter rpcs;
-    private final DataReaderRouter dataReader;
+    private final DataBrokerImpl dataReader;
     private final NotificationRouter notificationRouter;
     private final DataReader<InstanceIdentifier,CompositeNode> readWrapper;
     
@@ -48,7 +48,7 @@ public class MountPointImpl implements MountProvisionInstance {
     public MountPointImpl(InstanceIdentifier path) {
         this.mountPath = path;
         rpcs = new RpcRouterImpl("");
-        dataReader = new DataReaderRouter();
+        dataReader = new DataBrokerImpl();
         notificationRouter = new NotificationRouterImpl();
         readWrapper = new ReadWrapper();
     }
@@ -124,15 +124,13 @@ public class MountPointImpl implements MountProvisionInstance {
 
     @Override
     public DataModificationTransaction beginTransaction() {
-        // TODO Auto-generated method stub
-        return null;
+        return dataReader.beginTransaction();
     }
 
     @Override
     public ListenerRegistration<DataChangeListener> registerDataChangeListener(InstanceIdentifier path,
             DataChangeListener listener) {
-        // TODO Auto-generated method stub
-        return null;
+        return dataReader.registerDataChangeListener(path, listener);
     }
 
     @Override
@@ -143,8 +141,7 @@ public class MountPointImpl implements MountProvisionInstance {
     @Override
     public Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> registerCommitHandler(
             InstanceIdentifier path, DataCommitHandler<InstanceIdentifier, CompositeNode> commitHandler) {
-        // TODO Auto-generated method stub
-        return null;
+        return dataReader.registerCommitHandler(path, commitHandler);
     }
     
     @Override
@@ -208,7 +205,6 @@ public class MountPointImpl implements MountProvisionInstance {
     @Override
     public ListenerRegistration<RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>>> registerCommitHandlerListener(
             RegistrationListener<DataCommitHandlerRegistration<InstanceIdentifier, CompositeNode>> commitHandlerListener) {
-        // TODO Auto-generated method stub
-        return null;
+        return dataReader.registerCommitHandlerListener(commitHandlerListener);
     }
 }
index 19634d7..5d441bd 100644 (file)
@@ -7,12 +7,16 @@ import java.util.concurrent.ConcurrentMap
 import java.util.concurrent.ConcurrentHashMap
 import static com.google.common.base.Preconditions.*;
 import org.opendaylight.controller.sal.core.api.data.DataProviderService
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener
+import org.opendaylight.yangtools.concepts.util.ListenerRegistry
 
 class MountPointManagerImpl implements MountProvisionService {
     
     @Property
     DataProviderService dataBroker;
     
+    val ListenerRegistry<MountProvisionListener> listeners = ListenerRegistry.create()
+    
     ConcurrentMap<InstanceIdentifier,MountPointImpl> mounts = new ConcurrentHashMap();
     
     override createMountPoint(InstanceIdentifier path) {
@@ -20,15 +24,26 @@ class MountPointManagerImpl implements MountProvisionService {
         val mount = new MountPointImpl(path);
         registerMountPoint(mount);
         mounts.put(path,mount);
+        notifyMountCreated(path);
         return mount;
     }
     
+    def notifyMountCreated(InstanceIdentifier identifier) {
+        for(listener : listeners) {
+            listener.instance.onMountPointCreated(identifier);
+        }
+    }
+    
     def registerMountPoint(MountPointImpl impl) {
         dataBroker?.registerConfigurationReader(impl.mountPath,impl.readWrapper);
         dataBroker?.registerOperationalReader(impl.mountPath,impl.readWrapper);
         
     }
     
+    override registerProvisionListener(MountProvisionListener listener) {
+        listeners.register(listener)
+    }
+    
     
     override createOrGetMountPoint(InstanceIdentifier path) {
         val mount = mounts.get(path);
index 0d18cb3..0021dd8 100644 (file)
@@ -3,6 +3,7 @@ package org.opendaylight.controller.sal.dom.broker.osgi;
 import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
 import org.opendaylight.controller.sal.core.api.mount.MountProvisionService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.osgi.framework.ServiceReference;
 
@@ -24,4 +25,9 @@ public class MountProviderServiceProxy extends AbstractBrokerServiceProxy<MountP
     public MountProvisionInstance createOrGetMountPoint(InstanceIdentifier path) {
         return getDelegate().createOrGetMountPoint(path);
     }
+    
+    @Override
+    public ListenerRegistration<MountProvisionListener> registerProvisionListener(MountProvisionListener listener) {
+        return getDelegate().registerProvisionListener(listener);
+    }
 }
index bfe352a..da0790c 100644 (file)
@@ -1,61 +1,55 @@
 package org.opendaylight.controller.sal.connect.netconf
 
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance
-import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
-import org.opendaylight.controller.md.sal.common.api.data.DataReader
-import org.opendaylight.yangtools.yang.data.api.CompositeNode
-import org.opendaylight.controller.netconf.client.NetconfClient
-import org.opendaylight.controller.sal.core.api.RpcImplementation
-import static extension org.opendaylight.controller.sal.connect.netconf.NetconfMapping.*
+import com.google.common.base.Optional
+import com.google.common.collect.FluentIterable
+import io.netty.util.concurrent.EventExecutor
+import java.io.InputStream
 import java.net.InetSocketAddress
-import org.opendaylight.yangtools.yang.data.api.Node
-import org.opendaylight.yangtools.yang.data.api.SimpleNode
-import org.opendaylight.yangtools.yang.common.QName
+import java.net.URI
 import java.util.Collections
+import java.util.List
+import java.util.Set
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Future
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler
+import org.opendaylight.controller.md.sal.common.api.data.DataModification
+import org.opendaylight.controller.md.sal.common.api.data.DataReader
+import org.opendaylight.controller.netconf.api.NetconfMessage
+import org.opendaylight.controller.netconf.client.NetconfClient
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcher
-import org.opendaylight.yangtools.concepts.Registration
-import org.opendaylight.controller.sal.core.api.Provider
 import org.opendaylight.controller.sal.core.api.Broker.ProviderSession
-import org.opendaylight.controller.sal.core.api.mount.MountProvisionService
-import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.*;
+import org.opendaylight.controller.sal.core.api.Provider
+import org.opendaylight.controller.sal.core.api.RpcImplementation
 import org.opendaylight.controller.sal.core.api.data.DataBrokerService
 import org.opendaylight.controller.sal.core.api.data.DataModificationTransaction
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionService
 import org.opendaylight.protocol.framework.ReconnectStrategy
-import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler
-import org.opendaylight.controller.md.sal.common.api.data.DataModification
-import com.google.common.collect.FluentIterable
-import org.opendaylight.yangtools.yang.model.api.SchemaContext
-import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState
+import org.opendaylight.yangtools.concepts.Registration
+import org.opendaylight.yangtools.yang.common.QName
+import org.opendaylight.yangtools.yang.data.api.CompositeNode
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+import org.opendaylight.yangtools.yang.data.api.Node
+import org.opendaylight.yangtools.yang.data.api.SimpleNode
+import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
+import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl
+import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders
+import org.opendaylight.yangtools.yang.model.util.repo.SourceIdentifier
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl
-import java.io.InputStream
-import org.slf4j.LoggerFactory
+import org.opendaylight.yangtools.yang.parser.impl.util.YangSourceContext
 import org.slf4j.Logger
-import org.opendaylight.controller.netconf.client.AbstractNetconfClientNotifySessionListener
-import org.opendaylight.controller.netconf.client.NetconfClientSession
-import org.opendaylight.controller.netconf.api.NetconfMessage
-import io.netty.util.concurrent.EventExecutor
+import org.slf4j.LoggerFactory
 
-import java.util.Map
-import java.util.Set
-import com.google.common.collect.ImmutableMap
+import static com.google.common.base.Preconditions.*
+import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.*
 
-import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider
-import com.google.common.base.Optional
-import com.google.common.collect.ImmutableList
-import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders
-import static com.google.common.base.Preconditions.*;
-import java.util.concurrent.ExecutorService
-import java.util.concurrent.Future
-import org.opendaylight.controller.netconf.client.NetconfClientSessionListener
-import io.netty.util.concurrent.Promise
-import org.opendaylight.controller.netconf.util.xml.XmlElement
-import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants
-import java.util.concurrent.ExecutionException
-import java.util.concurrent.locks.ReentrantLock
+import static extension org.opendaylight.controller.sal.connect.netconf.NetconfMapping.*
+import org.opendaylight.controller.netconf.util.xml.XmlUtil
 
 class NetconfDevice implements Provider, // 
 DataReader<InstanceIdentifier, CompositeNode>, //
@@ -86,7 +80,8 @@ AutoCloseable {
     @Property
     var AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider;
 
-    private NetconfDeviceSchemaContextProvider schemaContextProvider
+    @Property
+    private NetconfDeviceSchemaContextProvider deviceContextProvider
 
     protected val Logger logger
 
@@ -105,9 +100,12 @@ AutoCloseable {
 
     @Property
     var NetconfClientDispatcher dispatcher
-    
+
     static val InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance();
 
+    @Property
+    var SchemaSourceProvider<InputStream> remoteSourceProvider
+
     public new(String name) {
         this.name = name;
         this.logger = LoggerFactory.getLogger(NetconfDevice.name + "#" + name);
@@ -120,11 +118,12 @@ AutoCloseable {
         checkState(schemaSourceProvider != null, "Schema Source Provider must be set.")
         checkState(eventExecutor != null, "Event executor must be set.");
 
-        val listener = new NetconfDeviceListener(this,eventExecutor);
+        val listener = new NetconfDeviceListener(this, eventExecutor);
         val task = startClientTask(dispatcher, listener)
-        if(mountInstance != null) {
+        if (mountInstance != null) {
             confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, this);
             operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, this);
+            commitHandlerReg = mountInstance.registerCommitHandler(ROOT_PATH, this)
         }
         return processingExecutor.submit(task) as Future<Void>;
 
@@ -132,27 +131,28 @@ AutoCloseable {
     }
 
     def Optional<SchemaContext> getSchemaContext() {
-        if (schemaContextProvider == null) {
+        if (deviceContextProvider == null) {
             return Optional.absent();
         }
-        return schemaContextProvider.currentContext;
+        return deviceContextProvider.currentContext;
     }
 
     private def Runnable startClientTask(NetconfClientDispatcher dispatcher, NetconfDeviceListener listener) {
+
         return [ |
             logger.info("Starting Netconf Client on: {}", socketAddress);
             client = NetconfClient.clientFor(name, socketAddress, reconnectStrategy, dispatcher, listener);
             logger.debug("Initial capabilities {}", initialCapabilities);
             var SchemaSourceProvider<String> delegate;
-            if (initialCapabilities.contains(NetconfMapping.IETF_NETCONF_MONITORING_MODULE)) {
-                delegate = new NetconfDeviceSchemaSourceProvider(this);
-            } else {
-                logger.info("Device does not support IETF Netconf Monitoring.", socketAddress);
+            if (NetconfRemoteSchemaSourceProvider.isSupportedFor(initialCapabilities)) {
+                delegate = new NetconfRemoteSchemaSourceProvider(this);
+            }  else {
+                logger.info("Netconf server {} does not support IETF Netconf Monitoring", socketAddress);
                 delegate = SchemaSourceProviders.<String>noopProvider();
             }
-            val sourceProvider = schemaSourceProvider.createInstanceFor(delegate);
-            schemaContextProvider = new NetconfDeviceSchemaContextProvider(this, sourceProvider);
-            schemaContextProvider.createContextFromCapabilities(initialCapabilities);
+            remoteSourceProvider = schemaSourceProvider.createInstanceFor(delegate);
+            deviceContextProvider = new NetconfDeviceSchemaContextProvider(this, remoteSourceProvider);
+            deviceContextProvider.createContextFromCapabilities(initialCapabilities);
             if (mountInstance != null && schemaContext.isPresent) {
                 mountInstance.schemaContext = schemaContext.get();
             }
@@ -175,18 +175,31 @@ AutoCloseable {
     override getSupportedRpcs() {
         Collections.emptySet;
     }
-    
+
     def createSubscription(String streamName) {
         val it = ImmutableCompositeNode.builder()
         QName = NETCONF_CREATE_SUBSCRIPTION_QNAME
-        addLeaf("stream",streamName);
-        invokeRpc(QName,toInstance())
+        addLeaf("stream", streamName);
+        invokeRpc(QName, toInstance())
     }
 
     override invokeRpc(QName rpc, CompositeNode input) {
-        val message = rpc.toRpcMessage(input);
-        val result = client.sendMessage(message, messegeRetryCount, messageTimeoutCount);
-        return result.toRpcResult();
+        try {
+            val message = rpc.toRpcMessage(input,schemaContext);
+            val result = sendMessageImpl(message, messegeRetryCount, messageTimeoutCount);
+            return result.toRpcResult(rpc, schemaContext);
+
+        } catch (Exception e) {
+            logger.error("Rpc was not processed correctly.", e)
+            throw e;
+        }
+    }
+
+    def NetconfMessage sendMessageImpl(NetconfMessage message, int retryCount, int timeout) {
+        logger.debug("Send message {}",XmlUtil.toString(message.document))
+        val result = client.sendMessage(message, retryCount, timeout);
+        NetconfMapping.checkValidReply(message, result)
+        return result;
     }
 
     override getProviderFunctionality() {
@@ -221,7 +234,7 @@ AutoCloseable {
         return null === transaction.readOperationalData(path);
     }
 
-    def Node<?> findNode(CompositeNode node, InstanceIdentifier identifier) {
+    static def Node<?> findNode(CompositeNode node, InstanceIdentifier identifier) {
 
         var Node<?> current = node;
         for (arg : identifier.path) {
@@ -229,12 +242,17 @@ AutoCloseable {
                 return null;
             } else if (current instanceof CompositeNode) {
                 val currentComposite = (current as CompositeNode);
-
-                current = currentComposite.getFirstCompositeByName(arg.nodeType.withoutRevision());
-                if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.nodeType.withoutRevision());
+                
+                current = currentComposite.getFirstCompositeByName(arg.nodeType);
+                if(current == null) {
+                    current = currentComposite.getFirstCompositeByName(arg.nodeType.withoutRevision());
+                }
+                if(current == null) {
+                    current = currentComposite.getFirstSimpleByName(arg.nodeType);
                 }
                 if (current == null) {
+                    current = currentComposite.getFirstSimpleByName(arg.nodeType.withoutRevision());
+                } if (current == null) {
                     return null;
                 }
             }
@@ -243,7 +261,9 @@ AutoCloseable {
     }
 
     override requestCommit(DataModification<InstanceIdentifier, CompositeNode> modification) {
-        throw new UnsupportedOperationException("TODO: auto-generated method stub")
+        val twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this, modification);
+        twoPhaseCommit.prepare()
+        return twoPhaseCommit;
     }
 
     def getInitialCapabilities() {
@@ -257,8 +277,18 @@ AutoCloseable {
                 val parts = split("\\?");
                 val namespace = parts.get(0);
                 val queryParams = FluentIterable.from(parts.get(1).split("&"));
-                val revision = queryParams.findFirst[startsWith("revision=")].replaceAll("revision=", "");
-                val moduleName = queryParams.findFirst[startsWith("module=")].replaceAll("module=", "");
+                var revision = queryParams.findFirst[startsWith("revision=")]?.replaceAll("revision=", "");
+                val moduleName = queryParams.findFirst[startsWith("module=")]?.replaceAll("module=", "");
+                if (revision === null) {
+                    logger.warn("Netconf device was not reporting revision correctly, trying to get amp;revision=");
+                    revision = queryParams.findFirst[startsWith("&amp;revision=")]?.replaceAll("revision=", "");
+                    if (revision != null) {
+                        logger.warn("Netconf device returned revision incorectly escaped for {}", it)
+                    }
+                }
+                if (revision == null) {
+                    return QName.create(URI.create(namespace), null, moduleName);
+                }
                 return QName.create(namespace, revision, moduleName);
             ].toSet();
         }
@@ -273,96 +303,6 @@ AutoCloseable {
 
 }
 
-package class NetconfDeviceListener extends NetconfClientSessionListener {
-
-    val NetconfDevice device
-    val EventExecutor eventExecutor
-
-    new(NetconfDevice device,EventExecutor eventExecutor) {
-        this.device = device
-        this.eventExecutor = eventExecutor
-    }
-
-    var Promise<NetconfMessage> messagePromise;
-    val promiseLock = new ReentrantLock;
-    
-    override onMessage(NetconfClientSession session, NetconfMessage message) {
-        if (isNotification(message)) {
-            onNotification(session, message);
-        } else try {
-            promiseLock.lock
-            if (messagePromise != null) {
-                messagePromise.setSuccess(message);
-                messagePromise = null;
-            }
-        } finally {
-            promiseLock.unlock
-        }
-    }
-
-    /**
-     * Method intended to customize notification processing.
-     * 
-     * @param session
-     *            {@see
-     *            NetconfClientSessionListener#onMessage(NetconfClientSession,
-     *            NetconfMessage)}
-     * @param message
-     *            {@see
-     *            NetconfClientSessionListener#onMessage(NetconfClientSession,
-     *            NetconfMessage)}
-     */
-    def void onNotification(NetconfClientSession session, NetconfMessage message) {
-        device.logger.debug("Received NETCONF notification.",message);
-        val domNotification = message?.toCompositeNode?.notificationBody;
-        if(domNotification != null) {
-            device?.mountInstance?.publish(domNotification);
-        }
-    }
-    
-    private static def CompositeNode getNotificationBody(CompositeNode node) {
-        for(child : node.children) {
-            if(child instanceof CompositeNode) {
-                return child as CompositeNode;
-            }
-        }
-    }
-
-    override getLastMessage(int attempts, int attemptMsDelay) throws InterruptedException {
-        val promise = promiseReply();
-        val messageAvailable = promise.await(attempts + attemptMsDelay);
-        if (messageAvailable) {
-            try {
-                return promise.get();
-            } catch (ExecutionException e) {
-                throw new IllegalStateException(e);
-            }
-        }
-
-        throw new IllegalStateException("Unsuccessful after " + attempts + " attempts.");
-
-    // throw new TimeoutException("Message was not received on time.");
-    }
-
-    def Promise<NetconfMessage> promiseReply() {
-        promiseLock.lock
-        try {
-        if (messagePromise == null) {
-            messagePromise = eventExecutor.newPromise();
-            return messagePromise;
-        }
-        return messagePromise;
-        } finally {
-            promiseLock.unlock
-        }
-    }
-
-    def boolean isNotification(NetconfMessage message) {
-        val xmle = XmlElement.fromDomDocument(message.getDocument());
-        return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(xmle.getName());
-    }
-}
-
 package class NetconfDeviceSchemaContextProvider {
 
     @Property
@@ -380,22 +320,29 @@ package class NetconfDeviceSchemaContextProvider {
     }
 
     def createContextFromCapabilities(Iterable<QName> capabilities) {
-
-        val modelsToParse = ImmutableMap.<QName, InputStream>builder();
-        for (cap : capabilities) {
-            val source = sourceProvider.getSchemaSource(cap.localName, Optional.fromNullable(cap.formattedRevision));
-            if (source.present) {
-                modelsToParse.put(cap, source.get());
-            }
+        val sourceContext = YangSourceContext.createFrom(capabilities, sourceProvider)
+        if (!sourceContext.missingSources.empty) {
+            device.logger.warn("Sources for following models are missing {}", sourceContext.missingSources);
+        }
+        device.logger.debug("Trying to create schema context from {}", sourceContext.validSources)
+        val modelsToParse = YangSourceContext.getValidInputStreams(sourceContext);
+        if (!sourceContext.validSources.empty) {
+            val schemaContext = tryToCreateContext(modelsToParse);
+            currentContext = Optional.fromNullable(schemaContext);
+        } else {
+            currentContext = Optional.absent();
         }
-        val context = tryToCreateContext(modelsToParse.build);
-        currentContext = Optional.fromNullable(context);
+        if (currentContext.present) {
+            device.logger.debug("Schema context successfully created.");
+        }
+
     }
 
-    def SchemaContext tryToCreateContext(Map<QName, InputStream> modelsToParse) {
+    def SchemaContext tryToCreateContext(List<InputStream> modelsToParse) {
         val parser = new YangParserImpl();
         try {
-            val models = parser.parseYangModelsFromStreams(ImmutableList.copyOf(modelsToParse.values));
+
+            val models = parser.parseYangModelsFromStreams(modelsToParse);
             val result = parser.resolveSchemaContext(models);
             return result;
         } catch (Exception e) {
@@ -404,33 +351,3 @@ package class NetconfDeviceSchemaContextProvider {
         }
     }
 }
-
-package class NetconfDeviceSchemaSourceProvider implements SchemaSourceProvider<String> {
-
-    val NetconfDevice device;
-
-    new(NetconfDevice device) {
-        this.device = device;
-    }
-
-    override getSchemaSource(String moduleName, Optional<String> revision) {
-        val it = ImmutableCompositeNode.builder() //
-        setQName(QName::create(NetconfState.QNAME, "get-schema")) //
-        addLeaf("format", "yang")
-        addLeaf("identifier", moduleName)
-        if (revision.present) {
-            addLeaf("version", revision.get())
-        }
-
-        device.logger.info("Loading YANG schema source for {}:{}", moduleName, revision)
-        val schemaReply = device.invokeRpc(getQName(), toInstance());
-
-        if (schemaReply.successful) {
-            val schemaBody = schemaReply.result.getFirstSimpleByName(
-                QName::create(NetconfState.QNAME.namespace, null, "data"))?.value;
-            device.logger.info("YANG Schema successfully received for: {}:{}", moduleName, revision);
-            return Optional.of(schemaBody as String);
-        }
-        return Optional.absent();
-    }
-}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java
new file mode 100644 (file)
index 0000000..8623d90
--- /dev/null
@@ -0,0 +1,155 @@
+package org.opendaylight.controller.sal.connect.netconf;
+
+import com.google.common.base.Objects;
+
+import io.netty.util.concurrent.EventExecutor;
+import io.netty.util.concurrent.Promise;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.eclipse.xtext.xbase.lib.Exceptions;
+import org.eclipse.xtext.xbase.lib.Functions.Function0;
+import org.opendaylight.controller.netconf.api.NetconfMessage;
+import org.opendaylight.controller.netconf.client.NetconfClientSession;
+import org.opendaylight.controller.netconf.client.NetconfClientSessionListener;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants;
+import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
+import org.opendaylight.controller.sal.connect.netconf.NetconfMapping;
+import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.w3c.dom.Document;
+
+@SuppressWarnings("all")
+class NetconfDeviceListener extends NetconfClientSessionListener {
+    private final NetconfDevice device;
+    private final EventExecutor eventExecutor;
+
+    public NetconfDeviceListener(final NetconfDevice device, final EventExecutor eventExecutor) {
+        this.device = device;
+        this.eventExecutor = eventExecutor;
+    }
+
+    private Promise<NetconfMessage> messagePromise;
+    private ConcurrentMap<String, Promise<NetconfMessage>> promisedMessages;
+
+    private final ReentrantLock promiseLock = new ReentrantLock();
+
+    public void onMessage(final NetconfClientSession session, final NetconfMessage message) {
+        if (isNotification(message)) {
+            this.onNotification(session, message);
+        } else {
+            try {
+                this.promiseLock.lock();
+                boolean _notEquals = (!Objects.equal(this.messagePromise, null));
+                if (_notEquals) {
+                    this.device.logger.debug("Setting promised reply {} with message {}", this.messagePromise, message);
+                    this.messagePromise.setSuccess(message);
+                    this.messagePromise = null;
+                }
+            } finally {
+                this.promiseLock.unlock();
+            }
+        }
+    }
+
+    /**
+     * Method intended to customize notification processing.
+     * 
+     * @param session
+     *            {@see
+     *            NetconfClientSessionListener#onMessage(NetconfClientSession,
+     *            NetconfMessage)}
+     * @param message
+     *            {@see
+     *            NetconfClientSessionListener#onMessage(NetconfClientSession,
+     *            NetconfMessage)}
+     */
+    public void onNotification(final NetconfClientSession session, final NetconfMessage message) {
+        this.device.logger.debug("Received NETCONF notification.", message);
+        CompositeNode _notificationBody = null;
+        CompositeNode _compositeNode = null;
+        if (message != null) {
+            _compositeNode = NetconfMapping.toCompositeNode(message,device.getSchemaContext());
+        }
+        if (_compositeNode != null) {
+            _notificationBody = NetconfDeviceListener.getNotificationBody(_compositeNode);
+        }
+        final CompositeNode domNotification = _notificationBody;
+        boolean _notEquals = (!Objects.equal(domNotification, null));
+        if (_notEquals) {
+            MountProvisionInstance _mountInstance = null;
+            if (this.device != null) {
+                _mountInstance = this.device.getMountInstance();
+            }
+            if (_mountInstance != null) {
+                _mountInstance.publish(domNotification);
+            }
+        }
+    }
+
+    private static CompositeNode getNotificationBody(final CompositeNode node) {
+        List<Node<? extends Object>> _children = node.getChildren();
+        for (final Node<? extends Object> child : _children) {
+            if ((child instanceof CompositeNode)) {
+                return ((CompositeNode) child);
+            }
+        }
+        return null;
+    }
+
+    public NetconfMessage getLastMessage(final int attempts, final int attemptMsDelay) throws InterruptedException {
+        final Promise<NetconfMessage> promise = this.promiseReply();
+        this.device.logger.debug("Waiting for reply {}", promise);
+        int _plus = (attempts * attemptMsDelay);
+        final boolean messageAvailable = promise.await(_plus);
+        if (messageAvailable) {
+            try {
+                try {
+                    return promise.get();
+                } catch (Throwable _e) {
+                    throw Exceptions.sneakyThrow(_e);
+                }
+            } catch (final Throwable _t) {
+                if (_t instanceof ExecutionException) {
+                    final ExecutionException e = (ExecutionException) _t;
+                    IllegalStateException _illegalStateException = new IllegalStateException(e);
+                    throw _illegalStateException;
+                } else {
+                    throw Exceptions.sneakyThrow(_t);
+                }
+            }
+        }
+        String _plus_1 = ("Unsuccessful after " + Integer.valueOf(attempts));
+        String _plus_2 = (_plus_1 + " attempts.");
+        IllegalStateException _illegalStateException_1 = new IllegalStateException(_plus_2);
+        throw _illegalStateException_1;
+    }
+
+    public synchronized Promise<NetconfMessage> promiseReply() {
+        this.device.logger.debug("Promising reply.");
+        this.promiseLock.lock();
+        try {
+            boolean _equals = Objects.equal(this.messagePromise, null);
+            if (_equals) {
+                Promise<NetconfMessage> _newPromise = this.eventExecutor.<NetconfMessage> newPromise();
+                this.messagePromise = _newPromise;
+                return this.messagePromise;
+            }
+            return this.messagePromise;
+        } finally {
+            this.promiseLock.unlock();
+        }
+    }
+
+    public boolean isNotification(final NetconfMessage message) {
+        Document _document = message.getDocument();
+        final XmlElement xmle = XmlElement.fromDomDocument(_document);
+        String _name = xmle.getName();
+        return XmlNetconfConstants.NOTIFICATION_ELEMENT_NAME.equals(_name);
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java
new file mode 100644 (file)
index 0000000..216a27a
--- /dev/null
@@ -0,0 +1,134 @@
+package org.opendaylight.controller.sal.connect.netconf;
+
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.eclipse.xtext.xbase.lib.IterableExtensions;
+import org.opendaylight.controller.md.sal.common.api.data.DataModification;
+import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.*;
+
+public class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction<InstanceIdentifier, CompositeNode> {
+
+    private NetconfDevice device;
+    private final DataModification<InstanceIdentifier, CompositeNode> modification;
+    private boolean candidateSupported = true;
+
+    public NetconfDeviceTwoPhaseCommitTransaction(NetconfDevice device,
+            DataModification<InstanceIdentifier, CompositeNode> modification) {
+        super();
+        this.device = device;
+        this.modification = modification;
+    }
+
+    public void prepare() {
+        for (InstanceIdentifier toRemove : modification.getRemovedConfigurationData()) {
+            sendRemove(toRemove);
+        }
+        for(Entry<InstanceIdentifier, CompositeNode> toUpdate : modification.getUpdatedConfigurationData().entrySet()) {
+            sendMerge(toUpdate.getKey(),toUpdate.getValue());
+        }
+
+    }
+
+    private void sendMerge(InstanceIdentifier key, CompositeNode value) {
+        sendEditRpc(createEditStructure(key, Optional.<String>absent(), Optional.of(value)));
+    }
+
+    private void sendRemove(InstanceIdentifier toRemove) {
+        sendEditRpc(createEditStructure(toRemove, Optional.of("remove"), Optional.<CompositeNode> absent()));
+    }
+
+    private void sendEditRpc(CompositeNode editStructure) {
+        CompositeNodeBuilder<ImmutableCompositeNode> builder = configurationRpcBuilder();
+        builder.setQName(NETCONF_EDIT_CONFIG_QNAME);
+        builder.add(editStructure);
+        
+        RpcResult<CompositeNode> rpcResult = device.invokeRpc(NETCONF_EDIT_CONFIG_QNAME, builder.toInstance());
+        Preconditions.checkState(rpcResult.isSuccessful(),"Rpc Result was unsuccessful");
+        
+    }
+
+    private CompositeNodeBuilder<ImmutableCompositeNode> configurationRpcBuilder() {
+        CompositeNodeBuilder<ImmutableCompositeNode> ret = ImmutableCompositeNode.builder();
+        
+        Node<?> targetNode;
+        if(candidateSupported) {
+            targetNode = ImmutableCompositeNode.create(NETCONF_CANDIDATE_QNAME, ImmutableList.<Node<?>>of());
+        } else {
+            targetNode = ImmutableCompositeNode.create(NETCONF_RUNNING_QNAME, ImmutableList.<Node<?>>of());
+        }
+        Node<?> targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.<Node<?>>of(targetNode));
+        ret.add(targetWrapperNode);
+        return ret;
+    }
+
+    private CompositeNode createEditStructure(InstanceIdentifier dataPath, Optional<String> action,
+            Optional<CompositeNode> lastChildOverride) {
+        List<PathArgument> path = dataPath.getPath();
+        List<PathArgument> reversed = Lists.reverse(path);
+        CompositeNode previous = null;
+        boolean isLast = true;
+        for (PathArgument arg : reversed) {
+            CompositeNodeBuilder<ImmutableCompositeNode> builder = ImmutableCompositeNode.builder();
+            builder.setQName(arg.getNodeType());
+
+            if (arg instanceof NodeIdentifierWithPredicates) {
+                for (Entry<QName, Object> entry : ((NodeIdentifierWithPredicates) arg).getKeyValues().entrySet()) {
+                    builder.addLeaf(entry.getKey(), entry.getValue());
+                }
+            }
+            if (isLast) {
+                if (action.isPresent()) {
+                    builder.setAttribute(NETCONF_ACTION_QNAME, action.get());
+                }
+                if (lastChildOverride.isPresent()) {
+                    List<Node<?>> children = lastChildOverride.get().getChildren();
+                    builder.addAll(children);
+                }
+            } else {
+                builder.add(previous);
+            }
+            previous = builder.toInstance();
+            isLast = false;
+        }
+        return ImmutableCompositeNode.create(NETCONF_CONFIG_QNAME, ImmutableList.<Node<?>>of(previous));
+    }
+
+    @Override
+    public RpcResult<Void> finish() throws IllegalStateException {
+        CompositeNodeBuilder<ImmutableCompositeNode> commitInput = ImmutableCompositeNode.builder();
+        commitInput.setQName(NETCONF_COMMIT_QNAME);
+        RpcResult<?> rpcResult = device.invokeRpc(NetconfMapping.NETCONF_COMMIT_QNAME, commitInput.toInstance());
+        return (RpcResult<Void>) rpcResult;
+    }
+
+    @Override
+    public DataModification<InstanceIdentifier, CompositeNode> getModification() {
+        return this.modification;
+    }
+
+    @Override
+    public RpcResult<Void> rollback() throws IllegalStateException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+}
index 794b582..c151e42 100644 (file)
@@ -22,6 +22,10 @@ import java.util.List
 import com.google.common.collect.ImmutableList
 import org.opendaylight.yangtools.yang.data.api.SimpleNode
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode
+import com.google.common.base.Preconditions
+import com.google.common.base.Optional
+import org.opendaylight.yangtools.yang.model.api.SchemaContext
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils
 
 class NetconfMapping {
 
@@ -36,8 +40,19 @@ class NetconfMapping {
     public static val NETCONF_FILTER_QNAME = QName.create(NETCONF_QNAME, "filter");
     public static val NETCONF_TYPE_QNAME = QName.create(NETCONF_QNAME, "type");
     public static val NETCONF_GET_CONFIG_QNAME = QName.create(NETCONF_QNAME, "get-config");
+    public static val NETCONF_EDIT_CONFIG_QNAME = QName.create(NETCONF_QNAME, "edit-config");
+    public static val NETCONF_DELETE_CONFIG_QNAME = QName.create(NETCONF_QNAME, "delete-config");
+    public static val NETCONF_ACTION_QNAME = QName.create(NETCONF_QNAME, "action");
+    public static val NETCONF_COMMIT_QNAME = QName.create(NETCONF_QNAME, "commit");
+    
+    public static val NETCONF_CONFIG_QNAME = QName.create(NETCONF_QNAME, "config");
     public static val NETCONF_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
+    public static val NETCONF_TARGET_QNAME = QName.create(NETCONF_QNAME, "target");
+    
+    public static val NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate");
     public static val NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running");
+    
+    
     public static val NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply");
     public static val NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok");
     public static val NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data");
@@ -80,11 +95,11 @@ class NetconfMapping {
         }
     }
 
-    static def CompositeNode toCompositeNode(NetconfMessage message) {
-        return message.toRpcResult().result;
+    static def CompositeNode toCompositeNode(NetconfMessage message,Optional<SchemaContext> ctx) {
+        return null//message.toRpcResult().result;
     }
 
-    static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node) {
+    static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node,Optional<SchemaContext> ctx) {
         val rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node));
         val w3cPayload = NodeUtils.buildShadowDomTree(rpcPayload);
         w3cPayload.documentElement.setAttribute("message-id", "m-" + messageId.andIncrement);
@@ -106,12 +121,41 @@ class NetconfMapping {
         
     }
 
-    static def RpcResult<CompositeNode> toRpcResult(NetconfMessage message) {
-        val rawRpc = message.document.toCompositeNode() as CompositeNode;
-
+    static def RpcResult<CompositeNode> toRpcResult(NetconfMessage message,QName rpc,Optional<SchemaContext> context) {
+        var CompositeNode rawRpc;
+        if(context.present) {
+            if(isDataRetrievalReply(rpc)) {
+                
+                val xmlData = message.document.dataSubtree
+                val dataNodes = XmlDocumentUtils.toDomNodes(xmlData,Optional.of(context.get.dataDefinitions))
+                
+                val it = ImmutableCompositeNode.builder()
+                setQName(NETCONF_RPC_REPLY_QNAME)
+                add(ImmutableCompositeNode.create(NETCONF_DATA_QNAME,dataNodes));
+                
+                rawRpc = it.toInstance;
+                //sys(xmlData)
+            } else {
+                val rpcSchema = context.get.operations.findFirst[QName == rpc]
+                rawRpc = message.document.toCompositeNode() as CompositeNode;
+            }
+            
+            
+            
+        } else {
+            rawRpc = message.document.toCompositeNode() as CompositeNode;
+        }
         //rawRpc.
         return Rpcs.getRpcResult(true, rawRpc, Collections.emptySet());
     }
+    
+    def static Element getDataSubtree(Document doc) {
+        doc.getElementsByTagNameNS(NETCONF_URI.toString,"data").item(0) as Element
+    }
+    
+    def static boolean isDataRetrievalReply(QName it) {
+        return NETCONF_URI == namespace && ( localName == NETCONF_GET_CONFIG_QNAME.localName || localName == NETCONF_GET_QNAME.localName) 
+    }
 
     static def wrap(QName name, Node<?> node) {
         if (node != null) {
@@ -141,6 +185,14 @@ class NetconfMapping {
     }
 
     public static def Node<?> toCompositeNode(Document document) {
-        return XmlDocumentUtils.toNode(document) as Node<?>
+        return XmlDocumentUtils.toDomNode(document) as Node<?>
     }
+    
+    public static def checkValidReply(NetconfMessage input, NetconfMessage output) {
+        val inputMsgId = input.document.documentElement.getAttribute("message-id")
+        val outputMsgId = output.document.documentElement.getAttribute("message-id")
+        Preconditions.checkState(inputMsgId == outputMsgId,"Rpc request and reply message IDs must be same.");
+        
+    }
+    
 }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfRemoteSchemaSourceProvider.java
new file mode 100644 (file)
index 0000000..12be689
--- /dev/null
@@ -0,0 +1,69 @@
+package org.opendaylight.controller.sal.connect.netconf;
+
+import java.util.Set;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.SimpleNode;
+import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
+import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+
+class NetconfRemoteSchemaSourceProvider implements SchemaSourceProvider<String> {
+
+    public static final QName IETF_NETCONF_MONITORING = QName.create(
+            "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring", "2010-10-04", "ietf-netconf-monitoring");
+    public static final QName GET_SCHEMA_QNAME = QName.create(IETF_NETCONF_MONITORING, "get-schema");
+    public static final QName GET_DATA_QNAME = QName.create(IETF_NETCONF_MONITORING, "data");
+
+    NetconfDevice device;
+
+    public NetconfRemoteSchemaSourceProvider(NetconfDevice device) {
+        super();
+        this.device = device;
+    }
+
+    @Override
+    public Optional<String> getSchemaSource(String moduleName, Optional<String> revision) {
+        CompositeNodeBuilder<ImmutableCompositeNode> request = ImmutableCompositeNode.builder(); //
+        request.setQName(GET_SCHEMA_QNAME) //
+                .addLeaf("format", "yang") //
+                .addLeaf("identifier", moduleName); //
+        if (revision.isPresent()) {
+            request.addLeaf("version", revision.get());
+        }
+
+        device.logger.info("Loading YANG schema source for {}:{}", moduleName, revision);
+        RpcResult<CompositeNode> schemaReply = device.invokeRpc(GET_SCHEMA_QNAME, request.toInstance());
+        if (schemaReply.isSuccessful()) {
+            String schemaBody = getSchemaFromRpc(schemaReply.getResult());
+            if (schemaBody != null) {
+                device.logger.info("YANG Schema successfully retrieved from remote for {}:{}", moduleName, revision);
+                return Optional.of(schemaBody);
+            }
+        }
+        device.logger.info("YANG shcema was not successfully retrieved.");
+        return Optional.absent();
+    }
+
+    private String getSchemaFromRpc(CompositeNode result) {
+        if (result == null) {
+            return null;
+        }
+        SimpleNode<?> simpleNode = result.getFirstSimpleByName(GET_DATA_QNAME.withoutRevision());
+        Object potential = simpleNode.getValue();
+        if (potential instanceof String) {
+            return (String) potential;
+        }
+        return null;
+    }
+    
+    public static final boolean isSupportedFor(Set<QName> capabilities) {
+        return capabilities.contains(IETF_NETCONF_MONITORING);
+    }
+}
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java
deleted file mode 100644 (file)
index e151fca..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.opendaylight.controller.sal.connect.netconf;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
-import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NodeList;
-
-import com.google.common.base.Strings;
-
-public class XmlDocumentUtils {
-
-    public static Node<?> toNode(Document doc) {
-        return toCompositeNode(doc.getDocumentElement());
-    }
-
-    private static Node<?> toCompositeNode(Element element) {
-        String orgNamespace = element.getNamespaceURI();
-        URI biNamespace = null;
-        if (orgNamespace != null) {
-            biNamespace = URI.create(orgNamespace);
-        }
-        QName qname = new QName(biNamespace, element.getLocalName());
-
-        List<Node<?>> values = new ArrayList<>();
-        NodeList nodes = element.getChildNodes();
-        boolean isSimpleObject = true;
-        String value = null;
-        for (int i = 0; i < nodes.getLength(); i++) {
-            org.w3c.dom.Node child = nodes.item(i);
-            if (child instanceof Element) {
-                isSimpleObject = false;
-                values.add(toCompositeNode((Element) child));
-            }
-            if (isSimpleObject && child instanceof org.w3c.dom.Text) {
-                value = element.getTextContent();
-                if (!Strings.isNullOrEmpty(value)) {
-                    isSimpleObject = true;
-                }
-            }
-        }
-
-        if (isSimpleObject) {
-            return new SimpleNodeTOImpl<>(qname, null, value);
-        }
-        return new CompositeNodeTOImpl(qname, null, values);
-    }
-}

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.