Merge "Move sal-remote to sal-rest-connector"
authorTomas Cere <tcere@cisco.com>
Thu, 29 Sep 2016 15:24:05 +0000 (15:24 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 29 Sep 2016 15:24:05 +0000 (15:24 +0000)
30 files changed:
netconf/config-netconf-connector/src/test/java/org/opendaylight/netconf/confignetconfconnector/osgi/NetconfOperationServiceImplTest.java [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditConfig.java
netconf/mdsal-netconf-connector/src/test/java/org/opendaylight/netconf/mdsal/connector/ops/NetconfMDSalMappingTest.java
netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/editConfigs/edit-config-merge-map-entry.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/editConfigs/edit-config-replace-map-entry.xml [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/get-config-map-entry.xml [new file with mode: 0644]
netconf/mdsal-netconf-notification/src/test/java/org/opendaylight/controller/config/yang/netconf/mdsal/notification/CapabilityChangeNotificationProducerTest.java
netconf/netconf-api/src/test/java/org/opendaylight/netconf/api/monitoring/SessionEventTest.java [new file with mode: 0644]
netconf/netconf-notifications-impl/src/main/java/org/opendaylight/netconf/notifications/impl/osgi/Activator.java
netconf/netconf-notifications-impl/src/test/java/org/opendaylight/netconf/notifications/impl/osgi/ActivatorTest.java [new file with mode: 0644]
netconf/sal-netconf-connector/pom.xml
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/SchemalessNetconfDevice.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacade.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/ReadOnlyTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/WriteCandidateRunningTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/WriteCandidateTx.java
netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/tx/WriteRunningTx.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/SchemalessNetconfDeviceTest.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacadeTest.java [new file with mode: 0644]
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapterTest.java
netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/RemoteDeviceIdTest.java [new file with mode: 0644]
netconf/yanglib/pom.xml
netconf/yanglib/src/main/java/org/opendaylight/yanglib/impl/YangLibServiceImpl.java
netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibRestAppTest.java [new file with mode: 0644]
netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibServiceImplTest.java [new file with mode: 0644]
pom.xml
restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/BaseYangSwaggerGenerator.java
restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/ModelGenerator.java
restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/model/builder/OperationBuilder.java
restconf/sal-rest-docgen/src/test/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGeneratorTest.java

diff --git a/netconf/config-netconf-connector/src/test/java/org/opendaylight/netconf/confignetconfconnector/osgi/NetconfOperationServiceImplTest.java b/netconf/config-netconf-connector/src/test/java/org/opendaylight/netconf/confignetconfconnector/osgi/NetconfOperationServiceImplTest.java
new file mode 100644 (file)
index 0000000..f286bd3
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2016 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.netconf.confignetconfconnector.osgi;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.controller.config.facade.xml.ConfigSubsystemFacade;
+import org.opendaylight.netconf.confignetconfconnector.operations.Commit;
+import org.opendaylight.netconf.confignetconfconnector.operations.DiscardChanges;
+import org.opendaylight.netconf.confignetconfconnector.operations.Lock;
+import org.opendaylight.netconf.confignetconfconnector.operations.UnLock;
+import org.opendaylight.netconf.confignetconfconnector.operations.Validate;
+import org.opendaylight.netconf.confignetconfconnector.operations.editconfig.EditConfig;
+import org.opendaylight.netconf.confignetconfconnector.operations.get.Get;
+import org.opendaylight.netconf.confignetconfconnector.operations.getconfig.GetConfig;
+import org.opendaylight.netconf.confignetconfconnector.operations.runtimerpc.RuntimeRpc;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+
+public class NetconfOperationServiceImplTest {
+
+    @Test
+    public void testOperationService() {
+        final ConfigSubsystemFacade configSubsystemFacade = mock(ConfigSubsystemFacade.class);
+        final NetconfOperationService netconfOperationService =
+                new NetconfOperationServiceImpl(configSubsystemFacade, "reportingID");
+
+        // testing operations in Set from NetconfOperationProvider
+
+        Set<NetconfOperation> operations = netconfOperationService.getNetconfOperations();
+
+        assertTrue(containInstance(operations, GetConfig.class));
+        assertTrue(containInstance(operations, EditConfig.class));
+        assertTrue(containInstance(operations, Commit.class));
+        assertTrue(containInstance(operations, Lock.class));
+        assertTrue(containInstance(operations, UnLock.class));
+        assertTrue(containInstance(operations, Get.class));
+        assertTrue(containInstance(operations, DiscardChanges.class));
+        assertTrue(containInstance(operations, Validate.class));
+        assertTrue(containInstance(operations, RuntimeRpc.class));
+
+        // verify closing service
+
+        doNothing().when(configSubsystemFacade).close();
+        netconfOperationService.close();
+
+        verify(configSubsystemFacade, times(1)).close();
+    }
+
+    private boolean containInstance(final Set<NetconfOperation> operations, final Class<?> cls) {
+        return operations.stream().filter(cls::isInstance).findFirst().isPresent();
+    }
+}
index 0d569e60121b2db634a281faf9f8973787a4f2e9..0a90d9c9d47aa980403da8fde1839acc03e7899b 100644 (file)
@@ -32,7 +32,10 @@ import org.opendaylight.netconf.util.mapping.AbstractSingletonNetconfOperation;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
 import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
@@ -76,7 +79,7 @@ public class EditConfig extends AbstractSingletonNetconfOperation {
 
         final XmlElement configElement = getElement(operationElement, CONFIG_KEY);
 
-        for (XmlElement element : configElement.getChildElements()) {
+        for (final XmlElement element : configElement.getChildElements()) {
             final String ns = element.getNamespace();
             final DataSchemaNode schemaNode = getSchemaNodeFromNamespace(ns, element).get();
 
@@ -102,45 +105,70 @@ public class EditConfig extends AbstractSingletonNetconfOperation {
     }
 
     private void executeChange(final DOMDataReadWriteTransaction rwtx, final DataTreeChange change) throws DocumentedException {
+        final YangInstanceIdentifier path = YangInstanceIdentifier.create(change.getPath());
+        final NormalizedNode<?, ?> changeData = change.getChangeRoot();
         switch (change.getAction()) {
         case NONE:
             return;
         case MERGE:
-            rwtx.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot());
+            createMapNodeIfNonExistent(rwtx, path, changeData);
+            rwtx.merge(LogicalDatastoreType.CONFIGURATION, path, changeData);
             break;
         case CREATE:
             try {
-                final Optional<NormalizedNode<?, ?>> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())).checkedGet();
+                final Optional<NormalizedNode<?, ?>> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, path).checkedGet();
                 if (readResult.isPresent()) {
                     throw new DocumentedException("Data already exists, cannot execute CREATE operation", ErrorType.protocol, ErrorTag.data_exists, ErrorSeverity.error);
                 }
-                rwtx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot());
-            } catch (ReadFailedException e) {
+                createMapNodeIfNonExistent(rwtx, path, changeData);
+                rwtx.put(LogicalDatastoreType.CONFIGURATION, path, changeData);
+            } catch (final ReadFailedException e) {
                 LOG.warn("Read from datastore failed when trying to read data for create operation", change, e);
             }
             break;
         case REPLACE:
-            rwtx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()), change.getChangeRoot());
+            createMapNodeIfNonExistent(rwtx, path, changeData);
+            rwtx.put(LogicalDatastoreType.CONFIGURATION, path, changeData);
             break;
         case DELETE:
             try {
-                final Optional<NormalizedNode<?, ?>> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath())).checkedGet();
+                final Optional<NormalizedNode<?, ?>> readResult = rwtx.read(LogicalDatastoreType.CONFIGURATION, path).checkedGet();
                 if (!readResult.isPresent()) {
                     throw new DocumentedException("Data is missing, cannot execute DELETE operation", ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error);
                 }
-                rwtx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()));
-            } catch (ReadFailedException e) {
+                rwtx.delete(LogicalDatastoreType.CONFIGURATION, path);
+            } catch (final ReadFailedException e) {
                 LOG.warn("Read from datastore failed when trying to read data for delete operation", change, e);
             }
             break;
         case REMOVE:
-            rwtx.delete(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(change.getPath()));
+            rwtx.delete(LogicalDatastoreType.CONFIGURATION, path);
             break;
         default:
             LOG.warn("Unknown/not implemented operation, not executing");
         }
     }
 
+    private void createMapNodeIfNonExistent(final DOMDataReadWriteTransaction rwtx, final YangInstanceIdentifier path,
+                                            final NormalizedNode change) throws DocumentedException {
+        if (!(change instanceof MapEntryNode)) {
+            return;
+        }
+        final YangInstanceIdentifier mapNodeYid = path.getParent();
+        try {
+            final Boolean mapNodeExists = rwtx.exists(LogicalDatastoreType.CONFIGURATION, mapNodeYid).checkedGet();
+            if (!mapNodeExists) {
+                final MapNode mixinNode = Builders.mapBuilder()
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(mapNodeYid.getLastPathArgument().getNodeType()))
+                        .build();
+                rwtx.put(LogicalDatastoreType.CONFIGURATION, mapNodeYid, mixinNode);
+            }
+        } catch (final ReadFailedException e) {
+            throw new DocumentedException("List node existence check failed", e, ErrorType.protocol, ErrorTag.data_missing, ErrorSeverity.error);
+
+        }
+    }
+
     private NormalizedNode parseIntoNormalizedNode(final DataSchemaNode schemaNode, final XmlElement element,
                                                    final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider) {
 
@@ -172,7 +200,7 @@ public class EditConfig extends AbstractSingletonNetconfOperation {
                 throw new NetconfDocumentedException("Unable to find module by namespace: " + namespace,
                         ErrorType.application, ErrorTag.unknown_namespace, ErrorSeverity.error);
             }
-            DataSchemaNode schemaNode =
+            final DataSchemaNode schemaNode =
                     module.getDataChildByName(QName.create(module.getQNameModule(), element.getName()));
             if (schemaNode != null) {
                 dataSchemaNode = Optional.of(schemaNode);
@@ -182,7 +210,7 @@ public class EditConfig extends AbstractSingletonNetconfOperation {
                         ErrorTag.unknown_namespace,
                         ErrorSeverity.error);
             }
-        } catch (URISyntaxException e) {
+        } catch (final URISyntaxException e) {
             LOG.debug("Unable to create URI for namespace : {}", namespace);
         }
 
@@ -215,7 +243,7 @@ public class EditConfig extends AbstractSingletonNetconfOperation {
 
     }
 
-    private XmlElement getElement(final XmlElement operationElement, String elementName) throws DocumentedException {
+    private XmlElement getElement(final XmlElement operationElement, final String elementName) throws DocumentedException {
         final Optional<XmlElement> childNode = operationElement.getOnlyChildElementOptionally(elementName);
         if (!childNode.isPresent()) {
             throw new DocumentedException(elementName + " element is missing",
index 11c8473606401e541355a8e7b8eaf98e2521e0d0..623162d74bf1f48d2b6deb651ee109e96f9c74df 100644 (file)
@@ -468,6 +468,20 @@ public class NetconfMDSalMappingTest {
         deleteDatastore();
     }
 
+    @Test
+    public void testReplaceMapEntry() throws Exception {
+        verifyResponse(edit("messages/mapping/editConfigs/edit-config-replace-map-entry.xml"), RPC_REPLY_OK);
+        verifyResponse(commit(), RPC_REPLY_OK);
+        verifyResponse(getConfigRunning(), XmlFileLoader.xmlFileToDocument("messages/mapping/get-config-map-entry.xml"));
+    }
+
+    @Test
+    public void testMergeMapEntry() throws Exception {
+        verifyResponse(edit("messages/mapping/editConfigs/edit-config-merge-map-entry.xml"), RPC_REPLY_OK);
+        verifyResponse(commit(), RPC_REPLY_OK);
+        verifyResponse(getConfigRunning(), XmlFileLoader.xmlFileToDocument("messages/mapping/get-config-map-entry.xml"));
+    }
+
     @Test
     public void testFiltering() throws Exception {
 
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/editConfigs/edit-config-merge-map-entry.xml b/netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/editConfigs/edit-config-merge-map-entry.xml
new file mode 100644 (file)
index 0000000..870ec0a
--- /dev/null
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (c) 2016 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
+  -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>none</default-operation>
+        <config>
+            <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+                <mapping-node xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="merge">
+                    <id>id1</id>
+                    <content>content1</content>
+                </mapping-node>
+            </mapping-nodes>
+        </config>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/editConfigs/edit-config-replace-map-entry.xml b/netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/editConfigs/edit-config-replace-map-entry.xml
new file mode 100644 (file)
index 0000000..d3dc541
--- /dev/null
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (c) 2016 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
+  -->
+
+<rpc message-id="a" a="64" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
+    <edit-config>
+        <target>
+            <candidate/>
+        </target>
+        <default-operation>none</default-operation>
+        <config>
+            <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+                <mapping-node xmlns:a="urn:ietf:params:xml:ns:netconf:base:1.0" a:operation="replace">
+                    <id>id1</id>
+                    <content>content1</content>
+                </mapping-node>
+            </mapping-nodes>
+        </config>
+    </edit-config>
+</rpc>
\ No newline at end of file
diff --git a/netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/get-config-map-entry.xml b/netconf/mdsal-netconf-connector/src/test/resources/messages/mapping/get-config-map-entry.xml
new file mode 100644 (file)
index 0000000..393f336
--- /dev/null
@@ -0,0 +1,18 @@
+<!--
+  ~ Copyright (c) 2016 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
+  -->
+
+<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" a="64" id="a" message-id="101" xmlnx="a:b:c:d">
+    <data>
+        <mapping-nodes xmlns="urn:opendaylight:mdsal:mapping:test">
+            <mapping-node>
+                <id>id1</id>
+                <content>content1</content>
+            </mapping-node>
+        </mapping-nodes>
+    </data>
+</rpc-reply>
\ No newline at end of file
index 8149ea00ff0b5e5e20b584798a6dbb899142bff1..e2156413bcd178af85de108ff27ff84682940d60 100644 (file)
@@ -57,7 +57,8 @@ public class CapabilityChangeNotificationProducerTest {
         Capabilities newCapabilities = new CapabilitiesBuilder().setCapability(newCapabilitiesList).build();
         Map<InstanceIdentifier<?>, DataObject> createdData = Maps.newHashMap();
         createdData.put(capabilitiesIdentifier, newCapabilities);
-        verifyDataTreeChange(null, newCapabilities, changedCapabilitesFrom(newCapabilitiesList, Collections.<Uri>emptyList()));
+        verifyDataTreeChange(DataObjectModification.ModificationType.WRITE, null, newCapabilities,
+                changedCapabilitesFrom(newCapabilitiesList, Collections.<Uri>emptyList()));
     }
 
     @Test
@@ -66,16 +67,27 @@ public class CapabilityChangeNotificationProducerTest {
         final List<Uri> updatedCapabilitiesList = Lists.newArrayList(new Uri("originalCapability"), new Uri("newCapability"));
         Capabilities originalCapabilities = new CapabilitiesBuilder().setCapability(originalCapabilitiesList).build();
         Capabilities updatedCapabilities = new CapabilitiesBuilder().setCapability(updatedCapabilitiesList).build();
-        verifyDataTreeChange(originalCapabilities, updatedCapabilities, changedCapabilitesFrom(
+        verifyDataTreeChange(DataObjectModification.ModificationType.WRITE, originalCapabilities, updatedCapabilities, changedCapabilitesFrom(
                 Lists.newArrayList(new Uri("newCapability")), Lists.newArrayList(new Uri("anotherOriginalCapability"))));
     }
 
+    @Test
+    public void testOnDataChangedDelete() {
+        final List<Uri> originalCapabilitiesList = Lists.newArrayList(new Uri("originalCapability"),
+                new Uri("anotherOriginalCapability"));
+        final Capabilities originalCapabilities =
+                new CapabilitiesBuilder().setCapability(originalCapabilitiesList).build();
+        verifyDataTreeChange(DataObjectModification.ModificationType.DELETE, originalCapabilities, null,
+                changedCapabilitesFrom(Collections.emptyList(), originalCapabilitiesList));
+    }
+
     @SuppressWarnings("unchecked")
-    private void verifyDataTreeChange(Capabilities originalCapabilities, Capabilities updatedCapabilities,
+    private void verifyDataTreeChange(final DataObjectModification.ModificationType modificationType,
+                                      Capabilities originalCapabilities, Capabilities updatedCapabilities,
                                       NetconfCapabilityChange expectedChange) {
         final DataTreeModification<Capabilities> treeChange2 = mock(DataTreeModification.class);
         final DataObjectModification<Capabilities> objectChange2 = mock(DataObjectModification.class);
-        doReturn(DataObjectModification.ModificationType.WRITE).when(objectChange2).getModificationType();
+        doReturn(modificationType).when(objectChange2).getModificationType();
         doReturn(objectChange2).when(treeChange2).getRootNode();
         doReturn(originalCapabilities).when(objectChange2).getDataBefore();
         doReturn(updatedCapabilities).when(objectChange2).getDataAfter();
diff --git a/netconf/netconf-api/src/test/java/org/opendaylight/netconf/api/monitoring/SessionEventTest.java b/netconf/netconf-api/src/test/java/org/opendaylight/netconf/api/monitoring/SessionEventTest.java
new file mode 100644 (file)
index 0000000..da4e4b2
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 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.netconf.api.monitoring;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+public class SessionEventTest {
+
+    @Test
+    public void test() {
+        final NetconfManagementSession session = mock(NetconfManagementSession.class);
+
+        assertEquals(SessionEvent.Type.IN_RPC_FAIL, SessionEvent.inRpcFail(session).getType());
+        assertEquals(SessionEvent.Type.IN_RPC_SUCCESS, SessionEvent.inRpcSuccess(session).getType());
+        assertEquals(SessionEvent.Type.NOTIFICATION, SessionEvent.notification(session).getType());
+        assertEquals(SessionEvent.Type.OUT_RPC_ERROR, SessionEvent.outRpcError(session).getType());
+    }
+}
index 89aee08e87f22476577928136c5f628647e9c3b1..1cbd5237389dad5900da775ce0458399ecb20721 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.netconf.notifications.impl.osgi;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Sets;
 import java.util.Collections;
 import java.util.Dictionary;
@@ -107,4 +108,9 @@ public class Activator implements BundleActivator {
             operationaServiceRegistration = null;
         }
     }
+
+    @VisibleForTesting
+    NetconfNotificationManager getNetconfNotificationManager() {
+        return netconfNotificationManager;
+    }
 }
diff --git a/netconf/netconf-notifications-impl/src/test/java/org/opendaylight/netconf/notifications/impl/osgi/ActivatorTest.java b/netconf/netconf-notifications-impl/src/test/java/org/opendaylight/netconf/notifications/impl/osgi/ActivatorTest.java
new file mode 100644 (file)
index 0000000..24e8652
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2016 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.netconf.notifications.impl.osgi;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Set;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.opendaylight.controller.config.util.capability.BasicCapability;
+import org.opendaylight.controller.config.util.capability.Capability;
+import org.opendaylight.netconf.api.monitoring.CapabilityListener;
+import org.opendaylight.netconf.api.util.NetconfConstants;
+import org.opendaylight.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.netconf.mapping.api.NetconfOperationServiceFactory;
+import org.opendaylight.netconf.notifications.NetconfNotification;
+import org.opendaylight.netconf.notifications.NetconfNotificationCollector;
+import org.opendaylight.netconf.notifications.impl.NetconfNotificationManager;
+import org.opendaylight.netconf.notifications.impl.ops.CreateSubscription;
+import org.opendaylight.netconf.notifications.impl.ops.Get;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+public class ActivatorTest {
+
+    @Test
+    public void testActivator() throws Exception {
+        final Activator activator = new Activator();
+        final BundleContext context = mock(BundleContext.class);
+
+
+        final ServiceRegistration netconfNotificationCollectorServiceRegistration  = mock(ServiceRegistration.class);
+        final ServiceRegistration operationaServiceRegistration = mock(ServiceRegistration.class);
+
+        // test registering services
+        doReturn(netconfNotificationCollectorServiceRegistration).when(context).
+                registerService(eq(NetconfNotificationCollector.class), any(NetconfNotificationManager.class), any());
+        doReturn(operationaServiceRegistration).when(context).
+                registerService(eq(NetconfOperationServiceFactory.class), any(NetconfOperationServiceFactory.class), any());
+
+        activator.start(context);
+
+        verify(context, times(1)).registerService(eq(NetconfNotificationCollector.class),
+                any(NetconfNotificationManager.class), eq(new Hashtable<>()));
+
+        final ArgumentCaptor<NetconfOperationServiceFactory> serviceFactoryArgumentCaptor =
+                ArgumentCaptor.forClass(NetconfOperationServiceFactory.class);
+
+        final Dictionary<String, String> properties = new Hashtable<>();
+        properties.put(NetconfConstants.SERVICE_NAME, NetconfConstants.NETCONF_MONITORING);
+
+        verify(context, times(1)).registerService(eq(NetconfOperationServiceFactory.class),
+                serviceFactoryArgumentCaptor.capture(), eq(properties));
+
+        // test service factory argument requisites
+        final NetconfOperationServiceFactory serviceFactory = serviceFactoryArgumentCaptor.getValue();
+
+        final Set<Capability> capabilities = Collections.singleton(new BasicCapability(NetconfNotification.NOTIFICATION_NAMESPACE));
+
+        assertEquals(capabilities.iterator().next().getCapabilityUri(), serviceFactory.getCapabilities().iterator().next().getCapabilityUri());
+        assertEquals(capabilities.iterator().next().getCapabilitySchema(), serviceFactory.getCapabilities().iterator().next().getCapabilitySchema());
+        assertEquals(capabilities.iterator().next().getModuleNamespace(), serviceFactory.getCapabilities().iterator().next().getModuleNamespace());
+        assertEquals(capabilities.iterator().next().getModuleName(), serviceFactory.getCapabilities().iterator().next().getModuleName());
+
+        final CapabilityListener listener = mock(CapabilityListener.class);
+
+        doNothing().when(listener).onCapabilitiesChanged(any(), any());
+
+        serviceFactory.registerCapabilityListener(listener);
+
+        verify(listener).onCapabilitiesChanged(serviceFactory.getCapabilities(), Collections.emptySet());
+
+        final NetconfOperationService netconfOperationService = serviceFactory.createService("id");
+        final Set<NetconfOperation> netconfOperations = netconfOperationService.getNetconfOperations();
+
+        final CreateSubscription createSubscription = new CreateSubscription("id", activator.getNetconfNotificationManager());
+
+        netconfOperations.forEach(
+                operation -> {
+                    if (operation instanceof CreateSubscription) {
+                        assertEquals(createSubscription.toString(), operation.toString());
+                    }
+                    if (operation instanceof Get) {
+                        assertEquals("id", ((Get) operation).getNetconfSessionIdForReporting());
+                    }
+                }
+        );
+
+        // test unregister after stop
+        doNothing().when(netconfNotificationCollectorServiceRegistration).unregister();
+        doNothing().when(operationaServiceRegistration).unregister();
+
+        activator.stop(context);
+
+        verify(netconfNotificationCollectorServiceRegistration, times(1)).unregister();
+        verify(operationaServiceRegistration, times(1)).unregister();
+
+    }
+
+
+}
index 7a91d064e51775ec7715e883614d582bd41b16e7..9adcf03eb64f716cc55dd7b152132a5d7b9c3b2a 100644 (file)
       <artifactId>sal-distributed-datastore</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.powermock</groupId>
+      <artifactId>powermock-module-junit4</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.powermock</groupId>
+      <artifactId>powermock-api-mockito</artifactId>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <scm>
index 758c46c80df8a9d73a6160a7dd988fe26c145f25..5a7e64c0f58aa54f489eb3b6fd38af2e44c2d95b 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.netconf.sal.connect.netconf;
 
+import com.google.common.annotations.VisibleForTesting;
 import org.opendaylight.netconf.api.NetconfMessage;
 import org.opendaylight.netconf.sal.connect.api.RemoteDevice;
 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
@@ -36,6 +37,16 @@ public class SchemalessNetconfDevice implements
         messageTransformer = new SchemalessMessageTransformer(counter);
     }
 
+    @VisibleForTesting
+    SchemalessNetconfDevice(final RemoteDeviceId id, final RemoteDeviceHandler<NetconfSessionPreferences> salFacade,
+                            final SchemalessMessageTransformer messageTransformer) {
+        this.id = id;
+        this.salFacade = salFacade;
+        final MessageCounter counter = new MessageCounter();
+        rpcTransformer = new BaseRpcSchemalessTransformer(counter);
+        this.messageTransformer = messageTransformer;
+    }
+
     @Override public void onRemoteSessionUp(final NetconfSessionPreferences remoteSessionCapabilities,
                                             final NetconfDeviceCommunicator netconfDeviceCommunicator) {
         final SchemalessNetconfDeviceRpc schemalessNetconfDeviceRpc = new SchemalessNetconfDeviceRpc(id,
index 829487a409a232c7ab040fbe6eee013047b7c613..90408690f33d008960856597a611ed7db2988e7b 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.netconf.sal.connect.netconf.sal;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Lists;
 import java.util.List;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
@@ -37,6 +38,14 @@ public final class NetconfDeviceSalFacade implements AutoCloseable, RemoteDevice
         registerToSal(domBroker, bindingBroker);
     }
 
+    @VisibleForTesting
+    NetconfDeviceSalFacade(final RemoteDeviceId id, NetconfDeviceSalProvider salProvider,
+                           final Broker domBroker, final BindingAwareBroker bindingBroker) {
+        this.id = id;
+        this.salProvider = salProvider;
+        registerToSal(domBroker, bindingBroker);
+    }
+
     public void registerToSal(final Broker domRegistryDependency, final BindingAwareBroker bindingBroker) {
         domRegistryDependency.registerProvider(salProvider);
         bindingBroker.registerProvider(salProvider);
index 8087fde8b709a95dc9c4d8e1f22c24a98033fc35..56fa78fe12faecda5d43e941c8fcc26cf98fb716 100644 (file)
@@ -10,15 +10,14 @@ package org.opendaylight.netconf.sal.connect.netconf.sal.tx;
 
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import java.util.concurrent.ExecutionException;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
+import org.opendaylight.netconf.sal.connect.netconf.util.NetconfRpcFutureCallback;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
 import org.opendaylight.yangtools.util.concurrent.MappingCheckedFuture;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -33,41 +32,24 @@ public final class ReadOnlyTx implements DOMDataReadOnlyTransaction {
 
     private final NetconfBaseOps netconfOps;
     private final RemoteDeviceId id;
-    private final FutureCallback<DOMRpcResult> loggingCallback;
 
     public ReadOnlyTx(final NetconfBaseOps netconfOps, final RemoteDeviceId id) {
         this.netconfOps = netconfOps;
         this.id = id;
-
-        // Simple logging callback to log result of read operation
-        loggingCallback = new FutureCallback<DOMRpcResult>() {
-            @Override
-            public void onSuccess(final DOMRpcResult result) {
-                if(AbstractWriteTx.isSuccess(result)) {
-                    LOG.trace("{}: Reading data successful", id);
-                } else {
-                    LOG.warn("{}: Reading data unsuccessful: {}", id, result.getErrors());
-                }
-
-            }
-
-            @Override
-            public void onFailure(final Throwable t) {
-                LOG.warn("{}: Reading data failed", id, t);
-            }
-        };
     }
 
     private CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readConfigurationData(
             final YangInstanceIdentifier path) {
-        final ListenableFuture<Optional<NormalizedNode<?, ?>>> configRunning = netconfOps.getConfigRunningData(loggingCallback, Optional.fromNullable(path));
+        final ListenableFuture<Optional<NormalizedNode<?, ?>>> configRunning = netconfOps.getConfigRunningData(
+                new NetconfRpcFutureCallback("Data read", id), Optional.fromNullable(path));
 
         return MappingCheckedFuture.create(configRunning, ReadFailedException.MAPPER);
     }
 
     private CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> readOperationalData(
             final YangInstanceIdentifier path) {
-        final ListenableFuture<Optional<NormalizedNode<?, ?>>> configCandidate = netconfOps.getData(loggingCallback, Optional.fromNullable(path));
+        final ListenableFuture<Optional<NormalizedNode<?, ?>>> configCandidate = netconfOps.getData(
+                new NetconfRpcFutureCallback("Data read", id), Optional.fromNullable(path));
 
         return MappingCheckedFuture.create(configCandidate, ReadFailedException.MAPPER);
     }
index 40a43c1cbe6eb61e1b2873e87cb3dc7117520566..2729444311417022e777eacb78fe639d046b2b5f 100644 (file)
@@ -8,8 +8,6 @@
 
 package org.opendaylight.netconf.sal.connect.netconf.sal.tx;
 
-import com.google.common.util.concurrent.FutureCallback;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfRpcFutureCallback;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -23,7 +21,6 @@ import org.slf4j.LoggerFactory;
  *     <li>Running datastore is locked as the first thing and this lock has to succeed</li>
  * </ul>
  */
-//TODO replace custom RPCs future callbacks with NetconfRpcFutureCallback
 public class WriteCandidateRunningTx extends WriteCandidateTx {
 
     private static final Logger LOG  = LoggerFactory.getLogger(WriteCandidateRunningTx.class);
@@ -45,24 +42,7 @@ public class WriteCandidateRunningTx extends WriteCandidateTx {
     }
 
     private void lockRunning() {
-        final FutureCallback<DOMRpcResult> lockRunningCallback = new FutureCallback<DOMRpcResult>() {
-            @Override
-            public void onSuccess(DOMRpcResult result) {
-                if (isSuccess(result)) {
-                    if (LOG.isTraceEnabled()) {
-                        LOG.trace("Lock running succesfull");
-                    }
-                } else {
-                    LOG.warn("{}: lock running invoked unsuccessfully: {}", id, result.getErrors());
-                }
-            }
-
-            @Override
-            public void onFailure(Throwable t) {
-                LOG.warn("{}: Failed to lock running. Failed to initialize transaction", id, t);
-            }
-        };
-        resultsFutures.add(netOps.lockRunning(lockRunningCallback));
+        resultsFutures.add(netOps.lockRunning(new NetconfRpcFutureCallback("Lock running", id)));
     }
 
     /**
index 489cb7bad1069a862631ce60c7e937a4a563dab8..6df2239f5140698f9072809366524d63ceeffecd 100644 (file)
@@ -19,7 +19,6 @@ import javax.annotation.Nullable;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
-import org.opendaylight.netconf.api.NetconfDocumentedException;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfRpcFutureCallback;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -53,7 +52,6 @@ import org.slf4j.LoggerFactory;
  *   <li>Commit and Unlock candidate datastore async</li>
  * </ol>
  */
-//TODO replace custom RPCs future callbacks with NetconfRpcFutureCallback
 public class WriteCandidateTx extends AbstractWriteTx {
 
     private static final Logger LOG  = LoggerFactory.getLogger(WriteCandidateTx.class);
@@ -90,7 +88,7 @@ public class WriteCandidateTx extends AbstractWriteTx {
             public void onSuccess(DOMRpcResult result) {
                 if (isSuccess(result)) {
                     if (LOG.isTraceEnabled()) {
-                        LOG.trace("Lock candidate succesfull");
+                        LOG.trace("Lock candidate successful");
                     }
                 } else {
                     LOG.warn("{}: lock candidate invoked unsuccessfully: {}", id, result.getErrors());
@@ -100,10 +98,7 @@ public class WriteCandidateTx extends AbstractWriteTx {
             @Override
             public void onFailure(Throwable t) {
                 LOG.warn("Lock candidate operation failed. {}", t);
-                NetconfDocumentedException e = new NetconfDocumentedException(id + ": Lock candidate operation failed.", NetconfDocumentedException.ErrorType.application,
-                        NetconfDocumentedException.ErrorTag.operation_failed, NetconfDocumentedException.ErrorSeverity.warning);
                 discardChanges();
-                throw new RuntimeException(e);
             }
         };
         resultsFutures.add(netOps.lockCandidate(lockCandidateCallback));
@@ -172,23 +167,9 @@ public class WriteCandidateTx extends AbstractWriteTx {
                               final DataContainerChild<?, ?> editStructure,
                               final Optional<ModifyAction> defaultOperation,
                               final String operation) {
-        FutureCallback<DOMRpcResult> editConfigCallback = new FutureCallback<DOMRpcResult>() {
-            @Override
-            public void onSuccess(DOMRpcResult result) {
-                if (isSuccess(result)) {
-                    if (LOG.isTraceEnabled()) {
-                        LOG.trace("Edit candidate succesfull");
-                    }
-                } else {
-                    LOG.warn("{}: Edit candidate invoked unsuccessfully: {}", id, result.getErrors());
-                }
-            }
 
-            @Override
-            public void onFailure(Throwable t) {
-                LOG.warn("Edit candidate operation failed. {}", t);
-            }
-        };
+        NetconfRpcFutureCallback editConfigCallback = new NetconfRpcFutureCallback("Edit candidate", id);
+
         if (defaultOperation.isPresent()) {
             resultsFutures.add(netOps.editConfigCandidate(
                     editConfigCallback, editStructure, defaultOperation.get(), rollbackSupport));
index c249bf7c4ad63c819c6a9b4dbddf79cd7222cea5..4716437ca49e80e27225929c6263a2f12c601570 100644 (file)
@@ -11,12 +11,10 @@ package org.opendaylight.netconf.sal.connect.netconf.sal.tx;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
-import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfBaseOps;
 import org.opendaylight.netconf.sal.connect.netconf.util.NetconfRpcFutureCallback;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
@@ -45,7 +43,6 @@ import org.slf4j.LoggerFactory;
  *   <li>Unlock running datastore on tx commit</li>
  * </ol>
  */
-//TODO replace custom RPCs future callbacks with NetconfRpcFutureCallback
 public class WriteRunningTx extends AbstractWriteTx {
 
     private static final Logger LOG  = LoggerFactory.getLogger(WriteRunningTx.class);
@@ -61,24 +58,7 @@ public class WriteRunningTx extends AbstractWriteTx {
     }
 
     private void lock() {
-        final FutureCallback<DOMRpcResult> lockCallback = new FutureCallback<DOMRpcResult>() {
-            @Override
-            public void onSuccess(DOMRpcResult result) {
-                if (isSuccess(result)) {
-                    if (LOG.isTraceEnabled()) {
-                        LOG.trace("Lock running succesfull");
-                    }
-                } else {
-                    LOG.warn("{}: lock running invoked unsuccessfully: {}", id, result.getErrors());
-                }
-            }
-
-            @Override
-            public void onFailure(Throwable t) {
-                LOG.warn("{}: Lock running operation failed. {}", id, t);
-            }
-        };
-        resultsFutures.add(netOps.lockRunning(lockCallback));
+        resultsFutures.add(netOps.lockRunning(new NetconfRpcFutureCallback("Lock running", id)));
     }
 
     @Override
@@ -116,23 +96,9 @@ public class WriteRunningTx extends AbstractWriteTx {
                               final DataContainerChild<?, ?> editStructure,
                               final Optional<ModifyAction> defaultOperation,
                               final String operation) {
-        FutureCallback<DOMRpcResult> editConfigCallback = new FutureCallback<DOMRpcResult>() {
-            @Override
-            public void onSuccess(DOMRpcResult result) {
-                if (isSuccess(result)) {
-                    if (LOG.isTraceEnabled()) {
-                        LOG.trace("Edit running succesfull");
-                    }
-                } else {
-                    LOG.warn("{}: Edit running invoked unsuccessfully: {}", id, result.getErrors());
-                }
-            }
 
-            @Override
-            public void onFailure(Throwable t) {
-                LOG.warn("{}: Error {} data to (running){}, data: {}", id, operation, path, data.orNull(), t);
-            }
-        };
+        NetconfRpcFutureCallback editConfigCallback = new NetconfRpcFutureCallback("Edit running", id);
+
         if (defaultOperation.isPresent()) {
             resultsFutures.add(
                     netOps.editConfigRunning(editConfigCallback, editStructure, defaultOperation.get(), rollbackSupport));
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/SchemalessNetconfDeviceTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/SchemalessNetconfDeviceTest.java
new file mode 100644 (file)
index 0000000..7dd8064
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2016 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.netconf.sal.connect.netconf;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import com.google.common.collect.Lists;
+import java.lang.reflect.Field;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.Collection;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.netconf.api.NetconfMessage;
+import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc;
+import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.SchemalessMessageTransformer;
+import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+public class SchemalessNetconfDeviceTest {
+
+    private static final String TEST_NAMESPACE = "test:namespace";
+    private static final String TEST_MODULE = "test-module";
+    private static final String TEST_REVISION = "2013-07-22";
+
+    @Test
+    public void testSessionOnMethods() throws Exception {
+        final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
+        final NetconfDeviceCommunicator listener = mockCloseableClass(NetconfDeviceCommunicator.class);
+        final SchemalessMessageTransformer messageTransformer = mock(SchemalessMessageTransformer.class);
+        final RemoteDeviceId remoteDeviceId = new RemoteDeviceId("test-D",
+                InetSocketAddress.createUnresolved("localhost", 22));
+        final Throwable throwable = new Throwable();
+
+        final SchemalessNetconfDevice device = new SchemalessNetconfDevice(remoteDeviceId, facade, messageTransformer);
+
+        final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
+                Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
+
+        final NetconfMessage netconfMessage = mock(NetconfMessage.class);
+
+        device.onRemoteSessionUp(sessionCaps, listener);
+        verify(facade).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
+
+        device.onNotification(netconfMessage);
+        verify(facade).onNotification(any(DOMNotification.class));
+
+        device.onRemoteSessionDown();
+        verify(facade).onDeviceDisconnected();
+
+        device.onRemoteSessionFailed(throwable);
+        verify(facade).onDeviceFailed(throwable);
+    }
+
+    @SuppressWarnings("unchecked")
+    private RemoteDeviceHandler<NetconfSessionPreferences> getFacade() throws Exception {
+        final RemoteDeviceHandler<NetconfSessionPreferences> remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
+        doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
+        doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
+        doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
+        return remoteDeviceHandler;
+    }
+
+    private <T extends AutoCloseable> T mockCloseableClass(final Class<T> remoteDeviceHandlerClass) throws Exception {
+        final T mock = mockClass(remoteDeviceHandlerClass);
+        doNothing().when(mock).close();
+        return mock;
+    }
+
+    private static <T> T mockClass(final Class<T> remoteDeviceHandlerClass) {
+        final T mock = mock(remoteDeviceHandlerClass);
+        Mockito.doReturn(remoteDeviceHandlerClass.getSimpleName()).when(mock).toString();
+        return mock;
+    }
+
+    private NetconfSessionPreferences getSessionCaps(final boolean addMonitor, final Collection<String> additionalCapabilities) {
+        final ArrayList<String> capabilities = Lists.newArrayList(
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0,
+                XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1);
+
+        if(addMonitor) {
+            capabilities.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
+        }
+
+        capabilities.addAll(additionalCapabilities);
+
+        return NetconfSessionPreferences.fromStrings(
+                capabilities);
+    }
+}
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacadeTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceSalFacadeTest.java
new file mode 100644 (file)
index 0000000..07577ca
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2016 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.netconf.sal.connect.netconf.sal;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import java.net.InetSocketAddress;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotification;
+import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
+import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences;
+import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({NetconfDeviceTopologyAdapter.class, NetconfDeviceSalProvider.MountInstance.class, NetconfSessionPreferences.class})
+public class NetconfDeviceSalFacadeTest {
+
+    private NetconfDeviceSalFacade deviceFacade;
+
+    private NetconfDeviceTopologyAdapter netconfDeviceTopologyAdapter;
+    private NetconfDeviceSalProvider.MountInstance mountInstance;
+
+    @Mock
+    private NetconfDeviceSalProvider salProvider;
+
+    @Before
+    public void setUp() throws Exception{
+        initMocks(this);
+        final InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8000);
+        final RemoteDeviceId remoteDeviceId = new RemoteDeviceId("test", address);
+
+        final Broker domRegistry = mock(Broker.class);
+        final BindingAwareBroker bindingRegistry = mock(BindingAwareBroker.class);
+        deviceFacade = new NetconfDeviceSalFacade(remoteDeviceId, salProvider, domRegistry, bindingRegistry);
+
+        netconfDeviceTopologyAdapter = PowerMockito.mock(NetconfDeviceTopologyAdapter.class);
+        mountInstance = PowerMockito.mock(NetconfDeviceSalProvider.MountInstance.class);
+
+        doReturn(netconfDeviceTopologyAdapter).when(salProvider).getTopologyDatastoreAdapter();
+        doNothing().when(netconfDeviceTopologyAdapter).updateDeviceData(any(Boolean.class), any(NetconfDeviceCapabilities.class));
+
+        doReturn(mountInstance).when(salProvider).getMountInstance();
+        doNothing().when(mountInstance).onTopologyDeviceDisconnected();
+    }
+
+    @Test
+    public void testOnDeviceDisconnected() {
+        deviceFacade.onDeviceDisconnected();
+
+        verify(netconfDeviceTopologyAdapter).updateDeviceData(eq(false), any(NetconfDeviceCapabilities.class));
+        verify(mountInstance, times(1)).onTopologyDeviceDisconnected();
+
+    }
+
+    @Test
+    public void testOnDeviceFailed() {
+        final Throwable throwable = new Throwable();
+        deviceFacade.onDeviceFailed(throwable);
+
+        verify(netconfDeviceTopologyAdapter).setDeviceAsFailed(throwable);
+        verify(mountInstance, times(1)).onTopologyDeviceDisconnected();
+    }
+
+    @Test
+    public void testOnDeviceClose() throws Exception {
+        deviceFacade.close();
+        verify(salProvider).close();
+    }
+
+    @Test
+    public void testOnDeviceConnected() {
+        final SchemaContext schemaContext = mock(SchemaContext.class);
+
+        final NetconfSessionPreferences netconfSessionPreferences = NetconfSessionPreferences.fromStrings(getCapabilities());
+
+        final DOMRpcService deviceRpc = mock(DOMRpcService.class);
+        deviceFacade.onDeviceConnected(schemaContext, netconfSessionPreferences, deviceRpc);
+
+        verify(mountInstance, times(1)).onTopologyDeviceConnected(eq(schemaContext), any(DOMDataBroker.class), eq(deviceRpc), any(NetconfDeviceNotificationService.class));
+        verify(netconfDeviceTopologyAdapter, times(1)).updateDeviceData(true, netconfSessionPreferences.getNetconfDeviceCapabilities());
+    }
+
+    @Test
+    public void testOnDeviceNotification() throws Exception {
+        final DOMNotification domNotification = mock(DOMNotification.class);
+        deviceFacade.onNotification(domNotification);
+        verify(mountInstance).publish(domNotification);
+    }
+
+   private List<String> getCapabilities(){
+        return Arrays.asList(NetconfMessageTransformUtil.NETCONF_CANDIDATE_URI.toString());
+    }
+}
index 2358819f90439f8e1a8abf0a2666dd63c0aa89ea..0af8a257e71c726dc26d78253a9ddc6b8a75151c 100644 (file)
@@ -23,28 +23,22 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumMap;
 import java.util.List;
-import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import javassist.ClassPool;
-import org.custommonkey.xmlunit.XMLUnit;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.internal.util.collections.Sets;
 import org.opendaylight.controller.cluster.databroker.ConcurrentDOMDataBroker;
-import org.opendaylight.controller.cluster.datastore.node.utils.AugmentationIdentifierGenerator;
-import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.binding.impl.BindingDOMDataBrokerAdapter;
 import org.opendaylight.controller.md.sal.binding.impl.BindingToNormalizedNodeCodec;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
 import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory;
@@ -52,15 +46,11 @@ import org.opendaylight.controller.sal.core.api.model.SchemaService;
 import org.opendaylight.controller.sal.core.spi.data.DOMStore;
 import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities;
 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeFields;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
-import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
 import org.opendaylight.yangtools.binding.data.codec.gen.impl.DataObjectSerializerGenerator;
 import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator;
 import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry;
@@ -289,4 +279,16 @@ public class NetconfDeviceTopologyAdapterTest {
         };
     }
 
+    @Test
+    public void testRemoveDeviceConfiguration() throws Exception {
+        doReturn(Futures.immediateCheckedFuture(null)).when(writeTx).submit();
+
+        NetconfDeviceTopologyAdapter adapter = new NetconfDeviceTopologyAdapter(id, txChain);
+        adapter.close();
+
+        verify(txChain, times(2)).newWriteOnlyTransaction();
+        verify(writeTx).delete(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath());
+        verify(writeTx, times(2)).submit();
+    }
+
 }
\ No newline at end of file
diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/RemoteDeviceIdTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/util/RemoteDeviceIdTest.java
new file mode 100644 (file)
index 0000000..02d3eea
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 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.netconf.sal.connect.netconf.util;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import java.net.InetSocketAddress;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
+
+public class RemoteDeviceIdTest {
+
+    @Test
+    public void testEquals() {
+        final InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8000);
+        final ModuleIdentifier identifier = new ModuleIdentifier("test", "test");
+
+        final RemoteDeviceId remoteDeviceId = new RemoteDeviceId("test", address);
+        final RemoteDeviceId remoteDeviceIdEqualName = new RemoteDeviceId(identifier, address);
+        final RemoteDeviceId remoteDeviceIdDiffName = new RemoteDeviceId("test-diff", address);
+
+        assertEquals(true, remoteDeviceId.equals(remoteDeviceId));
+        assertEquals(false, remoteDeviceId.equals(this));
+        assertEquals(false, remoteDeviceId.equals(remoteDeviceIdDiffName));
+        assertEquals(true, remoteDeviceId.equals(remoteDeviceIdEqualName));
+    }
+
+    @Test
+    public void testHashCode() {
+        final String name = "name";
+        final InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8000);
+        final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(name, address);
+        final RemoteDeviceId remoteDeviceIdEqualName = new RemoteDeviceId(name, address);
+        final RemoteDeviceId remoteDeviceIdDiffName = new RemoteDeviceId("test-diff", address);
+
+        assertEquals(remoteDeviceIdEqualName.hashCode(), remoteDeviceId.hashCode());
+        assertNotEquals(remoteDeviceIdDiffName.hashCode(), remoteDeviceId.hashCode());
+    }
+}
index 2252252e110743de3f22f48b9a081cdd1da41e75..8c083399fc28ceac882d4d49ce13a0803fb04898 100644 (file)
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
index 4e7b890e9a0f6a0132923904585fa58b5c6ff43b..f867971aea5051b9ba984025c48e687b2c6fe434 100644 (file)
@@ -15,10 +15,10 @@ import com.google.common.util.concurrent.CheckedFuture;
 import java.io.IOException;
 import org.opendaylight.yanglib.api.YangLibService;
 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
-import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -28,13 +28,13 @@ import org.slf4j.LoggerFactory;
 public class YangLibServiceImpl implements YangLibService {
     private static final Logger LOG = LoggerFactory.getLogger(YangLibServiceImpl.class);
 
-    private SharedSchemaRepository schemaRepository;
+    private SchemaRepository schemaRepository;
 
     public YangLibServiceImpl() {
 
     }
 
-    public void setSchemaRepository(final SharedSchemaRepository schemaRepository) {
+    public void setSchemaRepository(final SchemaRepository schemaRepository) {
         LOG.debug("Setting schema repository {}", schemaRepository);
         this.schemaRepository = schemaRepository;
     }
diff --git a/netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibRestAppTest.java b/netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibRestAppTest.java
new file mode 100644 (file)
index 0000000..bba2c29
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 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.yanglib.impl;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.BDDMockito;
+import org.opendaylight.yanglib.api.YangLibRestAppService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(FrameworkUtil.class)
+public class YangLibRestAppTest {
+
+    @Test
+    public void testYangLibRestApp() {
+        PowerMockito.mockStatic(FrameworkUtil.class);
+
+        final BundleContext bundleContext = mock(BundleContext.class);
+        final Bundle bundle = mock(Bundle.class);
+
+        BDDMockito.given(FrameworkUtil.getBundle(any())).willReturn(bundle);
+        when(bundle.getBundleContext()).thenReturn(bundleContext);
+
+        final YangLibRestApp yangLibRestApp = new YangLibRestApp();
+        final Set singleton = yangLibRestApp.getSingletons();
+
+        assertTrue(singleton.contains(yangLibRestApp.getYangLibService()));
+
+        verify(bundleContext, times(1)).registerService(eq(YangLibRestAppService.class.getName()), eq(yangLibRestApp), eq(null));
+    }
+}
diff --git a/netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibServiceImplTest.java b/netconf/yanglib/src/test/java/org/opendaylight/yanglib/impl/YangLibServiceImplTest.java
new file mode 100644 (file)
index 0000000..add8d67
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016 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.yanglib.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.Futures;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+
+public class YangLibServiceImplTest {
+
+    private final static String TEST_OUTPUT_STRING = "hello world";
+
+    @Test
+    public void testSchema() throws SchemaSourceException {
+
+        final SchemaRepository schemaRepository = mock(SchemaRepository.class);
+        final YangLibServiceImpl yangLibService = new YangLibServiceImpl();
+        yangLibService.setSchemaRepository(schemaRepository);
+
+        final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("name", "2016-01-01");
+
+        final YangTextSchemaSource yangTextSchemaSource = new YangTextSchemaSource(sourceIdentifier) {
+            @Override
+            protected MoreObjects.ToStringHelper addToStringAttributes(MoreObjects.ToStringHelper toStringHelper) {
+                return null;
+            }
+
+            @Override
+            public InputStream openStream() throws IOException {
+                return new ByteArrayInputStream(TEST_OUTPUT_STRING.getBytes());
+            }
+        };
+
+        final CheckedFuture<YangTextSchemaSource, SchemaSourceException> sourceFuture =
+                Futures.immediateCheckedFuture(yangTextSchemaSource);
+        doReturn(sourceFuture).when(schemaRepository).getSchemaSource(any(SourceIdentifier.class),
+                eq(YangTextSchemaSource.class));
+
+        final String outputStream = yangLibService.getSchema("name", "2016-01-01");
+        assertEquals(TEST_OUTPUT_STRING, outputStream);
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index 604a91ca0b4bbd61700746164d1bd7e327309a10..b7ff23ef51bf73de607daef39864e5008492229f 100644 (file)
--- a/pom.xml
+++ b/pom.xml
       </dependencies>
     </dependencyManagement>
 
-    <build>
-        <pluginManagement>
-            <plugins>
-                <plugin>
-                    <groupId>org.apache.maven.plugins</groupId>
-                    <artifactId>maven-checkstyle-plugin</artifactId>
-                    <dependencies>
-                        <dependency>
-                            <groupId>org.opendaylight.controller</groupId>
-                            <artifactId>checkstyle</artifactId>
-                            <version>0.4.0-SNAPSHOT</version>
-                        </dependency>
-                    </dependencies>
-                </plugin>
-            </plugins>
-        </pluginManagement>
-    </build>
-
     <profiles>
         <profile>
             <id>integrationtests</id>
index 647e88fa900e2d1d723b5149d423d814376bbf58..fd750b45407860841024ad5ba193f24720e1804d 100644 (file)
@@ -238,7 +238,7 @@ public class BaseYangSwaggerGenerator {
     }
 
     private void addRootPostLink(final Module module, final DataNodeContainer node, final List<Parameter> pathParams,
-            final String resourcePath, final List<Api> apis) {
+                                 final String resourcePath, final List<Api> apis) {
         if (containsListOrContainer(module.getChildNodes())) {
             final Api apiForRootPostUri = new Api();
             apiForRootPostUri.setPath(resourcePath);
@@ -372,12 +372,12 @@ public class BaseYangSwaggerGenerator {
         operationSpec.setNotes(rpcDefn.getDescription());
         operationSpec.setNickname(rpcDefn.getQName().getLocalName());
         if (rpcDefn.getOutput() != null) {
-            operationSpec.setType("(" + rpcDefn.getQName().getLocalName() + ")output");
+            operationSpec.setType("(" + rpcDefn.getQName().getLocalName() + ")output" + OperationBuilder.TOP);
         }
         if (rpcDefn.getInput() != null) {
             final Parameter payload = new Parameter();
             payload.setParamType("body");
-            payload.setType("(" + rpcDefn.getQName().getLocalName() + ")input");
+            payload.setType("(" + rpcDefn.getQName().getLocalName() + ")input" + OperationBuilder.TOP);
             operationSpec.setParameters(Collections.singletonList(payload));
             operationSpec.setConsumes(OperationBuilder.CONSUMES_PUT_POST);
         }
index 40a41f3be3be96aa7b8e2bfa0c78e69a1ce787a2..5813f1e4def9ccb5eb7cee0452cc3071ac3f962b 100644 (file)
@@ -20,7 +20,6 @@ import org.json.JSONException;
 import org.json.JSONObject;
 import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder;
 import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.Post;
-import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
@@ -138,7 +137,6 @@ public class ModelGenerator {
                 processDataNodeContainer((DataNodeContainer) childNode, moduleName, models, false, schemaContext);
             }
         }
-
     }
 
     /**
@@ -156,30 +154,53 @@ public class ModelGenerator {
         for (final RpcDefinition rpc : rpcs) {
             final ContainerSchemaNode input = rpc.getInput();
             if (input != null) {
-                final JSONObject properties = processChildren(input.getChildNodes(), input.getQName(), moduleName,
-                        models, true, schemaContext);
+                final JSONObject properties = processChildren(input.getChildNodes(), moduleName, models, true, schemaContext);
+
                 final String filename = "(" + rpc.getQName().getLocalName() + ")input";
                 final JSONObject childSchema = getSchemaTemplate();
                 childSchema.put(TYPE_KEY, OBJECT_TYPE);
                 childSchema.put(PROPERTIES_KEY, properties);
                 childSchema.put("id", filename);
                 models.put(filename, childSchema);
+
+                processTopData(filename, models, input);
             }
 
             final ContainerSchemaNode output = rpc.getOutput();
             if (output != null) {
-                final JSONObject properties = processChildren(output.getChildNodes(), output.getQName(), moduleName,
-                        models, true, schemaContext);
+                final JSONObject properties = processChildren(output.getChildNodes(), moduleName, models, true, schemaContext);
                 final String filename = "(" + rpc.getQName().getLocalName() + ")output";
                 final JSONObject childSchema = getSchemaTemplate();
                 childSchema.put(TYPE_KEY, OBJECT_TYPE);
                 childSchema.put(PROPERTIES_KEY, properties);
                 childSchema.put("id", filename);
                 models.put(filename, childSchema);
+
+                processTopData(filename, models, output);
             }
         }
     }
 
+    private static JSONObject processTopData(final String filename, final JSONObject models, final SchemaNode schemaNode) {
+        final JSONObject items = new JSONObject();
+
+        items.put(REF_KEY, filename);
+        final JSONObject dataNodeProperties = new JSONObject();
+        dataNodeProperties.put(TYPE_KEY, schemaNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE);
+        dataNodeProperties.put(ITEMS_KEY, items);
+
+        dataNodeProperties.putOpt(DESCRIPTION_KEY, schemaNode.getDescription());
+        final JSONObject properties = new JSONObject();
+        properties.put(schemaNode.getQName().getLocalName(), dataNodeProperties);
+        final JSONObject finalChildSchema = getSchemaTemplate();
+        finalChildSchema.put(TYPE_KEY, OBJECT_TYPE);
+        finalChildSchema.put(PROPERTIES_KEY, properties);
+        finalChildSchema.put(ID_KEY, filename + OperationBuilder.TOP);
+        models.put(filename + OperationBuilder.TOP, finalChildSchema);
+
+        return dataNodeProperties;
+    }
+
     /**
      * Processes the 'identity' statement in a yang model and maps it to a 'model' in the Swagger JSON spec.
      *
@@ -233,12 +254,11 @@ public class ModelGenerator {
     }
 
     private JSONObject processDataNodeContainer(final DataNodeContainer dataNode, final String moduleName, final JSONObject models,
-            final boolean isConfig, final SchemaContext schemaContext) throws JSONException, IOException {
+                                                final boolean isConfig, final SchemaContext schemaContext) throws JSONException, IOException {
         if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) {
             Preconditions.checkArgument(dataNode instanceof SchemaNode, "Data node should be also schema node");
             final Iterable<DataSchemaNode> containerChildren = dataNode.getChildNodes();
-            final JSONObject properties = processChildren(containerChildren, ((SchemaNode) dataNode).getQName(), moduleName,
-                    models, isConfig, schemaContext);
+            final JSONObject properties = processChildren(containerChildren, moduleName, models, isConfig, schemaContext);
 
             final String nodeName = (isConfig ? OperationBuilder.CONFIG : OperationBuilder.OPERATIONAL)
                     + ((SchemaNode) dataNode).getQName().getLocalName();
@@ -246,6 +266,7 @@ public class ModelGenerator {
             final JSONObject childSchema = getSchemaTemplate();
             childSchema.put(TYPE_KEY, OBJECT_TYPE);
             childSchema.put(PROPERTIES_KEY, properties);
+
             childSchema.put("id", nodeName);
             models.put(nodeName, childSchema);
 
@@ -254,13 +275,7 @@ public class ModelGenerator {
                         createPropertiesForPost(dataNode));
             }
 
-            final JSONObject items = new JSONObject();
-            items.put(REF_KEY, nodeName);
-            final JSONObject dataNodeProperties = new JSONObject();
-            dataNodeProperties.put(TYPE_KEY, dataNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE);
-            dataNodeProperties.put(ITEMS_KEY, items);
-
-            return dataNodeProperties;
+            return processTopData(nodeName, models, (SchemaNode) dataNode);
         }
         return null;
     }
@@ -293,27 +308,25 @@ public class ModelGenerator {
         return properties;
     }
 
-    private JSONObject processChildren(final Iterable<DataSchemaNode> nodes, final QName parentQName, final String moduleName,
-            final JSONObject models, final SchemaContext schemaContext) throws JSONException, IOException {
-        return processChildren(nodes, parentQName, moduleName, models, true, schemaContext);
+    private JSONObject processChildren(final Iterable<DataSchemaNode> nodes, final String moduleName,
+                                       final JSONObject models, final SchemaContext schemaContext) throws JSONException, IOException {
+        return processChildren(nodes, moduleName, models, true, schemaContext);
     }
 
     /**
      * Processes the nodes.
      */
-    private JSONObject processChildren(final Iterable<DataSchemaNode> nodes, final QName parentQName,
-            final String moduleName, final JSONObject models, final boolean isConfig, final SchemaContext schemaContext)
+    private JSONObject processChildren(final Iterable<DataSchemaNode> nodes, final String moduleName, final JSONObject models,
+                                       final boolean isConfig, final SchemaContext schemaContext)
             throws JSONException, IOException {
-
         final JSONObject properties = new JSONObject();
-
         for (final DataSchemaNode node : nodes) {
             if (node.isConfiguration() == isConfig) {
-
                 final String name = resolveNodesName(node, topLevelModule, schemaContext);
-                JSONObject property = null;
+                final JSONObject property;
                 if (node instanceof LeafSchemaNode) {
                     property = processLeafNode((LeafSchemaNode) node);
+
                 } else if (node instanceof ListSchemaNode) {
                     property = processDataNodeContainer((ListSchemaNode) node, moduleName, models, isConfig,
                             schemaContext);
@@ -334,7 +347,6 @@ public class ModelGenerator {
                 } else {
                     throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
                 }
-
                 property.putOpt(DESCRIPTION_KEY, node.getDescription());
                 properties.put(name, property);
             }
@@ -364,8 +376,7 @@ public class ModelGenerator {
         final JSONArray choiceProps = new JSONArray();
         for (final ChoiceCaseNode choiceCase : cases) {
             final String choiceName = choiceCase.getQName().getLocalName();
-            final JSONObject choiceProp = processChildren(choiceCase.getChildNodes(), choiceCase.getQName(), moduleName,
-                    models, schemaContext);
+            final JSONObject choiceProp = processChildren(choiceCase.getChildNodes(), moduleName, models, schemaContext);
             final JSONObject choiceObj = new JSONObject();
             choiceObj.put(choiceName, choiceProp);
             choiceObj.put(TYPE_KEY, OBJECT_TYPE);
index e0bc84d7367a3d2d3478607469a6de0ae16e5a5d..385a08b4852d3d286b0a8b7cd65d570fb5827498 100644 (file)
@@ -20,6 +20,7 @@ public final class OperationBuilder {
 
     public static final String OPERATIONAL = "(operational)";
     public static final String CONFIG = "(config)";
+    public static final String TOP = "-TOP";
 
     public static final List<String> CONSUMES_PUT_POST = new ArrayList<>();
 
@@ -71,7 +72,8 @@ public final class OperationBuilder {
             final List<Parameter> parameters = new ArrayList<>(params);
             final Parameter payload = new Parameter();
             payload.setParamType("body");
-            payload.setType(CONFIG + nodeName);
+            payload.setType(CONFIG + nodeName + TOP);
+            payload.setName(CONFIG + nodeName);
             parameters.add(payload);
             spec.setParameters(parameters);
             return this;
@@ -110,7 +112,7 @@ public final class OperationBuilder {
                 if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
                     final Parameter payload = new Parameter();
                     payload.setParamType("body");
-                    payload.setType(CONFIG + node.getQName().getLocalName());
+                    payload.setType(CONFIG + node.getQName().getLocalName() + TOP);
                     payload.setName("**" + CONFIG + node.getQName().getLocalName());
                     parameters.add(payload);
                 }
index c72b5f019dff53a24c8ec22779c4ded342ceb5e7..d0a200bcde0f2e5e17d110f87e486fea07083997 100644 (file)
@@ -94,12 +94,12 @@ public class ApiDocGeneratorTest {
         final Api lstApi = findApi("/config/toaster2:lst/", doc);
         assertNotNull("Api /config/toaster2:lst/ wasn't found", lstApi);
         assertTrue("POST for cont1 in lst is missing",
-                findOperation(lstApi.getOperations(), "POST", "(config)lstPOST", "(config)lst1", "(config)cont1"));
+                findOperation(lstApi.getOperations(), "POST", "(config)lstPOST", "(config)lst1-TOP", "(config)cont1-TOP"));
 
         final Api cont1Api = findApi("/config/toaster2:lst/cont1/", doc);
         assertNotNull("Api /config/toaster2:lst/cont1/ wasn't found", cont1Api);
         assertTrue("POST for cont11 in cont1 is missing",
-                findOperation(cont1Api.getOperations(), "POST", "(config)cont1POST", "(config)cont11", "(config)lst11"));
+                findOperation(cont1Api.getOperations(), "POST", "(config)cont1POST", "(config)cont11-TOP", "(config)lst11-TOP"));
 
         // no POST URI
         final Api cont11Api = findApi("/config/toaster2:lst/cont1/cont11/", doc);
@@ -167,24 +167,46 @@ public class ApiDocGeneratorTest {
         final JSONObject models = doc.getModels();
         assertNotNull(models);
         try {
+            final JSONObject configLstTop = models.getJSONObject("(config)lst-TOP");
+            assertNotNull(configLstTop);
+
+            containsReferences(configLstTop, "lst");
+
             final JSONObject configLst = models.getJSONObject("(config)lst");
             assertNotNull(configLst);
 
             containsReferences(configLst, "lst1");
             containsReferences(configLst, "cont1");
 
+            final JSONObject configLst1Top = models.getJSONObject("(config)lst1-TOP");
+            assertNotNull(configLst1Top);
+
+            containsReferences(configLst1Top, "lst1");
+
             final JSONObject configLst1 = models.getJSONObject("(config)lst1");
             assertNotNull(configLst1);
 
+            final JSONObject configCont1Top = models.getJSONObject("(config)cont1-TOP");
+            assertNotNull(configCont1Top);
+
+            containsReferences(configCont1Top, "cont1");
             final JSONObject configCont1 = models.getJSONObject("(config)cont1");
             assertNotNull(configCont1);
 
             containsReferences(configCont1, "cont11");
             containsReferences(configCont1, "lst11");
 
+            final JSONObject configCont11Top = models.getJSONObject("(config)cont11-TOP");
+            assertNotNull(configCont11Top);
+
+            containsReferences(configCont11Top, "cont11");
             final JSONObject configCont11 = models.getJSONObject("(config)cont11");
             assertNotNull(configCont11);
 
+            final JSONObject configlst11Top = models.getJSONObject("(config)lst11-TOP");
+            assertNotNull(configlst11Top);
+
+            containsReferences(configlst11Top, "lst11");
             final JSONObject configLst11 = models.getJSONObject("(config)lst11");
             assertNotNull(configLst11);
         } catch (final JSONException e) {
@@ -229,6 +251,29 @@ public class ApiDocGeneratorTest {
         }
     }
 
+    @Test
+    public void testRPCsModel() throws Exception {
+        Preconditions.checkArgument(this.helper.getModules() != null, "No modules found");
+
+        for (final Module m : this.helper.getModules()) {
+            if (m.getQNameModule().getNamespace().toString().equals(NAMESPACE_2)
+                    && m.getQNameModule().getRevision().equals(REVISION_2)) {
+                final ApiDeclaration doc = this.generator.getSwaggerDocSpec(m, "http://localhost:8080/restconf", "",
+                        this.schemaContext);
+                assertNotNull(doc);
+
+                final JSONObject models = doc.getModels();
+                final JSONObject inputTop = models.getJSONObject("(make-toast)input-TOP");
+                final String testString = "{\"input\":{\"type\":\"object\",\"items\":{\"$ref\":\"(make-toast)input\"}}}";
+                assertEquals(testString, inputTop.getJSONObject("properties").toString());
+                final JSONObject input = models.getJSONObject("(make-toast)input");
+                final JSONObject properties = input.getJSONObject("properties");
+                assertTrue(properties.has("toasterDoneness"));
+                assertTrue(properties.has("toasterToastType"));
+            }
+        }
+    }
+
     /**
      * Tests whether from yang files are generated all required paths for HTTP operations (GET, DELETE, PUT, POST)
      *