From 4cfaa1203b382eca36e18b7ad56d2493037cfda2 Mon Sep 17 00:00:00 2001 From: Tony Tkacik Date: Sun, 10 Nov 2013 07:16:03 +0100 Subject: [PATCH] Updated sal-netconf-connector mountpoint integration - Updated sal-netconf-connector mountpoint integration, added end-to-end test container from restconf to netconf to config subsystem. Change-Id: I7bd5e415fb6ac383ac131ef8e3124d91f7dfca1f Signed-off-by: Tony Tkacik --- .../md-sal/sal-netconf-connector/pom.xml | 21 ++ .../sal/connect/netconf/InventoryUtils.java | 39 ++++ .../sal/connect/netconf/NetconfDevice.xtend | 95 +++++++++ .../netconf/NetconfDeviceManager.xtend | 84 ++++++++ .../netconf/NetconfInventoryUtils.java | 22 ++ .../sal/connect/netconf/NetconfMapping.xtend | 95 +++++++++ .../sal/connect/netconf/NetconfProvider.java | 35 ++++ .../sal/connect/netconf/XmlDocumentUtils.java | 54 +++++ .../sal/connector/netconf/test/MountTest.java | 197 ++++++++++++++++++ .../md-sal/test/sal-rest-connector-it/pom.xml | 101 +++++++++ .../it/ServiceProviderController.java | 110 +++++++++- 11 files changed, 843 insertions(+), 10 deletions(-) create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceManager.xtend create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfProvider.java create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java create mode 100644 opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connector/netconf/test/MountTest.java diff --git a/opendaylight/md-sal/sal-netconf-connector/pom.xml b/opendaylight/md-sal/sal-netconf-connector/pom.xml index 59ec7a780b..6ef5780c8a 100644 --- a/opendaylight/md-sal/sal-netconf-connector/pom.xml +++ b/opendaylight/md-sal/sal-netconf-connector/pom.xml @@ -40,6 +40,13 @@ yang-data-impl 0.5.9-SNAPSHOT + + org.opendaylight.controller + sal-broker-impl + 1.0-SNAPSHOT + test + jar + junit junit @@ -168,4 +175,18 @@ bundle + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.controller.sal.connect.netconf.NetconfProvider + + + + + diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java new file mode 100644 index 0000000000..8350e39c21 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/InventoryUtils.java @@ -0,0 +1,39 @@ +package org.opendaylight.controller.sal.connect.netconf; + +import java.net.URI; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; + +public class InventoryUtils { + + private static final URI INVENTORY_NAMESPACE = URI.create("urn:opendaylight:inventory"); + private static final Date INVENTORY_REVISION = date(); + public static final QName INVENTORY_NODES = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "nodes"); + public static final QName INVENTORY_NODE = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "node"); + public static final QName INVENTORY_ID = new QName(INVENTORY_NAMESPACE, INVENTORY_REVISION, "id"); + + public static final InstanceIdentifier INVENTORY_PATH = InstanceIdentifier.builder().node(INVENTORY_NODES) + .toInstance(); + public static final QName NETCONF_INVENTORY_MOUNT = null; + + + + private static Date date() { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd"); + try { + return formatter.parse("2013-08-19"); + } catch (ParseException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + + +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend new file mode 100644 index 0000000000..0171c1f9e3 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.xtend @@ -0,0 +1,95 @@ +package org.opendaylight.controller.sal.connect.netconf + +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier +import org.opendaylight.controller.md.sal.common.api.data.DataReader +import org.opendaylight.yangtools.yang.data.api.CompositeNode +import org.opendaylight.controller.netconf.client.NetconfClient +import org.opendaylight.controller.sal.core.api.RpcImplementation +import static extension org.opendaylight.controller.sal.connect.netconf.NetconfMapping.* +import java.net.InetSocketAddress +import org.opendaylight.yangtools.yang.data.api.Node +import org.opendaylight.yangtools.yang.data.api.SimpleNode +import org.opendaylight.yangtools.yang.common.QName +import java.util.Collections +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher +import org.opendaylight.yangtools.concepts.Registration + +class NetconfDevice implements DataReader, RpcImplementation { + + var NetconfClient client; + + @Property + var InetSocketAddress socketAddress; + + @Property + val MountProvisionInstance mountInstance; + + @Property + val InstanceIdentifier path; + + Registration> operReaderReg + + Registration> confReaderReg + + public new(MountProvisionInstance mount,InstanceIdentifier path) { + _mountInstance = mount; + _path = path; + } + + def start(NetconfClientDispatcher dispatcher) { + client = new NetconfClient("sal-netconf-connector", socketAddress, dispatcher); + + confReaderReg = mountInstance.registerConfigurationReader(path,this); + operReaderReg = mountInstance.registerOperationalReader(path,this); + } + + override readConfigurationData(InstanceIdentifier path) { + val result = invokeRpc(NETCONF_GET_CONFIG_QNAME, wrap(NETCONF_GET_CONFIG_QNAME, path.toFilterStructure())); + val data = result.result.getFirstCompositeByName(NETCONF_DATA_QNAME); + return data?.findNode(path) as CompositeNode; + } + + override readOperationalData(InstanceIdentifier path) { + val result = invokeRpc(NETCONF_GET_QNAME, wrap(NETCONF_GET_QNAME, path.toFilterStructure())); + val data = result.result.getFirstCompositeByName(NETCONF_DATA_QNAME); + return data?.findNode(path) as CompositeNode; + } + + override getSupportedRpcs() { + Collections.emptySet; + } + + override invokeRpc(QName rpc, CompositeNode input) { + val message = rpc.toRpcMessage(input); + val result = client.sendMessage(message); + return result.toRpcResult(); + } + + def Node findNode(CompositeNode node, InstanceIdentifier identifier) { + + var Node current = node; + for (arg : identifier.path) { + if (current instanceof SimpleNode) { + return null; + } else if (current instanceof CompositeNode) { + val currentComposite = (current as CompositeNode); + + current = currentComposite.getFirstCompositeByName(arg.nodeType); + if (current == null) { + current = currentComposite.getFirstSimpleByName(arg.nodeType); + } + if (current == null) { + return null; + } + } + } + return current; + } + + public def stop() { + confReaderReg?.close() + operReaderReg?.close() + } + +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceManager.xtend b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceManager.xtend new file mode 100644 index 0000000000..2fe145e18a --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceManager.xtend @@ -0,0 +1,84 @@ +package org.opendaylight.controller.sal.connect.netconf + +import org.opendaylight.controller.sal.core.api.Broker.ProviderSession +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService +import org.opendaylight.controller.md.sal.common.api.data.DataProvisionService +import org.opendaylight.controller.sal.core.api.data.DataProviderService +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier +import org.opendaylight.yangtools.yang.common.QName +import static org.opendaylight.controller.sal.connect.netconf.InventoryUtils.*; +import static extension org.opendaylight.controller.sal.connect.netconf.NetconfInventoryUtils.*; + +import org.opendaylight.controller.sal.core.api.data.DataChangeListener +import org.opendaylight.yangtools.yang.data.api.CompositeNode +import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent +import java.util.Map +import java.util.concurrent.ConcurrentHashMap +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher +import java.io.OptionalDataException +import com.google.common.base.Optional +import java.net.SocketAddress +import java.net.InetSocketAddress + +class NetconfDeviceManager { + + val Map devices = new ConcurrentHashMap; + + var ProviderSession session; + + @Property + var DataProviderService dataService; + + @Property + var MountProvisionService mountService; + + val nodeUpdateListener = new NetconfInventoryListener(this); + + + @Property + var NetconfClientDispatcher dispatcher; + + def void start() { + dataService?.registerDataChangeListener(INVENTORY_PATH, nodeUpdateListener); + if(dispatcher == null) { + dispatcher = new NetconfClientDispatcher(Optional.absent); + } + } + + def netconfNodeAdded(InstanceIdentifier path, CompositeNode node) { + val address = node.endpointAddress; + val port = Integer.parseInt(node.endpointPort); + netconfNodeAdded(path,new InetSocketAddress(address,port)) + + } + + def netconfNodeAdded(InstanceIdentifier path, InetSocketAddress address) { + + val mountPointPath = path; + val mountPoint = mountService.createOrGetMountPoint(mountPointPath); + val localPath = InstanceIdentifier.builder().toInstance; + val netconfDevice = new NetconfDevice(mountPoint,localPath); + netconfDevice.setSocketAddress(address); + netconfDevice.start(dispatcher); + } + + def netconfNodeRemoved(InstanceIdentifier path) { + + } + +} + +class NetconfInventoryListener implements DataChangeListener { + + val NetconfDeviceManager manager; + + new(NetconfDeviceManager manager) { + this.manager = manager; + } + + override onDataChanged(DataChangeEvent change) { + + //manager.netconfNodeAdded(path, change); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java new file mode 100644 index 0000000000..a69f6708a1 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfInventoryUtils.java @@ -0,0 +1,22 @@ +package org.opendaylight.controller.sal.connect.netconf; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; + +public class NetconfInventoryUtils { + + + public static final QName NETCONF_MOUNT = null; + public static final QName NETCONF_ENDPOINT = null; + public static final QName NETCONF_ENDPOINT_ADDRESS = null; + public static final QName NETCONF_ENDPOINT_PORT = null; + + + public static String getEndpointAddress(CompositeNode node) { + return node.getCompositesByName(NETCONF_ENDPOINT).get(0).getFirstSimpleByName(NETCONF_ENDPOINT_ADDRESS).getValue().toString(); + } + + public static String getEndpointPort(CompositeNode node) { + return node.getCompositesByName(NETCONF_ENDPOINT).get(0).getFirstSimpleByName(NETCONF_ENDPOINT_PORT).getValue().toString(); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend new file mode 100644 index 0000000000..d23ec1cd61 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.xtend @@ -0,0 +1,95 @@ +package org.opendaylight.controller.sal.connect.netconf + +import org.opendaylight.controller.netconf.api.NetconfMessage +import org.opendaylight.yangtools.yang.data.api.CompositeNode +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier +import org.opendaylight.yangtools.yang.common.QName +import org.opendaylight.yangtools.yang.common.RpcResult +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl +import java.net.URI +import java.util.Collections +import org.opendaylight.yangtools.yang.data.api.Node +import org.opendaylight.yangtools.yang.data.impl.NodeUtils +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates +import java.util.ArrayList +import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl +import java.util.concurrent.atomic.AtomicInteger +import org.w3c.dom.Document +import org.w3c.dom.Element +import org.opendaylight.controller.sal.common.util.Rpcs + +class NetconfMapping { + + public static val NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0") + public static val NETCONF_QNAME = new QName(NETCONF_URI,null,"netconf"); + public static val NETCONF_RPC_QNAME = new QName(NETCONF_QNAME,"rpc"); + public static val NETCONF_GET_QNAME = new QName(NETCONF_QNAME,"get"); + public static val NETCONF_GET_CONFIG_QNAME = new QName(NETCONF_QNAME,"get-config"); + public static val NETCONF_RPC_REPLY_QNAME = new QName(NETCONF_QNAME,"rpc-reply"); + public static val NETCONF_OK_QNAME = new QName(NETCONF_QNAME,"ok"); + public static val NETCONF_DATA_QNAME = new QName(NETCONF_QNAME,"data"); + + + static val messageId = new AtomicInteger(0); + + + + static def Node toFilterStructure(InstanceIdentifier identifier) { + var Node previous = null; + for (component : identifier.path.reverse) { + val Node current = component.toNode(previous); + previous = current; + } + return previous; + } + + static def dispatch Node toNode(NodeIdentifierWithPredicates argument, Node node) { + val list = new ArrayList>(); + for( arg : argument.keyValues.entrySet) { + list.add = new SimpleNodeTOImpl(arg.key,null,arg.value); + } + return new CompositeNodeTOImpl(argument.nodeType,null,list) + } + + static def dispatch Node toNode(PathArgument argument, Node node) { + if(node != null) { + return new CompositeNodeTOImpl(argument.nodeType,null,Collections.singletonList(node)); + } else { + return new SimpleNodeTOImpl(argument.nodeType,null,null); + } + } + + static def CompositeNode toCompositeNode(NetconfMessage message) { + return message.toRpcResult().result; + } + + static def NetconfMessage toRpcMessage(QName rpc, CompositeNode node) { + val rpcPayload = wrap(NETCONF_RPC_QNAME,node); + val w3cPayload = NodeUtils.buildShadowDomTree(rpcPayload); + w3cPayload.documentElement.setAttribute("message-id","m-"+ messageId.andIncrement); + return new NetconfMessage(w3cPayload); + } + + static def RpcResult toRpcResult(NetconfMessage message) { + val rawRpc = message.document.toCompositeNode() as CompositeNode; + //rawRpc. + + return Rpcs.getRpcResult(true,rawRpc,Collections.emptySet()); + } + + + static def wrap(QName name,Node node) { + if(node != null) { + return new CompositeNodeTOImpl(name,null,Collections.singletonList(node)); + } + else { + return new CompositeNodeTOImpl(name,null,Collections.emptyList()); + } + } + + + public static def Node toCompositeNode(Document document) { + return XmlDocumentUtils.toCompositeNode(document) as Node + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfProvider.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfProvider.java new file mode 100644 index 0000000000..8cf5f0274c --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfProvider.java @@ -0,0 +1,35 @@ +package org.opendaylight.controller.sal.connect.netconf; + +import java.util.Hashtable; + +import org.opendaylight.controller.sal.core.api.AbstractProvider; +import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.controller.sal.core.api.data.DataProviderService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; +import org.osgi.framework.BundleContext; + +public class NetconfProvider extends AbstractProvider { + + private NetconfDeviceManager netconfDeviceManager; + + @Override + protected void startImpl(BundleContext context) { + netconfDeviceManager = new NetconfDeviceManager(); + context.registerService(NetconfDeviceManager.class, netconfDeviceManager, new Hashtable()); + } + + + @Override + public void onSessionInitiated(ProviderSession session) { + MountProvisionService mountService = session.getService(MountProvisionService.class); + + + netconfDeviceManager.setMountService(mountService); + netconfDeviceManager.start(); + } + + @Override + protected void stopImpl(BundleContext context) { + + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java new file mode 100644 index 0000000000..3f6b4e1f4c --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/XmlDocumentUtils.java @@ -0,0 +1,54 @@ +package org.opendaylight.controller.sal.connect.netconf; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; +import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +public class XmlDocumentUtils { + + public static CompositeNode toCompositeNode(Document doc) { + return (CompositeNode) toCompositeNode(doc.getDocumentElement()); + } + + private static Node toCompositeNode(Element element) { + String orgNamespace = element.getNamespaceURI(); + URI biNamespace = null; + if (orgNamespace != null) { + biNamespace = URI.create(orgNamespace); + } + QName qname = new QName(biNamespace, element.getLocalName()); + + List> values = new ArrayList<>(); + NodeList nodes = element.getChildNodes(); + boolean isSimpleObject = false; + String value = null; + for (int i = 0; i < nodes.getLength(); i++) { + org.w3c.dom.Node child = nodes.item(i); + if (child instanceof Element) { + isSimpleObject = false; + values.add(toCompositeNode((Element) child)); + } + if (!isSimpleObject && child instanceof org.w3c.dom.Text) { + value = element.getTextContent(); + if (value.matches(".*\\w.*")) { + isSimpleObject = true; + break; + } + } + } + + if (isSimpleObject) { + return new SimpleNodeTOImpl<>(qname, null, value); + } + return new CompositeNodeTOImpl(qname, null, values); + } +} diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connector/netconf/test/MountTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connector/netconf/test/MountTest.java new file mode 100644 index 0000000000..4abf0e13f8 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connector/netconf/test/MountTest.java @@ -0,0 +1,197 @@ +package org.opendaylight.controller.sal.connector.netconf.test; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.*; +import io.netty.channel.ChannelFuture; +import io.netty.util.HashedWheelTimer; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.management.ManagementFactory; +import java.net.InetSocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import javax.net.ssl.SSLContext; +import javax.xml.parsers.ParserConfigurationException; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.spi.ModuleFactory; +import org.opendaylight.controller.config.yang.store.api.YangStoreException; +import org.opendaylight.controller.config.yang.store.impl.HardcodedYangStoreService; +import org.opendaylight.controller.config.yang.test.impl.DepTestImplModuleFactory; +import org.opendaylight.controller.config.yang.test.impl.NetconfTestImplModuleFactory; +import org.opendaylight.controller.config.yang.test.impl.TestImplModuleFactory; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.client.NetconfClient; +import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; +import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; +import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; +import org.opendaylight.controller.netconf.impl.NetconfServerSessionListenerFactory; +import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory; +import org.opendaylight.controller.netconf.impl.SessionIdProvider; +import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceFactoryListenerImpl; +import org.opendaylight.controller.netconf.util.test.XmlFileLoader; +import org.opendaylight.controller.sal.connect.netconf.InventoryUtils; +import org.opendaylight.controller.sal.connect.netconf.NetconfDevice; +import org.opendaylight.controller.sal.connect.netconf.NetconfDeviceManager; +import org.opendaylight.controller.sal.connect.netconf.NetconfInventoryUtils; +import org.opendaylight.controller.sal.core.api.data.DataBrokerService; +import org.opendaylight.controller.sal.core.api.data.DataProviderService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; +import org.opendaylight.controller.sal.dom.broker.DataBrokerImpl; +import org.opendaylight.controller.sal.dom.broker.MountPointManagerImpl; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; + +public class MountTest extends AbstractConfigTest { + + private static final InetSocketAddress tcpAddress = new InetSocketAddress("127.0.0.1", 12023); + private static final InetSocketAddress tlsAddress = new InetSocketAddress("127.0.0.1", 12024); + private static final URI NETCONF_MONITORING_NS = URI.create("urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"); + + private static final QName NETCONF_MONITORING = new QName(NETCONF_MONITORING_NS, new Date(2010,10,04), "ietf-netconf-monitoring"); + private static final QName NETCONF_MONITORING_STATE = new QName(NETCONF_MONITORING,"netconf-state"); + + + private NetconfMessage getConfig, getConfigCandidate, editConfig, closeSession; + private DefaultCommitNotificationProducer commitNot; + private NetconfServerDispatcher dispatch; + private DataProviderService dataBroker; + private MountPointManagerImpl mountManager; + private NetconfDeviceManager netconfManager; + + private static QName CONFIG_MODULES = new QName( + URI.create("urn:opendaylight:params:xml:ns:yang:controller:config"), null, "modules"); + private static QName CONFIG_SERVICES = new QName( + URI.create("urn:opendaylight:params:xml:ns:yang:controller:config"), null, "modules"); + + private NetconfClient createSession(final InetSocketAddress address, NetconfClientDispatcher dispatcher) throws InterruptedException { + final NetconfClient netconfClient = new NetconfClient("test " + address.toString(), address, 5000, dispatcher); + return netconfClient; + } + + @Before + public void setUp() throws Exception { + super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(getModuleFactories().toArray( + new ModuleFactory[0]))); + + loadMessages(); + + NetconfOperationServiceFactoryListenerImpl factoriesListener = new NetconfOperationServiceFactoryListenerImpl(); + factoriesListener.onAddNetconfOperationServiceFactory(new NetconfOperationServiceFactoryImpl(getYangStore())); + + commitNot = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer()); + + dispatch = createDispatcher(Optional. absent(), factoriesListener); + ChannelFuture s = dispatch.createServer(tcpAddress); + s.await(); + + dataBroker = new DataBrokerImpl(); + mountManager = new MountPointManagerImpl(); + mountManager.setDataBroker(dataBroker); + netconfManager = new NetconfDeviceManager(); + + netconfManager.setMountService(mountManager); + netconfManager.setDataService(dataBroker); + netconfManager.start(); + + try (NetconfClient netconfClient = createSession(tcpAddress, netconfManager.getDispatcher())) { + // send edit_config.xml + final Document rpcReply = netconfClient.sendMessage(this.editConfig).getDocument(); + assertNotNull(rpcReply); + } + } + + + protected List getModuleFactories() { + return getModuleFactoriesS(); + } + + static List getModuleFactoriesS() { + return Lists.newArrayList(new TestImplModuleFactory(), new DepTestImplModuleFactory(), + new NetconfTestImplModuleFactory()); + } + + private void loadMessages() throws IOException, SAXException, ParserConfigurationException { + this.editConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/edit_config.xml"); + this.getConfig = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig.xml"); + this.getConfigCandidate = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/getConfig_candidate.xml"); + this.closeSession = XmlFileLoader.xmlFileToNetconfMessage("netconfMessages/closeSession.xml"); + } + + private NetconfServerDispatcher createDispatcher(Optional sslC, + NetconfOperationServiceFactoryListenerImpl factoriesListener) { + SessionIdProvider idProvider = new SessionIdProvider(); + NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory( + new HashedWheelTimer(5000, TimeUnit.MILLISECONDS), factoriesListener, idProvider); + + NetconfServerSessionListenerFactory listenerFactory = new NetconfServerSessionListenerFactory( + factoriesListener, commitNot, idProvider); + + return new NetconfServerDispatcher(sslC, serverNegotiatorFactory, listenerFactory); + } + + private HardcodedYangStoreService getYangStore() throws YangStoreException, IOException { + final Collection yangDependencies = getBasicYangs(); + return new HardcodedYangStoreService(yangDependencies); + } + + private Collection getBasicYangs() throws IOException { + List paths = Arrays.asList("/META-INF/yang/config.yang", "/META-INF/yang/rpc-context.yang", + "/META-INF/yang/config-test.yang", "/META-INF/yang/config-test-impl.yang", + "/META-INF/yang/ietf-inet-types.yang"); + final Collection yangDependencies = new ArrayList<>(); + for (String path : paths) { + final InputStream is = Preconditions + .checkNotNull(getClass().getResourceAsStream(path), path + " not found"); + yangDependencies.add(is); + } + return yangDependencies; + } + + @Test + public void test() { + // MountProvisionInstance mount = + // Mockito.mock(MountProvisionInstance.class); + InstanceIdentifier path = InstanceIdentifier.builder(InventoryUtils.INVENTORY_PATH) + .node(InventoryUtils.INVENTORY_NODE).toInstance(); + netconfManager.netconfNodeAdded(path, tcpAddress); + InstanceIdentifier mountPointPath = path; + MountProvisionInstance mountPoint = mountManager.getMountPoint(mountPointPath); + + CompositeNode data = mountPoint.readOperationalData(InstanceIdentifier.builder().node(CONFIG_MODULES) + .toInstance()); + assertNotNull(data); + assertEquals(CONFIG_MODULES, data.getNodeType()); + + CompositeNode data2 = mountPoint.readOperationalData(InstanceIdentifier.builder().toInstance()); + assertNotNull(data2); + + InstanceIdentifier fullPath = InstanceIdentifier.builder(mountPointPath).node(CONFIG_MODULES).toInstance(); + + CompositeNode data3 = dataBroker.readOperationalData(fullPath); + assertNotNull(data3); + assertEquals(CONFIG_MODULES, data.getNodeType()); + } + +} diff --git a/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml b/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml index b5b1642797..8569783ad7 100644 --- a/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml +++ b/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml @@ -21,6 +21,8 @@ 3.1.3.RELEASE 1.17 3.1.3.RELEASE + 0.2.2-SNAPSHOT + 0.2.2-SNAPSHOT @@ -107,12 +109,23 @@ 1.0-SNAPSHOT test + + org.opendaylight.controller + sal-netconf-connector + 1.0-SNAPSHOT + test + org.opendaylight.controller sal-binding-it 1.0-SNAPSHOT test + + org.opendaylight.controller.thirdparty + exificient + 0.9.2 + org.ops4j.pax.exam pax-exam-container-native @@ -125,6 +138,12 @@ ${exam.version} test + + io.netty + netty-all + 4.0.10.Final + test + org.ops4j.pax.exam pax-exam-link-mvn @@ -623,5 +642,87 @@ org.apache.catalina.filters.CorsFilter 7.0.42 + + + + org.opendaylight.controller + config-api + ${config.version} + + + org.opendaylight.controller + config-manager + ${config.version} + + + org.opendaylight.controller + config-util + ${config.version} + + + org.opendaylight.controller + yang-jmx-generator + ${config.version} + + + org.opendaylight.controller + yang-store-api + ${config.version} + + + org.opendaylight.controller + yang-store-impl + ${config.version} + + + org.opendaylight.controller + logback-config + ${config.version} + + + org.opendaylight.controller + config-persister-api + ${config.version} + + + org.opendaylight.controller + config-persister-file-adapter + ${config.version} + + + org.opendaylight.controller + netconf-api + ${netconf.version} + + + org.opendaylight.controller + netconf-impl + ${netconf.version} + + + org.opendaylight.controller + netconf-util + ${netconf.version} + + + org.opendaylight.controller + netconf-client + ${netconf.version} + + + org.opendaylight.controller + netconf-mapping-api + ${netconf.version} + + + org.opendaylight.controller + config-netconf-connector + ${netconf.version} + + + org.opendaylight.controller + config-persister-impl + ${netconf.version} + diff --git a/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java b/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java index 1087da22f9..23ba8aaea4 100644 --- a/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java +++ b/opendaylight/md-sal/test/sal-rest-connector-it/src/test/java/org/opendaylight/controller/test/restconf/it/ServiceProviderController.java @@ -1,5 +1,6 @@ package org.opendaylight.controller.test.restconf.it; +import static junit.framework.Assert.assertEquals; import static org.junit.Assert.*; import static org.ops4j.pax.exam.CoreOptions.junitBundles; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; @@ -8,14 +9,27 @@ import static org.ops4j.pax.exam.CoreOptions.systemPackages; import static org.ops4j.pax.exam.CoreOptions.systemProperty; import static org.ops4j.pax.exam.CoreOptions.maven; +import java.net.InetSocketAddress; +import java.net.URI; import java.util.Collection; +import java.util.Collections; +import java.util.Map; import javax.inject.Inject; import org.junit.Test; import org.junit.runner.RunWith; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.connect.netconf.InventoryUtils; +import org.opendaylight.controller.sal.connect.netconf.NetconfDeviceManager; +import org.opendaylight.controller.sal.connect.netconf.NetconfInventoryUtils; +import org.opendaylight.controller.sal.core.api.data.DataBrokerService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; import org.opendaylight.controller.test.sal.binding.it.TestHelper; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.ops4j.pax.exam.Configuration; import org.ops4j.pax.exam.CoreOptions; import org.ops4j.pax.exam.Option; @@ -34,23 +48,55 @@ public class ServiceProviderController { public static final String YANG = "org.opendaylight.yangtools"; public static final String SAMPLE = "org.opendaylight.controller.samples"; + private static QName CONFIG_MODULES = new QName( + URI.create("urn:opendaylight:params:xml:ns:yang:controller:config"), null, "modules"); + private static QName CONFIG_SERVICES = new QName( + URI.create("urn:opendaylight:params:xml:ns:yang:controller:config"), null, "modules"); + @Inject + BundleContext context; + + @Inject + MountProvisionService mountService; + + @Inject + DataBrokerService dataBroker; + + @Inject + NetconfDeviceManager netconfManager; + @Test public void properInitialized() throws Exception { - Thread.sleep(30*60*1000); // Waiting for services to get wired. - assertTrue(true); - // assertTrue(consumer.createToast(WhiteBread.class, 5)); + Map arg = Collections.singletonMap(InventoryUtils.INVENTORY_ID, "foo"); - } + InstanceIdentifier path = InstanceIdentifier.builder(InventoryUtils.INVENTORY_PATH) + .nodeWithKey(InventoryUtils.INVENTORY_NODE, InventoryUtils.INVENTORY_ID, "foo").toInstance(); - // @Inject - // BindingAwareBroker broker; + netconfManager.netconfNodeAdded(path, new InetSocketAddress("127.0.0.1", 8383)); - // @Inject - // ToastConsumer consumer; + + InstanceIdentifier mountPointPath = path; + + /** We retrive a mountpoint **/ + MountProvisionInstance mountPoint = mountService.getMountPoint(mountPointPath); + CompositeNode data = mountPoint.readOperationalData(InstanceIdentifier.builder().node(CONFIG_MODULES) + .toInstance()); + assertNotNull(data); + assertEquals(CONFIG_MODULES, data.getNodeType()); - @Inject - BundleContext ctx; + CompositeNode data2 = mountPoint.readOperationalData(InstanceIdentifier.builder().toInstance()); + assertNotNull(data2); + + InstanceIdentifier fullPath = InstanceIdentifier.builder(mountPointPath).node(CONFIG_MODULES).toInstance(); + + CompositeNode data3 = dataBroker.readOperationalData(fullPath); + assertNotNull(data3); + assertEquals(CONFIG_MODULES, data.getNodeType()); + + //Thread.sleep(30 * 60 * 1000); // Waiting for services to get wired. + //assertTrue(true); + // assertTrue(consumer.createToast(WhiteBread.class, 5)); + } @Configuration public Option[] config() { @@ -63,6 +109,7 @@ public class ServiceProviderController { mdSalCoreBundles(), baseModelBundles(), flowCapableModelBundles(), + configMinumumBundles(), // mavenBundle(ODL, // "sal-binding-broker-impl").versionAsInProject().update(), // @@ -79,6 +126,7 @@ public class ServiceProviderController { // mavenBundle(SAMPLE, // "zeromq-test-provider").versionAsInProject(), // mavenBundle(ODL, "sal-rest-connector").versionAsInProject(), // + mavenBundle(ODL, "sal-netconf-connector").versionAsInProject(), // mavenBundle(YANG, "concepts").versionAsInProject(), mavenBundle(YANG, "yang-binding").versionAsInProject(), // @@ -89,6 +137,7 @@ public class ServiceProviderController { mavenBundle(YANG, "yang-model-util").versionAsInProject(), // mavenBundle(YANG, "yang-parser-api").versionAsInProject(), mavenBundle(YANG, "yang-parser-impl").versionAsInProject(), + mavenBundle(YANG + ".thirdparty", "xtend-lib-osgi").versionAsInProject(), // mavenBundle(YANG + ".thirdparty", "antlr4-runtime-osgi-nohead").versionAsInProject(), // mavenBundle("com.google.guava", "guava").versionAsInProject(), // @@ -105,8 +154,15 @@ public class ServiceProviderController { // earlier. systemProperty("osgi.bundles.defaultStartLevel").value("4"), + systemProperty("netconf.tcp.address").value("127.0.0.1"), + systemProperty("netconf.tcp.port").value("8383"), + // Set the systemPackages (used by clustering) systemPackages("sun.reflect", "sun.reflect.misc", "sun.misc"), + + mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"), + mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"), + mavenBundle("org.slf4j", "jcl-over-slf4j").versionAsInProject(), mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), @@ -140,6 +196,40 @@ public class ServiceProviderController { // mavenBundle("commons-fileupload", // "commons-fileupload").versionAsInProject(), + mavenBundle("io.netty", "netty-handler").versionAsInProject(), + mavenBundle("io.netty", "netty-codec").versionAsInProject(), + mavenBundle("io.netty", "netty-buffer").versionAsInProject(), + mavenBundle("io.netty", "netty-transport").versionAsInProject(), + mavenBundle("io.netty", "netty-common").versionAsInProject(), + + mavenBundle(ODL, "config-api").versionAsInProject(), + mavenBundle(ODL, "config-manager").versionAsInProject(), + mavenBundle(ODL, "config-util").versionAsInProject(), + mavenBundle(ODL, "yang-jmx-generator").versionAsInProject(), + mavenBundle(ODL, "yang-store-api").versionAsInProject(), + mavenBundle(ODL, "yang-store-impl").versionAsInProject(), + mavenBundle(ODL, "logback-config").versionAsInProject(), + mavenBundle(ODL, "config-persister-api").versionAsInProject(), + // mavenBundle(ODL,"config-persister-file-adapter").versionAsInProject(), + mavenBundle(ODL, "netconf-api").versionAsInProject(), + mavenBundle(ODL, "netconf-impl").versionAsInProject(), + mavenBundle(ODL, "netconf-client").versionAsInProject(), + mavenBundle(ODL, "netconf-util").versionAsInProject(), + mavenBundle(ODL + ".thirdparty", "ganymed", "1.0-SNAPSHOT"), + mavenBundle(ODL, "netconf-mapping-api").versionAsInProject(), + mavenBundle(ODL, "config-netconf-connector").versionAsInProject(), + mavenBundle(ODL, "config-persister-impl").versionAsInProject(), + + mavenBundle("org.opendaylight.bgpcep", "framework").versionAsInProject(), + mavenBundle("org.opendaylight.bgpcep", "util").versionAsInProject(), + mavenBundle(YANG, "binding-generator-spi").versionAsInProject(), // + mavenBundle(YANG, "binding-model-api").versionAsInProject(), // + mavenBundle(YANG, "binding-generator-util").versionAsInProject(), + mavenBundle(YANG, "yang-parser-impl").versionAsInProject(), + mavenBundle(YANG, "binding-type-provider").versionAsInProject(), + + mavenBundle("org.opendaylight.controller.thirdparty", "exificient", "0.9.2"), + mavenBundle("equinoxSDK381", "javax.servlet").versionAsInProject(), mavenBundle("equinoxSDK381", "javax.servlet.jsp").versionAsInProject(), mavenBundle("equinoxSDK381", "org.eclipse.equinox.ds").versionAsInProject(), -- 2.36.6