Merge "API for accessing the controller version"
authorAndrew Kim <andrekim@cisco.com>
Tue, 7 Jan 2014 15:48:18 +0000 (15:48 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 7 Jan 2014 15:48:18 +0000 (15:48 +0000)
19 files changed:
opendaylight/containermanager/api/src/main/java/org/opendaylight/controller/containermanager/ContainerFlowConfig.java
opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/connect/dom/BindingIndependentConnector.java
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/sal/binding/test/bugfix/PutAugmentationTest.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/MountProvisionInstance.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/MountPointImpl.java
opendaylight/md-sal/sal-netconf-connector/pom.xml
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.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/NetconfMapping.xtend
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java
opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/YangModelInputStreamAdapter.java [new file with mode: 0644]
opendaylight/md-sal/sal-netconf-connector/src/main/yang/odl-sal-netconf-connector-cfg.yang
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/RestconfProvider.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/impl/XmlMapper.java
opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/ControllerContext.xtend
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ControllerContextTest.java
opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/ReadConfAndOperDataTest.java
opendaylight/netconf/netconf-client/src/main/java/org/opendaylight/controller/netconf/client/NetconfClient.java

index 9740a9259833c0ef9d60ad821eaea1e0ee723aef..9d65ad343c4c540e2d93d750524becac00a3e9b8 100644 (file)
@@ -15,7 +15,9 @@ import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
@@ -308,9 +310,10 @@ public class ContainerFlowConfig implements Serializable {
     }
 
     /**
-     * Match Source IP Address.
+     * Match the set of these vlans with that of flowSpec's vlans.
      *
-     * @param flowSpec Flow Specification
+     * @param flowSpec
+     *            Flow Specification
      * @return true, if successful
      */
     private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
@@ -320,7 +323,8 @@ public class ContainerFlowConfig implements Serializable {
         if (dlVlan == null || flowSpec.dlVlan == null) {
             return false;
         }
-        return dlVlan.equals(flowSpec.dlVlan);
+
+        return this.getVlanList().equals(flowSpec.getVlanList());
     }
 
     /**
@@ -404,18 +408,34 @@ public class ContainerFlowConfig implements Serializable {
     }
 
     /**
-     * Returns the vlan id number
+     * Returns the vlan id number for all vlans specified
      *
-     * @return the vlan id number
+     * @return the vlan id number for all vlans specified
      */
-    public Short getVlanId() {
-        Short vlan = 0;
+    public Set<Short> getVlanList() {
+        /*
+         * example: Vlan = "1,3,5-12"
+         * elemArray = ["1" "3" "5-12"]
+         * elem[2] = "5-12" --> limits = ["5" "12"]
+         * vlanList = [1 3 5 6 7 8 9 10 11 12]
+         */
+        Set<Short> vlanList = new HashSet<Short>();
         try {
-            vlan = Short.parseShort(dlVlan);
+            String[] elemArray = dlVlan.split(",");
+            for (String elem : elemArray) {
+                if (elem.contains("-")) {
+                    String[] limits = elem.split("-");
+                    for (short j = Short.valueOf(limits[0]); j <= Short.valueOf(limits[1]); j++) {
+                        vlanList.add(Short.valueOf(j));
+                    }
+                } else {
+                    vlanList.add(Short.valueOf(elem));
+                }
+            }
         } catch (NumberFormatException e) {
 
         }
-        return vlan;
+        return vlanList;
     }
 
     /**
@@ -617,13 +637,25 @@ public class ContainerFlowConfig implements Serializable {
         if (dlVlan != null) {
             short vlanId = 0;
             try {
-                vlanId = Short.parseShort(dlVlan);
+                String[] elemArray = dlVlan.split(",");
+                for (String elem : elemArray) {
+                    if (elem.contains("-")) {
+                        String[] limits = elem.split("-");
+                        if (Short.parseShort(limits[0]) < 0
+                                || Short.parseShort(limits[0]) >= Short.parseShort(limits[1])
+                                || Short.parseShort(limits[1]) > 0xfff) {
+                            return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
+                        }
+                    } else {
+                        vlanId = Short.parseShort(elem);
+                        if (vlanId < 0 || vlanId > 0xfff) {
+                            return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
+                        }
+                    }
+                }
             } catch (NumberFormatException e) {
                 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
             }
-            if (vlanId < 0 || vlanId > 0xfff) {
-                return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
-            }
         }
         return new Status(StatusCode.SUCCESS);
     }
@@ -706,20 +738,46 @@ public class ContainerFlowConfig implements Serializable {
 
     /**
      * Returns the matches.
-     * If unidirectional flag is set, there will be only one match in the list
-     * If unidirectional flag is unset there will be two matches in the list,
+     * If unidirectional flag is set, there will be only one match per vlan in the list
+     * If unidirectional flag is unset there will be two matches per vlan in the list,
      * only if the specified flow has an intrinsic direction.
      * For Ex. if the cFlow only has the protocol field configured, no matter
-     * if unidirectional flag is set or not, only one match will be returned
+     * if unidirectional flag is set or not, only one match per vlan will be returned
      * The client just has to iterate over the returned list
      * @return the matches
      */
     public List<Match> getMatches() {
         List<Match> matches = new ArrayList<Match>();
-        Match match = new Match();
 
         if (this.dlVlan != null && !this.dlVlan.isEmpty()) {
-            match.setField(MatchType.DL_VLAN, this.getVlanId());
+            for(Short vlan:getVlanList()){
+                Match match = getMatch(vlan);
+                matches.add(match);
+            }
+        }
+        else{
+            Match match = getMatch(null);
+            matches.add(match);
+        }
+
+        if (!ContainerFlowConfig.unidirectional) {
+            List<Match> forwardMatches = new ArrayList<Match>(matches);
+            for (Match match : forwardMatches) {
+                Match reverse = match.reverse();
+                if (!match.equals(reverse)) {
+                    matches.add(reverse);
+                }
+            }
+        }
+
+        return matches;
+    }
+
+    private Match getMatch(Short vlan){
+        Match match = new Match();
+
+        if (vlan != null) {
+            match.setField(MatchType.DL_VLAN, vlan);
         }
         if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
             String parts[] = this.nwSrc.split("/");
@@ -756,15 +814,7 @@ public class ContainerFlowConfig implements Serializable {
         if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
             match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
         }
-
-        matches.add(match);
-        if(!ContainerFlowConfig.unidirectional) {
-            Match reverse = match.reverse();
-            if (!match.equals(reverse)) {
-                matches.add(reverse);
-            }
-        }
-        return matches;
+        return match;
     }
 
     /*
index daa3914cf73fd717c63cb2c3e251dc175ffe1191..0a769921d80410742cb22b4ad7ee8cc0e9920f0a 100644 (file)
@@ -115,32 +115,32 @@ public class BindingIndependentConnector implements //
     public DataObject readOperationalData(InstanceIdentifier<? extends DataObject> path) {
         try {
             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
-
             CompositeNode result = biDataService.readOperationalData(biPath);
-            Class<? extends DataObject> targetType = path.getTargetType();
-
-            if (Augmentation.class.isAssignableFrom(targetType)) {
-                path = mappingService.fromDataDom(biPath);
-                Class<? extends Augmentation<?>> augmentType = (Class<? extends Augmentation<?>>) targetType;
-                DataObject parentTo = mappingService.dataObjectFromDataDom(path, result);
-                if (parentTo instanceof Augmentable<?>) {
-                    return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType);
-                }
-
-            }
-            return mappingService.dataObjectFromDataDom(path, result);
-
+            return potentialAugmentationRead(path,biPath,result);
         } catch (DeserializationException e) {
             throw new IllegalStateException(e);
         }
     }
 
+    private DataObject potentialAugmentationRead(InstanceIdentifier<? extends DataObject> path, org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath, CompositeNode result) throws DeserializationException {
+        Class<? extends DataObject> targetType = path.getTargetType();
+        if (Augmentation.class.isAssignableFrom(targetType)) {
+            path = mappingService.fromDataDom(biPath);
+            Class<? extends Augmentation<?>> augmentType = (Class<? extends Augmentation<?>>) targetType;
+            DataObject parentTo = mappingService.dataObjectFromDataDom(path, result);
+            if (parentTo instanceof Augmentable<?>) {
+                return (DataObject) ((Augmentable) parentTo).getAugmentation(augmentType);
+            }
+        }
+        return mappingService.dataObjectFromDataDom(path, result);
+    }
+
     @Override
     public DataObject readConfigurationData(InstanceIdentifier<? extends DataObject> path) {
         try {
             org.opendaylight.yangtools.yang.data.api.InstanceIdentifier biPath = mappingService.toDataDom(path);
             CompositeNode result = biDataService.readConfigurationData(biPath);
-            return mappingService.dataObjectFromDataDom(path, result);
+            return potentialAugmentationRead(path,biPath,result);
         } catch (DeserializationException e) {
             throw new IllegalStateException(e);
         }
index 96d0361b1dd5194e9665ab89a80c774e64d5cb4a..d9b16af469727f80e66d608dfb576a936ddfa8c9 100644 (file)
@@ -12,6 +12,8 @@ import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
 import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActions;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.SupportedActionsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.flow.node.supported.actions.ActionType;
@@ -130,8 +132,60 @@ public class PutAugmentationTest extends AbstractDataServiceTest implements Data
         assertBindingIndependentVersion(NODE_INSTANCE_ID_BI);
         testNodeRemove();
     }
+    
+    @Test
+    public void putNodeWithAugmentation() throws Exception {
+        
+        NodeBuilder nodeBuilder = new NodeBuilder();
+        nodeBuilder.setId(new NodeId(NODE_ID));
+        nodeBuilder.setKey(NODE_KEY);
+        FlowCapableNodeBuilder fnub = new FlowCapableNodeBuilder();
+        fnub.setHardware("Hardware Foo");
+        fnub.setManufacturer("Manufacturer Foo");
+        fnub.setSerialNumber("Serial Foo");
+        fnub.setDescription("Description Foo");
+        fnub.setSoftware("JUnit emulated");
+        FlowCapableNode fnu = fnub.build();
+        
+        nodeBuilder.addAugmentation(FlowCapableNode.class, fnu);
+        DataModificationTransaction baseTransaction = baDataService.beginTransaction();
+        baseTransaction.putOperationalData(NODE_INSTANCE_ID_BA, nodeBuilder.build());
+        RpcResult<TransactionStatus> result = baseTransaction.commit().get();
+        assertEquals(TransactionStatus.COMMITED, result.getResult());
+        
+        FlowCapableNode readedAugmentation = (FlowCapableNode) baDataService.readOperationalData(InstanceIdentifier.builder(NODE_INSTANCE_ID_BA).augmentation(FlowCapableNode.class).toInstance());
+        assertNotNull(readedAugmentation);
+        assertEquals(fnu.getHardware(), readedAugmentation.getHardware());
+        
+        testPutNodeConnectorWithAugmentation();
+        testNodeRemove();
+    }
 
     
+    private void testPutNodeConnectorWithAugmentation() throws Exception {
+        NodeConnectorKey ncKey = new NodeConnectorKey(new NodeConnectorId("test:0:0"));
+        InstanceIdentifier<NodeConnector> ncPath = InstanceIdentifier.builder(NODE_INSTANCE_ID_BA)
+        .child(NodeConnector.class, ncKey).toInstance();
+        InstanceIdentifier<FlowCapableNodeConnector> ncAugmentPath = InstanceIdentifier.builder(ncPath)
+        .augmentation(FlowCapableNodeConnector.class).toInstance();
+        
+        NodeConnectorBuilder nc = new NodeConnectorBuilder();
+        nc.setKey(ncKey);
+        
+        FlowCapableNodeConnectorBuilder fncb = new FlowCapableNodeConnectorBuilder();
+        fncb.setName("Baz");
+        nc.addAugmentation(FlowCapableNodeConnector.class, fncb.build());
+        
+        DataModificationTransaction baseTransaction = baDataService.beginTransaction();
+        baseTransaction.putOperationalData(ncPath, nc.build());
+        RpcResult<TransactionStatus> result = baseTransaction.commit().get();
+        assertEquals(TransactionStatus.COMMITED, result.getResult());
+        
+        FlowCapableNodeConnector readedAugmentation = (FlowCapableNodeConnector) baDataService.readOperationalData(ncAugmentPath);
+        assertNotNull(readedAugmentation);
+        assertEquals(fncb.getName(), readedAugmentation.getName());
+    }
+
     private void testNodeRemove() throws Exception {
         DataModificationTransaction transaction = baDataService.beginTransaction();
         transaction.removeOperationalData(NODE_INSTANCE_ID_BA);
index 1596165601a0155dfda3cb8c1160c8c4baf41e1e..910c7cb62365d01ca43e6d30dec43966f1a8f233 100644 (file)
@@ -16,8 +16,11 @@ import org.opendaylight.controller.sal.core.api.notify.NotificationService;
 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.model.api.SchemaContext;
 
 public interface MountInstance extends NotificationService, DataBrokerService {
 
     Future<RpcResult<CompositeNode>> rpc(QName type, CompositeNode input);
+    
+    SchemaContext getSchemaContext();
 }
index 92542bc3455e497a6082fe64cf6607a290b30723..e5fc4b7aa4a1338c666034db88be0113302cff67 100644 (file)
@@ -3,6 +3,8 @@ package org.opendaylight.controller.sal.core.api.mount;
 import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
 import org.opendaylight.controller.sal.core.api.data.DataProviderService;
 import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService;
+import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public interface MountProvisionInstance extends //
         MountInstance,//
@@ -10,4 +12,6 @@ public interface MountProvisionInstance extends //
         RpcProvisionRegistry,//
         DataProviderService {
 
+    void setSchemaContext(SchemaContext optional);
+
 }
index 7509a38a1429308029f25bcc85d90a32b6ed8f2d..ab5b145064c828cfa083f74a21beff4e66e9074a 100644 (file)
@@ -31,6 +31,7 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public class MountPointImpl implements MountProvisionInstance {
 
@@ -42,6 +43,8 @@ public class MountPointImpl implements MountProvisionInstance {
     
     private final InstanceIdentifier mountPath;
 
+    private SchemaContext schemaContext;
+
     public MountPointImpl(InstanceIdentifier path) {
         this.mountPath = path;
         rpcs = new RpcRouterImpl("");
@@ -163,6 +166,14 @@ public class MountPointImpl implements MountProvisionInstance {
         // NOOP
     }
     
+    public SchemaContext getSchemaContext() {
+        return schemaContext;
+    }
+
+    public void setSchemaContext(SchemaContext schemaContext) {
+        this.schemaContext = schemaContext;
+    }
+
     class ReadWrapper implements DataReader<InstanceIdentifier, CompositeNode> {
         
         
index fe613565a6232b608ab91ed33e014b327c4a4e2a..2323b09f996d040360f19e0d9206865bac3ff658 100644 (file)
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.opendaylight.controller</groupId>
+            <artifactId>sal-binding-broker-impl</artifactId>
+            <version>1.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+           <groupId>org.opendaylight.controller</groupId>
+           <artifactId>sal-binding-broker-impl</artifactId>
+           <version>1.0-SNAPSHOT</version>
+           <type>test-jar</type>
+           <scope>test</scope>
+        </dependency>
+        <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>ietf-netconf-monitoring</artifactId>
+        <version>0.2.3-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>org.opendaylight.yangtools.model</groupId>
             <artifactId>ietf-inet-types</artifactId>
index 55a1fbfe486a03f033ba961586675cb21beea2d6..f5d592c00715772a87483d09e0ab96d0c4f99697 100644 (file)
@@ -12,8 +12,12 @@ package org.opendaylight.controller.config.yang.md.sal.connector.netconf;
 import io.netty.channel.EventLoopGroup;
 import io.netty.util.concurrent.GlobalEventExecutor;
 
+import java.io.File;
+import java.io.InputStream;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 import javax.net.ssl.SSLContext;
 
@@ -24,6 +28,10 @@ import org.opendaylight.controller.netconf.util.handler.ssh.authentication.Login
 import org.opendaylight.controller.sal.connect.netconf.NetconfDevice;
 import org.opendaylight.protocol.framework.ReconnectStrategy;
 import org.opendaylight.protocol.framework.TimedReconnectStrategy;
+import org.opendaylight.yangtools.yang.model.util.repo.AbstractCachingSchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.FilesystemSchemaCachingProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProvider;
+import org.opendaylight.yangtools.yang.model.util.repo.SchemaSourceProviders;
 import org.osgi.framework.BundleContext;
 
 import static com.google.common.base.Preconditions.*;
@@ -37,6 +45,8 @@ import com.google.common.net.InetAddresses;
 public final class NetconfConnectorModule extends org.opendaylight.controller.config.yang.md.sal.connector.netconf.AbstractNetconfConnectorModule
 {
 
+    private static ExecutorService GLOBAL_PROCESSING_EXECUTOR = null;
+    private static AbstractCachingSchemaSourceProvider<String, InputStream> GLOBAL_NETCONF_SOURCE_PROVIDER = null;
     private BundleContext bundleContext;
 
     public NetconfConnectorModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) {
@@ -74,31 +84,56 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co
         } else {
             addressValue = getAddress().getIpv6Address().getValue();
         }
-        
         */
         ReconnectStrategy strategy = new TimedReconnectStrategy(GlobalEventExecutor.INSTANCE, attemptMsTimeout, 1000, 1.0, null,
                 Long.valueOf(connectionAttempts), null);
         
-        
-        device.setStrategy(strategy);
+        device.setReconnectStrategy(strategy);
         
         InetAddress addr = InetAddresses.forString(addressValue);
         InetSocketAddress socketAddress = new InetSocketAddress(addr , getPort().intValue());
+
+        
+        device.setProcessingExecutor(getGlobalProcessingExecutor());
+        
         device.setSocketAddress(socketAddress);
+        device.setEventExecutor(getEventExecutorDependency());
+        device.setDispatcher(createDispatcher());
+        device.setSchemaSourceProvider(getGlobalNetconfSchemaProvider(bundleContext));
         
+        getDomRegistryDependency().registerProvider(device, bundleContext);
+        device.start();
+        return device;
+    }
+
+    private ExecutorService getGlobalProcessingExecutor() {
+        if(GLOBAL_PROCESSING_EXECUTOR == null) {
+            
+            GLOBAL_PROCESSING_EXECUTOR = Executors.newCachedThreadPool();
+            
+        }
+        return GLOBAL_PROCESSING_EXECUTOR;
+    }
+
+    private synchronized AbstractCachingSchemaSourceProvider<String, InputStream> getGlobalNetconfSchemaProvider(BundleContext bundleContext) {
+        if(GLOBAL_NETCONF_SOURCE_PROVIDER == null) {
+            String storageFile = "cache/schema";
+            File directory = bundleContext.getDataFile(storageFile);
+            SchemaSourceProvider<String> defaultProvider = SchemaSourceProviders.noopProvider();
+            GLOBAL_NETCONF_SOURCE_PROVIDER = FilesystemSchemaCachingProvider.createFromStringSourceProvider(defaultProvider, directory);
+        }
+        return GLOBAL_NETCONF_SOURCE_PROVIDER;
+    }
+
+    private NetconfClientDispatcher createDispatcher() {
         EventLoopGroup bossGroup = getBossThreadGroupDependency();
         EventLoopGroup workerGroup = getWorkerThreadGroupDependency();
-        NetconfClientDispatcher dispatcher = null;
         if(getTcpOnly()) {
-            dispatcher = new NetconfClientDispatcher( bossGroup, workerGroup);
+            return new NetconfClientDispatcher( bossGroup, workerGroup);
         } else {
             AuthenticationHandler authHandler = new LoginPassword(getUsername(),getPassword());
-            dispatcher = new NetconfSshClientDispatcher(authHandler , bossGroup, workerGroup);
+            return new NetconfSshClientDispatcher(authHandler , bossGroup, workerGroup);
         }
-        getDomRegistryDependency().registerProvider(device, bundleContext);
-        
-        device.start(dispatcher);
-        return device;
     }
 
     public void setBundleContext(BundleContext bundleContext) {
index 7c4bf5facad6a3dd94b0f1bd6a73e301a7820143..bfe352ad41322cf78404426a5582afc22a4e074d 100644 (file)
@@ -25,13 +25,43 @@ import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl
 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.yang.parser.impl.YangParserImpl
+import java.io.InputStream
+import org.slf4j.LoggerFactory
+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
 
-class NetconfDevice implements 
-    Provider, // 
-    DataReader<InstanceIdentifier, CompositeNode>, //
-    DataCommitHandler<InstanceIdentifier, CompositeNode>, //
-    RpcImplementation, //
-    AutoCloseable {
+import java.util.Map
+import java.util.Set
+import com.google.common.collect.ImmutableMap
+
+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
+
+class NetconfDevice implements Provider, // 
+DataReader<InstanceIdentifier, CompositeNode>, //
+DataCommitHandler<InstanceIdentifier, CompositeNode>, //
+RpcImplementation, //
+AutoCloseable {
 
     var NetconfClient client;
 
@@ -41,35 +71,97 @@ class NetconfDevice implements
     @Property
     var MountProvisionInstance mountInstance;
 
+    @Property
+    var EventExecutor eventExecutor;
+
+    @Property
+    var ExecutorService processingExecutor;
+
     @Property
     var InstanceIdentifier path;
 
     @Property
-    var ReconnectStrategy strategy;
+    var ReconnectStrategy reconnectStrategy;
+
+    @Property
+    var AbstractCachingSchemaSourceProvider<String, InputStream> schemaSourceProvider;
+
+    private NetconfDeviceSchemaContextProvider schemaContextProvider
+
+    protected val Logger logger
 
     Registration<DataReader<InstanceIdentifier, CompositeNode>> operReaderReg
     Registration<DataReader<InstanceIdentifier, CompositeNode>> confReaderReg
     Registration<DataCommitHandler<InstanceIdentifier, CompositeNode>> commitHandlerReg
-    
+
     val String name
     MountProvisionService mountService
+
+    int messegeRetryCount = 5;
+
+    int messageTimeoutCount = 5 * 1000;
+
+    Set<QName> cachedCapabilities
+
+    @Property
+    var NetconfClientDispatcher dispatcher
     
-    
+    static val InstanceIdentifier ROOT_PATH = InstanceIdentifier.builder().toInstance();
+
     public new(String name) {
         this.name = name;
+        this.logger = LoggerFactory.getLogger(NetconfDevice.name + "#" + name);
         this.path = InstanceIdentifier.builder(INVENTORY_PATH).nodeWithKey(INVENTORY_NODE,
             Collections.singletonMap(INVENTORY_ID, name)).toInstance;
     }
 
-    def start(NetconfClientDispatcher dispatcher) {
-        client = NetconfClient.clientFor(name, socketAddress, strategy, dispatcher);
-        confReaderReg = mountInstance.registerConfigurationReader(path, this);
-        operReaderReg = mountInstance.registerOperationalReader(path, this);
-        //commitHandlerReg = mountInstance.registerCommitHandler(path,this);
+    def start() {
+        checkState(dispatcher != null, "Dispatcher must be set.");
+        checkState(schemaSourceProvider != null, "Schema Source Provider must be set.")
+        checkState(eventExecutor != null, "Event executor must be set.");
+
+        val listener = new NetconfDeviceListener(this,eventExecutor);
+        val task = startClientTask(dispatcher, listener)
+        if(mountInstance != null) {
+            confReaderReg = mountInstance.registerConfigurationReader(ROOT_PATH, this);
+            operReaderReg = mountInstance.registerOperationalReader(ROOT_PATH, this);
+        }
+        return processingExecutor.submit(task) as Future<Void>;
+
+    //commitHandlerReg = mountInstance.registerCommitHandler(path,this);
+    }
+
+    def Optional<SchemaContext> getSchemaContext() {
+        if (schemaContextProvider == null) {
+            return Optional.absent();
+        }
+        return schemaContextProvider.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);
+                delegate = SchemaSourceProviders.<String>noopProvider();
+            }
+            val sourceProvider = schemaSourceProvider.createInstanceFor(delegate);
+            schemaContextProvider = new NetconfDeviceSchemaContextProvider(this, sourceProvider);
+            schemaContextProvider.createContextFromCapabilities(initialCapabilities);
+            if (mountInstance != null && schemaContext.isPresent) {
+                mountInstance.schemaContext = schemaContext.get();
+            }
+        ]
     }
 
     override readConfigurationData(InstanceIdentifier path) {
-        val result = invokeRpc(NETCONF_GET_CONFIG_QNAME, wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, path.toFilterStructure()));
+        val result = invokeRpc(NETCONF_GET_CONFIG_QNAME,
+            wrap(NETCONF_GET_CONFIG_QNAME, CONFIG_SOURCE_RUNNING, path.toFilterStructure()));
         val data = result.result.getFirstCompositeByName(NETCONF_DATA_QNAME);
         return data?.findNode(path) as CompositeNode;
     }
@@ -83,10 +175,17 @@ class NetconfDevice implements
     override getSupportedRpcs() {
         Collections.emptySet;
     }
+    
+    def createSubscription(String streamName) {
+        val it = ImmutableCompositeNode.builder()
+        QName = NETCONF_CREATE_SUBSCRIPTION_QNAME
+        addLeaf("stream",streamName);
+        invokeRpc(QName,toInstance())
+    }
 
     override invokeRpc(QName rpc, CompositeNode input) {
         val message = rpc.toRpcMessage(input);
-        val result = client.sendMessage(message);
+        val result = client.sendMessage(message, messegeRetryCount, messageTimeoutCount);
         return result.toRpcResult();
     }
 
@@ -96,30 +195,28 @@ class NetconfDevice implements
 
     override onSessionInitiated(ProviderSession session) {
         val dataBroker = session.getService(DataBrokerService);
-        
-        
-        
+
         val transaction = dataBroker.beginTransaction
-        if(transaction.operationalNodeNotExisting) {
-            transaction.putOperationalData(path,nodeWithId)
+        if (transaction.operationalNodeNotExisting) {
+            transaction.putOperationalData(path, nodeWithId)
         }
-        if(transaction.configurationNodeNotExisting) {
-            transaction.putConfigurationData(path,nodeWithId)
+        if (transaction.configurationNodeNotExisting) {
+            transaction.putConfigurationData(path, nodeWithId)
         }
         transaction.commit().get();
         mountService = session.getService(MountProvisionService);
-        mountInstance = mountService.createOrGetMountPoint(path);
+        mountInstance = mountService?.createOrGetMountPoint(path);
     }
-    
+
     def getNodeWithId() {
-        val id = new SimpleNodeTOImpl(INVENTORY_ID,null,name);
-        return new CompositeNodeTOImpl(INVENTORY_NODE,null,Collections.singletonList(id));
+        val id = new SimpleNodeTOImpl(INVENTORY_ID, null, name);
+        return new CompositeNodeTOImpl(INVENTORY_NODE, null, Collections.singletonList(id));
     }
-    
+
     def boolean configurationNodeNotExisting(DataModificationTransaction transaction) {
         return null === transaction.readConfigurationData(path);
     }
-    
+
     def boolean operationalNodeNotExisting(DataModificationTransaction transaction) {
         return null === transaction.readOperationalData(path);
     }
@@ -133,9 +230,9 @@ class NetconfDevice implements
             } else if (current instanceof CompositeNode) {
                 val currentComposite = (current as CompositeNode);
 
-                current = currentComposite.getFirstCompositeByName(arg.nodeType);
+                current = currentComposite.getFirstCompositeByName(arg.nodeType.withoutRevision());
                 if (current == null) {
-                    current = currentComposite.getFirstSimpleByName(arg.nodeType);
+                    current = currentComposite.getFirstSimpleByName(arg.nodeType.withoutRevision());
                 }
                 if (current == null) {
                     return null;
@@ -149,6 +246,25 @@ class NetconfDevice implements
         throw new UnsupportedOperationException("TODO: auto-generated method stub")
     }
 
+    def getInitialCapabilities() {
+        val capabilities = client?.capabilities;
+        if (capabilities == null) {
+            return null;
+        }
+        if (cachedCapabilities == null) {
+            cachedCapabilities = FluentIterable.from(capabilities).filter[
+                contains("?") && contains("module=") && contains("revision=")].transform [
+                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=", "");
+                return QName.create(namespace, revision, moduleName);
+            ].toSet();
+        }
+        return cachedCapabilities;
+    }
+
     override close() {
         confReaderReg?.close()
         operReaderReg?.close()
@@ -156,3 +272,165 @@ class NetconfDevice implements
     }
 
 }
+
+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
+    val NetconfDevice device;
+
+    @Property
+    val SchemaSourceProvider<InputStream> sourceProvider;
+
+    @Property
+    var Optional<SchemaContext> currentContext;
+
+    new(NetconfDevice device, SchemaSourceProvider<InputStream> sourceProvider) {
+        _device = device
+        _sourceProvider = sourceProvider
+    }
+
+    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 context = tryToCreateContext(modelsToParse.build);
+        currentContext = Optional.fromNullable(context);
+    }
+
+    def SchemaContext tryToCreateContext(Map<QName, InputStream> modelsToParse) {
+        val parser = new YangParserImpl();
+        try {
+            val models = parser.parseYangModelsFromStreams(ImmutableList.copyOf(modelsToParse.values));
+            val result = parser.resolveSchemaContext(models);
+            return result;
+        } catch (Exception e) {
+            device.logger.debug("Error occured during parsing YANG schemas", e);
+            return null;
+        }
+    }
+}
+
+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();
+    }
+}
index 78f6d59f77ed2b2a7025b578d6090929ceead2dd..794b58294eeb13d54ad8385e4f75dac3e3f5c695 100644 (file)
@@ -20,51 +20,63 @@ import org.w3c.dom.Element
 import org.opendaylight.controller.sal.common.util.Rpcs
 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
 
 class NetconfMapping {
 
     public static val NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0")
-    public static val NETCONF_QNAME = new QName(NETCONF_URI,null,"netconf");
-    public static val NETCONF_RPC_QNAME = new QName(NETCONF_QNAME,"rpc");
-    public static val NETCONF_GET_QNAME = new QName(NETCONF_QNAME,"get");
-    public static val NETCONF_GET_CONFIG_QNAME = new QName(NETCONF_QNAME,"get-config");
-    public static val NETCONF_SOURCE_QNAME = new QName(NETCONF_QNAME,"source");
-    public static val NETCONF_RUNNING_QNAME = new QName(NETCONF_QNAME,"running");
-    public static val NETCONF_RPC_REPLY_QNAME = new QName(NETCONF_QNAME,"rpc-reply");
-    public static val NETCONF_OK_QNAME = new QName(NETCONF_QNAME,"ok");
-    public static val NETCONF_DATA_QNAME = new QName(NETCONF_QNAME,"data");
+    public static val NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
+    public static val NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0")
     
-     static List<Node<?>> RUNNING = Collections.<Node<?>>singletonList(new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME,null,null));
-    public static val CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME,null,RUNNING);
-
-    static val messageId = new AtomicInteger(0);
     
-   
+    public static val NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf");
+    public static val NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc");
+    public static val NETCONF_GET_QNAME = QName.create(NETCONF_QNAME, "get");
+    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_SOURCE_QNAME = QName.create(NETCONF_QNAME, "source");
+    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");
+    public static val NETCONF_CREATE_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"create-subscription");
+    public static val NETCONF_CANCEL_SUBSCRIPTION_QNAME = QName.create(NETCONF_NOTIFICATION_URI,null,"cancel-subscription");
+    public static val IETF_NETCONF_MONITORING_MODULE = QName.create(NETCONF_MONITORING_URI, "2010-10-04","ietf-netconf-monitoring");
 
+    static List<Node<?>> RUNNING = Collections.<Node<?>>singletonList(
+        new SimpleNodeTOImpl(NETCONF_RUNNING_QNAME, null, null));
+    public static val CONFIG_SOURCE_RUNNING = new CompositeNodeTOImpl(NETCONF_SOURCE_QNAME, null, RUNNING);
 
+    static val messageId = new AtomicInteger(0);
 
     static def Node<?> toFilterStructure(InstanceIdentifier identifier) {
         var Node<?> previous = null;
-        for (component : identifier.path.reverse) {
+        if(identifier.path.empty) {
+            return null;
+        }
+        
+        for (component : identifier.path.reverseView) {
             val Node<?> current = component.toNode(previous);
             previous = current;
         }
-        return previous;
+        return filter("subtree",previous);
     }
-    
+
     static def dispatch Node<?> toNode(NodeIdentifierWithPredicates argument, Node<?> node) {
         val list = new ArrayList<Node<?>>();
-        forarg : argument.keyValues.entrySet) {
-            list.add = new SimpleNodeTOImpl(arg.key,null,arg.value);
+        for (arg : argument.keyValues.entrySet) {
+            list.add = new SimpleNodeTOImpl(arg.key, null, arg.value);
         }
-        return new CompositeNodeTOImpl(argument.nodeType,null,list)
+        return new CompositeNodeTOImpl(argument.nodeType, null, list)
     }
-    
+
     static def dispatch Node<?> toNode(PathArgument argument, Node<?> node) {
-        if(node != null) {
-            return new CompositeNodeTOImpl(argument.nodeType,null,Collections.singletonList(node));
+        if (node != null) {
+            return new CompositeNodeTOImpl(argument.nodeType, null, Collections.singletonList(node));
         } else {
-            return new SimpleNodeTOImpl(argument.nodeType,null,null);
+            return new SimpleNodeTOImpl(argument.nodeType, null, null);
         }
     }
 
@@ -73,40 +85,62 @@ class NetconfMapping {
     }
 
     static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node) {
-        val rpcPayload = wrap(NETCONF_RPC_QNAME,node);
+        val rpcPayload = wrap(NETCONF_RPC_QNAME, flattenInput(node));
         val w3cPayload = NodeUtils.buildShadowDomTree(rpcPayload);
-        w3cPayload.documentElement.setAttribute("message-id","m-"+ messageId.andIncrement);
+        w3cPayload.documentElement.setAttribute("message-id", "m-" + messageId.andIncrement);
         return new NetconfMessage(w3cPayload);
     }
+    
+    def static flattenInput(CompositeNode node) {
+        val inputQName = QName.create(node.nodeType,"input");
+        val input = node.getFirstCompositeByName(inputQName);
+        if(input == null) return node;
+        if(input instanceof CompositeNode) {
+            
+            val nodes = ImmutableList.builder() //
+                .addAll(input.children) //
+                .addAll(node.children.filter[nodeType != inputQName]) //
+                .build()
+            return ImmutableCompositeNode.create(node.nodeType,nodes);
+        } 
+        
+    }
 
     static def RpcResult<CompositeNode> toRpcResult(NetconfMessage message) {
         val rawRpc = message.document.toCompositeNode() as CompositeNode;
+
         //rawRpc.
-        
-        return Rpcs.getRpcResult(true,rawRpc,Collections.emptySet());
+        return Rpcs.getRpcResult(true, rawRpc, Collections.emptySet());
     }
-    
-    
-    static def wrap(QName name,Node<?> node) {
-        if(node != null) {
-            return new CompositeNodeTOImpl(name,null,Collections.singletonList(node));
-        }
-        else {
-            return new CompositeNodeTOImpl(name,null,Collections.emptyList());
+
+    static def wrap(QName name, Node<?> node) {
+        if (node != null) {
+            return new CompositeNodeTOImpl(name, null, Collections.singletonList(node));
+        } else {
+            return new CompositeNodeTOImpl(name, null, Collections.emptyList());
         }
     }
-    
-        static def wrap(QName name,Node<?> additional,Node<?> node) {
-        if(node != null) {
-            return new CompositeNodeTOImpl(name,null,ImmutableList.of(additional,node));
+
+    static def wrap(QName name, Node<?> additional, Node<?> node) {
+        if (node != null) {
+            return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional, node));
+        } else {
+            return new CompositeNodeTOImpl(name, null, ImmutableList.of(additional));
         }
-        else {
-            return new CompositeNodeTOImpl(name,null,ImmutableList.of(additional));
+    }
+
+    static def filter(String type, Node<?> node) {
+        val it = ImmutableCompositeNode.builder(); //
+        setQName(NETCONF_FILTER_QNAME);
+        setAttribute(NETCONF_TYPE_QNAME,type);
+        if (node != null) {
+            return add(node).toInstance();
+        } else {
+            return toInstance();
         }
     }
-    
-    
+
     public static def Node<?> toCompositeNode(Document document) {
-        return XmlDocumentUtils.toCompositeNode(document) as Node<?>
+        return XmlDocumentUtils.toNode(document) as Node<?>
     }
 }
index 3f6b4e1f4cd9dd75fd1fcb59a3f504970618a7bf..e151fca00969d9bc5e4e29fbe81bc04ca1173c7d 100644 (file)
@@ -13,10 +13,12 @@ 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 CompositeNode toCompositeNode(Document doc) {
-        return (CompositeNode) toCompositeNode(doc.getDocumentElement());
+    public static Node<?> toNode(Document doc) {
+        return toCompositeNode(doc.getDocumentElement());
     }
 
     private static Node<?> toCompositeNode(Element element) {
@@ -29,7 +31,7 @@ public class XmlDocumentUtils {
 
         List<Node<?>> values = new ArrayList<>();
         NodeList nodes = element.getChildNodes();
-        boolean isSimpleObject = false;
+        boolean isSimpleObject = true;
         String value = null;
         for (int i = 0; i < nodes.getLength(); i++) {
             org.w3c.dom.Node child = nodes.item(i);
@@ -37,11 +39,10 @@ public class XmlDocumentUtils {
                 isSimpleObject = false;
                 values.add(toCompositeNode((Element) child));
             }
-            if (!isSimpleObject && child instanceof org.w3c.dom.Text) {
+            if (isSimpleObject && child instanceof org.w3c.dom.Text) {
                 value = element.getTextContent();
-                if (value.matches(".*\\w.*")) {
+                if (!Strings.isNullOrEmpty(value)) {
                     isSimpleObject = true;
-                    break;
                 }
             }
         }
diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/YangModelInputStreamAdapter.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/YangModelInputStreamAdapter.java
new file mode 100644 (file)
index 0000000..0c0070c
--- /dev/null
@@ -0,0 +1,88 @@
+package org.opendaylight.controller.sal.connect.netconf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringBufferInputStream;
+import java.io.StringReader;
+
+import org.opendaylight.yangtools.concepts.Delegator;
+import org.opendaylight.yangtools.yang.common.QName;
+
+/**
+ * 
+ *
+ */
+public class YangModelInputStreamAdapter extends InputStream implements Delegator<InputStream> {
+
+    final String source;
+    final QName moduleIdentifier;
+    final InputStream delegate;
+    
+    
+    
+    private YangModelInputStreamAdapter(String source, QName moduleIdentifier, InputStream delegate) {
+        super();
+        this.source = source;
+        this.moduleIdentifier = moduleIdentifier;
+        this.delegate = delegate;
+    }
+
+    public int read() throws IOException {
+        return delegate.read();
+    }
+
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    public int read(byte[] b) throws IOException {
+        return delegate.read(b);
+    }
+
+    public boolean equals(Object obj) {
+        return delegate.equals(obj);
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        return delegate.read(b, off, len);
+    }
+
+    public long skip(long n) throws IOException {
+        return delegate.skip(n);
+    }
+
+    public int available() throws IOException {
+        return delegate.available();
+    }
+
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    public void mark(int readlimit) {
+        delegate.mark(readlimit);
+    }
+
+    public void reset() throws IOException {
+        delegate.reset();
+    }
+
+    public boolean markSupported() {
+        return delegate.markSupported();
+    }
+
+    @Override
+    public InputStream getDelegate() {
+        return delegate;
+    }
+
+    @Override
+    public String toString() {
+        return "YangModelInputStreamAdapter [moduleIdentifier=" + moduleIdentifier + ", delegate=" + delegate + "]";
+    }
+
+    public static YangModelInputStreamAdapter create(QName name, String module) {
+        InputStream stringInput = new StringBufferInputStream(module);
+        return new YangModelInputStreamAdapter(null, name, stringInput );
+    }
+}
index 923851411090a9c93f9b0cf1d59e9be1a197a92b..2fae7ee021d0bc074861e38b6b50fd042ac4a402 100644 (file)
@@ -81,6 +81,14 @@ module odl-sal-netconf-connector-cfg {
                     }
                 }
             }
+
+            container event-executor {
+                uses config:service-ref {
+                    refine type {
+                        config:required-identity netty:netty-event-executor;
+                    }
+                }
+            }
         }
     }
 }
\ No newline at end of file
index 242f18d240122597bea4d1dac29a4f1a31ea9e31..1bceee88cb9b034ac1f17c6ef1ef2b467a9cbf7b 100644 (file)
@@ -9,6 +9,7 @@ import org.opendaylight.controller.sal.core.api.Provider;
 import org.opendaylight.controller.sal.core.api.data.DataBrokerService;
 import org.opendaylight.controller.sal.core.api.model.SchemaService;
 import org.opendaylight.controller.sal.core.api.model.SchemaServiceListener;
+import org.opendaylight.controller.sal.core.api.mount.MountService;
 import org.opendaylight.controller.sal.restconf.impl.BrokerFacade;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
@@ -37,6 +38,7 @@ public class RestconfProvider implements BundleActivator, Provider, ServiceTrack
         SchemaService schemaService = session.getService(SchemaService.class);
         listenerRegistration = schemaService.registerSchemaServiceListener(ControllerContext.getInstance());
         ControllerContext.getInstance().setSchemas(schemaService.getGlobalContext());
+        ControllerContext.getInstance().setMountService(session.getService(MountService.class));
     }
 
     @Override
index a3d658e7bfec43bca4dc743a460cab8bd6cea9de..0e9bad046b1f1ba52233ecaff2da40b8dcca5126 100644 (file)
 package org.opendaylight.controller.sal.rest.impl;
 
-import java.util.Set;
-
 import javax.activation.UnsupportedDataTypeException;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.parsers.DocumentBuilderFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
-import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO;
-import org.opendaylight.controller.sal.restconf.impl.IdentityValuesDTO.IdentityValue;
-import org.opendaylight.controller.sal.restconf.impl.RestCodec;
-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.api.SimpleNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
-import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
-import org.opendaylight.yangtools.yang.model.api.YangNode;
-import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.w3c.dom.Document;
-import org.w3c.dom.Element;
 
-import com.google.common.base.Preconditions;
 
 public class XmlMapper {
-    
-    private final Logger logger = LoggerFactory.getLogger(XmlMapper.class); 
-
     public Document write(CompositeNode data, DataNodeContainer schema) throws UnsupportedDataTypeException {
-        Preconditions.checkNotNull(data);
-        Preconditions.checkNotNull(schema);
-
-        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-        Document doc = null;
-        try {
-            DocumentBuilder bob = dbf.newDocumentBuilder();
-            doc = bob.newDocument();
-        } catch (ParserConfigurationException e) {
-            return null;
-        }
-
-        if (schema instanceof ContainerSchemaNode || schema instanceof ListSchemaNode) {
-            doc.appendChild(translateToXmlAndReturnRootElement(doc, data, schema));
-            return doc;
-        } else {
-            throw new UnsupportedDataTypeException(
-                    "Schema can be ContainerSchemaNode or ListSchemaNode. Other types are not supported yet.");
-        }
-    }
-
-    private Element translateToXmlAndReturnRootElement(Document doc, Node<?> data, YangNode schema)
-            throws UnsupportedDataTypeException {
-        QName dataType = data.getNodeType();
-        Element itemEl = doc.createElementNS(dataType.getNamespace().toString(), dataType.getLocalName());
-        if (data instanceof SimpleNode<?>) {
-            if (schema instanceof LeafListSchemaNode) {
-                writeValueOfNodeByType(itemEl, (SimpleNode<?>) data, ((LeafListSchemaNode) schema).getType(), (DataSchemaNode) schema);
-            } else if (schema instanceof LeafSchemaNode) {
-                writeValueOfNodeByType(itemEl, (SimpleNode<?>) data, ((LeafSchemaNode) schema).getType(), (DataSchemaNode) schema);
-            } else {
-                Object value = data.getValue();
-                if (value != null) {
-                    itemEl.setTextContent(String.valueOf(value));
-                }
-            }
-        } else { // CompositeNode
-            for (Node<?> child : ((CompositeNode) data).getChildren()) {
-                DataSchemaNode childSchema = null;
-                if(schema != null){
-                    childSchema = findFirstSchemaForNode(child, ((DataNodeContainer) schema).getChildNodes());
-                    if (logger.isDebugEnabled()) {
-                        if (childSchema == null) {
-                            logger.debug("Probably the data node \"" + ((child == null) ? "" : child.getNodeType().getLocalName())
-                                    + "\" is not conform to schema");
-                        }
-                    }
-                }
-                itemEl.appendChild(translateToXmlAndReturnRootElement(doc, child, childSchema));
-            }
-        }
-        return itemEl;
+        return XmlDocumentUtils.toDocument(data, schema, XmlDocumentUtils.defaultValueCodecProvider());
     }
-
-    private void writeValueOfNodeByType(Element element, SimpleNode<?> node, TypeDefinition<?> type, DataSchemaNode schema) {
-
-        TypeDefinition<?> baseType = RestUtil.resolveBaseTypeFrom(type);
-
-        if (baseType instanceof IdentityrefTypeDefinition) {
-            if (node.getValue() instanceof QName) {
-                IdentityValuesDTO valueDTO = (IdentityValuesDTO) RestCodec.from(type).serialize(node.getValue());
-                IdentityValue value = valueDTO.getValuesWithNamespaces().get(0);
-                String prefix = "x";
-                if (value.getPrefix() != null && !value.getPrefix().isEmpty()) {
-                    prefix = value.getPrefix();
-                }
-                element.setAttribute("xmlns:" + prefix, value.getNamespace());
-                element.setTextContent(prefix + ":" + value.getValue());
-            } else {
-                logger.debug("Value of " + baseType.getQName().getNamespace() + ":"
-                        + baseType.getQName().getLocalName() + " is not instance of " + QName.class + " but is " + node.getValue().getClass());
-                element.setTextContent(String.valueOf(node.getValue()));
-            }
-        } else {
-            if (node.getValue() != null) {
-                String value = String.valueOf(RestCodec.from(baseType).serialize(node.getValue()));
-                if (value.equals("null")) {
-                    value = String.valueOf(node.getValue());
-                }
-                element.setTextContent(value);
-            }
-        }
-    }
-
-    private DataSchemaNode findFirstSchemaForNode(Node<?> node, Set<DataSchemaNode> dataSchemaNode) {
-        if (dataSchemaNode != null && node != null) {
-            for (DataSchemaNode dsn : dataSchemaNode) {
-                if (node.getNodeType().getLocalName().equals(dsn.getQName().getLocalName())) {
-                    return dsn;
-                } else if (dsn instanceof ChoiceNode) {
-                    for (ChoiceCaseNode choiceCase : ((ChoiceNode) dsn).getCases()) {
-                        DataSchemaNode foundDsn = findFirstSchemaForNode(node, choiceCase.getChildNodes());
-                        if (foundDsn != null) {
-                            return foundDsn;
-                        }
-                    }
-                }
-            }
-        }
-        return null;
-    }
-
 }
index eec2d452a178c15da2f0c70befa8c1367aae31eb..1a60e14589998adbe8390495fe022d4bd5cc30a6 100644 (file)
@@ -34,13 +34,19 @@ import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition
 import org.slf4j.LoggerFactory
 
 import static com.google.common.base.Preconditions.*
+import org.opendaylight.controller.sal.core.api.mount.MountService
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode
 
 class ControllerContext implements SchemaServiceListener {
     val static LOG = LoggerFactory.getLogger(ControllerContext)
     val static ControllerContext INSTANCE = new ControllerContext
     val static NULL_VALUE = "null"
 
-    var SchemaContext schemas;
+    @Property
+    var SchemaContext globalSchema;
+    
+    @Property
+    var MountService mountService;
 
     private val BiMap<URI, String> uriToModuleName = HashBiMap.create();
     private val Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
@@ -57,7 +63,7 @@ class ControllerContext implements SchemaServiceListener {
     }
 
     private def void checkPreconditions() {
-        if (schemas === null) {
+        if (globalSchema === null) {
             throw new ResponseException(Response.Status.SERVICE_UNAVAILABLE, RestconfProvider::NOT_INITALIZED_MSG)
         }
     }
@@ -75,29 +81,24 @@ class ControllerContext implements SchemaServiceListener {
         if (pathArgs.head.empty) {
             pathArgs.remove(0)
         }
-        val schemaNode = ret.collectPathArguments(pathArgs, restconfInstance.findModule);
+        val schemaNode = ret.collectPathArguments(pathArgs, globalSchema.findModule(pathArgs.head));
         if (schemaNode === null) {
             return null
         }
         return new InstanceIdWithSchemaNode(ret.toInstance, schemaNode)
     }
 
-    private def findModule(String restconfInstance) {
-        checkPreconditions
-        checkNotNull(restconfInstance);
-        val pathArgs = restconfInstance.split("/");
-        if (pathArgs.empty) {
-            return null;
-        }
-        val modulWithFirstYangStatement = pathArgs.filter[s|s.contains(":")].head
-        val startModule = modulWithFirstYangStatement.toModuleName();
-        return getLatestModule(startModule)
+    private static def findModule(SchemaContext context,String argument) {
+        //checkPreconditions
+        checkNotNull(argument);
+        val startModule = argument.toModuleName();
+        return context.getLatestModule(startModule)
     }
 
-    def getLatestModule(String moduleName) {
-        checkPreconditions
+    static def getLatestModule(SchemaContext schema,String moduleName) {
+        checkArgument(schema != null);
         checkArgument(moduleName !== null && !moduleName.empty)
-        val modules = schemas.modules.filter[m|m.name == moduleName]
+        val modules = schema.modules.filter[m|m.name == moduleName]
         var latestModule = modules.head
         for (module : modules) {
             if (module.revision.after(latestModule.revision)) {
@@ -112,7 +113,7 @@ class ControllerContext implements SchemaServiceListener {
         val elements = path.path;
         val ret = new StringBuilder();
         val startQName = elements.get(0).nodeType;
-        val initialModule = schemas.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
+        val initialModule = globalSchema.findModuleByNamespaceAndRevision(startQName.namespace, startQName.revision)
         var node = initialModule as DataSchemaNode;
         for (element : elements) {
             node = node.childByQName(element.nodeType);
@@ -139,7 +140,7 @@ class ControllerContext implements SchemaServiceListener {
         checkPreconditions
         var module = uriToModuleName.get(namespace)
         if (module === null) {
-            val moduleSchemas = schemas.findModuleByNamespace(namespace);
+            val moduleSchemas = globalSchema.findModuleByNamespace(namespace);
             if(moduleSchemas === null) return null
             var latestModule = moduleSchemas.head
             for (m : moduleSchemas) {
@@ -157,7 +158,7 @@ class ControllerContext implements SchemaServiceListener {
     def findNamespaceByModule(String module) {
         var namespace = moduleNameToUri.get(module)
         if (namespace === null) {
-            val moduleSchemas = schemas.modules.filter[it|it.name.equals(module)]
+            val moduleSchemas = globalSchema.modules.filter[it|it.name.equals(module)]
             var latestModule = moduleSchemas.head
             for (m : moduleSchemas) {
                 if (m.revision.after(latestModule.revision)) {
@@ -175,7 +176,7 @@ class ControllerContext implements SchemaServiceListener {
         checkPreconditions
         var module = uriToModuleName.get(qname.namespace)
         if (module === null) {
-            val moduleSchema = schemas.findModuleByNamespaceAndRevision(qname.namespace, qname.revision);
+            val moduleSchema = globalSchema.findModuleByNamespaceAndRevision(qname.namespace, qname.revision);
             if(moduleSchema === null) throw new IllegalArgumentException()
             uriToModuleName.put(qname.namespace, moduleSchema.name)
             module = moduleSchema.name;
@@ -244,25 +245,22 @@ class ControllerContext implements SchemaServiceListener {
         }
         val nodeRef = strings.head;
 
-        val nodeName = nodeRef.toNodeName();
-        val targetNode = parentNode.getDataChildByName(nodeName);
-        if (targetNode === null) {
-            val children = parentNode.childNodes
-            for (child : children) {
-                if (child instanceof ChoiceNode) {
-                    val choice = child as ChoiceNode
-                    for (caze : choice.cases) {
-                        val result = builder.collectPathArguments(strings, caze as DataNodeContainer);
-                        if (result !== null)
-                            return result
-                    }
-                }
-            }
+        val nodeName = nodeRef.toNodeName;
+        var targetNode = parentNode.findInstanceDataChild(nodeName);
+        if (targetNode instanceof ChoiceNode) {
             return null
         }
-        if (targetNode instanceof ChoiceNode) {
+        
+        if (targetNode === null) {
+            // Node is possibly in other mount point
+            val partialPath = builder.toInstance;
+            val mountPointSchema = mountService?.getMountPoint(partialPath)?.schemaContext;
+            if(mountPointSchema != null) {
+                return builder.collectPathArguments(strings, mountPointSchema.findModule(strings.head));
+            }
             return null
         }
+        
 
         // Number of consumed elements
         var consumed = 1;
@@ -302,6 +300,32 @@ class ControllerContext implements SchemaServiceListener {
 
         return targetNode
     }
+    
+    static def DataSchemaNode findInstanceDataChild(DataNodeContainer container, String name) {
+        // FIXME: Add namespace comparison
+        var potentialNode = container.getDataChildByName(name);
+        if(potentialNode.instantiatedDataSchema) {
+            return potentialNode;
+        }
+        val allCases = container.childNodes.filter(ChoiceNode).map[cases].flatten
+        for (caze : allCases) {
+            potentialNode = caze.findInstanceDataChild(name);
+            if(potentialNode != null) {
+                return potentialNode;
+            }
+        }
+        return null;
+    }
+    
+    static def boolean isInstantiatedDataSchema(DataSchemaNode node) {
+        switch node {
+            LeafSchemaNode: return true
+            LeafListSchemaNode: return true
+            ContainerSchemaNode: return true
+            ListSchemaNode: return true
+            default: return false
+        }
+    }
 
     private def void addKeyValue(HashMap<QName, Object> map, DataSchemaNode node, String uriValue) {
         checkNotNull(uriValue);
@@ -319,7 +343,7 @@ class ControllerContext implements SchemaServiceListener {
         map.put(node.QName, decoded);
     }
 
-    private def String toModuleName(String str) {
+    private static def String toModuleName(String str) {
         checkNotNull(str)
         if (str.contains(":")) {
             val args = str.split(":");
@@ -343,7 +367,7 @@ class ControllerContext implements SchemaServiceListener {
     private def QName toQName(String name) {
         val module = name.toModuleName;
         val node = name.toNodeName;
-        val namespace = FluentIterable.from(schemas.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)]) //
+        val namespace = FluentIterable.from(globalSchema.modules.sort[o1,o2 | o1.revision.compareTo(o2.revision)]) //
             .transform[QName.create(namespace,revision,it.name)].findFirst[module == localName]
         ;
         return QName.create(namespace,node);
@@ -354,7 +378,7 @@ class ControllerContext implements SchemaServiceListener {
     }
 
     override onGlobalContextUpdated(SchemaContext context) {
-        this.schemas = context;
+        this.globalSchema = context;
         for (operation : context.operations) {
             val qname = operation.QName;
             qnameToRpc.put(qname, operation);
index 39c0d3b34f67159e7de8a1c1fc4a73809ae648b8..08ec3f2b0c73c192a02f237132513ccce9b9522e 100644 (file)
@@ -7,8 +7,15 @@ import java.util.Set;
 
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.mockito.Mock;
+
+import static org.mockito.Mockito.*;
+
+import org.opendaylight.controller.sal.core.api.mount.MountInstance;
+import org.opendaylight.controller.sal.core.api.mount.MountService;
 import org.opendaylight.controller.sal.restconf.impl.ControllerContext;
 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
+import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
@@ -19,14 +26,16 @@ public class ControllerContextTest {
 
     @BeforeClass
     public static void init() throws FileNotFoundException {
-        Set<Module> allModules = TestUtils.loadModules(ControllerContextTest.class.getResource("/full-versions/yangs").getPath());
+        Set<Module> allModules = TestUtils.loadModules(ControllerContextTest.class.getResource("/full-versions/yangs")
+                .getPath());
         SchemaContext schemaContext = TestUtils.loadSchemaContext(allModules);
         controllerContext.setSchemas(schemaContext);
     }
 
     @Test
     public void testToInstanceIdentifierList() throws FileNotFoundException {
-        InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:userWithoutClass/foo");
+        InstanceIdWithSchemaNode instanceIdentifier = controllerContext
+                .toInstanceIdentifier("simple-nodes:userWithoutClass/foo");
         assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "userWithoutClass");
 
         instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:userWithoutClass/foo/full-name");
@@ -49,28 +58,63 @@ public class ControllerContextTest {
 
     }
 
+    @Test
+    public void testToInstanceIdentifierMountPoint() throws FileNotFoundException {
+        try {
+            String mountPointPath = "simple-nodes:user/foo/boo";
+            String nestedPath = "simple-nodes:user/foo/boo/simple-nodes:users";
+            InstanceIdWithSchemaNode mountInstanceIdentifier = controllerContext.toInstanceIdentifier(mountPointPath);
+            assertEquals("user", mountInstanceIdentifier.getSchemaNode().getQName().getLocalName());
+
+            MountInstance mountInstance = mock(MountInstance.class);
+            MountService mountService = mock(MountService.class);
+
+            controllerContext.setMountService(mountService);
+            // when(mountService.getMountPoint(any(InstanceIdentifier.class))).thenReturn(null);
+
+            when(mountService.getMountPoint(eq(mountInstanceIdentifier.getInstanceIdentifier()))).thenReturn(
+                    mountInstance);
+
+            when(mountInstance.getSchemaContext()).thenReturn(controllerContext.getGlobalSchema());
+
+            InstanceIdWithSchemaNode mountedInstanceIdentifier = controllerContext.toInstanceIdentifier(nestedPath);
+            assertEquals("users", mountedInstanceIdentifier.getSchemaNode().getQName().getLocalName());
+
+            mountedInstanceIdentifier = controllerContext.toInstanceIdentifier(mountPointPath + "/" + mountPointPath);
+            assertEquals("user", mountedInstanceIdentifier.getSchemaNode().getQName().getLocalName());
+
+            mountedInstanceIdentifier = controllerContext
+                    .toInstanceIdentifier("simple-nodes:user/foo/var/simple-nodes:users");
+            assertNull(mountedInstanceIdentifier);
+
+        } finally {
+            controllerContext.setMountService(null);
+        }
+
+    }
+
     @Test
     public void testToInstanceIdentifierContainer() throws FileNotFoundException {
         InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:users");
         assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "users");
         assertTrue(instanceIdentifier.getSchemaNode() instanceof ContainerSchemaNode);
-        assertEquals(2, ((ContainerSchemaNode)instanceIdentifier.getSchemaNode()).getChildNodes().size());
+        assertEquals(2, ((ContainerSchemaNode) instanceIdentifier.getSchemaNode()).getChildNodes().size());
     }
 
     @Test
     public void testToInstanceIdentifierChoice() throws FileNotFoundException {
         InstanceIdWithSchemaNode instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/beer");
         assertEquals(instanceIdentifier.getSchemaNode().getQName().getLocalName(), "beer");
-        
+
         instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/snack");
         assertNull(instanceIdentifier);
-        
+
         instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/sports-arena");
         assertNull(instanceIdentifier);
-        
+
         instanceIdentifier = controllerContext.toInstanceIdentifier("simple-nodes:food/snack/sports-arena");
         assertNull(instanceIdentifier);
-        
+
     }
 
 }
index cac77eb368d1423fa91c33a304e0695d208c4e2a..0bb03cb0ad77b5d7da5468f84faa05983e3d8703 100644 (file)
@@ -86,6 +86,7 @@ public class ReadConfAndOperDataTest extends JerseyTest {
     public void testReadOperationalData() throws UnsupportedEncodingException, FileNotFoundException {
         String uri = createUri("/operational/", "ietf-interfaces:interfaces/interface/eth0");
 
+        
         CompositeNode loadedCompositeNode = TestUtils.loadCompositeNodeWithXmlTreeBuilder("/parts/ietf-interfaces_interfaces.xml");
         when(brokerFacade.readOperationalData(any(InstanceIdentifier.class))).thenReturn(loadedCompositeNode);
 
index 2ce779a1f54e9ce4f68b9c7f1db478d9de4e0201..8b8c886a1c0328b558a4992c7d67b6b99b71800c 100644 (file)
@@ -71,6 +71,10 @@ public class NetconfClient implements Closeable {
         return new NetconfClient(clientLabelForLogging,address,strat,netconfClientDispatcher);
     }
 
+    public static NetconfClient clientFor(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat, NetconfClientDispatcher netconfClientDispatcher,NetconfClientSessionListener listener) throws InterruptedException {
+        return new NetconfClient(clientLabelForLogging,address,strat,netconfClientDispatcher,listener);
+    }
+
     public NetconfClient(String clientLabelForLogging, InetSocketAddress address, int connectTimeoutMs,
             NetconfClientDispatcher netconfClientDispatcher) throws InterruptedException {
         this(clientLabelForLogging, address,
@@ -83,6 +87,17 @@ public class NetconfClient implements Closeable {
                 DEFAULT_CONNECT_TIMEOUT), netconfClientDispatcher);
     }
 
+    public NetconfClient(String clientLabelForLogging, InetSocketAddress address, ReconnectStrategy strat,
+            NetconfClientDispatcher netconfClientDispatcher, NetconfClientSessionListener listener) throws InterruptedException{
+        this.label = clientLabelForLogging;
+        dispatch = netconfClientDispatcher;
+        sessionListener = listener;
+        Future<NetconfClientSession> clientFuture = dispatch.createClient(address, sessionListener, strat);
+        this.address = address;
+        clientSession = get(clientFuture);
+        this.sessionId = clientSession.getSessionId();
+    }
+
     public NetconfMessage sendMessage(NetconfMessage message) {
         return sendMessage(message, 5, 1000);
     }
@@ -90,6 +105,7 @@ public class NetconfClient implements Closeable {
     public NetconfMessage sendMessage(NetconfMessage message, int attempts, int attemptMsDelay) {
         long startTime = System.currentTimeMillis();
         Preconditions.checkState(clientSession.isUp(), "Session was not up yet");
+        //logger.debug("Sending message: {}",XmlUtil.toString(message.getDocument()));
         clientSession.sendMessage(message);
         try {
             return sessionListener.getLastMessage(attempts, attemptMsDelay);