From a94233f06e83dcd9c7baaa75aa71bb2b0eb448e3 Mon Sep 17 00:00:00 2001 From: Rudolf Brisuda Date: Mon, 19 Sep 2016 17:51:23 +0200 Subject: [PATCH] netconf-console unit tests added Change-Id: I3975e0740935dbbe6bc65f6e3ea3febf56507f39 Signed-off-by: Rudolf Brisuda --- netconf/netconf-console/pom.xml | 24 + .../console/commands/NetconfCommandUtils.java | 10 +- .../commands/NetconfConnectDeviceCommand.java | 8 + .../NetconfDisconnectDeviceCommand.java | 10 + .../commands/NetconfShowDeviceCommand.java | 10 + .../commands/NetconfUpdateDeviceCommand.java | 7 + .../commands/NetconfCommandUtilsTest.java | 50 ++ .../NetconfCommandsImplCallsTest.java | 141 ++++++ .../console/impl/NetconfCommandsImplTest.java | 353 ++++++++++++++ .../impl/NetconfConsoleProviderTest.java | 59 +++ .../schemas/ietf-inet-types@2013-07-15.yang | 457 ++++++++++++++++++ .../schemas/netconf-node-topology.yang | 243 ++++++++++ .../schemas/network-topology@2013-10-21.yang | 339 +++++++++++++ .../src/test/resources/schemas/yang-ext.yang | 79 +++ 14 files changed, 1786 insertions(+), 4 deletions(-) create mode 100644 netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandUtilsTest.java create mode 100644 netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandsImplCallsTest.java create mode 100644 netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfCommandsImplTest.java create mode 100644 netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfConsoleProviderTest.java create mode 100644 netconf/netconf-console/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang create mode 100644 netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang create mode 100644 netconf/netconf-console/src/test/resources/schemas/network-topology@2013-10-21.yang create mode 100644 netconf/netconf-console/src/test/resources/schemas/yang-ext.yang diff --git a/netconf/netconf-console/pom.xml b/netconf/netconf-console/pom.xml index 8e4e05f93c..b4c260009a 100644 --- a/netconf/netconf-console/pom.xml +++ b/netconf/netconf-console/pom.xml @@ -40,5 +40,29 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL sal-netconf-connector ${mdsal.version} + + org.mockito + mockito-all + test + + + org.opendaylight.controller + sal-distributed-datastore + test + + + org.powermock + powermock-module-junit4 + test + + + org.powermock + powermock-api-mockito + test + + + org.powermock + powermock-core + \ No newline at end of file diff --git a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfCommandUtils.java b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfCommandUtils.java index 67024f910b..40ac48f17c 100644 --- a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfCommandUtils.java +++ b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfCommandUtils.java @@ -25,11 +25,13 @@ public class NetconfCommandUtils { if (Strings.isNullOrEmpty(devicePort)) { return false; } - Integer port = Integer.parseInt(devicePort); - if (port != null && port >= 0 && port <= 65535) { - return true; + Integer port; + try { + port = Integer.parseInt(devicePort); + } catch (NumberFormatException e) { + return false; } - return false; + return port >= 0 && port <= 65535; } public static boolean isIpValid(final String deviceIp) { diff --git a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java index 322f3d2cbe..a41839789c 100644 --- a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java +++ b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfConnectDeviceCommand.java @@ -8,6 +8,7 @@ package org.opendaylight.netconf.console.commands; +import com.google.common.annotations.VisibleForTesting; import org.apache.karaf.shell.commands.Command; import org.apache.karaf.shell.commands.Option; import org.apache.karaf.shell.console.AbstractAction; @@ -30,6 +31,13 @@ public class NetconfConnectDeviceCommand extends AbstractAction { this.service = service; } + @VisibleForTesting + NetconfConnectDeviceCommand(final NetconfCommands service, final String deviceIp, final String devicePort) { + this.service = service; + this.deviceIp = deviceIp; + this.devicePort = devicePort; + } + @Option(name = "-i", aliases = { "--ipaddress" }, description = "IP address of the netconf device", diff --git a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfDisconnectDeviceCommand.java b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfDisconnectDeviceCommand.java index 94361901fd..b1abf2fa25 100644 --- a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfDisconnectDeviceCommand.java +++ b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfDisconnectDeviceCommand.java @@ -8,6 +8,7 @@ package org.opendaylight.netconf.console.commands; +import com.google.common.annotations.VisibleForTesting; import org.apache.karaf.shell.commands.Command; import org.apache.karaf.shell.commands.Option; import org.apache.karaf.shell.console.AbstractAction; @@ -24,6 +25,15 @@ public class NetconfDisconnectDeviceCommand extends AbstractAction { this.service = service; } + @VisibleForTesting + NetconfDisconnectDeviceCommand(final NetconfCommands service, final String deviceId, final String deviceIp, + final String devicePort) { + this.service = service; + this.deviceId = deviceId; + this.deviceIp = deviceIp; + this.devicePort = devicePort; + } + @Option(name = "-i", aliases = { "--ipaddress" }, description = "IP address of the netconf device", diff --git a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfShowDeviceCommand.java b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfShowDeviceCommand.java index b50ea71549..46d36dceb5 100644 --- a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfShowDeviceCommand.java +++ b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfShowDeviceCommand.java @@ -8,6 +8,7 @@ package org.opendaylight.netconf.console.commands; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Strings; import java.util.List; import java.util.Map; @@ -28,6 +29,15 @@ public class NetconfShowDeviceCommand extends AbstractAction { this.service = service; } + @VisibleForTesting + NetconfShowDeviceCommand(final NetconfCommands service, final String deviceId, final String deviceIp, + final String devicePort) { + this.service = service; + this.deviceId = deviceId; + this.deviceIp = deviceIp; + this.devicePort = devicePort; + } + @Option(name = "-id", aliases = { "--identifier" }, description = "Node Identifier of the netconf device", diff --git a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfUpdateDeviceCommand.java b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfUpdateDeviceCommand.java index b03556948e..79b6bb498e 100644 --- a/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfUpdateDeviceCommand.java +++ b/netconf/netconf-console/src/main/java/org/opendaylight/netconf/console/commands/NetconfUpdateDeviceCommand.java @@ -8,6 +8,7 @@ package org.opendaylight.netconf.console.commands; +import com.google.common.annotations.VisibleForTesting; import java.util.HashMap; import java.util.Map; @@ -26,6 +27,12 @@ public class NetconfUpdateDeviceCommand extends AbstractAction { this.service = service; } + @VisibleForTesting + NetconfUpdateDeviceCommand(final NetconfCommands service, final String newIp) { + this.service = service; + this.newIp = newIp; + } + @Option(name = "-id", aliases = { "--nodeId" }, description = "NETCONF node ID of the netconf device", diff --git a/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandUtilsTest.java b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandUtilsTest.java new file mode 100644 index 0000000000..0c19ce22d7 --- /dev/null +++ b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandUtilsTest.java @@ -0,0 +1,50 @@ +/* + * 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.console.commands; + +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +import org.junit.Test; + +public class NetconfCommandUtilsTest { + + @Test + public void testIsPortValid() { + final boolean portTrue = NetconfCommandUtils.isPortValid("65535"); + final boolean portTrue2 = NetconfCommandUtils.isPortValid("0"); + + final boolean portFalse = NetconfCommandUtils.isPortValid("123x"); + final boolean portFalse2 = NetconfCommandUtils.isPortValid("65536"); + final boolean portFalse3 = NetconfCommandUtils.isPortValid(""); + + assertTrue(portTrue); + assertTrue(portTrue2); + assertFalse(portFalse); + assertFalse(portFalse2); + assertFalse(portFalse3); + + } + + @Test + public void testIsIpValid() { + final boolean ipTrue = NetconfCommandUtils.isIpValid("0.0.0.0"); + final boolean ipTrue2 = NetconfCommandUtils.isIpValid("255.255.255.255"); + + final boolean ipFalse = NetconfCommandUtils.isIpValid("256.1.1.1"); + final boolean ipFalse2 = NetconfCommandUtils.isIpValid("123.145.12.x"); + final boolean ipFalse3 = NetconfCommandUtils.isIpValid(""); + + assertTrue(ipTrue); + assertTrue(ipTrue2); + assertFalse(ipFalse); + assertFalse(ipFalse2); + assertFalse(ipFalse3); + } +} diff --git a/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandsImplCallsTest.java b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandsImplCallsTest.java new file mode 100644 index 0000000000..0b5292522c --- /dev/null +++ b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/commands/NetconfCommandsImplCallsTest.java @@ -0,0 +1,141 @@ +/* + * 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.console.commands; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.collect.Lists; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.opendaylight.netconf.console.api.NetconfCommands; +import org.opendaylight.netconf.console.utils.NetconfConsoleConstants; + +public class NetconfCommandsImplCallsTest { + + @Mock + private NetconfCommands netconfCommands; + + @Before + public void setUp(){ + initMocks(this); + } + + @Test + public void testConnectDeviceCommand() throws Exception { + NetconfConnectDeviceCommand netconfConnectDeviceCommand = + new NetconfConnectDeviceCommand(netconfCommands); + netconfConnectDeviceCommand.doExecute(); + verify(netconfCommands, times(0)).connectDevice(any(), any()); + + netconfConnectDeviceCommand = new NetconfConnectDeviceCommand(netconfCommands, "192.168.1.1", "7777"); + + netconfConnectDeviceCommand.doExecute(); + doNothing().when(netconfCommands).connectDevice(any(), any()); + verify(netconfCommands, times(1)).connectDevice(any(), any()); + } + + @Test + public void testDisconnectDeviceCommand() throws Exception { + NetconfDisconnectDeviceCommand netconfDisconnectDeviceCommand = new NetconfDisconnectDeviceCommand(netconfCommands); + netconfDisconnectDeviceCommand.doExecute(); + + verify(netconfCommands, times(0)).connectDevice(any(), any()); + + netconfDisconnectDeviceCommand = new NetconfDisconnectDeviceCommand(netconfCommands, "deviceId", null, null); + + doReturn(true).when(netconfCommands).disconnectDevice(any()); + netconfDisconnectDeviceCommand.doExecute(); + + verify(netconfCommands, times(1)).disconnectDevice(any()); + + netconfDisconnectDeviceCommand = + new NetconfDisconnectDeviceCommand(netconfCommands, null, "192.168.1.1", "7777"); + + doReturn(true).when(netconfCommands).disconnectDevice(any(), any()); + netconfDisconnectDeviceCommand.doExecute(); + + verify(netconfCommands, times(1)).disconnectDevice(any(), any()); + } + + @Test + public void testListDeviceCommand() throws Exception { + final NetconfListDevicesCommand netconfListDeviceCommand = new NetconfListDevicesCommand(netconfCommands); + doReturn(getDeviceHashMap()).when(netconfCommands).listDevices(); + + netconfListDeviceCommand.doExecute(); + + verify(netconfCommands, times(1)).listDevices(); + } + + @Test + public void testShowDeviceCommand() throws Exception { + NetconfShowDeviceCommand netconfShowDeviceCommand = new NetconfShowDeviceCommand(netconfCommands); + netconfShowDeviceCommand.doExecute(); + + verify(netconfCommands, times(0)).showDevice(any()); + + netconfShowDeviceCommand = new NetconfShowDeviceCommand(netconfCommands, "deviceId", null, null); + + doReturn(getDeviceHashMap()).when(netconfCommands).showDevice(any()); + netconfShowDeviceCommand.doExecute(); + + verify(netconfCommands, times(1)).showDevice(any()); + + netconfShowDeviceCommand = new NetconfShowDeviceCommand(netconfCommands, null, "192.168.1.1", "7777"); + + doReturn(getDeviceHashMap()).when(netconfCommands).showDevice(any(), any()); + netconfShowDeviceCommand.doExecute(); + + verify(netconfCommands, times(1)).showDevice(any(), any()); + } + + @Test + @SuppressWarnings("unchecked") + public void testUpdateDeviceCommand() throws Exception { + final NetconfUpdateDeviceCommand netconfUpdateDeviceCommand = + new NetconfUpdateDeviceCommand(netconfCommands, "192.168.1.1"); + + final ArgumentCaptor hashMapArgumentCaptor = ArgumentCaptor.forClass(HashMap.class); + + doReturn("").when(netconfCommands).updateDevice(anyString(), anyString(), anyString(), any()); + + netconfUpdateDeviceCommand.doExecute(); + + verify(netconfCommands, times(1)).updateDevice(anyString(), anyString(), anyString(), hashMapArgumentCaptor.capture()); + + assertTrue(hashMapArgumentCaptor.getValue().containsKey(NetconfConsoleConstants.NETCONF_IP)); + assertEquals("192.168.1.1", hashMapArgumentCaptor.getValue().get(NetconfConsoleConstants.NETCONF_IP)); + } + + private HashMap getDeviceHashMap() { + final HashMap>> devices = new HashMap<>(); + final HashMap> deviceMap = new HashMap<>(); + deviceMap.put(NetconfConsoleConstants.NETCONF_IP, Lists.newArrayList("192.168.1.1")); + deviceMap.put(NetconfConsoleConstants.NETCONF_PORT, Lists.newArrayList("7777")); + deviceMap.put(NetconfConsoleConstants.STATUS, Lists.newArrayList("connecting")); + deviceMap.put(NetconfConsoleConstants.AVAILABLE_CAPABILITIES, Lists.newArrayList("cap1", "cap2", "cap3")); + devices.put("device", deviceMap); + return devices; + } + +} diff --git a/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfCommandsImplTest.java b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfCommandsImplTest.java new file mode 100644 index 0000000000..96c5b271e1 --- /dev/null +++ b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfCommandsImplTest.java @@ -0,0 +1,353 @@ +/* + * 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.console.impl; + +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; + +import com.google.common.collect.ImmutableList; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import javassist.ClassPool; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.controller.cluster.databroker.ConcurrentDOMDataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.MountPointService; +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.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStoreFactory; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.controller.sal.core.spi.data.DOMStore; +import org.opendaylight.netconf.console.utils.NetconfConsoleConstants; +import org.opendaylight.netconf.console.utils.NetconfConsoleUtils; +import org.opendaylight.netconf.console.utils.NetconfIidFactory; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.HostBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +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.NetconfNodeBuilder; +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.netconf.node.connection.status.AvailableCapabilities; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.AvailableCapabilitiesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.network.topology.topology.topology.types.TopologyNetconf; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +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.TopologyBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey; +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.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; +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; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.sal.binding.generator.impl.GeneratedClassLoadingStrategy; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.util.concurrent.SpecialExecutors; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor; +import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline; + +public class NetconfCommandsImplTest { + + private static final String NODE_ID = "NodeID"; + private static final String IP = "192.168.1.1"; + private static final int PORT = 1234; + private static final NetconfNodeConnectionStatus.ConnectionStatus CONN_STATUS = + NetconfNodeConnectionStatus.ConnectionStatus.Connected; + private static final String CAP_PREFIX = "prefix"; + + private DataBroker dataBroker; + private SchemaContext schemaContext; + private NetconfCommandsImpl netconfCommands; + + @Before + public void setUp() throws TransactionCommitFailedException, TimeoutException, InterruptedException { + schemaContext = parseYangStreams(getYangSchemas()); + schemaContext.getModules(); + final SchemaService schemaService = createSchemaService(); + + final DOMStore operStore = InMemoryDOMDataStoreFactory.create("DOM-OPER", schemaService); + final DOMStore configStore = InMemoryDOMDataStoreFactory.create("DOM-CFG", schemaService); + + final EnumMap datastores = new EnumMap<>(LogicalDatastoreType.class); + datastores.put(LogicalDatastoreType.CONFIGURATION, configStore); + datastores.put(LogicalDatastoreType.OPERATIONAL, operStore); + + final ExecutorService listenableFutureExecutor = SpecialExecutors.newBlockingBoundedCachedThreadPool( + 16, 16, "CommitFutures"); + + final ConcurrentDOMDataBroker cDOMDataBroker = new ConcurrentDOMDataBroker(datastores, listenableFutureExecutor); + + final ClassPool pool = ClassPool.getDefault(); + final DataObjectSerializerGenerator generator = StreamWriterGenerator.create(JavassistUtils.forClassPool(pool)); + final BindingNormalizedNodeCodecRegistry codecRegistry = new BindingNormalizedNodeCodecRegistry(generator); + final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); + codecRegistry.onBindingRuntimeContextUpdated(BindingRuntimeContext.create(moduleInfoBackedContext, schemaContext)); + + final GeneratedClassLoadingStrategy loading = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy(); + final BindingToNormalizedNodeCodec bindingToNormalized = new BindingToNormalizedNodeCodec(loading, codecRegistry); + bindingToNormalized.onGlobalContextUpdated(schemaContext); + dataBroker = new BindingDOMDataBrokerAdapter(cDOMDataBroker, bindingToNormalized); + + final MountPointService mountPointService = mock(MountPointService.class); + netconfCommands = new NetconfCommandsImpl(dataBroker, mountPointService); + } + + @Test + public void testListDevice() throws TimeoutException, TransactionCommitFailedException { + createTopology(LogicalDatastoreType.OPERATIONAL); + + final Map map = netconfCommands.listDevices(); + map.containsKey(NetconfConsoleConstants.NETCONF_ID); + assertTrue(map.containsKey(NODE_ID)); + + final Map mapNode = (Map) map.get(NODE_ID); + assertBaseNodeAttributes(mapNode); + } + + @Test + public void testShowDevice() throws TimeoutException, TransactionCommitFailedException { + createTopology(LogicalDatastoreType.OPERATIONAL); + + final Map mapCorrect = netconfCommands.showDevice(IP, String.valueOf(PORT)); + mapCorrect.containsKey(NetconfConsoleConstants.NETCONF_ID); + assertTrue(mapCorrect.containsKey(NODE_ID)); + + assertBaseNodeAttributesImmutableList((Map) mapCorrect.get(NODE_ID)); + + final Map mapWrongPort = netconfCommands.showDevice(IP, "1"); + assertFalse(mapWrongPort.containsKey(NODE_ID)); + + final Map mapWrongIP = netconfCommands.showDevice("1.1.1.1", String.valueOf(PORT)); + assertFalse(mapWrongIP.containsKey(NODE_ID)); + + final Map mapId = netconfCommands.showDevice(NODE_ID); + assertTrue(mapId.containsKey(NODE_ID)); + assertBaseNodeAttributesImmutableList((Map) mapId.get(NODE_ID)); + } + + @Test + public void testConnectDisconnectDevice() throws InterruptedException, TimeoutException, TransactionCommitFailedException { + final NetconfNode netconfNode = new NetconfNodeBuilder().setPort(new PortNumber(7777)). + setHost(HostBuilder.getDefaultInstance("10.10.1.1")).build(); + + createTopology(LogicalDatastoreType.CONFIGURATION); + netconfCommands.connectDevice(netconfNode, "netconf-ID"); + NetconfConsoleUtils.waitForUpdate("10.10.1.1"); + + final Topology topology = NetconfConsoleUtils.read(LogicalDatastoreType.CONFIGURATION, + NetconfIidFactory.NETCONF_TOPOLOGY_IID, dataBroker); + final List nodes = topology.getNode(); + assertEquals(2, nodes.size()); + + final Optional storedNode = nodes.stream().filter(node -> + node.getKey().getNodeId().getValue().equals("netconf-ID")).findFirst(); + + assertTrue(storedNode.isPresent()); + + NetconfNode storedNetconfNode = storedNode.get().getAugmentation(NetconfNode.class); + assertEquals(7777, storedNetconfNode.getPort().getValue().longValue()); + assertEquals("10.10.1.1", storedNetconfNode.getHost().getIpAddress().getIpv4Address().getValue()); + + netconfCommands.disconnectDevice("netconf-ID"); + + final Topology topologyDeleted = NetconfConsoleUtils.read(LogicalDatastoreType.CONFIGURATION, + NetconfIidFactory.NETCONF_TOPOLOGY_IID, dataBroker); + final List nodesDeleted = topologyDeleted.getNode(); + assertEquals(1, nodesDeleted.size()); + + final Optional storedNodeDeleted = nodesDeleted.stream().filter(node -> + node.getKey().getNodeId().getValue().equals("netconf-ID")).findFirst(); + + assertFalse(storedNodeDeleted.isPresent()); + } + + @Test + public void testUpdateDevice() throws TimeoutException, TransactionCommitFailedException { + //We need both, read data from OPERATIONAL DS and update data in CONFIGURATIONAL DS + createTopology(LogicalDatastoreType.OPERATIONAL); + createTopology(LogicalDatastoreType.CONFIGURATION); + + final Map update = new HashMap<>(); + update.put(NetconfConsoleConstants.NETCONF_IP, "7.7.7.7"); + update.put(NetconfConsoleConstants.TCP_ONLY, "true"); + + netconfCommands.updateDevice(NODE_ID, "admin", "admin", update); + NetconfConsoleUtils.waitForUpdate("7.7.7.7"); + + final Topology topology = NetconfConsoleUtils.read(LogicalDatastoreType.CONFIGURATION, + NetconfIidFactory.NETCONF_TOPOLOGY_IID, dataBroker); + final List nodes = topology.getNode(); + assertEquals(1, nodes.size()); + + final Optional storedNode = nodes.stream().filter(node -> + node.getKey().getNodeId().getValue().equals(NODE_ID)).findFirst(); + assertTrue(storedNode.isPresent()); + + NetconfNode storedNetconfNode = storedNode.get().getAugmentation(NetconfNode.class); + assertEquals("7.7.7.7", storedNetconfNode.getHost().getIpAddress().getIpv4Address().getValue()); + } + + @Test + public void testNetconfNodeFromIp() throws TimeoutException, TransactionCommitFailedException { + final List nodesNotExist = NetconfConsoleUtils.getNetconfNodeFromIp(IP, dataBroker); + assertNull(nodesNotExist); + createTopology(LogicalDatastoreType.OPERATIONAL); + final List nodes = NetconfConsoleUtils.getNetconfNodeFromIp(IP, dataBroker); + assertNotNull(nodes); + assertEquals(1, nodes.size()); + } + + private void createTopology(LogicalDatastoreType dataStoreType) throws TransactionCommitFailedException, TimeoutException { + final List nodes = new ArrayList<>(); + final Node node = getNetconfNode(NODE_ID, IP, PORT, CONN_STATUS, CAP_PREFIX); + nodes.add(node); + + final Topology topology = new TopologyBuilder(). + setKey(new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName()))). + setTopologyId(new TopologyId(TopologyNetconf.QNAME.getLocalName())).setNode(nodes).build(); + + final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + writeTransaction.put(dataStoreType, NetconfIidFactory.NETCONF_TOPOLOGY_IID, topology); + writeTransaction.submit().checkedGet(2, TimeUnit.SECONDS); + } + + private Node getNetconfNode(String nodeIdent, String ip, int portNumber, NetconfNodeConnectionStatus.ConnectionStatus cs, + String notificationCapabilityPrefix) { + + final Host host = HostBuilder.getDefaultInstance(ip); + final PortNumber port = new PortNumber(portNumber); + + final List avCapList = new ArrayList<>(); + avCapList.add(notificationCapabilityPrefix + "_availableCapabilityString1"); + final AvailableCapabilities avCaps = new AvailableCapabilitiesBuilder().setAvailableCapability(avCapList).build(); + + final NetconfNode nn = new NetconfNodeBuilder().setConnectionStatus(cs).setHost(host).setPort(port). + setAvailableCapabilities(avCaps).build(); + final NodeId nodeId = new NodeId(nodeIdent); + final NodeKey nk = new NodeKey(nodeId); + final NodeBuilder nb = new NodeBuilder(); + nb.setKey(nk); + nb.setNodeId(nodeId); + nb.addAugmentation(NetconfNode.class, nn); + return nb.build(); + } + + private void assertBaseNodeAttributes(Map mapNode) { + + assertTrue(mapNode.containsKey(NetconfConsoleConstants.NETCONF_ID)); + assertTrue(mapNode.containsKey(NetconfConsoleConstants.NETCONF_IP)); + assertTrue(mapNode.containsKey(NetconfConsoleConstants.NETCONF_PORT)); + assertTrue(mapNode.containsKey(NetconfConsoleConstants.STATUS)); + + assertEquals(NODE_ID, mapNode.get(NetconfConsoleConstants.NETCONF_ID)); + assertEquals(IP, mapNode.get(NetconfConsoleConstants.NETCONF_IP)); + assertEquals(String.valueOf(PORT), mapNode.get(NetconfConsoleConstants.NETCONF_PORT)); + assertEquals(CONN_STATUS.name().toLowerCase(), mapNode.get(NetconfConsoleConstants.STATUS)); + } + + private void assertBaseNodeAttributesImmutableList(Map mapNode) { + assertTrue(mapNode.containsKey(NetconfConsoleConstants.NETCONF_ID)); + assertTrue(mapNode.containsKey(NetconfConsoleConstants.NETCONF_IP)); + assertTrue(mapNode.containsKey(NetconfConsoleConstants.NETCONF_PORT)); + assertTrue(mapNode.containsKey(NetconfConsoleConstants.STATUS)); + + assertEquals(ImmutableList.of(NODE_ID), mapNode.get(NetconfConsoleConstants.NETCONF_ID)); + assertEquals(ImmutableList.of(IP), mapNode.get(NetconfConsoleConstants.NETCONF_IP)); + assertEquals(ImmutableList.of(String.valueOf(PORT)), mapNode.get(NetconfConsoleConstants.NETCONF_PORT)); + assertEquals(ImmutableList.of(CONN_STATUS.name()), mapNode.get(NetconfConsoleConstants.STATUS)); + } + + private List getYangSchemas() { + final List schemaPaths = Arrays.asList("/schemas/network-topology@2013-10-21.yang", + "/schemas/ietf-inet-types@2013-07-15.yang", "/schemas/yang-ext.yang", "/schemas/netconf-node-topology.yang"); + final List schemas = new ArrayList<>(); + for (String schemaPath : schemaPaths) { + final InputStream resourceAsStream = getClass().getResourceAsStream(schemaPath); + schemas.add(resourceAsStream); + } + return schemas; + } + + private static SchemaContext parseYangStreams(final List streams) { + CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR + .newBuild(); + final SchemaContext schemaContext; + try { + schemaContext = reactor.buildEffective(streams); + } catch (ReactorException e) { + throw new RuntimeException("Unable to build schema context from " + streams, e); + } + return schemaContext; + } + + private SchemaService createSchemaService() { + return new SchemaService() { + + @Override + public void addModule(Module module) { + } + + @Override + public void removeModule(Module module) { + + } + + @Override + public SchemaContext getSessionContext() { + return schemaContext; + } + + @Override + public SchemaContext getGlobalContext() { + return schemaContext; + } + + @Override + public ListenerRegistration registerSchemaContextListener(final SchemaContextListener listener) { + listener.onGlobalContextUpdated(getGlobalContext()); + return new ListenerRegistration() { + @Override + public void close() { + + } + + @Override + public SchemaContextListener getInstance() { + return listener; + } + }; + } + }; + } +} diff --git a/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfConsoleProviderTest.java b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfConsoleProviderTest.java new file mode 100644 index 0000000000..ef20f81def --- /dev/null +++ b/netconf/netconf-console/src/test/java/org/opendaylight/netconf/console/impl/NetconfConsoleProviderTest.java @@ -0,0 +1,59 @@ +/* + * 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.console.impl; + +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 static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.powermock.api.mockito.PowerMockito.when; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.BDDMockito; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.MountPointService; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.netconf.console.api.NetconfCommands; +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 NetconfConsoleProviderTest { + + @Test + public void testProvider() throws Exception { + final NetconfConsoleProvider netconfConsoleProvider = new NetconfConsoleProvider(); + + PowerMockito.mockStatic(FrameworkUtil.class); + + final BindingAwareBroker.ProviderContext session = mock(BindingAwareBroker.ProviderContext.class); + final MountPointService mountPointService = mock(MountPointService.class); + final BundleContext bundleContext = mock(BundleContext.class); + final DataBroker dataBroker = mock(DataBroker.class); + final Bundle bundle = mock(Bundle.class); + + doReturn(dataBroker).when(session).getSALService(DataBroker.class); + doReturn(mountPointService).when(session).getSALService(MountPointService.class); + BDDMockito.given(FrameworkUtil.getBundle(any())).willReturn(bundle); + when(bundle.getBundleContext()).thenReturn(bundleContext); + + netconfConsoleProvider.onSessionInitiated(session); + + verify(bundleContext, times(1)).registerService(eq(NetconfCommands.class), any(NetconfCommandsImpl.class), eq(null)); + + } +} diff --git a/netconf/netconf-console/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang b/netconf/netconf-console/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang new file mode 100644 index 0000000000..5c6f139a27 --- /dev/null +++ b/netconf/netconf-console/src/test/resources/schemas/ietf-inet-types@2013-07-15.yang @@ -0,0 +1,457 @@ +module ietf-inet-types { + + namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types"; + prefix "inet"; + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: + WG List: + + WG Chair: David Kessens + + + WG Chair: Juergen Schoenwaelder + + + Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of generally useful derived + YANG data types for Internet addresses and related things. + + Copyright (c) 2013 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 6991; see + the RFC itself for full legal notices."; + + revision 2013-07-15 { + description + "This revision adds the following new data types: + - ip-address-no-zone + - ipv4-address-no-zone + - ipv6-address-no-zone"; + reference + "RFC 6991: Common YANG Data Types"; + } + + revision 2010-09-24 { + description + "Initial revision."; + reference + "RFC 6021: Common YANG Data Types"; + } + + /*** collection of types related to protocol fields ***/ + + typedef ip-version { + type enumeration { + enum unknown { + value "0"; + description + "An unknown or unspecified version of the Internet + protocol."; + } + enum ipv4 { + value "1"; + description + "The IPv4 protocol as defined in RFC 791."; + } + enum ipv6 { + value "2"; + description + "The IPv6 protocol as defined in RFC 2460."; + } + } + description + "This value represents the version of the IP protocol. + + In the value set and its semantics, this type is equivalent + to the InetVersion textual convention of the SMIv2."; + reference + "RFC 791: Internet Protocol + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + typedef dscp { + type uint8 { + range "0..63"; + } + description + "The dscp type represents a Differentiated Services Code Point + that may be used for marking packets in a traffic stream. + In the value set and its semantics, this type is equivalent + to the Dscp textual convention of the SMIv2."; + reference + "RFC 3289: Management Information Base for the Differentiated + Services Architecture + RFC 2474: Definition of the Differentiated Services Field + (DS Field) in the IPv4 and IPv6 Headers + RFC 2780: IANA Allocation Guidelines For Values In + the Internet Protocol and Related Headers"; + } + + typedef ipv6-flow-label { + type uint32 { + range "0..1048575"; + } + description + "The ipv6-flow-label type represents the flow identifier or Flow + Label in an IPv6 packet header that may be used to + discriminate traffic flows. + + In the value set and its semantics, this type is equivalent + to the IPv6FlowLabel textual convention of the SMIv2."; + reference + "RFC 3595: Textual Conventions for IPv6 Flow Label + RFC 2460: Internet Protocol, Version 6 (IPv6) Specification"; + } + + typedef port-number { + type uint16 { + range "0..65535"; + } + description + "The port-number type represents a 16-bit port number of an + Internet transport-layer protocol such as UDP, TCP, DCCP, or + SCTP. Port numbers are assigned by IANA. A current list of + all assignments is available from . + + Note that the port number value zero is reserved by IANA. In + situations where the value zero does not make sense, it can + be excluded by subtyping the port-number type. + In the value set and its semantics, this type is equivalent + to the InetPortNumber textual convention of the SMIv2."; + reference + "RFC 768: User Datagram Protocol + RFC 793: Transmission Control Protocol + RFC 4960: Stream Control Transmission Protocol + RFC 4340: Datagram Congestion Control Protocol (DCCP) + RFC 4001: Textual Conventions for Internet Network Addresses"; + } + + /*** collection of types related to autonomous systems ***/ + + typedef as-number { + type uint32; + description + "The as-number type represents autonomous system numbers + which identify an Autonomous System (AS). An AS is a set + of routers under a single technical administration, using + an interior gateway protocol and common metrics to route + packets within the AS, and using an exterior gateway + protocol to route packets to other ASes. IANA maintains + the AS number space and has delegated large parts to the + regional registries. + + Autonomous system numbers were originally limited to 16 + bits. BGP extensions have enlarged the autonomous system + number space to 32 bits. This type therefore uses an uint32 + base type without a range restriction in order to support + a larger autonomous system number space. + + In the value set and its semantics, this type is equivalent + to the InetAutonomousSystemNumber textual convention of + the SMIv2."; + reference + "RFC 1930: Guidelines for creation, selection, and registration + of an Autonomous System (AS) + RFC 4271: A Border Gateway Protocol 4 (BGP-4) + RFC 4001: Textual Conventions for Internet Network Addresses + RFC 6793: BGP Support for Four-Octet Autonomous System (AS) + Number Space"; + } + + /*** collection of types related to IP addresses and hostnames ***/ + + typedef ip-address { + type union { + type inet:ipv4-address; + type inet:ipv6-address; + } + description + "The ip-address type represents an IP address and is IP + version neutral. The format of the textual representation + implies the IP version. This type supports scoped addresses + by allowing zone identifiers in the address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '(%[\p{N}\p{L}]+)?'; + } + description + "The ipv4-address type represents an IPv4 address in + dotted-quad notation. The IPv4 address may include a zone + index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format for the zone index is the numerical + format"; + } + + typedef ipv6-address { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(%[\p{N}\p{L}]+)?'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(%.+)?'; + } + description + "The ipv6-address type represents an IPv6 address in full, + mixed, shortened, and shortened-mixed notation. The IPv6 + address may include a zone index, separated by a % sign. + + The zone index is used to disambiguate identical address + values. For link-local addresses, the zone index will + typically be the interface index number or the name of an + interface. If the zone index is not present, the default + zone of the device will be used. + + The canonical format of IPv6 addresses uses the textual + representation defined in Section 4 of RFC 5952. The + canonical format for the zone index is the numerical + format as described in Section 11.2 of RFC 4007."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-address-no-zone { + type union { + type inet:ipv4-address-no-zone; + type inet:ipv6-address-no-zone; + } + description + "The ip-address-no-zone type represents an IP address and is + IP version neutral. The format of the textual representation + implies the IP version. This type does not support scoped + addresses since it does not allow zone identifiers in the + address format."; + reference + "RFC 4007: IPv6 Scoped Address Architecture"; + } + + typedef ipv4-address-no-zone { + type inet:ipv4-address { + pattern '[0-9\.]*'; + } + description + "An IPv4 address without a zone index. This type, derived from + ipv4-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + } + + typedef ipv6-address-no-zone { + type inet:ipv6-address { + pattern '[0-9a-fA-F:\.]*'; + } + description + "An IPv6 address without a zone index. This type, derived from + ipv6-address, may be used in situations where the zone is + known from the context and hence no zone index is needed."; + reference + "RFC 4291: IP Version 6 Addressing Architecture + RFC 4007: IPv6 Scoped Address Architecture + RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + typedef ip-prefix { + type union { + type inet:ipv4-prefix; + type inet:ipv6-prefix; + } + description + "The ip-prefix type represents an IP prefix and is IP + version neutral. The format of the textual representations + implies the IP version."; + } + + typedef ipv4-prefix { + type string { + pattern + '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}' + + '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])' + + '/(([0-9])|([1-2][0-9])|(3[0-2]))'; + } + description + "The ipv4-prefix type represents an IPv4 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 32. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The canonical format of an IPv4 prefix has all bits of + the IPv4 address set to zero that are not part of the + IPv4 prefix."; + } + + typedef ipv6-prefix { + type string { + pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}' + + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|' + + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}' + + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))' + + '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))'; + pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|' + + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)' + + '(/.+)'; + } + description + "The ipv6-prefix type represents an IPv6 address prefix. + The prefix length is given by the number following the + slash character and must be less than or equal to 128. + + A prefix length value of n corresponds to an IP address + mask that has n contiguous 1-bits from the most + significant bit (MSB) and all other bits set to 0. + + The IPv6 address should have all bits that do not belong + to the prefix set to zero. + + The canonical format of an IPv6 prefix has all bits of + the IPv6 address set to zero that are not part of the + IPv6 prefix. Furthermore, the IPv6 address is represented + as defined in Section 4 of RFC 5952."; + reference + "RFC 5952: A Recommendation for IPv6 Address Text + Representation"; + } + + /*** collection of domain name and URI types ***/ + + typedef domain-name { + type string { + pattern + '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*' + + '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)' + + '|\.'; + length "1..253"; + } + description + "The domain-name type represents a DNS domain name. The + name SHOULD be fully qualified whenever possible. + + Internet domain names are only loosely specified. Section + 3.5 of RFC 1034 recommends a syntax (modified in Section + 2.1 of RFC 1123). The pattern above is intended to allow + for current practice in domain name use, and some possible + future expansion. It is designed to hold various types of + domain names, including names used for A or AAAA records + (host names) and other records, such as SRV records. Note + that Internet host names have a stricter syntax (described + in RFC 952) than the DNS recommendations in RFCs 1034 and + 1123, and that systems that want to store host names in + schema nodes using the domain-name type are recommended to + adhere to this stricter standard to ensure interoperability. + + The encoding of DNS names in the DNS protocol is limited + to 255 characters. Since the encoding consists of labels + prefixed by a length bytes and there is a trailing NULL + byte, only 253 characters can appear in the textual dotted + notation. + + The description clause of schema nodes using the domain-name + type MUST describe when and how these names are resolved to + IP addresses. Note that the resolution of a domain-name value + may require to query multiple DNS records (e.g., A for IPv4 + and AAAA for IPv6). The order of the resolution process and + which DNS record takes precedence can either be defined + explicitly or may depend on the configuration of the + resolver. + + Domain-name values use the US-ASCII encoding. Their canonical + format uses lowercase US-ASCII characters. Internationalized + domain names MUST be A-labels as per RFC 5890."; + reference + "RFC 952: DoD Internet Host Table Specification + RFC 1034: Domain Names - Concepts and Facilities + RFC 1123: Requirements for Internet Hosts -- Application + and Support + RFC 2782: A DNS RR for specifying the location of services + (DNS SRV) + RFC 5890: Internationalized Domain Names in Applications + (IDNA): Definitions and Document Framework"; + } + + typedef host { + type union { + type inet:ip-address; + type inet:domain-name; + } + description + "The host type represents either an IP address or a DNS + domain name."; + } + + typedef uri { + type string; + description + "The uri type represents a Uniform Resource Identifier + (URI) as defined by STD 66. + + Objects using the uri type MUST be in US-ASCII encoding, + and MUST be normalized as described by RFC 3986 Sections + 6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary + percent-encoding is removed, and all case-insensitive + characters are set to lowercase except for hexadecimal + digits, which are normalized to uppercase as described in + Section 6.2.2.1. + + The purpose of this normalization is to help provide + unique URIs. Note that this normalization is not + sufficient to provide uniqueness. Two URIs that are + textually distinct after this normalization may still be + equivalent. + + Objects using the uri type may restrict the schemes that + they permit. For example, 'data:' and 'urn:' schemes + might not be appropriate. + + A zero-length URI is not a valid URI. This can be used to + express 'URI absent' where required. + + In the value set and its semantics, this type is equivalent + to the Uri SMIv2 textual convention defined in RFC 5017."; + reference + "RFC 3986: Uniform Resource Identifier (URI): Generic Syntax + RFC 3305: Report from the Joint W3C/IETF URI Planning Interest + Group: Uniform Resource Identifiers (URIs), URLs, + and Uniform Resource Names (URNs): Clarifications + and Recommendations + RFC 5017: MIB Textual Conventions for Uniform Resource + Identifiers (URIs)"; + } + +} diff --git a/netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang b/netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang new file mode 100644 index 0000000000..62b88b974f --- /dev/null +++ b/netconf/netconf-console/src/test/resources/schemas/netconf-node-topology.yang @@ -0,0 +1,243 @@ +module netconf-node-topology { + namespace "urn:opendaylight:netconf-node-topology"; + prefix "nettop"; + + import network-topology { prefix nt; revision-date 2013-10-21; } + import yang-ext { prefix ext; revision-date "2013-07-09";} + import ietf-inet-types { prefix inet; revision-date "2013-07-15"; } + + revision "2015-01-14" { + description "Initial revision of Topology model"; + } + + augment "/nt:network-topology/nt:topology/nt:topology-types" { + container topology-netconf { + } + } + + grouping netconf-node-credentials { + + choice credentials { + config true; + case login-password { + leaf username { + type string; + } + + leaf password { + type string; + } + } + } + } + + grouping netconf-node-connection-parameters { + + leaf host { + type inet:host; + } + + leaf port { + type inet:port-number; + } + + leaf tcp-only { + config true; + type boolean; + } + + leaf schemaless { + type boolean; + default false; + } + + container yang-module-capabilities { + config true; + leaf override { + type boolean; + default false; + description "Whether to override or merge this list of capabilities with capabilities from device"; + } + + leaf-list capability { + type string; + description "Set a list of capabilities to override capabilities provided in device's hello message. + Can be used for devices that do not report any yang modules in their hello message"; + } + } + + leaf reconnect-on-changed-schema { + config true; + type boolean; + default false; + description "If true, the connector would auto disconnect/reconnect when schemas are changed in the remote device. + The connector subscribes (right after connect) to base netconf notifications and listens for netconf-capability-change notification"; + } + + leaf connection-timeout-millis { + description "Specifies timeout in milliseconds after which connection must be established."; + config true; + type uint32; + default 20000; + } + + leaf default-request-timeout-millis { + description "Timeout for blocking operations within transactions."; + config true; + type uint32; + default 60000; + } + + leaf max-connection-attempts { + description "Maximum number of connection retries. Non positive value or null is interpreted as infinity."; + config true; + type uint32; + default 0; // retry forever + } + + leaf between-attempts-timeout-millis { + description "Initial timeout in milliseconds to wait between connection attempts. Will be multiplied by sleep-factor with every additional attempt"; + config true; + type uint16; + default 2000; + } + + leaf sleep-factor { + config true; + type decimal64 { + fraction-digits 1; + } + default 1.5; + } + + // Keepalive configuration + leaf keepalive-delay { + config true; + type uint32; + default 120; + description "Netconf connector sends keepalive RPCs while the session is idle, this delay specifies the delay between keepalive RPC in seconds + If a value <1 is provided, no keepalives will be sent"; + } + + leaf concurrent-rpc-limit { + config true; + type uint16; + default 0; + description "Limit of concurrent messages that can be send before reply messages are received. + If value <1 is provided, no limit will be enforced"; + } + } + + grouping netconf-node-connection-status { + + leaf connection-status { + config false; + type enumeration { + enum connecting; + enum connected; + enum unable-to-connect; + } + } + + container clustered-connection-status { + config false; + list node-status { + leaf node { + type string; + } + leaf status { + type enumeration { + enum connected; + enum unavailable; + enum failed; + } + } + } + } + + leaf connected-message { + config false; + type string; + } + + container available-capabilities { + config false; + leaf-list available-capability { + type string; + } + } + + container unavailable-capabilities { + config false; + list unavailable-capability { + leaf capability { + type string; + } + + leaf failure-reason { + type enumeration { + enum missing-source; + enum unable-to-resolve; + } + } + } + } + + container pass-through { + when "../connection-status = connected"; + description + "When the underlying node is connected, its NETCONF context + is available verbatim under this container through the + mount extension."; + } + + } + + grouping netconf-schema-storage { + leaf schema-cache-directory { + config true; + type string; + default "schema"; + description "The destination schema repository for yang files relative to the cache directory. This may be specified per netconf mount + so that the loaded yang files are stored to a distinct directory to avoid potential conflict."; + } + + container yang-library { + leaf yang-library-url { + config true; + type inet:uri; + description "Yang library to be plugged as additional source provider into the shared schema repository"; + } + + // credentials for basic http authentication + leaf username { + config true; + type string; + } + + leaf password { + config true; + type string; + } + } + } + + grouping netconf-node-fields { + + uses netconf-node-credentials; + + uses netconf-node-connection-parameters; + + uses netconf-node-connection-status; + + uses netconf-schema-storage; + + } + + augment "/nt:network-topology/nt:topology/nt:node" { + when "../../nt:topology-types/topology-netconf"; + ext:augment-identifier "netconf-node"; + + uses netconf-node-fields; + } +} diff --git a/netconf/netconf-console/src/test/resources/schemas/network-topology@2013-10-21.yang b/netconf/netconf-console/src/test/resources/schemas/network-topology@2013-10-21.yang new file mode 100644 index 0000000000..dc5dcad708 --- /dev/null +++ b/netconf/netconf-console/src/test/resources/schemas/network-topology@2013-10-21.yang @@ -0,0 +1,339 @@ +module network-topology { + yang-version 1; + namespace "urn:TBD:params:xml:ns:yang:network-topology"; + // replace with IANA namespace when assigned + prefix "nt"; + + import ietf-inet-types { prefix "inet"; revision-date 2013-07-15; } + + organization "TBD"; + + contact "WILL-BE-DEFINED-LATER"; + + description + "This module defines a model for the topology of a network. + Key design decisions are as follows: + A topology consists of a set of nodes and links. + Links are point-to-point and unidirectional. + Bidirectional connections need to be represented through + two separate links. + Multipoint connections, broadcast domains etc can be represented + through a hierarchy of nodes, then connecting nodes at + upper layers of the hierarchy."; + + revision 2013-10-21 { + description + "Initial revision."; + } + + typedef topology-id { + type inet:uri; + description + "An identifier for a topology."; + } + + typedef node-id { + type inet:uri; + description + "An identifier for a node in a topology. + The identifier may be opaque. + The identifier SHOULD be chosen such that the same node in a + real network topology will always be identified through the + same identifier, even if the model is instantiated in separate + datastores. An implementation MAY choose to capture semantics + in the identifier, for example to indicate the type of node + and/or the type of topology that the node is a part of."; + } + + + typedef link-id { + type inet:uri; + description + "An identifier for a link in a topology. + The identifier may be opaque. + The identifier SHOULD be chosen such that the same link in a + real network topology will always be identified through the + same identifier, even if the model is instantiated in separate + datastores. An implementation MAY choose to capture semantics + in the identifier, for example to indicate the type of link + and/or the type of topology that the link is a part of."; + } + + typedef tp-id { + type inet:uri; + description + "An identifier for termination points on a node. + The identifier may be opaque. + The identifier SHOULD be chosen such that the same TP in a + real network topology will always be identified through the + same identifier, even if the model is instantiated in separate + datastores. An implementation MAY choose to capture semantics + in the identifier, for example to indicate the type of TP + and/or the type of node and topology that the TP is a part of."; + } + + typedef tp-ref { + type leafref { + path "/network-topology/topology/node/termination-point/tp-id"; + } + description + "A type for an absolute reference to a termination point. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + typedef topology-ref { + type leafref { + path "/network-topology/topology/topology-id"; + } + description + "A type for an absolute reference a topology instance."; + } + + typedef node-ref { + type leafref { + path "/network-topology/topology/node/node-id"; + } + description + + "A type for an absolute reference to a node instance. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + + typedef link-ref { + type leafref { + path "/network-topology/topology/link/link-id"; + } + description + "A type for an absolute reference a link instance. + (This type should not be used for relative references. + In such a case, a relative path should be used instead.)"; + } + + grouping tp-attributes { + description + "The data objects needed to define a termination point. + (This only includes a single leaf at this point, used + to identify the termination point.) + Provided in a grouping so that in addition to the datastore, + the data can also be included in notifications."; + leaf tp-id { + type tp-id; + } + leaf-list tp-ref { + type tp-ref; + config false; + description + "The leaf list identifies any termination points that the + termination point is dependent on, or maps onto. + Those termination points will themselves be contained + in a supporting node. + This dependency information can be inferred from + the dependencies between links. For this reason, + this item is not separately configurable. Hence no + corresponding constraint needs to be articulated. + The corresponding information is simply provided by the + implementing system."; + } + } + + grouping node-attributes { + description + "The data objects needed to define a node. + The objects are provided in a grouping so that in addition to + the datastore, the data can also be included in notifications + as needed."; + + leaf node-id { + type node-id; + description + "The identifier of a node in the topology. + A node is specific to a topology to which it belongs."; + } + list supporting-node { + description + "This list defines vertical layering information for nodes. + It allows to capture for any given node, which node (or nodes) + in the corresponding underlay topology it maps onto. + A node can map to zero, one, or more nodes below it; + accordingly there can be zero, one, or more elements in the list. + If there are specific layering requirements, for example + specific to a particular type of topology that only allows + for certain layering relationships, the choice + below can be augmented with additional cases. + A list has been chosen rather than a leaf-list in order + to provide room for augmentations, e.g. for + statistics or priorization information associated with + supporting nodes."; + // This is not what was published in the initial draft, + // added topology-ref leaf and added it to the key + key "topology-ref node-ref"; + leaf topology-ref { + type topology-ref; + } + leaf node-ref { + type node-ref; + } + } + } + + grouping link-attributes { + // This is a grouping, not defined inline with the link definition itself, + // so it can be included in a notification, if needed + leaf link-id { + type link-id; + description + "The identifier of a link in the topology. + A link is specific to a topology to which it belongs."; + } + container source { + leaf source-node { + mandatory true; + type node-ref; + description + "Source node identifier, must be in same topology."; + } + leaf source-tp { + type tp-ref; + description + "Termination point within source node that terminates the link."; + + } + } + container destination { + leaf dest-node { + mandatory true; + type node-ref; + description + "Destination node identifier, must be in same topology."; + } + leaf dest-tp { + type tp-ref; + description + "Termination point within destination node that terminates the link."; + } + } + list supporting-link { + key "link-ref"; + leaf link-ref { + type link-ref; + } + } + } + + + container network-topology { + list topology { + description " + This is the model of an abstract topology. + A topology contains nodes and links. + Each topology MUST be identified by + unique topology-id for reason that a network could contain many + topologies. + "; + key "topology-id"; + leaf topology-id { + type topology-id; + description " + It is presumed that a datastore will contain many topologies. To + distinguish between topologies it is vital to have UNIQUE + topology identifiers. + "; + } + leaf server-provided { + type boolean; + config false; + description " + Indicates whether the topology is configurable by clients, + or whether it is provided by the server. This leaf is + + populated by the server implementing the model. + It is set to false for topologies that are created by a client; + it is set to true otherwise. If it is set to true, any + attempt to edit the topology MUST be rejected. + "; + } + container topology-types { + description + "This container is used to identify the type, or types + (as a topology can support several types simultaneously), + of the topology. + Topology types are the subject of several integrity constraints + that an implementing server can validate in order to + maintain integrity of the datastore. + Topology types are indicated through separate data nodes; + the set of topology types is expected to increase over time. + To add support for a new topology, an augmenting module + needs to augment this container with a new empty optional + container to indicate the new topology type. + The use of a container allows to indicate a subcategorization + of topology types. + The container SHALL NOT be augmented with any data nodes + that serve a purpose other than identifying a particular + topology type. + "; + } + list underlay-topology { + key "topology-ref"; + leaf topology-ref { + type topology-ref; + } + // a list, not a leaf-list, to allow for potential augmentation + // with properties specific to the underlay topology, + // such as statistics, preferences, or cost. + description + "Identifies the topology, or topologies, that this topology + is dependent on."; + } + + list node { + description "The list of network nodes defined for the topology."; + key "node-id"; + uses node-attributes; + must "boolean(../underlay-topology[*]/node[./supporting-nodes/node-ref])"; + // This constraint is meant to ensure that a referenced node is in fact + // a node in an underlay topology. + list termination-point { + description + + "A termination point can terminate a link. + Depending on the type of topology, a termination point could, + for example, refer to a port or an interface."; + key "tp-id"; + uses tp-attributes; + } + } + + list link { + description " + A Network Link connects a by Local (Source) node and + a Remote (Destination) Network Nodes via a set of the + nodes' termination points. + As it is possible to have several links between the same + source and destination nodes, and as a link could potentially + be re-homed between termination points, to ensure that we + would always know to distinguish between links, every link + is identified by a dedicated link identifier. + Note that a link models a point-to-point link, not a multipoint + link. + Layering dependencies on links in underlay topologies are + not represented as the layering information of nodes and of + termination points is sufficient. + "; + key "link-id"; + uses link-attributes; + must "boolean(../underlay-topology/link[./supporting-link])"; + // Constraint: any supporting link must be part of an underlay topology + must "boolean(../node[./source/source-node])"; + // Constraint: A link must have as source a node of the same topology + must "boolean(../node[./destination/dest-node])"; + // Constraint: A link must have as source a destination of the same topology + must "boolean(../node/termination-point[./source/source-tp])"; + // Constraint: The source termination point must be contained in the source node + must "boolean(../node/termination-point[./destination/dest-tp])"; + // Constraint: The destination termination point must be contained + // in the destination node + } + } + } +} diff --git a/netconf/netconf-console/src/test/resources/schemas/yang-ext.yang b/netconf/netconf-console/src/test/resources/schemas/yang-ext.yang new file mode 100644 index 0000000000..0fbe94ddc4 --- /dev/null +++ b/netconf/netconf-console/src/test/resources/schemas/yang-ext.yang @@ -0,0 +1,79 @@ +module yang-ext { + yang-version 1; + namespace "urn:opendaylight:yang:extension:yang-ext"; + prefix "ext"; + + contact "Anton Tkacik "; + + description + "Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v1.0 which accompanies this distribution, + and is available at http://www.eclipse.org/legal/epl-v10.html"; + + revision "2013-07-09" { + description ""; + } + + // Augmentation name + + extension "augment-identifier" { + description + "YANG language extension which assigns an identifier to + augmentation. Augment identifier is used to identify + specific augment statement by name. + + The identifier syntax is defined formally defined by the rule + 'identifier' in Section 12 of RFC 6020. + + All augment identifiers defined in a namespace MUST be unique. + The namespace of augment identifiers is shared by module and + its submodules."; + + /* + Discussion: + This extension allows for ease of development / debug + of YANG modules and it is suitable for code generation, + where each augment statement is nicely identified by + unique name instead of combination of augment target + and when condition. + */ + argument "identifier"; + } + + + // Context-aware RPCs + + grouping rpc-context-ref { + description + "A reference to RPC context."; + leaf context-instance { + type instance-identifier; + description "Pointer to the context. "; + } + } + + extension "rpc-context-instance" { + description + "YANG language extension which defines enclosing (parent) + schema node as referencable context for RPCs. + + The argument is identity which is used to identify RPC context + type."; + + argument "context-type"; + } + + extension "context-reference" { + argument "context-type"; + } + + extension "context-instance" { + argument "context-type"; + } + + extension "instance-target" { + argument "path"; + } +} -- 2.36.6