From d4e0ecaeb1e2fae65e50b14b7270ded16cf2f6b2 Mon Sep 17 00:00:00 2001 From: Rudolf Brisuda Date: Fri, 7 Oct 2016 11:44:23 +0200 Subject: [PATCH] Bug 6714 - Use singleton service in clustered netconf topology Implemented clustered netconf topology with using clustered singleton service. Change-Id: I0e71a9934a70c6bce8eeab0644a5bd05c5548a8c Signed-off-by: Rudolf Brisuda --- features/netconf-connector/pom.xml | 10 +- .../src/main/features/features.xml | 6 +- netconf/netconf-artifacts/pom.xml | 5 + netconf/netconf-topology-singleton/pom.xml | 76 ++++ .../singleton/api/NetconfDOMTransaction.java | 70 +++ .../api/NetconfTopologySingletonService.java | 15 + .../singleton/api/RemoteDeviceConnector.java | 28 ++ .../api/RemoteOperationTxProcessor.java | 75 ++++ .../singleton/impl/MasterSalFacade.java | 177 ++++++++ .../singleton/impl/NetconfDOMDataBroker.java | 78 ++++ .../singleton/impl/NetconfNodeManager.java | 143 ++++++ .../impl/NetconfTopologyContext.java | 169 +++++++ .../impl/NetconfTopologyManager.java | 243 ++++++++++ .../singleton/impl/ProxyDOMRpcService.java | 37 ++ .../impl/ProxyYangTextSourceProvider.java | 70 +++ .../impl/RemoteDeviceConnectorImpl.java | 425 ++++++++++++++++++ .../impl/RemoteOperationTxProcessorImpl.java | 155 +++++++ .../singleton/impl/SlaveSalFacade.java | 79 ++++ .../impl/actors/NetconfNodeActor.java | 235 ++++++++++ .../impl/tx/NetconfMasterDOMTransaction.java | 150 +++++++ .../impl/tx/NetconfProxyDOMTransaction.java | 170 +++++++ .../impl/tx/NetconfReadOnlyTransaction.java | 112 +++++ .../impl/tx/NetconfWriteOnlyTransaction.java | 111 +++++ .../impl/utils/NetconfConnectorDTO.java | 48 ++ .../impl/utils/NetconfTopologySetup.java | 254 +++++++++++ .../impl/utils/NetconfTopologyUtils.java | 117 +++++ .../messages/AskForMasterMountPoint.java | 18 + .../CreateInitialMasterActorData.java | 37 ++ .../messages/MasterActorDataInitialized.java | 18 + .../messages/NormalizedNodeMessage.java | 67 +++ .../messages/RefreshSetupMasterActorData.java | 39 ++ .../messages/RegisterMountPoint.java | 30 ++ .../singleton/messages/SubmitFailedReply.java | 17 + .../messages/UnregisterSlaveMountPoint.java | 18 + .../messages/YangTextSchemaSourceRequest.java | 29 ++ .../messages/transactions/CancelRequest.java | 12 + .../messages/transactions/DeleteRequest.java | 31 ++ .../transactions/EmptyReadResponse.java | 17 + .../messages/transactions/ExistsRequest.java | 31 ++ .../messages/transactions/MergeRequest.java | 31 ++ .../messages/transactions/PutRequest.java | 31 ++ .../messages/transactions/ReadRequest.java | 31 ++ .../messages/transactions/SubmitReply.java | 17 + .../messages/transactions/SubmitRequest.java | 12 + .../transactions/TransactionRequest.java | 19 + .../blueprint/netconf-topology-singleton.xml | 71 +++ .../singleton/impl/NetconfNodeActorTest.java | 221 +++++++++ .../impl/NetconfTopologyManagerTest.java | 271 +++++++++++ .../impl/RemoteDeviceConnectorImplTest.java | 271 +++++++++++ .../TestingRemoteDeviceConnectorImpl.java | 47 ++ .../impl/tx/ReadOnlyTransactionTest.java | 262 +++++++++++ .../impl/tx/WriteOnlyTransactionTest.java | 257 +++++++++++ .../impl/utils/NetconfTopologyUtilTest.java | 59 +++ netconf/pom.xml | 1 + .../sal/NetconfDeviceTopologyAdapter.java | 43 +- .../src/main/yang/netconf-node-topology.yang | 4 + 56 files changed, 5060 insertions(+), 10 deletions(-) create mode 100644 netconf/netconf-topology-singleton/pom.xml create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfDOMTransaction.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfTopologySingletonService.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteDeviceConnector.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteOperationTxProcessor.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/MasterSalFacade.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfDOMDataBroker.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeManager.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyContext.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyYangTextSourceProvider.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteOperationTxProcessorImpl.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/SlaveSalFacade.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfMasterDOMTransaction.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfProxyDOMTransaction.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfReadOnlyTransaction.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfWriteOnlyTransaction.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfConnectorDTO.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/AskForMasterMountPoint.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/CreateInitialMasterActorData.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/MasterActorDataInitialized.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/NormalizedNodeMessage.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RefreshSetupMasterActorData.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RegisterMountPoint.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/SubmitFailedReply.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/UnregisterSlaveMountPoint.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/YangTextSchemaSourceRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/CancelRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/DeleteRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/EmptyReadResponse.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ExistsRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/MergeRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/PutRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ReadRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitReply.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/TransactionRequest.java create mode 100644 netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml create mode 100644 netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java create mode 100644 netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java create mode 100644 netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java create mode 100644 netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/TestingRemoteDeviceConnectorImpl.java create mode 100644 netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/ReadOnlyTransactionTest.java create mode 100644 netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/WriteOnlyTransactionTest.java create mode 100644 netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtilTest.java diff --git a/features/netconf-connector/pom.xml b/features/netconf-connector/pom.xml index 2137bf49a2..623efe53fe 100644 --- a/features/netconf-connector/pom.xml +++ b/features/netconf-connector/pom.xml @@ -125,12 +125,6 @@ config xml - - ${project.groupId} - netconf-topology-config - clustered-config - xml - ${project.groupId} netconf-tcp @@ -147,6 +141,10 @@ org.bouncycastle bcprov-jdk15on + + ${project.groupId} + netconf-topology-singleton + ${project.groupId} diff --git a/features/netconf-connector/src/main/features/features.xml b/features/netconf-connector/src/main/features/features.xml index 31eb932c75..25b5bf0944 100644 --- a/features/netconf-connector/src/main/features/features.xml +++ b/features/netconf-connector/src/main/features/features.xml @@ -55,10 +55,10 @@ mvn:org.opendaylight.netconf/netconf-topology-config/{{VERSION}}/xml/config - - odl-netconf-ssh + + odl-netconf-ssh odl-netconf-connector - mvn:org.opendaylight.netconf/netconf-topology-config/{{VERSION}}/xml/clustered-config + mvn:org.opendaylight.netconf/netconf-topology-singleton/{{VERSION}} diff --git a/netconf/netconf-artifacts/pom.xml b/netconf/netconf-artifacts/pom.xml index 7ec3258696..d90781931c 100644 --- a/netconf/netconf-artifacts/pom.xml +++ b/netconf/netconf-artifacts/pom.xml @@ -294,6 +294,11 @@ ${project.version} test-jar + + ${project.groupId} + netconf-topology-singleton + ${project.version} + ${project.groupId} diff --git a/netconf/netconf-topology-singleton/pom.xml b/netconf/netconf-topology-singleton/pom.xml new file mode 100644 index 0000000000..fb3c450c4b --- /dev/null +++ b/netconf/netconf-topology-singleton/pom.xml @@ -0,0 +1,76 @@ + + + + 4.0.0 + + + org.opendaylight.controller + config-parent + 0.5.1-SNAPSHOT + + + + org.opendaylight.netconf + netconf-topology-singleton + 1.1.1-SNAPSHOT + ${project.artifactId} + bundle + + + + + org.opendaylight.netconf + netconf-subsystem + ${project.version} + pom + import + + + + + + + org.opendaylight.netconf + mdsal-netconf-notification + + + org.opendaylight.mdsal.model + ietf-topology + + + com.typesafe.akka + akka-actor_2.11 + + + org.opendaylight.netconf + sal-netconf-connector + + + com.typesafe.akka + akka-cluster_2.11 + + + org.opendaylight.controller + sal-clustering-commons + + + org.mockito + mockito-all + test + + + com.typesafe.akka + akka-testkit_2.11 + test + + + + \ No newline at end of file diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfDOMTransaction.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfDOMTransaction.java new file mode 100644 index 0000000000..ec9c717440 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfDOMTransaction.java @@ -0,0 +1,70 @@ +/* + * 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.topology.singleton.api; + +import com.google.common.base.Optional; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import scala.concurrent.Future; + +/** + * Provides API for all operations of read and write transactions + */ +public interface NetconfDOMTransaction { + + /** + * Read data from particular data-store + * @param store data-store type + * @param path unique identifier of a particular node instance in the data tree + * @return result as future + */ + Future> read(LogicalDatastoreType store, YangInstanceIdentifier path); + + /** + * Test existence of node in certain data-store + * @param store data-store type + * @param path unique identifier of a particular node instance in the data tree + * @return result as future + */ + Future exists(LogicalDatastoreType store, YangInstanceIdentifier path); + + /** + * Put data to particular data-store + * @param store data-store type + * @param data data for inserting included in NormalizedNodeMessage object + */ + void put(LogicalDatastoreType store, NormalizedNodeMessage data); + + /** + * Merge data with existing node in particular data-store + * @param store data-store type + * @param data data for merging included in NormalizedNodeMessage object + */ + void merge(LogicalDatastoreType store, NormalizedNodeMessage data); + + /** + * Delete node in particular data-store in path + * @param store data-store type + * @param path unique identifier of a particular node instance in the data tree + */ + void delete(LogicalDatastoreType store, YangInstanceIdentifier path); + + /** + * Cancel operation + * @return success or not + */ + boolean cancel(); + + /** + * Commit opened transaction. + * @return void or raised exception + */ + Future submit(); +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfTopologySingletonService.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfTopologySingletonService.java new file mode 100644 index 0000000000..37095becdf --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/NetconfTopologySingletonService.java @@ -0,0 +1,15 @@ +/* + * 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.topology.singleton.api; + +/** + * Provides API for advertising services for blue print service + */ +public interface NetconfTopologySingletonService { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteDeviceConnector.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteDeviceConnector.java new file mode 100644 index 0000000000..bb74b754b3 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteDeviceConnector.java @@ -0,0 +1,28 @@ +/* + * 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.topology.singleton.api; + +import akka.actor.ActorRef; + +/** + * Provides API for connection odl (master) with device + */ +public interface RemoteDeviceConnector { + + /** + * Create device communicator and open device connection + * @param masterActorRef master actor reference + */ + void startRemoteDeviceConnection(ActorRef masterActorRef); + + /** + * Stop device communicator + */ + void stopRemoteDeviceConnection(); +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteOperationTxProcessor.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteOperationTxProcessor.java new file mode 100644 index 0000000000..9023847da3 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/api/RemoteOperationTxProcessor.java @@ -0,0 +1,75 @@ +/* + * 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.topology.singleton.api; + +import akka.actor.ActorRef; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +/** + * Provides API for remote calling operations of transactions. Slave sends message of particular + * operation to master and master performs it. + */ +public interface RemoteOperationTxProcessor { + + /** + * Delete node in particular data-store in path + * @param store data-store type + * @param path unique identifier of a particular node instance in the data tree + */ + void doDelete(LogicalDatastoreType store, YangInstanceIdentifier path); + + /** + * Commit opened transaction. + * @param recipient recipient of submit result + * @param sender sender of submit result + */ + void doSubmit(ActorRef recipient, ActorRef sender); + + /** + * Cancel operation + * @param recipient recipient of cancel result + * @param sender sender of cancel result + */ + void doCancel(ActorRef recipient, ActorRef sender); + + /** + * Put data to particular data-store + * @param store data-store type + * @param data data for inserting included in NormalizedNodeMessage object + */ + void doPut(LogicalDatastoreType store, NormalizedNodeMessage data); + + /** + * Merge data with existing node in particular data-store + * @param store data-store type + * @param data data for merging included in NormalizedNodeMessage object + */ + void doMerge(LogicalDatastoreType store, NormalizedNodeMessage data); + + /** + * Read data from particular data-store + * @param store data-store type + * @param path unique identifier of a particular node instance in the data tree + * @param recipient recipient of read result + * @param sender sender of read result + */ + void doRead(LogicalDatastoreType store, YangInstanceIdentifier path, ActorRef recipient, ActorRef sender); + + /** + * Test existence of node in certain data-store + * @param store data-store type + * @param path unique identifier of a particular node instance in the data tree + * @param recipient recipient of exists result + * @param sender sender of exists result + */ + void doExists(LogicalDatastoreType store, YangInstanceIdentifier path, ActorRef recipient, ActorRef sender); + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/MasterSalFacade.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/MasterSalFacade.java new file mode 100644 index 0000000000..7fa8e5aaad --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/MasterSalFacade.java @@ -0,0 +1,177 @@ +/* + * 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.topology.singleton.impl; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.cluster.Cluster; +import akka.dispatch.OnComplete; +import akka.pattern.Patterns; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import java.util.List; +import java.util.stream.Collectors; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMNotification; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceNotificationService; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalProvider; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.impl.tx.NetconfMasterDOMTransaction; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import scala.concurrent.Future; + +class MasterSalFacade implements AutoCloseable, RemoteDeviceHandler { + + private static final Logger LOG = LoggerFactory.getLogger(MasterSalFacade.class); + + private final RemoteDeviceId id; + + private SchemaContext remoteSchemaContext = null; + private NetconfSessionPreferences netconfSessionPreferences = null; + private DOMRpcService deviceRpc = null; + private final NetconfDeviceSalProvider salProvider; + + private final ActorRef masterActorRef; + private final ActorSystem actorSystem; + private DOMDataBroker deviceDataBroker = null; + + MasterSalFacade(final RemoteDeviceId id, + final Broker domBroker, + final BindingAwareBroker bindingBroker, + final ActorSystem actorSystem, + final ActorRef masterActorRef) { + this.id = id; + this.salProvider = new NetconfDeviceSalProvider(id); + this.actorSystem = actorSystem; + this.masterActorRef = masterActorRef; + + registerToSal(domBroker, bindingBroker); + } + + private void registerToSal(final Broker domRegistryDependency, final BindingAwareBroker bindingBroker) { + // TODO: remove use of provider, there is possible directly create mount instance and + // TODO: NetconfDeviceTopologyAdapter in constructor = less complexity + + domRegistryDependency.registerProvider(salProvider); + bindingBroker.registerProvider(salProvider); + } + + @Override + public void onDeviceConnected(final SchemaContext remoteSchemaContext, + final NetconfSessionPreferences netconfSessionPreferences, + final DOMRpcService deviceRpc) { + this.remoteSchemaContext = remoteSchemaContext; + this.netconfSessionPreferences = netconfSessionPreferences; + this.deviceRpc = deviceRpc; + + registerMasterMountPoint(); + + sendInitialDataToActor().onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable failure, final Object success) throws Throwable { + if (failure == null) { + updateDeviceData(); + return; + } + throw failure; + } + }, actorSystem.dispatcher()); + + } + + @Override + public void onDeviceDisconnected() { + salProvider.getTopologyDatastoreAdapter().updateDeviceData(false, new NetconfDeviceCapabilities()); + unregisterMasterMountPoint(); + } + + @Override + public void onDeviceFailed(final Throwable throwable) { + salProvider.getTopologyDatastoreAdapter().setDeviceAsFailed(throwable); + unregisterMasterMountPoint(); + } + + @Override + public void onNotification(final DOMNotification domNotification) { + salProvider.getMountInstance().publish(domNotification); + } + + @Override + public void close() { + unregisterMasterMountPoint(); + closeGracefully(salProvider); + } + + private void registerMasterMountPoint() { + Preconditions.checkNotNull(id); + Preconditions.checkNotNull(remoteSchemaContext, + "Device has no remote schema context yet. Probably not fully connected."); + Preconditions.checkNotNull(netconfSessionPreferences, + "Device has no capabilities yet. Probably not fully connected."); + + final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService(); + + LOG.info("Creating master data broker for device {}", id); + + final NetconfDOMTransaction masterDOMTransactions = + new NetconfMasterDOMTransaction(id, remoteSchemaContext, deviceRpc, netconfSessionPreferences); + deviceDataBroker = + new NetconfDOMDataBroker(actorSystem, id, masterDOMTransactions); + salProvider.getMountInstance() + .onTopologyDeviceConnected(remoteSchemaContext, deviceDataBroker, deviceRpc, notificationService); + } + + private Future sendInitialDataToActor() { + final List sourceIdentifiers = + remoteSchemaContext.getAllModuleIdentifiers().stream().map(mi -> + RevisionSourceIdentifier.create(mi.getName(), + (SimpleDateFormatUtil.DEFAULT_DATE_REV == mi.getRevision() ? Optional.absent() : + Optional.of(SimpleDateFormatUtil.getRevisionFormat().format(mi.getRevision()))))) + .collect(Collectors.toList()); + + // send initial data to master actor and create actor for providing it + return Patterns.ask(masterActorRef, new CreateInitialMasterActorData(deviceDataBroker, sourceIdentifiers), + NetconfTopologyUtils.TIMEOUT); + } + + private void updateDeviceData() { + Cluster cluster = Cluster.get(actorSystem); + salProvider.getTopologyDatastoreAdapter().updateClusteredDeviceData(true, cluster.selfAddress().toString(), + netconfSessionPreferences.getNetconfDeviceCapabilities()); + } + + private void unregisterMasterMountPoint() { + salProvider.getMountInstance().onTopologyDeviceDisconnected(); + } + + private void closeGracefully(final AutoCloseable resource) { + if (resource != null) { + try { + resource.close(); + } catch (final Exception e) { + LOG.warn("{}: Ignoring exception while closing {}", id, resource, e); + } + } + } + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfDOMDataBroker.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfDOMDataBroker.java new file mode 100644 index 0000000000..99ddc7fa30 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfDOMDataBroker.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015 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.topology.singleton.impl; + +import akka.actor.ActorSystem; +import java.util.Collections; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBrokerExtension; +import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMTransactionChain; +import org.opendaylight.netconf.sal.connect.netconf.sal.tx.ReadWriteTx; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.impl.tx.NetconfReadOnlyTransaction; +import org.opendaylight.netconf.topology.singleton.impl.tx.NetconfWriteOnlyTransaction; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +public class NetconfDOMDataBroker implements DOMDataBroker { + + private final RemoteDeviceId id; + private final NetconfDOMTransaction masterDataBroker; + private final ActorSystem actorSystem; + + public NetconfDOMDataBroker(final ActorSystem actorSystem, final RemoteDeviceId id, + final NetconfDOMTransaction masterDataBroker) { + this.id = id; + this.masterDataBroker = masterDataBroker; + this.actorSystem = actorSystem; + } + + @Override + public DOMDataReadOnlyTransaction newReadOnlyTransaction() { + return new NetconfReadOnlyTransaction(actorSystem, masterDataBroker); + } + + @Override + public DOMDataReadWriteTransaction newReadWriteTransaction() { + return new ReadWriteTx(new NetconfReadOnlyTransaction(actorSystem, masterDataBroker), + new NetconfWriteOnlyTransaction(actorSystem, masterDataBroker)); + } + + @Override + public DOMDataWriteTransaction newWriteOnlyTransaction() { + return new NetconfWriteOnlyTransaction(actorSystem, masterDataBroker); + } + + @Override + public ListenerRegistration registerDataChangeListener( + LogicalDatastoreType store, YangInstanceIdentifier path, DOMDataChangeListener listener, + DataChangeScope triggeringScope) { + throw new UnsupportedOperationException(id + ": Data change listeners not supported for netconf mount point"); + } + + @Override + public DOMTransactionChain createTransactionChain(TransactionChainListener listener) { + throw new UnsupportedOperationException(id + ": Transaction chains not supported for netconf mount point"); + } + + @Nonnull + @Override + public Map, DOMDataBrokerExtension> getSupportedExtensions() { + return Collections.emptyMap(); + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeManager.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeManager.java new file mode 100644 index 0000000000..e90ea66253 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeManager.java @@ -0,0 +1,143 @@ +/* + * 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.topology.singleton.impl; + +import akka.actor.ActorRef; +import akka.actor.PoisonPill; +import java.util.Collection; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.NetconfTopologySingletonService; +import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +import org.opendaylight.netconf.topology.singleton.messages.AskForMasterMountPoint; +import org.opendaylight.netconf.topology.singleton.messages.UnregisterSlaveMountPoint; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +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.NodeKey; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Managing and reacting on data tree changes in specific netconf node when master writes status to the operational + * data store (e.g. handling lifecycle of slave mount point). + */ +class NetconfNodeManager + implements ClusteredDataTreeChangeListener, NetconfTopologySingletonService, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeManager.class); + + private NetconfTopologySetup setup; + private ListenerRegistration dataChangeListenerRegistration; + private RemoteDeviceId id; + private final SchemaSourceRegistry schemaRegistry; + private final SchemaRepository schemaRepository; + private ActorRef slaveActorRef; + + NetconfNodeManager(final NetconfTopologySetup setup, + final RemoteDeviceId id, final SchemaSourceRegistry schemaRegistry, + final SchemaRepository schemaRepository) { + this.setup = setup; + this.id = id; + this.schemaRegistry = schemaRegistry; + this.schemaRepository = schemaRepository; + } + + @Override + public void onDataTreeChanged(@Nonnull final Collection> changes) { + for (final DataTreeModification change : changes) { + final DataObjectModification rootNode = change.getRootNode(); + final NodeId nodeId = NetconfTopologyUtils.getNodeId(rootNode.getIdentifier()); + switch (rootNode.getModificationType()) { + case SUBTREE_MODIFIED: + LOG.debug("Operational for node {} updated. Trying to register slave mount point", nodeId); + handleSlaveMountPoint(rootNode); + break; + case WRITE: + if (rootNode.getDataBefore() != null) { + LOG.debug("Operational for node {} rewrited. Trying to register slave mount point", nodeId); + } else { + LOG.debug("Operational for node {} created. Trying to register slave mount point", nodeId); + } + handleSlaveMountPoint(rootNode); + break; + case DELETE: + LOG.debug("Operational for node {} deleted. Trying to remove slave mount point", nodeId); + closeActor(); + break; + default: + LOG.debug("Uknown operation for node: {}", nodeId); + } + } + } + + @Override + public void close() { + closeActor(); + + if (dataChangeListenerRegistration != null) { + dataChangeListenerRegistration.close(); + dataChangeListenerRegistration = null; + } + } + + private void closeActor() { + if (slaveActorRef != null) { + slaveActorRef.tell(new UnregisterSlaveMountPoint(), ActorRef.noSender()); + slaveActorRef.tell(PoisonPill.getInstance(), ActorRef.noSender()); + slaveActorRef = null; + } + } + + void registerDataTreeChangeListener(final String topologyId, final NodeKey key) { + LOG.debug("Registering data tree change listener on node {}", key); + dataChangeListenerRegistration = setup.getDataBroker().registerDataTreeChangeListener( + new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, + NetconfTopologyUtils.createTopologyNodeListPath(key, topologyId)), this); + } + + private void handleSlaveMountPoint(final DataObjectModification rootNode) { + @SuppressWarnings("ConstantConditions") + final NetconfNode netconfNodeAfter = rootNode.getDataAfter().getAugmentation(NetconfNode.class); + + if (NetconfNodeConnectionStatus.ConnectionStatus.Connected.equals(netconfNodeAfter.getConnectionStatus())) { + createActorRef(); + final String masterAddress = netconfNodeAfter.getClusteredConnectionStatus().getNetconfMasterNode(); + final String path = NetconfTopologyUtils.createActorPath(masterAddress, + NetconfTopologyUtils.createMasterActorName(id.getName(), + netconfNodeAfter.getClusteredConnectionStatus().getNetconfMasterNode())); + setup.getActorSystem().actorSelection(path).tell(new AskForMasterMountPoint(), slaveActorRef); + } else { ; + closeActor(); + } + } + + private void createActorRef() { + if (slaveActorRef == null) { + slaveActorRef = setup.getActorSystem().actorOf(NetconfNodeActor.props(setup, id, schemaRegistry, + schemaRepository), id.getName()); + } + } + + void refreshDevice(final NetconfTopologySetup netconfTopologyDeviceSetup, final RemoteDeviceId remoteDeviceId) { + setup = netconfTopologyDeviceSetup; + id = remoteDeviceId; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyContext.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyContext.java new file mode 100644 index 0000000000..0f8255cdaa --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyContext.java @@ -0,0 +1,169 @@ +/* + * 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.topology.singleton.impl; + +import static org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; + +import akka.actor.ActorRef; +import akka.cluster.Cluster; +import akka.dispatch.OnComplete; +import akka.pattern.Patterns; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import javax.annotation.Nonnull; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService; +import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.RemoteDeviceConnector; +import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import scala.concurrent.Future; + +class NetconfTopologyContext implements ClusterSingletonService { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyContext.class); + + private final ServiceGroupIdentifier serviceGroupIdent; + private NetconfTopologySetup netconfTopologyDeviceSetup; + private RemoteDeviceId remoteDeviceId; + private RemoteDeviceConnector remoteDeviceConnector; + private NetconfNodeManager netconfNodeManager; + private boolean finalClose = false; + private boolean isMaster; + + private ActorRef masterActorRef; + + NetconfTopologyContext(final NetconfTopologySetup netconfTopologyDeviceSetup, + final ServiceGroupIdentifier serviceGroupIdent) { + this.netconfTopologyDeviceSetup = Preconditions.checkNotNull(netconfTopologyDeviceSetup); + this.serviceGroupIdent = serviceGroupIdent; + + remoteDeviceId = NetconfTopologyUtils.createRemoteDeviceId(netconfTopologyDeviceSetup.getNode().getNodeId(), + netconfTopologyDeviceSetup.getNode().getAugmentation(NetconfNode.class)); + + remoteDeviceConnector = new RemoteDeviceConnectorImpl(netconfTopologyDeviceSetup, remoteDeviceId); + + netconfNodeManager = createNodeDeviceManager(); + + } + + @Override + public void instantiateServiceInstance() { + LOG.info("Master was selected: {}", remoteDeviceId.getHost().getIpAddress()); + + isMaster = true; + + // master should not listen on netconf-node operational datastore + if (netconfNodeManager != null) { + netconfNodeManager.close(); + netconfNodeManager = null; + } + + if (!finalClose) { + final String masterAddress = Cluster.get(netconfTopologyDeviceSetup.getActorSystem()).selfAddress().toString(); + masterActorRef = netconfTopologyDeviceSetup.getActorSystem().actorOf(NetconfNodeActor.props( + netconfTopologyDeviceSetup, remoteDeviceId, DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY), + NetconfTopologyUtils.createMasterActorName(remoteDeviceId.getName(), masterAddress)); + + remoteDeviceConnector.startRemoteDeviceConnection(masterActorRef); + } + + } + + // called when master is down/changed to slave + @Override + public ListenableFuture closeServiceInstance() { + + if (!finalClose) { + // in case that master changes role to slave, new NodeDeviceManager must be created and listener registered + netconfNodeManager = createNodeDeviceManager(); + } + if (masterActorRef != null) { + netconfTopologyDeviceSetup.getActorSystem().stop(masterActorRef); + masterActorRef = null; + } + if (remoteDeviceConnector != null) { + remoteDeviceConnector.stopRemoteDeviceConnection(); + } + + return Futures.immediateCheckedFuture(null); + } + + @Override + public ServiceGroupIdentifier getIdentifier() { + return serviceGroupIdent; + } + + private NetconfNodeManager createNodeDeviceManager() { + final NetconfNodeManager ndm = + new NetconfNodeManager(netconfTopologyDeviceSetup, remoteDeviceId, DEFAULT_SCHEMA_REPOSITORY, + DEFAULT_SCHEMA_REPOSITORY); + ndm.registerDataTreeChangeListener(netconfTopologyDeviceSetup.getTopologyId(), + netconfTopologyDeviceSetup.getNode().getKey()); + + return ndm; + } + + void closeFinal() throws Exception { + finalClose = true; + + if (netconfNodeManager != null) { + netconfNodeManager.close(); + } + + if (remoteDeviceConnector != null) { + remoteDeviceConnector.stopRemoteDeviceConnection(); + } + + if (masterActorRef != null) { + netconfTopologyDeviceSetup.getActorSystem().stop(masterActorRef); + masterActorRef = null; + } + } + + /** + * If configuration data was changed + * @param setup new setup + */ + void refresh(@Nonnull final NetconfTopologySetup setup) { + netconfTopologyDeviceSetup = Preconditions.checkNotNull(setup); + remoteDeviceId = NetconfTopologyUtils.createRemoteDeviceId(netconfTopologyDeviceSetup.getNode().getNodeId(), + netconfTopologyDeviceSetup.getNode().getAugmentation(NetconfNode.class)); + + if (isMaster) { + remoteDeviceConnector.stopRemoteDeviceConnection(); + } + if (!isMaster) { + netconfNodeManager.refreshDevice(netconfTopologyDeviceSetup, remoteDeviceId); + } + remoteDeviceConnector = new RemoteDeviceConnectorImpl(netconfTopologyDeviceSetup, remoteDeviceId); + + if (isMaster) { + final Future future = Patterns.ask(masterActorRef, new RefreshSetupMasterActorData( + netconfTopologyDeviceSetup, remoteDeviceId), NetconfTopologyUtils.TIMEOUT); + + future.onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable failure, final Object success) throws Throwable { + if (failure != null) { + LOG.error("Failed to refresh master actor data: {}", failure); + return; + } + remoteDeviceConnector.startRemoteDeviceConnection(masterActorRef); + } + }, netconfTopologyDeviceSetup.getActorSystem().dispatcher()); + } + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java new file mode 100644 index 0000000000..6cad5c1c04 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java @@ -0,0 +1,243 @@ +/* + * 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.topology.singleton.impl; + +import akka.actor.ActorSystem; +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import io.netty.util.concurrent.EventExecutor; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nonnull; +import org.opendaylight.controller.cluster.ActorSystemProvider; +import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; +import org.opendaylight.controller.config.threadpool.ThreadPool; +import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration; +import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; +import org.opendaylight.netconf.client.NetconfClientDispatcher; +import org.opendaylight.netconf.topology.singleton.api.NetconfTopologySingletonService; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup.NetconfTopologySetupBuilder; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopologyBuilder; +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.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfTopologyManager + implements ClusteredDataTreeChangeListener, NetconfTopologySingletonService, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfTopologyManager.class); + + private final Map, NetconfTopologyContext> contexts = new HashMap<>(); + private final Map, ClusterSingletonServiceRegistration> + clusterRegistrations = new HashMap<>(); + + private ListenerRegistration dataChangeListenerRegistration; + + private final DataBroker dataBroker; + private final RpcProviderRegistry rpcProviderRegistry; + private final ClusterSingletonServiceProvider clusterSingletonServiceProvider; + private final BindingAwareBroker bindingAwareBroker; + private final ScheduledThreadPool keepaliveExecutor; + private final ThreadPool processingExecutor; + private final Broker domBroker; + private final ActorSystem actorSystem; + private final EventExecutor eventExecutor; + private final NetconfClientDispatcher clientDispatcher; + private final String topologyId; + + public NetconfTopologyManager(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry, + final ClusterSingletonServiceProvider clusterSingletonServiceProvider, + final BindingAwareBroker bindingAwareBroker, + final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor, + final Broker domBroker, final ActorSystemProvider actorSystemProvider, final EventExecutor eventExecutor, + final NetconfClientDispatcher clientDispatcher, final String topologyId) { + this.dataBroker = Preconditions.checkNotNull(dataBroker); + this.rpcProviderRegistry = Preconditions.checkNotNull(rpcProviderRegistry); + this.clusterSingletonServiceProvider = Preconditions.checkNotNull(clusterSingletonServiceProvider); + this.bindingAwareBroker = Preconditions.checkNotNull(bindingAwareBroker); + this.keepaliveExecutor = Preconditions.checkNotNull(keepaliveExecutor); + this.processingExecutor = Preconditions.checkNotNull(processingExecutor); + this.domBroker = Preconditions.checkNotNull(domBroker); + this.actorSystem = Preconditions.checkNotNull(actorSystemProvider).getActorSystem(); + this.eventExecutor = Preconditions.checkNotNull(eventExecutor); + this.clientDispatcher = Preconditions.checkNotNull(clientDispatcher); + this.topologyId = Preconditions.checkNotNull(topologyId); + } + + // Blueprint init method + public void init() { + dataChangeListenerRegistration = registerDataTreeChangeListener(topologyId); + } + + @Override + public void onDataTreeChanged(@Nonnull final Collection> changes) { + for (DataTreeModification change : changes) { + final DataObjectModification rootNode = change.getRootNode(); + final InstanceIdentifier dataModifIdent = change.getRootPath().getRootIdentifier(); + final NodeId nodeId = NetconfTopologyUtils.getNodeId(rootNode.getIdentifier()); + switch (rootNode.getModificationType()) { + case SUBTREE_MODIFIED: + LOG.debug("Config for node {} updated", nodeId); + refreshNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter()); + break; + case WRITE: + if (contexts.containsKey(dataModifIdent)) { + LOG.debug("RemoteDevice{{}} was already configured, reconfiguring node...", nodeId); + refreshNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter()); + } else { + LOG.debug("Config for node {} created", nodeId); + startNetconfDeviceContext(dataModifIdent, rootNode.getDataAfter()); + } + break; + case DELETE: + LOG.debug("Config for node {} deleted", nodeId); + stopNetconfDeviceContext(dataModifIdent); + break; + default: + LOG.warn("Unknown operation for {}.", nodeId); + } + } + } + + private void refreshNetconfDeviceContext(InstanceIdentifier instanceIdentifier, Node node) { + final NetconfTopologyContext context = contexts.get(instanceIdentifier); + context.refresh(createSetup(instanceIdentifier, node)); + } + + private void startNetconfDeviceContext(final InstanceIdentifier instanceIdentifier, final Node node) { + final NetconfNode netconfNode = node.getAugmentation(NetconfNode.class); + Preconditions.checkNotNull(netconfNode); + Preconditions.checkNotNull(netconfNode.getHost()); + Preconditions.checkNotNull(netconfNode.getHost().getIpAddress()); + + final ServiceGroupIdentifier serviceGroupIdent = + ServiceGroupIdentifier.create(instanceIdentifier.toString()); + + final NetconfTopologyContext newNetconfTopologyContext = + new NetconfTopologyContext(createSetup(instanceIdentifier, node), serviceGroupIdent); + + final ClusterSingletonServiceRegistration clusterSingletonServiceRegistration = + clusterSingletonServiceProvider.registerClusterSingletonService(newNetconfTopologyContext); + + clusterRegistrations.put(instanceIdentifier, clusterSingletonServiceRegistration); + contexts.put(instanceIdentifier, newNetconfTopologyContext); + } + + private void stopNetconfDeviceContext(final InstanceIdentifier instanceIdentifier) { + if (contexts.containsKey(instanceIdentifier)) { + try { + clusterRegistrations.get(instanceIdentifier).close(); + contexts.get(instanceIdentifier).closeFinal(); + } catch (Exception e) { + LOG.warn("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier); + } + contexts.remove(instanceIdentifier); + clusterRegistrations.remove(instanceIdentifier); + } + } + + @Override + public void close() { + if (dataChangeListenerRegistration != null) { + dataChangeListenerRegistration.close(); + dataChangeListenerRegistration = null; + } + contexts.forEach((instanceIdentifier, netconfTopologyContext) -> { + try { + netconfTopologyContext.closeFinal(); + } catch (Exception e) { + LOG.warn("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier); + } + }); + clusterRegistrations.forEach((instanceIdentifier, clusterSingletonServiceRegistration) -> { + try { + clusterSingletonServiceRegistration.close(); + } catch (Exception e) { + LOG.warn("Error at unregistering from cluster. InstanceIdentifier: " + instanceIdentifier); + } + }); + contexts.clear(); + clusterRegistrations.clear(); + } + + private ListenerRegistration registerDataTreeChangeListener(String topologyId) { + final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction(); + initTopology(wtx, LogicalDatastoreType.CONFIGURATION, topologyId); + initTopology(wtx, LogicalDatastoreType.OPERATIONAL, topologyId); + Futures.addCallback(wtx.submit(), new FutureCallback() { + @Override + public void onSuccess(Void result) { + LOG.debug("topology initialization successful"); + } + + @Override + public void onFailure(@Nonnull Throwable throwable) { + LOG.error("Unable to initialize netconf-topology, {}", throwable); + } + }); + + LOG.debug("Registering datastore listener"); + return dataBroker.registerDataTreeChangeListener( + new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + NetconfTopologyUtils.createTopologyListPath(topologyId).child(Node.class)), this); + } + + private void initTopology(final WriteTransaction wtx, final LogicalDatastoreType datastoreType, String topologyId) { + final NetworkTopology networkTopology = new NetworkTopologyBuilder().build(); + final InstanceIdentifier networkTopologyId = + InstanceIdentifier.builder(NetworkTopology.class).build(); + wtx.merge(datastoreType, networkTopologyId, networkTopology); + final Topology topology = new TopologyBuilder().setTopologyId(new TopologyId(topologyId)).build(); + wtx.merge(datastoreType, networkTopologyId.child(Topology.class, + new TopologyKey(new TopologyId(topologyId))), topology); + } + + private NetconfTopologySetup createSetup(final InstanceIdentifier instanceIdentifier, final Node node) { + final NetconfTopologySetupBuilder builder = NetconfTopologySetupBuilder.create() + .setClusterSingletonServiceProvider(clusterSingletonServiceProvider) + .setDataBroker(dataBroker) + .setInstanceIdentifier(instanceIdentifier) + .setRpcProviderRegistry(rpcProviderRegistry) + .setNode(node) + .setBindingAwareBroker(bindingAwareBroker) + .setActorSystem(actorSystem) + .setEventExecutor(eventExecutor) + .setDomBroker(domBroker) + .setKeepaliveExecutor(keepaliveExecutor) + .setProcessingExecutor(processingExecutor) + .setTopologyId(topologyId) + .setNetconfClientDispatcher(clientDispatcher); + + return builder.build(); + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java new file mode 100644 index 0000000000..c1c843014b --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyDOMRpcService.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015 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.topology.singleton.impl; + +import com.google.common.util.concurrent.CheckedFuture; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcAvailabilityListener; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcException; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; + +public class ProxyDOMRpcService implements DOMRpcService { + + @Nonnull + @Override + public CheckedFuture invokeRpc(@Nonnull final SchemaPath type, + @Nullable final NormalizedNode input) { + throw new UnsupportedOperationException("InvokeRpc: DOMRpc service not working in cluster."); + } + + @Nonnull + @Override + public ListenerRegistration registerRpcListener( + @Nonnull final T listener) { + throw new UnsupportedOperationException("RegisterRpcListener: DOMRpc service not working in cluster."); + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyYangTextSourceProvider.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyYangTextSourceProvider.java new file mode 100644 index 0000000000..27514c788b --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/ProxyYangTextSourceProvider.java @@ -0,0 +1,70 @@ +/* + * 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.topology.singleton.impl; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.dispatch.Futures; +import akka.dispatch.OnComplete; +import akka.pattern.Patterns; +import com.google.common.collect.Sets; +import java.util.Set; +import javax.annotation.Nonnull; +import org.opendaylight.controller.cluster.schema.provider.RemoteYangTextSourceProvider; +import org.opendaylight.controller.cluster.schema.provider.impl.YangTextSchemaSourceSerializationProxy; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +import org.opendaylight.netconf.topology.singleton.messages.YangTextSchemaSourceRequest; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import scala.concurrent.Future; +import scala.concurrent.impl.Promise; + +public class ProxyYangTextSourceProvider implements RemoteYangTextSourceProvider { + + private final ActorRef masterRef; + private final ActorContext actorContext; + + public ProxyYangTextSourceProvider(final ActorRef masterRef, final ActorContext actorContext) { + this.masterRef = masterRef; + this.actorContext = actorContext; + } + + @Override + public Future> getProvidedSources() { + // NOOP + return Futures.successful(Sets.newHashSet()); + } + + @Override + public Future getYangTextSchemaSource( + @Nonnull final SourceIdentifier sourceIdentifier) { + + final Future scalaFuture = Patterns.ask(masterRef, + new YangTextSchemaSourceRequest(sourceIdentifier), NetconfTopologyUtils.TIMEOUT); + + final Promise.DefaultPromise promise = new Promise.DefaultPromise<>(); + + scalaFuture.onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable failure, final Object success) throws Throwable { + if (failure != null) { + promise.failure(failure); + return; + } + if (success instanceof Throwable) { + promise.failure((Throwable) success); + return; + } + promise.success((YangTextSchemaSourceSerializationProxy) success); + } + }, actorContext.dispatcher()); + + return promise.future(); + + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java new file mode 100644 index 0000000000..47405b87c8 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImpl.java @@ -0,0 +1,425 @@ +/* + * 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.topology.singleton.impl; + +import akka.actor.ActorRef; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import io.netty.util.concurrent.EventExecutor; +import java.io.File; +import java.math.BigDecimal; +import java.net.InetSocketAddress; +import java.net.URL; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import javax.annotation.Nullable; +import org.opendaylight.netconf.api.NetconfMessage; +import org.opendaylight.netconf.client.NetconfClientSessionListener; +import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; +import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; +import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfigurationBuilder; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler; +import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPassword; +import org.opendaylight.netconf.sal.connect.api.RemoteDevice; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; +import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; +import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; +import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; +import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; +import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences; +import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade; +import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.RemoteDeviceConnector; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfConnectorDTO; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +import org.opendaylight.protocol.framework.ReconnectStrategy; +import org.opendaylight.protocol.framework.ReconnectStrategyFactory; +import org.opendaylight.protocol.framework.TimedReconnectStrategy; +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.IpAddress; +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.netconf.node.credentials.Credentials; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; +import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; +import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; +import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoteDeviceConnectorImpl implements RemoteDeviceConnector { + + private static final Logger LOG = LoggerFactory.getLogger(RemoteDeviceConnectorImpl.class); + + /** + * Keeps track of initialized Schema resources. A Map is maintained in which the key represents the name + * of the schema cache directory, and the value is a corresponding SchemaResourcesDTO. The + * SchemaResourcesDTO is essentially a container that allows for the extraction of the + * SchemaRegistry and SchemaContextFactory which should be used for a particular + * Netconf mount. Access to schemaResourcesDTOs should be surrounded by appropriate + * synchronization locks. + */ + private static final Map schemaResourcesDTOs = new HashMap<>(); + + private SchemaSourceRegistry schemaRegistry = NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; + private SchemaRepository schemaRepository = NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; + + private final NetconfTopologySetup netconfTopologyDeviceSetup; + private final RemoteDeviceId remoteDeviceId; + + private SchemaContextFactory schemaContextFactory = NetconfTopologyUtils.DEFAULT_SCHEMA_CONTEXT_FACTORY; + private NetconfConnectorDTO deviceCommunicatorDTO; + + // Initializes default constant instances for the case when the default schema repository + // directory cache/schema is used. + static { + schemaResourcesDTOs.put(NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY, + new NetconfDevice.SchemaResourcesDTO(NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY, + NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY, + NetconfTopologyUtils.DEFAULT_SCHEMA_CONTEXT_FACTORY, + new NetconfStateSchemasResolverImpl())); + NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(NetconfTopologyUtils.DEFAULT_CACHE); + NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener( + TextToASTTransformer.create(NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY, + NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY)); + } + + public RemoteDeviceConnectorImpl(final NetconfTopologySetup netconfTopologyDeviceSetup, + final RemoteDeviceId remoteDeviceId) { + + this.netconfTopologyDeviceSetup = Preconditions.checkNotNull(netconfTopologyDeviceSetup); + this.remoteDeviceId = remoteDeviceId; + } + + @Override + public void startRemoteDeviceConnection(final ActorRef deviceContextActorRef) { + + final NetconfNode netconfNode = netconfTopologyDeviceSetup.getNode().getAugmentation(NetconfNode.class); + final NodeId nodeId = netconfTopologyDeviceSetup.getNode().getNodeId(); + Preconditions.checkNotNull(netconfNode.getHost()); + Preconditions.checkNotNull(netconfNode.getPort()); + Preconditions.checkNotNull(netconfNode.isTcpOnly()); + + this.deviceCommunicatorDTO = createDeviceCommunicator(nodeId, netconfNode, deviceContextActorRef); + final NetconfDeviceCommunicator deviceCommunicator = deviceCommunicatorDTO.getCommunicator(); + final NetconfClientSessionListener netconfClientSessionListener = deviceCommunicatorDTO.getSessionListener(); + final NetconfReconnectingClientConfiguration clientConfig = + getClientConfig(netconfClientSessionListener, netconfNode); + final ListenableFuture future = deviceCommunicator + .initializeRemoteConnection(netconfTopologyDeviceSetup.getNetconfClientDispatcher(), clientConfig); + + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(NetconfDeviceCapabilities result) { + LOG.debug("{}: Connector started succesfully", nodeId.getValue()); + } + + @Override + public void onFailure(@Nullable Throwable throwable) { + LOG.error("{}: Connector failed, {}", nodeId.getValue(), throwable); + } + }); + } + + @Override + public void stopRemoteDeviceConnection() { + Preconditions.checkNotNull(deviceCommunicatorDTO, "Device communicator was not created."); + try { + deviceCommunicatorDTO.close(); + } catch (Exception e) { + LOG.warn("{}: Error at closing device communicator.", remoteDeviceId); + } + } + + @VisibleForTesting + NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node, + final ActorRef deviceContextActorRef) { + //setup default values since default value is not supported in mdsal + final Long defaultRequestTimeoutMillis = node.getDefaultRequestTimeoutMillis() == null + ? NetconfTopologyUtils.DEFAULT_REQUEST_TIMEOUT_MILLIS : node.getDefaultRequestTimeoutMillis(); + final Long keepaliveDelay = node.getKeepaliveDelay() == null + ? NetconfTopologyUtils.DEFAULT_KEEPALIVE_DELAY : node.getKeepaliveDelay(); + final Boolean reconnectOnChangedSchema = node.isReconnectOnChangedSchema() == null + ? NetconfTopologyUtils.DEFAULT_RECONNECT_ON_CHANGED_SCHEMA : node.isReconnectOnChangedSchema(); + + RemoteDeviceHandler salFacade = new MasterSalFacade(remoteDeviceId, + netconfTopologyDeviceSetup.getDomBroker(), netconfTopologyDeviceSetup.getBindingAwareBroker(), + netconfTopologyDeviceSetup.getActorSystem(), deviceContextActorRef); + if (keepaliveDelay > 0) { + LOG.info("Device: {} , Adding keepalive facade.", nodeId); + salFacade = new KeepaliveSalFacade(remoteDeviceId, salFacade, + netconfTopologyDeviceSetup.getKeepaliveExecutor().getExecutor(), keepaliveDelay, + defaultRequestTimeoutMillis); + } + + // pre register yang library sources as fallback schemas to schema registry + List> registeredYangLibSources = Lists.newArrayList(); + if (node.getYangLibrary() != null) { + final String yangLibURL = node.getYangLibrary().getYangLibraryUrl().getValue(); + final String yangLibUsername = node.getYangLibrary().getUsername(); + final String yangLigPassword = node.getYangLibrary().getPassword(); + + LibraryModulesSchemas libraryModulesSchemas; + if (yangLibURL != null) { + if (yangLibUsername != null && yangLigPassword != null) { + libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL, yangLibUsername, yangLigPassword); + } else { + libraryModulesSchemas = LibraryModulesSchemas.create(yangLibURL); + } + + for (Map.Entry sourceIdentifierURLEntry : + libraryModulesSchemas.getAvailableModels().entrySet()) { + registeredYangLibSources + .add(schemaRegistry.registerSchemaSource( + new YangLibrarySchemaYangSourceProvider(remoteDeviceId, + libraryModulesSchemas.getAvailableModels()), + PotentialSchemaSource + .create(sourceIdentifierURLEntry.getKey(), YangTextSchemaSource.class, + PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); + } + } + } + + final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = setupSchemaCacheDTO(nodeId, node); + final RemoteDevice device; + if (node.isSchemaless()) { + device = new SchemalessNetconfDevice(remoteDeviceId, salFacade); + } else { + device = new NetconfDeviceBuilder() + .setReconnectOnSchemasChange(reconnectOnChangedSchema) + .setSchemaResourcesDTO(schemaResourcesDTO) + .setGlobalProcessingExecutor(netconfTopologyDeviceSetup.getProcessingExecutor().getExecutor()) + .setId(remoteDeviceId) + .setSalFacade(salFacade) + .build(); + } + + final Optional userCapabilities = getUserCapabilities(node); + final int rpcMessageLimit = + node.getConcurrentRpcLimit() == null + ? NetconfTopologyUtils.DEFAULT_CONCURRENT_RPC_LIMIT : node.getConcurrentRpcLimit(); + + if (rpcMessageLimit < 1) { + LOG.info("Device: {}, Concurrent rpc limit is smaller than 1, no limit will be enforced.", remoteDeviceId); + } + + return new NetconfConnectorDTO( + userCapabilities.isPresent() + ? new NetconfDeviceCommunicator( + remoteDeviceId, device, new UserPreferences(userCapabilities.get(), + node.getYangModuleCapabilities().isOverride()), rpcMessageLimit) : + new NetconfDeviceCommunicator(remoteDeviceId, device, rpcMessageLimit), salFacade); + } + + private Optional getUserCapabilities(final NetconfNode node) { + if (node.getYangModuleCapabilities() == null) { + return Optional.empty(); + } + + final List capabilities = node.getYangModuleCapabilities().getCapability(); + if (capabilities == null || capabilities.isEmpty()) { + return Optional.empty(); + } + + final NetconfSessionPreferences parsedOverrideCapabilities = + NetconfSessionPreferences.fromStrings(capabilities); + Preconditions.checkState(parsedOverrideCapabilities.getNonModuleCaps().isEmpty(), + "Capabilities to override can only contain module based capabilities, non-module capabilities " + + "will be retrieved from the device, configured non-module capabilities: " + + parsedOverrideCapabilities.getNonModuleCaps()); + + return Optional.of(parsedOverrideCapabilities); + } + + private NetconfDevice.SchemaResourcesDTO setupSchemaCacheDTO(final NodeId nodeId, final NetconfNode node) { + // Setup information related to the SchemaRegistry, SchemaResourceFactory, etc. + NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = null; + final String moduleSchemaCacheDirectory = node.getSchemaCacheDirectory(); + // Only checks to ensure the String is not empty or null; further checks related to directory accessibility + // and file permissions are handled during the FilesystemSchemaSourceCache initialization. + if (!Strings.isNullOrEmpty(moduleSchemaCacheDirectory)) { + // If a custom schema cache directory is specified, create the backing DTO; otherwise, the SchemaRegistry + // and SchemaContextFactory remain the default values. + if (!moduleSchemaCacheDirectory.equals(NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY)) { + // Multiple modules may be created at once; synchronize to avoid issues with data consistency among + // threads. + synchronized (schemaResourcesDTOs) { + // Look for the cached DTO to reuse SchemaRegistry and SchemaContextFactory variables if + // they already exist + schemaResourcesDTO = schemaResourcesDTOs.get(moduleSchemaCacheDirectory); + if (schemaResourcesDTO == null) { + schemaResourcesDTO = createSchemaResourcesDTO(moduleSchemaCacheDirectory); + schemaResourcesDTO.getSchemaRegistry().registerSchemaSourceListener( + TextToASTTransformer.create((SchemaRepository) schemaResourcesDTO.getSchemaRegistry(), + schemaResourcesDTO.getSchemaRegistry()) + ); + schemaResourcesDTOs.put(moduleSchemaCacheDirectory, schemaResourcesDTO); + } + } + LOG.info("{} : netconf connector will use schema cache directory {} instead of {}", + nodeId.getValue(), moduleSchemaCacheDirectory, NetconfTopologyUtils.DEFAULT_CACHE_DIRECTORY); + } + } else { + LOG.info("{} : using the default directory {}", + nodeId.getValue(), NetconfTopologyUtils.QUALIFIED_DEFAULT_CACHE_DIRECTORY); + } + + if (schemaResourcesDTO == null) { + schemaResourcesDTO = + new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaContextFactory, + new NetconfStateSchemasResolverImpl()); + } + + return schemaResourcesDTO; + } + + /** + * Creates the backing Schema classes for a particular directory. + * + * @param moduleSchemaCacheDirectory The string directory relative to "cache" + * @return A DTO containing the Schema classes for the Netconf mount. + */ + private NetconfDevice.SchemaResourcesDTO createSchemaResourcesDTO(final String moduleSchemaCacheDirectory) { + final SharedSchemaRepository repository = new SharedSchemaRepository(moduleSchemaCacheDirectory); + final SchemaContextFactory schemaContextFactory + = repository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT); + this.schemaRegistry = repository; + this.schemaContextFactory = schemaContextFactory; + + final FilesystemSchemaSourceCache deviceCache = + createDeviceFilesystemCache(moduleSchemaCacheDirectory); + repository.registerSchemaSourceListener(deviceCache); + return new NetconfDevice.SchemaResourcesDTO(repository, repository, schemaContextFactory, + new NetconfStateSchemasResolverImpl()); + } + + /** + * Creates a FilesystemSchemaSourceCache for the custom schema cache directory. + * + * @param schemaCacheDirectory The custom cache directory relative to "cache" + * @return A FilesystemSchemaSourceCache for the custom schema cache directory + */ + private FilesystemSchemaSourceCache createDeviceFilesystemCache( + final String schemaCacheDirectory) { + final String relativeSchemaCacheDirectory = + NetconfTopologyUtils.CACHE_DIRECTORY + File.separator + schemaCacheDirectory; + return new FilesystemSchemaSourceCache<>(schemaRegistry, YangTextSchemaSource.class, + new File(relativeSchemaCacheDirectory)); + } + + //TODO: duplicate code + private InetSocketAddress getSocketAddress(final Host host, int port) { + if (host.getDomainName() != null) { + return new InetSocketAddress(host.getDomainName().getValue(), port); + } else { + final IpAddress ipAddress = host.getIpAddress(); + final String ip = ipAddress.getIpv4Address() != null ? ipAddress.getIpv4Address().getValue() : + ipAddress.getIpv6Address().getValue(); + return new InetSocketAddress(ip, port); + } + } + + private static final class TimedReconnectStrategyFactory implements ReconnectStrategyFactory { + private final Long connectionAttempts; + private final EventExecutor executor; + private final double sleepFactor; + private final int minSleep; + + TimedReconnectStrategyFactory(final EventExecutor executor, final Long maxConnectionAttempts, + final int minSleep, final BigDecimal sleepFactor) { + if (maxConnectionAttempts != null && maxConnectionAttempts > 0) { + connectionAttempts = maxConnectionAttempts; + } else { + connectionAttempts = null; + } + + this.sleepFactor = sleepFactor.doubleValue(); + this.executor = executor; + this.minSleep = minSleep; + } + + @Override + public ReconnectStrategy createReconnectStrategy() { + final Long maxSleep = null; + final Long deadline = null; + + return new TimedReconnectStrategy(executor, minSleep, + minSleep, sleepFactor, maxSleep, connectionAttempts, deadline); + } + } + + @VisibleForTesting + NetconfReconnectingClientConfiguration getClientConfig(final NetconfClientSessionListener listener, + final NetconfNode node) { + + //setup default values since default value is not supported in mdsal + final long clientConnectionTimeoutMillis = node.getConnectionTimeoutMillis() == null + ? NetconfTopologyUtils.DEFAULT_CONNECTION_TIMEOUT_MILLIS : node.getConnectionTimeoutMillis(); + final long maxConnectionAttempts = node.getMaxConnectionAttempts() == null + ? NetconfTopologyUtils.DEFAULT_MAX_CONNECTION_ATTEMPTS : node.getMaxConnectionAttempts(); + final int betweenAttemptsTimeoutMillis = node.getBetweenAttemptsTimeoutMillis() == null + ? NetconfTopologyUtils.DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS : node.getBetweenAttemptsTimeoutMillis(); + final BigDecimal sleepFactor = node.getSleepFactor() == null + ? NetconfTopologyUtils.DEFAULT_SLEEP_FACTOR : node.getSleepFactor(); + + final InetSocketAddress socketAddress = getSocketAddress(node.getHost(), node.getPort().getValue()); + + final ReconnectStrategyFactory sf = + new TimedReconnectStrategyFactory(netconfTopologyDeviceSetup.getEventExecutor(), maxConnectionAttempts, + betweenAttemptsTimeoutMillis, sleepFactor); + final ReconnectStrategy strategy = sf.createReconnectStrategy(); + + final AuthenticationHandler authHandler; + final Credentials credentials = node.getCredentials(); + if (credentials instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) { + authHandler = new LoginPassword( + ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getUsername(), + ((org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPassword) credentials).getPassword()); + } else { + throw new IllegalStateException("Only login/password authentification is supported"); + } + + return NetconfReconnectingClientConfigurationBuilder.create() + .withAddress(socketAddress) + .withConnectionTimeoutMillis(clientConnectionTimeoutMillis) + .withReconnectStrategy(strategy) + .withAuthHandler(authHandler) + .withProtocol(node.isTcpOnly() + ? NetconfClientConfiguration.NetconfClientProtocol.TCP + : NetconfClientConfiguration.NetconfClientProtocol.SSH) + .withConnectStrategyFactory(sf) + .withSessionListener(listener) + .build(); + } + + @VisibleForTesting + Map getSchemaResourcesDTOs() { + return schemaResourcesDTOs; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteOperationTxProcessorImpl.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteOperationTxProcessorImpl.java new file mode 100644 index 0000000000..da95e9f02a --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/RemoteOperationTxProcessorImpl.java @@ -0,0 +1,155 @@ +/* + * 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.topology.singleton.impl; + +import akka.actor.ActorRef; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.RemoteOperationTxProcessor; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; +import org.opendaylight.netconf.topology.singleton.messages.SubmitFailedReply; +import org.opendaylight.netconf.topology.singleton.messages.transactions.EmptyReadResponse; +import org.opendaylight.netconf.topology.singleton.messages.transactions.SubmitReply; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RemoteOperationTxProcessorImpl implements RemoteOperationTxProcessor, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(RemoteOperationTxProcessorImpl.class); + + private final DOMDataBroker dataBroker; + private final RemoteDeviceId id; + private DOMDataWriteTransaction writeTx; + private DOMDataReadOnlyTransaction readTx; + + public RemoteOperationTxProcessorImpl(final DOMDataBroker dataBroker, final RemoteDeviceId id) { + this.dataBroker = dataBroker; + this.id = id; + this.readTx = dataBroker.newReadOnlyTransaction(); + } + + @Override + public void doDelete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + if (writeTx == null) { + writeTx = dataBroker.newWriteOnlyTransaction(); + } + writeTx.delete(store, path); + } + + @Override + public void doSubmit(final ActorRef recipient, final ActorRef sender) { + if (writeTx != null) { + CheckedFuture submitFuture = writeTx.submit(); + Futures.addCallback(submitFuture, new FutureCallback() { + @Override + public void onSuccess(Void result) { + recipient.tell(new SubmitReply(), sender); + } + + @Override + public void onFailure(@Nonnull Throwable throwable) { + recipient.tell(throwable, sender); + } + }); + } else { + recipient.tell(new SubmitFailedReply(), sender); + LOG.warn("{}: Couldn't submit transaction because it was already closed.", id); + } + } + + @Override + public void doCancel(final ActorRef recipient, final ActorRef sender) { + boolean cancel = false; + if (writeTx != null) { + cancel = writeTx.cancel(); + } + recipient.tell(cancel, sender); + } + + @Override + public void doPut(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + if (writeTx == null) { + writeTx = dataBroker.newWriteOnlyTransaction(); + } + writeTx.put(store, data.getIdentifier(), data.getNode()); + } + + @Override + public void doMerge(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + if (writeTx == null) { + writeTx = dataBroker.newWriteOnlyTransaction(); + } + writeTx.merge(store, data.getIdentifier(), data.getNode()); + } + + @Override + public void doRead(final LogicalDatastoreType store, final YangInstanceIdentifier path, final ActorRef recipient, + final ActorRef sender) { + final CheckedFuture>, ReadFailedException> readFuture = + readTx.read(store, path); + + Futures.addCallback(readFuture, new FutureCallback>>() { + + @Override + public void onSuccess(final Optional> result) { + if (!result.isPresent()) { + recipient.tell(new EmptyReadResponse(), sender); + return; + } + recipient.tell(new NormalizedNodeMessage(path, result.get()), sender); + } + + @Override + public void onFailure(@Nonnull final Throwable throwable) { + recipient.tell(throwable, sender); + } + }); + } + + @Override + public void doExists(final LogicalDatastoreType store, final YangInstanceIdentifier path, final ActorRef recipient, + final ActorRef sender) { + final CheckedFuture readFuture = + readTx.exists(store, path); + Futures.addCallback(readFuture, new FutureCallback() { + @Override + public void onSuccess(final Boolean result) { + if (result == null) { + recipient.tell(false, sender); + } else { + recipient.tell(result, sender); + } + } + + @Override + public void onFailure(@Nonnull final Throwable throwable) { + recipient.tell(throwable, sender); + } + }); + } + + @Override + public void close() throws Exception { + if (readTx != null) { + readTx.close(); + } + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/SlaveSalFacade.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/SlaveSalFacade.java new file mode 100644 index 0000000000..772020bcaa --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/SlaveSalFacade.java @@ -0,0 +1,79 @@ +/* + * 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.topology.singleton.impl; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceNotificationService; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceSalProvider; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.impl.tx.NetconfProxyDOMTransaction; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SlaveSalFacade { + + private static final Logger LOG = LoggerFactory.getLogger(SlaveSalFacade.class); + + private final RemoteDeviceId id; + private final NetconfDeviceSalProvider salProvider; + + private final ActorSystem actorSystem; + + public SlaveSalFacade(final RemoteDeviceId id, + final Broker domBroker, + final ActorSystem actorSystem) { + this.id = id; + this.salProvider = new NetconfDeviceSalProvider(id); + this.actorSystem = actorSystem; + + registerToSal(domBroker); + } + + private void registerToSal(final Broker domRegistryDependency) { + domRegistryDependency.registerProvider(salProvider); + + } + + public void registerSlaveMountPoint(final SchemaContext remoteSchemaContext, final DOMRpcService deviceRpc, + final ActorRef masterActorRef) { + final NetconfDeviceNotificationService notificationService = new NetconfDeviceNotificationService(); + + final NetconfDOMTransaction proxyDOMTransactions = + new NetconfProxyDOMTransaction(actorSystem, masterActorRef); + + final NetconfDOMDataBroker netconfDeviceDataBroker = + new NetconfDOMDataBroker(actorSystem, id, proxyDOMTransactions); + + salProvider.getMountInstance().onTopologyDeviceConnected(remoteSchemaContext, netconfDeviceDataBroker, + deviceRpc, notificationService); + + LOG.info("{}: Slave mount point registered.", id); + } + + public void unregisterSlaveMountPoint() { + salProvider.getMountInstance().onTopologyDeviceDisconnected(); + } + + public void close() { + unregisterSlaveMountPoint(); + try { + salProvider.getMountInstance().close(); + } catch (Exception exception) { + LOG.warn("{}: Exception in closing slave sal facade: {}", id, exception); + } + + } + + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java new file mode 100644 index 0000000000..75c7e62e8a --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java @@ -0,0 +1,235 @@ +/* + * 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.topology.singleton.impl.actors; + +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.actor.UntypedActor; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import java.io.IOException; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.controller.cluster.schema.provider.RemoteYangTextSourceProvider; +import org.opendaylight.controller.cluster.schema.provider.impl.RemoteSchemaProvider; +import org.opendaylight.controller.cluster.schema.provider.impl.YangTextSchemaSourceSerializationProxy; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.RemoteOperationTxProcessor; +import org.opendaylight.netconf.topology.singleton.impl.ProxyDOMRpcService; +import org.opendaylight.netconf.topology.singleton.impl.ProxyYangTextSourceProvider; +import org.opendaylight.netconf.topology.singleton.impl.RemoteOperationTxProcessorImpl; +import org.opendaylight.netconf.topology.singleton.impl.SlaveSalFacade; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.messages.AskForMasterMountPoint; +import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData; +import org.opendaylight.netconf.topology.singleton.messages.MasterActorDataInitialized; +import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData; +import org.opendaylight.netconf.topology.singleton.messages.RegisterMountPoint; +import org.opendaylight.netconf.topology.singleton.messages.UnregisterSlaveMountPoint; +import org.opendaylight.netconf.topology.singleton.messages.YangTextSchemaSourceRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.CancelRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.DeleteRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.ExistsRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.MergeRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.PutRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.ReadRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.SubmitRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.TransactionRequest; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfNodeActor extends UntypedActor { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeActor.class); + + private NetconfTopologySetup setup; + private RemoteDeviceId id; + private final SchemaSourceRegistry schemaRegistry; + private final SchemaRepository schemaRepository; + + private RemoteOperationTxProcessor operationsProcessor; + private List sourceIdentifiers; + private SlaveSalFacade slaveSalManager; + + public static Props props(final NetconfTopologySetup setup, + final RemoteDeviceId id, final SchemaSourceRegistry schemaRegistry, + final SchemaRepository schemaRepository) { + return Props.create(NetconfNodeActor.class, () -> + new NetconfNodeActor(setup, id, schemaRegistry, schemaRepository)); + } + + private NetconfNodeActor(final NetconfTopologySetup setup, + final RemoteDeviceId id, SchemaSourceRegistry schemaRegistry, + final SchemaRepository schemaRepository) { + this.setup = setup; + this.id = id; + this.schemaRegistry = schemaRegistry; + this.schemaRepository = schemaRepository; + } + + @Override + public void onReceive(final Object message) throws Exception { + if (message instanceof CreateInitialMasterActorData) { // master + + sourceIdentifiers = ((CreateInitialMasterActorData) message).getSourceIndentifiers(); + operationsProcessor = + new RemoteOperationTxProcessorImpl(((CreateInitialMasterActorData) message).getDeviceDataBroker(), + id); + sender().tell(new MasterActorDataInitialized(), self()); + + LOG.debug("{}: Master is ready.", id); + + } else if (message instanceof RefreshSetupMasterActorData) { + setup = ((RefreshSetupMasterActorData) message).getNetconfTopologyDeviceSetup(); + id = ((RefreshSetupMasterActorData) message).getRemoteDeviceId(); + sender().tell(new MasterActorDataInitialized(), self()); + } else if (message instanceof AskForMasterMountPoint) { // master + // only master contains reference to operations processor + if (operationsProcessor != null) { + getSender().tell(new RegisterMountPoint(sourceIdentifiers), getSelf()); + } + + } else if (message instanceof TransactionRequest) { // master + + resolveProxyCalls(message, sender(), getSelf()); + + } else if (message instanceof YangTextSchemaSourceRequest) { // master + + final YangTextSchemaSourceRequest yangTextSchemaSourceRequest = (YangTextSchemaSourceRequest) message; + sendYangTextSchemaSourceProxy(yangTextSchemaSourceRequest.getSourceIdentifier(), sender()); + + } else if (message instanceof RegisterMountPoint) { //slaves + + sourceIdentifiers = ((RegisterMountPoint) message).getSourceIndentifiers(); + registerSlaveMountPoint(getSender()); + + } else if (message instanceof UnregisterSlaveMountPoint) { //slaves + if (slaveSalManager != null) { + slaveSalManager.close(); + slaveSalManager = null; + } + + } + } + + private void resolveProxyCalls(final Object message, final ActorRef recipient, final ActorRef futureSender) { + if (message instanceof ReadRequest) { + + final ReadRequest readRequest = (ReadRequest) message; + operationsProcessor.doRead(readRequest.getStore(), readRequest.getPath(), recipient, futureSender); + + } else if (message instanceof ExistsRequest) { + + final ExistsRequest readRequest = (ExistsRequest) message; + operationsProcessor.doExists(readRequest.getStore(), readRequest.getPath(), recipient, futureSender); + + } else if (message instanceof MergeRequest) { + + final MergeRequest mergeRequest = (MergeRequest) message; + operationsProcessor.doMerge(mergeRequest.getStore(), mergeRequest.getNormalizedNodeMessage()); + + } else if (message instanceof PutRequest) { + + final PutRequest putRequest = (PutRequest) message; + operationsProcessor.doPut(putRequest.getStore(), putRequest.getNormalizedNodeMessage()); + + } else if (message instanceof DeleteRequest) { + + final DeleteRequest deleteRequest = (DeleteRequest) message; + operationsProcessor.doDelete(deleteRequest.getStore(), deleteRequest.getPath()); + + } else if (message instanceof CancelRequest) { + + operationsProcessor.doCancel(recipient, futureSender); + + } else if (message instanceof SubmitRequest) { + + operationsProcessor.doSubmit(recipient, futureSender); + } + } + + private void sendYangTextSchemaSourceProxy(final SourceIdentifier sourceIdentifier, final ActorRef sender) { + final CheckedFuture yangTextSchemaSource = + schemaRepository.getSchemaSource(sourceIdentifier, YangTextSchemaSource.class); + + Futures.addCallback(yangTextSchemaSource, new FutureCallback() { + @Override + public void onSuccess(final YangTextSchemaSource yangTextSchemaSource) { + try { + sender.tell(new YangTextSchemaSourceSerializationProxy(yangTextSchemaSource), getSelf()); + } catch (IOException exception) { + sender.tell(exception.getCause(), getSelf()); + } + } + + @Override + public void onFailure(@Nonnull final Throwable throwable) { + sender.tell(throwable, getSelf()); + } + }); + } + + private void registerSlaveMountPoint(final ActorRef masterReference) { + if (this.slaveSalManager != null) { + slaveSalManager.close(); + } + slaveSalManager = new SlaveSalFacade(id, setup.getDomBroker(), setup.getActorSystem()); + + final CheckedFuture remoteSchemaContext = + getSchemaContext(masterReference); + final DOMRpcService deviceRpc = getDOMRpcService(); + + Futures.addCallback(remoteSchemaContext, new FutureCallback() { + @Override + public void onSuccess(final SchemaContext result) { + LOG.info("{}: Schema context resolved: {}", id, result.getModules()); + slaveSalManager.registerSlaveMountPoint(result, deviceRpc, masterReference); + } + + @Override + public void onFailure(@Nonnull final Throwable throwable) { + LOG.error("{}: Failed to register mount point: {}", id, throwable); + } + }); + } + + private DOMRpcService getDOMRpcService() { + return new ProxyDOMRpcService(); + } + + private CheckedFuture getSchemaContext(ActorRef masterReference) { + + final RemoteYangTextSourceProvider remoteYangTextSourceProvider = + new ProxyYangTextSourceProvider(masterReference, getContext()); + final RemoteSchemaProvider remoteProvider = new RemoteSchemaProvider(remoteYangTextSourceProvider, + getContext().dispatcher()); + + sourceIdentifiers.forEach(sourceId -> + schemaRegistry.registerSchemaSource(remoteProvider, PotentialSchemaSource.create(sourceId, + YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); + + final SchemaContextFactory schemaContextFactory + = schemaRepository.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT); + + return schemaContextFactory.createSchemaContext(sourceIdentifiers); + } + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfMasterDOMTransaction.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfMasterDOMTransaction.java new file mode 100644 index 0000000000..602eb75503 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfMasterDOMTransaction.java @@ -0,0 +1,150 @@ +/* + * 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.topology.singleton.impl.tx; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import javax.annotation.Nonnull; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceDataBroker; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import scala.concurrent.Future; +import scala.concurrent.impl.Promise.DefaultPromise; + +public class NetconfMasterDOMTransaction implements NetconfDOMTransaction { + + private final DOMDataBroker delegateBroker; + + private DOMDataReadOnlyTransaction readTx; + private DOMDataWriteTransaction writeTx; + + public NetconfMasterDOMTransaction(final RemoteDeviceId id, + final SchemaContext schemaContext, final DOMRpcService rpc, + final NetconfSessionPreferences netconfSessionPreferences) { + + delegateBroker = new NetconfDeviceDataBroker(id, schemaContext, rpc, netconfSessionPreferences); + + // only ever need 1 readTx since it doesnt need to be closed + readTx = delegateBroker.newReadOnlyTransaction(); + } + + public NetconfMasterDOMTransaction(final DOMDataBroker delegateBroker) { + this.delegateBroker = delegateBroker; + + // only ever need 1 readTx since it doesnt need to be closed + readTx = delegateBroker.newReadOnlyTransaction(); + } + + @Override + public Future> read(final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + final CheckedFuture>, ReadFailedException> readFuture = readTx.read(store, path); + + final DefaultPromise> promise = new DefaultPromise<>(); + Futures.addCallback(readFuture, new FutureCallback>>() { + @Override + public void onSuccess(final Optional> result) { + if (!result.isPresent()) { + promise.success(Optional.absent()); + } else { + promise.success(Optional.of(new NormalizedNodeMessage(path, result.get()))); + } + } + + @Override + public void onFailure(@Nonnull final Throwable throwable) { + promise.failure(throwable); + } + }); + return promise.future(); + } + + @Override + public Future exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + final CheckedFuture existsFuture = readTx.exists(store, path); + + final DefaultPromise promise = new DefaultPromise<>(); + Futures.addCallback(existsFuture, new FutureCallback() { + @Override + public void onSuccess(final Boolean result) { + promise.success(result); + } + + @Override + public void onFailure(@Nonnull final Throwable throwable) { + promise.failure(throwable); + } + }); + return promise.future(); + } + + @Override + public void put(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + if (writeTx == null) { + writeTx = delegateBroker.newWriteOnlyTransaction(); + } + writeTx.put(store, data.getIdentifier(), data.getNode()); + } + + @Override + public void merge(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + if (writeTx == null) { + writeTx = delegateBroker.newWriteOnlyTransaction(); + } + writeTx.merge(store, data.getIdentifier(), data.getNode()); + } + + @Override + public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + if (writeTx == null) { + writeTx = delegateBroker.newWriteOnlyTransaction(); + } + writeTx.delete(store, path); + } + + @Override + public boolean cancel() { + return writeTx.cancel(); + } + + @Override + public Future submit() { + final CheckedFuture submitFuture = writeTx.submit(); + final DefaultPromise promise = new DefaultPromise<>(); + Futures.addCallback(submitFuture, new FutureCallback() { + @Override + public void onSuccess(final Void result) { + promise.success(result); + writeTx = null; + } + + @Override + public void onFailure(@Nonnull final Throwable throwable) { + promise.failure(throwable); + writeTx = null; + } + }); + return promise.future(); + } + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfProxyDOMTransaction.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfProxyDOMTransaction.java new file mode 100644 index 0000000000..ad9cdd8d64 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfProxyDOMTransaction.java @@ -0,0 +1,170 @@ +/* + * 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.topology.singleton.impl.tx; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.dispatch.OnComplete; +import akka.pattern.Patterns; +import com.google.common.base.Optional; +import org.opendaylight.controller.config.util.xml.DocumentedException; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; +import org.opendaylight.netconf.topology.singleton.messages.SubmitFailedReply; +import org.opendaylight.netconf.topology.singleton.messages.transactions.CancelRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.DeleteRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.EmptyReadResponse; +import org.opendaylight.netconf.topology.singleton.messages.transactions.ExistsRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.MergeRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.PutRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.ReadRequest; +import org.opendaylight.netconf.topology.singleton.messages.transactions.SubmitRequest; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import scala.concurrent.Await; +import scala.concurrent.Future; +import scala.concurrent.impl.Promise.DefaultPromise; + + +public class NetconfProxyDOMTransaction implements NetconfDOMTransaction { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfProxyDOMTransaction.class); + + private final ActorSystem actorSystem; + private final ActorRef masterContextRef; + + public NetconfProxyDOMTransaction(final ActorSystem actorSystem, final ActorRef masterContextRef) { + this.actorSystem = actorSystem; + this.masterContextRef = masterContextRef; + } + + @Override + public Future> read(final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + + final Future readScalaFuture = + Patterns.ask(masterContextRef, new ReadRequest(store, path), NetconfTopologyUtils.TIMEOUT); + + final DefaultPromise> promise = new DefaultPromise<>(); + + readScalaFuture.onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable failure, final Object success) throws Throwable { + if (failure != null) { // ask timeout + Exception exception = new DocumentedException("Master is down. Please try again.", + DocumentedException.ErrorType.application, DocumentedException.ErrorTag.operation_failed, + DocumentedException.ErrorSeverity.warning); + promise.failure(exception); + return; + } + if (success instanceof Throwable) { // Error sended by master + promise.failure((Throwable) success); + return; + } + if (success instanceof EmptyReadResponse) { + promise.success(Optional.absent()); + return; + } + + promise.success(Optional.of((NormalizedNodeMessage) success)); + } + }, actorSystem.dispatcher()); + + return promise.future(); + } + + @Override + public Future exists(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + final Future existsScalaFuture = + Patterns.ask(masterContextRef, new ExistsRequest(store, path), NetconfTopologyUtils.TIMEOUT); + + final DefaultPromise promise = new DefaultPromise<>(); + existsScalaFuture.onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable failure, final Object success) throws Throwable { + if (failure != null) { // ask timeout + Exception exception = new DocumentedException("Master is down. Please try again.", + DocumentedException.ErrorType.application, DocumentedException.ErrorTag.operation_failed, + DocumentedException.ErrorSeverity.warning); + promise.failure(exception); + return; + } + if (success instanceof Throwable) { + promise.failure((Throwable) success); + return; + } + promise.success((Boolean) success); + } + }, actorSystem.dispatcher()); + return promise.future(); + } + + @Override + public void put(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + masterContextRef.tell(new PutRequest(store, data), ActorRef.noSender()); + + } + + @Override + public void merge(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + masterContextRef.tell(new MergeRequest(store, data), ActorRef.noSender()); + } + + @Override + public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + masterContextRef.tell(new DeleteRequest(store, path), ActorRef.noSender()); + } + + @Override + public boolean cancel() { + final Future cancelScalaFuture = + Patterns.ask(masterContextRef, new CancelRequest(), NetconfTopologyUtils.TIMEOUT); + try { + // here must be Await because AsyncWriteTransaction do not return future + return (boolean) Await.result(cancelScalaFuture, NetconfTopologyUtils.TIMEOUT.duration()); + } catch (Exception e) { + return false; + } + } + + @Override + public Future submit() { + final Future submitScalaFuture = + Patterns.ask(masterContextRef, new SubmitRequest(), NetconfTopologyUtils.TIMEOUT); + + final DefaultPromise promise = new DefaultPromise<>(); + + submitScalaFuture.onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable failure, final Object success) throws Throwable { + if (failure != null) { // ask timeout + Exception exception = new DocumentedException("Master is down. Please try again.", + DocumentedException.ErrorType.application, DocumentedException.ErrorTag.operation_failed, + DocumentedException.ErrorSeverity.warning); + promise.failure(exception); + return; + } + if (success instanceof Throwable) { + promise.failure((Throwable) success); + } else { + if (success instanceof SubmitFailedReply) { + LOG.error("Transaction was not submitted."); + } + promise.success(null); + } + } + }, actorSystem.dispatcher()); + + return promise.future(); + } + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfReadOnlyTransaction.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfReadOnlyTransaction.java new file mode 100644 index 0000000000..3af25c62b9 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfReadOnlyTransaction.java @@ -0,0 +1,112 @@ +/* + * 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.topology.singleton.impl.tx; + +import akka.actor.ActorSystem; +import akka.dispatch.OnComplete; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.SettableFuture; +import javax.annotation.Nullable; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import scala.concurrent.Future; + +public class NetconfReadOnlyTransaction implements DOMDataReadOnlyTransaction { + + private final NetconfDOMTransaction delegate; + private final ActorSystem actorSystem; + + public NetconfReadOnlyTransaction(final ActorSystem actorSystem, final NetconfDOMTransaction delegate) { + this.delegate = delegate; + this.actorSystem = actorSystem; + } + + @Override + public void close() { + //NOOP + } + + @Override + public CheckedFuture>, ReadFailedException> read(final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + final Future> future = delegate.read(store, path); + final SettableFuture>> settableFuture = SettableFuture.create(); + final CheckedFuture>, ReadFailedException> checkedFuture; + checkedFuture = Futures.makeChecked(settableFuture, new Function() { + @Nullable + @Override + public ReadFailedException apply(Exception cause) { + return new ReadFailedException("Read from transaction failed", cause); + } + }); + future.onComplete(new OnComplete>() { + @Override + public void onComplete(final Throwable throwable, + final Optional normalizedNodeMessage) throws Throwable { + if (throwable == null) { + if (normalizedNodeMessage.isPresent()) { + settableFuture.set(normalizedNodeMessage.transform(new Function>() { + + @Nullable + @Override + public NormalizedNode apply(final NormalizedNodeMessage input) { + return input.getNode(); + } + })); + } else { + settableFuture.set(Optional.absent()); + } + } else { + settableFuture.setException(throwable); + } + } + }, actorSystem.dispatcher()); + return checkedFuture; + } + + @Override + public CheckedFuture exists(final LogicalDatastoreType store, + final YangInstanceIdentifier path) { + final Future existsFuture = delegate.exists(store, path); + final SettableFuture settableFuture = SettableFuture.create(); + final CheckedFuture checkedFuture; + checkedFuture = Futures.makeChecked(settableFuture, new Function() { + @Nullable + @Override + public ReadFailedException apply(Exception cause) { + return new ReadFailedException("Read from transaction failed", cause); + } + }); + existsFuture.onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable throwable, final Boolean result) throws Throwable { + if (throwable == null) { + settableFuture.set(result); + } else { + settableFuture.setException(throwable); + } + } + }, actorSystem.dispatcher()); + return checkedFuture; + } + + @Override + public Object getIdentifier() { + return this; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfWriteOnlyTransaction.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfWriteOnlyTransaction.java new file mode 100644 index 0000000000..bef72a256b --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/tx/NetconfWriteOnlyTransaction.java @@ -0,0 +1,111 @@ +/* + * 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.topology.singleton.impl.tx; + +import akka.actor.ActorSystem; +import akka.dispatch.OnComplete; +import com.google.common.base.Function; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import javax.annotation.Nullable; +import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +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.api.DOMDataWriteTransaction; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import scala.concurrent.Future; + +public class NetconfWriteOnlyTransaction implements DOMDataWriteTransaction { + + private final NetconfDOMTransaction delegate; + private final ActorSystem actorSystem; + + public NetconfWriteOnlyTransaction(final ActorSystem actorSystem, final NetconfDOMTransaction delegate) { + this.delegate = delegate; + this.actorSystem = actorSystem; + } + + @Override + public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, + final NormalizedNode data) { + delegate.put(store, new NormalizedNodeMessage(path, data)); + } + + @Override + public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, + final NormalizedNode data) { + delegate.merge(store, new NormalizedNodeMessage(path, data)); + } + + @Override + public boolean cancel() { + return delegate.cancel(); + } + + @Override + public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + delegate.delete(store, path); + } + + @Override + public CheckedFuture submit() { + final Future submit = delegate.submit(); + final SettableFuture settFuture = SettableFuture.create(); + final CheckedFuture checkedFuture; + checkedFuture = Futures.makeChecked(settFuture, new Function() { + @Nullable + @Override + public TransactionCommitFailedException apply(Exception input) { + return new TransactionCommitFailedException("Transaction commit failed", input); + } + }); + submit.onComplete(new OnComplete() { + @Override + public void onComplete(Throwable throwable, Void object) throws Throwable { + if (throwable == null) { + settFuture.set(object); + } else { + settFuture.setException(throwable); + } + } + }, actorSystem.dispatcher()); + return checkedFuture; + } + + @Override + public ListenableFuture> commit() { + final Future commit = delegate.submit(); + final SettableFuture> settFuture = SettableFuture.create(); + commit.onComplete(new OnComplete() { + @Override + public void onComplete(final Throwable throwable, final Void result) throws Throwable { + if (throwable == null) { + TransactionStatus status = TransactionStatus.SUBMITED; + RpcResult rpcResult = RpcResultBuilder.success(status).build(); + settFuture.set(rpcResult); + } else { + settFuture.setException(throwable); + } + } + }, actorSystem.dispatcher()); + return settFuture; + } + + @Override + public Object getIdentifier() { + return this; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfConnectorDTO.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfConnectorDTO.java new file mode 100644 index 0000000000..06578783be --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfConnectorDTO.java @@ -0,0 +1,48 @@ +/* + * 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.topology.singleton.impl.utils; + +import org.opendaylight.netconf.client.NetconfClientSessionListener; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; + +public class NetconfConnectorDTO implements AutoCloseable { + + private final NetconfDeviceCommunicator communicator; + private final RemoteDeviceHandler facade; + + public NetconfConnectorDTO(final NetconfDeviceCommunicator communicator, + final RemoteDeviceHandler facade) { + this.communicator = communicator; + this.facade = facade; + } + + public NetconfDeviceCommunicator getCommunicator() { + return communicator; + } + + public RemoteDeviceHandler getFacade() { + return facade; + } + + public NetconfClientSessionListener getSessionListener() { + return communicator; + } + + @Override + public void close() throws Exception { + if (communicator != null) { + communicator.close(); + } + if (facade != null) { + facade.close(); + } + } +} \ No newline at end of file diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java new file mode 100644 index 0000000000..d607f33d2b --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java @@ -0,0 +1,254 @@ +/* + * 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.topology.singleton.impl.utils; + +import akka.actor.ActorSystem; +import io.netty.util.concurrent.EventExecutor; +import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; +import org.opendaylight.controller.config.threadpool.ThreadPool; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; +import org.opendaylight.netconf.client.NetconfClientDispatcher; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class NetconfTopologySetup { + + private final ClusterSingletonServiceProvider clusterSingletonServiceProvider; + private final RpcProviderRegistry rpcProviderRegistry; + private final DataBroker dataBroker; + private final InstanceIdentifier instanceIdentifier; + private final Node node; + private final BindingAwareBroker bindingAwareBroker; + private final ScheduledThreadPool keepaliveExecutor; + private final ThreadPool processingExecutor; + private final Broker domBroker; + private final ActorSystem actorSystem; + private final EventExecutor eventExecutor; + private final NetconfClientDispatcher netconfClientDispatcher; + private final String topologyId; + private NetconfTopologySetup(final NetconfTopologySetupBuilder builder) { + this.clusterSingletonServiceProvider = builder.getClusterSingletonServiceProvider(); + this.rpcProviderRegistry = builder.getRpcProviderRegistry(); + this.dataBroker = builder.getDataBroker(); + this.instanceIdentifier = builder.getInstanceIdentifier(); + this.node = builder.getNode(); + this.bindingAwareBroker = builder.getBindingAwareBroker(); + this.keepaliveExecutor = builder.getKeepaliveExecutor(); + this.processingExecutor = builder.getProcessingExecutor(); + this.domBroker = builder.getDomBroker(); + this.actorSystem = builder.getActorSystem(); + this.eventExecutor = builder.getEventExecutor(); + this.netconfClientDispatcher = builder.getNetconfClientDispatcher(); + this.topologyId = builder.getTopologyId(); + } + + public ClusterSingletonServiceProvider getClusterSingletonServiceProvider() { + return clusterSingletonServiceProvider; + } + + public RpcProviderRegistry getRpcProviderRegistry() { + return rpcProviderRegistry; + } + + public DataBroker getDataBroker() { + return dataBroker; + } + + public InstanceIdentifier getInstanceIdentifier() { + return instanceIdentifier; + } + + public Node getNode() { + return node; + } + + public BindingAwareBroker getBindingAwareBroker() { + return bindingAwareBroker; + } + + public ThreadPool getProcessingExecutor() { + return processingExecutor; + } + + public ScheduledThreadPool getKeepaliveExecutor() { + return keepaliveExecutor; + } + + public Broker getDomBroker() { + return domBroker; + } + + public ActorSystem getActorSystem() { + return actorSystem; + } + + public EventExecutor getEventExecutor() { + return eventExecutor; + } + + public String getTopologyId() { + return topologyId; + } + + public NetconfClientDispatcher getNetconfClientDispatcher() { + return netconfClientDispatcher; + } + + public static class NetconfTopologySetupBuilder { + + private ClusterSingletonServiceProvider clusterSingletonServiceProvider; + private RpcProviderRegistry rpcProviderRegistry; + private DataBroker dataBroker; + private InstanceIdentifier instanceIdentifier; + private Node node; + private BindingAwareBroker bindingAwareBroker; + private ScheduledThreadPool keepaliveExecutor; + private ThreadPool processingExecutor; + private Broker domBroker; + private ActorSystem actorSystem; + private EventExecutor eventExecutor; + private String topologyId; + private NetconfClientDispatcher netconfClientDispatcher; + + public NetconfTopologySetupBuilder(){ + } + + private ClusterSingletonServiceProvider getClusterSingletonServiceProvider() { + return clusterSingletonServiceProvider; + } + + public NetconfTopologySetupBuilder setClusterSingletonServiceProvider( + final ClusterSingletonServiceProvider clusterSingletonServiceProvider) { + this.clusterSingletonServiceProvider = clusterSingletonServiceProvider; + return this; + } + + private RpcProviderRegistry getRpcProviderRegistry() { + return rpcProviderRegistry; + } + + public NetconfTopologySetupBuilder setRpcProviderRegistry(final RpcProviderRegistry rpcProviderRegistry) { + this.rpcProviderRegistry = rpcProviderRegistry; + return this; + } + + private DataBroker getDataBroker() { + return dataBroker; + } + + public NetconfTopologySetupBuilder setDataBroker(final DataBroker dataBroker) { + this.dataBroker = dataBroker; + return this; + } + + private InstanceIdentifier getInstanceIdentifier() { + return instanceIdentifier; + } + + public NetconfTopologySetupBuilder setInstanceIdentifier(final InstanceIdentifier instanceIdentifier) { + this.instanceIdentifier = instanceIdentifier; + return this; + } + + public Node getNode() { + return node; + } + + public NetconfTopologySetupBuilder setNode(final Node node) { + this.node = node; + return this; + } + + public NetconfTopologySetup build() { + return new NetconfTopologySetup(this); + } + + private BindingAwareBroker getBindingAwareBroker() { + return bindingAwareBroker; + } + + public NetconfTopologySetupBuilder setBindingAwareBroker(BindingAwareBroker bindingAwareBroker) { + this.bindingAwareBroker = bindingAwareBroker; + return this; + } + + private ScheduledThreadPool getKeepaliveExecutor() { + return keepaliveExecutor; + } + + public NetconfTopologySetupBuilder setKeepaliveExecutor(ScheduledThreadPool keepaliveExecutor) { + this.keepaliveExecutor = keepaliveExecutor; + return this; + } + + private ThreadPool getProcessingExecutor() { + return processingExecutor; + } + + public NetconfTopologySetupBuilder setProcessingExecutor(ThreadPool processingExecutor) { + this.processingExecutor = processingExecutor; + return this; + } + + private Broker getDomBroker() { + return domBroker; + } + + public NetconfTopologySetupBuilder setDomBroker(Broker domBroker) { + this.domBroker = domBroker; + return this; + } + + private ActorSystem getActorSystem() { + return actorSystem; + } + + public NetconfTopologySetupBuilder setActorSystem(ActorSystem actorSystem) { + this.actorSystem = actorSystem; + return this; + } + + private EventExecutor getEventExecutor() { + return eventExecutor; + } + + public NetconfTopologySetupBuilder setEventExecutor(EventExecutor eventExecutor) { + this.eventExecutor = eventExecutor; + return this; + } + + private String getTopologyId() { + return topologyId; + } + + public NetconfTopologySetupBuilder setTopologyId(String topologyId) { + this.topologyId = topologyId; + return this; + } + + private NetconfClientDispatcher getNetconfClientDispatcher() { + return netconfClientDispatcher; + } + + public NetconfTopologySetupBuilder setNetconfClientDispatcher(NetconfClientDispatcher clientDispatcher) { + this.netconfClientDispatcher = clientDispatcher; + return this; + } + + public static NetconfTopologySetupBuilder create() { + return new NetconfTopologySetupBuilder(); + } + } + + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java new file mode 100644 index 0000000000..5114faecd2 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtils.java @@ -0,0 +1,117 @@ +/* + * 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.topology.singleton.impl.utils; + +import akka.cluster.Member; +import akka.util.Timeout; +import java.io.File; +import java.math.BigDecimal; +import java.net.InetSocketAddress; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.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.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.NodeKey; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache; +import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository; +import scala.concurrent.duration.Duration; + +public class NetconfTopologyUtils { + + private static final String DEFAULT_SCHEMA_REPOSITORY_NAME = "sal-netconf-connector"; + + public static final Timeout TIMEOUT = new Timeout(Duration.create(10, "seconds")); + + public static final long DEFAULT_REQUEST_TIMEOUT_MILLIS = 60000L; + public static final int DEFAULT_KEEPALIVE_DELAY = 0; + public static final boolean DEFAULT_RECONNECT_ON_CHANGED_SCHEMA = false; + public static final int DEFAULT_CONCURRENT_RPC_LIMIT = 0; + public static final int DEFAULT_MAX_CONNECTION_ATTEMPTS = 0; + public static final int DEFAULT_BETWEEN_ATTEMPTS_TIMEOUT_MILLIS = 2000; + public static final long DEFAULT_CONNECTION_TIMEOUT_MILLIS = 20000L; + public static final BigDecimal DEFAULT_SLEEP_FACTOR = new BigDecimal(1.5); + + + // The default cache directory relative to CACHE_DIRECTORY + + public static final String DEFAULT_CACHE_DIRECTORY = "schema"; + + // Filesystem based caches are stored relative to the cache directory. + public static final String CACHE_DIRECTORY = "cache"; + + // The qualified schema cache directory cache/schema + public static final String QUALIFIED_DEFAULT_CACHE_DIRECTORY = + CACHE_DIRECTORY + File.separator + DEFAULT_CACHE_DIRECTORY; + + // The default schema repository in the case that one is not specified. + public static final SharedSchemaRepository DEFAULT_SCHEMA_REPOSITORY = + new SharedSchemaRepository(DEFAULT_SCHEMA_REPOSITORY_NAME); + + + // The default FilesystemSchemaSourceCache, which stores cached files in cache/schema. + public static final FilesystemSchemaSourceCache DEFAULT_CACHE = + new FilesystemSchemaSourceCache<>(DEFAULT_SCHEMA_REPOSITORY, YangTextSchemaSource.class, + new File(QUALIFIED_DEFAULT_CACHE_DIRECTORY)); + + // The default factory for creating SchemaContext instances. + public static final SchemaContextFactory DEFAULT_SCHEMA_CONTEXT_FACTORY = + DEFAULT_SCHEMA_REPOSITORY.createSchemaContextFactory(SchemaSourceFilter.ALWAYS_ACCEPT); + + public static RemoteDeviceId createRemoteDeviceId(final NodeId nodeId, final NetconfNode node) { + IpAddress ipAddress = node.getHost().getIpAddress(); + InetSocketAddress address = new InetSocketAddress(ipAddress.getIpv4Address() != null + ? ipAddress.getIpv4Address().getValue() : ipAddress.getIpv6Address().getValue(), + node.getPort().getValue()); + return new RemoteDeviceId(nodeId.getValue(), address); + } + + public static String createActorPath(String masterMember, String name) { + return masterMember + "/user/" + name; + } + + public static String createMasterActorName(String name, String masterAddress) { + return masterAddress.replaceAll("//", "") + "_" + name; + } + + public static NodeId getNodeId(final InstanceIdentifier.PathArgument pathArgument) { + if (pathArgument instanceof InstanceIdentifier.IdentifiableItem) { + + final Identifier key = ((InstanceIdentifier.IdentifiableItem) pathArgument).getKey(); + if (key instanceof NodeKey) { + return ((NodeKey) key).getNodeId(); + } + } + throw new IllegalStateException("Unable to create NodeId from: " + pathArgument); + } + + public static KeyedInstanceIdentifier createTopologyListPath(final String topologyId) { + final InstanceIdentifier networkTopology = InstanceIdentifier.create(NetworkTopology.class); + return networkTopology.child(Topology.class, new TopologyKey(new TopologyId(topologyId))); + } + + public static InstanceIdentifier createTopologyNodeListPath(final NodeKey key, final String topologyId) { + return createTopologyListPath(topologyId) + .child(Node.class, new NodeKey(new NodeId(key.getNodeId().getValue()))); + } + + public static InstanceIdentifier createTopologyNodePath(final String topologyId) { + return createTopologyListPath(topologyId).child(Node.class); + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/AskForMasterMountPoint.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/AskForMasterMountPoint.java new file mode 100644 index 0000000000..9fb3ac2785 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/AskForMasterMountPoint.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.topology.singleton.messages; + +import java.io.Serializable; + +/** + * After master is connected, slaves send the message to master and master triggers registering slave mount point + * with reply 'RegisterMountPoint' which includes needed parameters. + */ +public class AskForMasterMountPoint implements Serializable { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/CreateInitialMasterActorData.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/CreateInitialMasterActorData.java new file mode 100644 index 0000000000..2117f699df --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/CreateInitialMasterActorData.java @@ -0,0 +1,37 @@ +/* + * 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.topology.singleton.messages; + +import java.io.Serializable; +import java.util.List; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; + +/** + * Master sends this message to the own actor to set necessary parameters. + */ +public class CreateInitialMasterActorData implements Serializable { + + private final DOMDataBroker deviceDataBroker; + private final List allSourceIdentifiers; + + public CreateInitialMasterActorData(final DOMDataBroker deviceDataBroker, + final List allSourceIdentifiers) { + this.deviceDataBroker = deviceDataBroker; + this.allSourceIdentifiers = allSourceIdentifiers; + } + + public DOMDataBroker getDeviceDataBroker() { + return deviceDataBroker; + } + + public List getSourceIndentifiers() { + return allSourceIdentifiers; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/MasterActorDataInitialized.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/MasterActorDataInitialized.java new file mode 100644 index 0000000000..51c698be43 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/MasterActorDataInitialized.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.topology.singleton.messages; + +import java.io.Serializable; + +/** + * Due to possibility of race condition (when data-store is updated before data are initialized in master actor), only + * when this message is received by master, operational data-store is changed. + */ +public class MasterActorDataInitialized implements Serializable { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/NormalizedNodeMessage.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/NormalizedNodeMessage.java new file mode 100644 index 0000000000..48a86749f0 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/NormalizedNodeMessage.java @@ -0,0 +1,67 @@ +/* + * 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.topology.singleton.messages; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeDataInput; +import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeDataOutput; +import org.opendaylight.controller.cluster.datastore.node.utils.stream.NormalizedNodeInputOutput; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; + +/** + * Message which holds node data, prepared to sending between remote hosts with serialization. + */ +public class NormalizedNodeMessage implements Externalizable { + private static final long serialVersionUID = 1L; + + private YangInstanceIdentifier identifier = null; + private NormalizedNode node = null; + + public NormalizedNodeMessage() { + // empty constructor needed for Externalizable + } + + public NormalizedNodeMessage(final YangInstanceIdentifier identifier, final NormalizedNode node) { + this.identifier = identifier; + this.node = node; + } + + public YangInstanceIdentifier getIdentifier() { + return identifier; + } + + public NormalizedNode getNode() { + return node; + } + + @Override + public void writeExternal(final ObjectOutput out) throws IOException { + final NormalizedNodeDataOutput dataOutput = NormalizedNodeInputOutput.newDataOutput(out); + final NormalizedNodeWriter normalizedNodeWriter = + NormalizedNodeWriter.forStreamWriter((NormalizedNodeStreamWriter) dataOutput); + + dataOutput.writeYangInstanceIdentifier(identifier); + + normalizedNodeWriter.write(node); + } + + @Override + public void readExternal(final ObjectInput in) throws IOException, ClassNotFoundException { + final NormalizedNodeDataInput dataInput = NormalizedNodeInputOutput.newDataInput(in); + + identifier = dataInput.readYangInstanceIdentifier(); + node = dataInput.readNormalizedNode(); + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RefreshSetupMasterActorData.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RefreshSetupMasterActorData.java new file mode 100644 index 0000000000..f75b034032 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RefreshSetupMasterActorData.java @@ -0,0 +1,39 @@ +/* + * 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.topology.singleton.messages; + +import java.io.Serializable; +import java.util.List; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; + +/** + * Master sends this message to the own actor to refresh setup data + */ +public class RefreshSetupMasterActorData implements Serializable { + + private final NetconfTopologySetup netconfTopologyDeviceSetup; + private final RemoteDeviceId remoteDeviceId; + + public RefreshSetupMasterActorData(final NetconfTopologySetup netconfTopologyDeviceSetup, + final RemoteDeviceId remoteDeviceId) { + this.netconfTopologyDeviceSetup = netconfTopologyDeviceSetup; + this.remoteDeviceId = remoteDeviceId; + } + + public NetconfTopologySetup getNetconfTopologyDeviceSetup() { + return netconfTopologyDeviceSetup; + } + + public RemoteDeviceId getRemoteDeviceId() { + return remoteDeviceId; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RegisterMountPoint.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RegisterMountPoint.java new file mode 100644 index 0000000000..26c634859d --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/RegisterMountPoint.java @@ -0,0 +1,30 @@ +/* + * 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.topology.singleton.messages; + +import java.io.Serializable; +import java.util.List; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; + +/** + * Master sends the message to slave with necessary parameters for creating slave mount point. + */ +public class RegisterMountPoint implements Serializable { + + private final List allSourceIdentifiers; + + public RegisterMountPoint(final List allSourceIdentifiers) { + this.allSourceIdentifiers = allSourceIdentifiers; + } + + public List getSourceIndentifiers() { + return allSourceIdentifiers; + } + +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/SubmitFailedReply.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/SubmitFailedReply.java new file mode 100644 index 0000000000..0aa4f7f501 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/SubmitFailedReply.java @@ -0,0 +1,17 @@ +/* + * 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.topology.singleton.messages; + +import java.io.Serializable; + +/** + * Message sent from master back to the slave when submit is not performed, tx is closed + */ +public class SubmitFailedReply implements Serializable { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/UnregisterSlaveMountPoint.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/UnregisterSlaveMountPoint.java new file mode 100644 index 0000000000..7c05b6ae75 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/UnregisterSlaveMountPoint.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.topology.singleton.messages; + +import java.io.Serializable; + +/** + * Slave sends the message when unregisters slave mount point (in NetconfNodeManager + * close method). Message must be sended before slave actor is poisoned. + */ +public class UnregisterSlaveMountPoint implements Serializable { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/YangTextSchemaSourceRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/YangTextSchemaSourceRequest.java new file mode 100644 index 0000000000..f8dab7320f --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/YangTextSchemaSourceRequest.java @@ -0,0 +1,29 @@ +/* + * 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.topology.singleton.messages; + +import java.io.Serializable; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; + +/** + * Slave sends message to master when tries to resolve schema with particular sourceIdentifier (proxy call). + * Master responds with resolved schema source. + */ +public class YangTextSchemaSourceRequest implements Serializable { + + private final SourceIdentifier sourceIdentifier; + + public YangTextSchemaSourceRequest(final SourceIdentifier sourceIdentifier) { + this.sourceIdentifier = sourceIdentifier; + } + + public SourceIdentifier getSourceIdentifier() { + return sourceIdentifier; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/CancelRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/CancelRequest.java new file mode 100644 index 0000000000..902dfdca73 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/CancelRequest.java @@ -0,0 +1,12 @@ +/* + * 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.topology.singleton.messages.transactions; + +public class CancelRequest implements TransactionRequest { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/DeleteRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/DeleteRequest.java new file mode 100644 index 0000000000..03b3db0c26 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/DeleteRequest.java @@ -0,0 +1,31 @@ +/* + * 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.topology.singleton.messages.transactions; + +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +public class DeleteRequest implements TransactionRequest { + + private final LogicalDatastoreType store; + private final YangInstanceIdentifier path; + + public DeleteRequest(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + this.store = store; + this.path = path; + } + + public LogicalDatastoreType getStore() { + return store; + } + + public YangInstanceIdentifier getPath() { + return path; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/EmptyReadResponse.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/EmptyReadResponse.java new file mode 100644 index 0000000000..6eaea938d2 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/EmptyReadResponse.java @@ -0,0 +1,17 @@ +/* + * 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.topology.singleton.messages.transactions; + +import java.io.Serializable; + +/** + * Message is sended when read result do not present any value. + */ +public class EmptyReadResponse implements Serializable { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ExistsRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ExistsRequest.java new file mode 100644 index 0000000000..92266c1773 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ExistsRequest.java @@ -0,0 +1,31 @@ +/* + * 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.topology.singleton.messages.transactions; + +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +public class ExistsRequest implements TransactionRequest { + + private final LogicalDatastoreType store; + private final YangInstanceIdentifier path; + + public ExistsRequest(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + this.store = store; + this.path = path; + } + + public LogicalDatastoreType getStore() { + return store; + } + + public YangInstanceIdentifier getPath() { + return path; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/MergeRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/MergeRequest.java new file mode 100644 index 0000000000..21766264b7 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/MergeRequest.java @@ -0,0 +1,31 @@ +/* + * 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.topology.singleton.messages.transactions; + +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; + +public class MergeRequest implements TransactionRequest { + + private final NormalizedNodeMessage data; + private final LogicalDatastoreType store; + + public MergeRequest(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + this.store = store; + this.data = data; + } + + public NormalizedNodeMessage getNormalizedNodeMessage() { + return data; + } + + public LogicalDatastoreType getStore() { + return store; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/PutRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/PutRequest.java new file mode 100644 index 0000000000..61294d5ff9 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/PutRequest.java @@ -0,0 +1,31 @@ +/* + * 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.topology.singleton.messages.transactions; + +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.netconf.topology.singleton.messages.NormalizedNodeMessage; + +public class PutRequest implements TransactionRequest { + + private final LogicalDatastoreType store; + private final NormalizedNodeMessage data; + + public PutRequest(final LogicalDatastoreType store, final NormalizedNodeMessage data) { + this.store = store; + this.data = data; + } + + public NormalizedNodeMessage getNormalizedNodeMessage() { + return data; + } + + public LogicalDatastoreType getStore() { + return store; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ReadRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ReadRequest.java new file mode 100644 index 0000000000..49c6a40faf --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/ReadRequest.java @@ -0,0 +1,31 @@ +/* + * 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.topology.singleton.messages.transactions; + +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +public class ReadRequest implements TransactionRequest { + + private final LogicalDatastoreType store; + private final YangInstanceIdentifier path; + + public ReadRequest(final LogicalDatastoreType store, final YangInstanceIdentifier path) { + this.store = store; + this.path = path; + } + + public LogicalDatastoreType getStore() { + return store; + } + + public YangInstanceIdentifier getPath() { + return path; + } +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitReply.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitReply.java new file mode 100644 index 0000000000..46e6ee33b7 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitReply.java @@ -0,0 +1,17 @@ +/* + * 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.topology.singleton.messages.transactions; + +import java.io.Serializable; + +/** + * Message sent from master back to the slave when submit is successfully performed. + */ +public class SubmitReply implements Serializable { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitRequest.java new file mode 100644 index 0000000000..d764aa43bd --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/SubmitRequest.java @@ -0,0 +1,12 @@ +/* + * 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.topology.singleton.messages.transactions; + +public class SubmitRequest implements TransactionRequest { +} diff --git a/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/TransactionRequest.java b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/TransactionRequest.java new file mode 100644 index 0000000000..b5ef9f1863 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/messages/transactions/TransactionRequest.java @@ -0,0 +1,19 @@ +/* + * 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.topology.singleton.messages.transactions; + +import java.io.Serializable; + +/** + * API for transaction request messages, slave sends these message types to master for performing required operation. + * This interface helps better handle request messages in actor. All messages are send with operations defined in + * NetconfProxyDOMTransaction. Messages requiring response are send by ask otherwise with tell. + */ +public interface TransactionRequest extends Serializable { +} diff --git a/netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml b/netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml new file mode 100644 index 0000000000..64471a1d9c --- /dev/null +++ b/netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java new file mode 100644 index 0000000000..a77104e28d --- /dev/null +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfNodeActorTest.java @@ -0,0 +1,221 @@ +/* + * 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.topology.singleton.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; + +import akka.actor.ActorContext; +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.pattern.Patterns; +import akka.testkit.JavaTestKit; +import akka.testkit.TestActorRef; +import akka.util.Timeout; +import com.google.common.base.MoreObjects; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.List; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.opendaylight.controller.cluster.schema.provider.impl.YangTextSchemaSourceSerializationProxy; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.messages.AskForMasterMountPoint; +import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData; +import org.opendaylight.netconf.topology.singleton.messages.MasterActorDataInitialized; +import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData; +import org.opendaylight.netconf.topology.singleton.messages.RegisterMountPoint; +import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository; +import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import scala.concurrent.Await; +import scala.concurrent.Future; +import scala.concurrent.duration.Duration; + +public class NetconfNodeActorTest { + + private static final Timeout TIMEOUT = new Timeout(Duration.create(5, "seconds")); + private static ActorSystem system; + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + private ActorRef masterRef; + private RemoteDeviceId remoteDeviceId; + + @Before + public void setup() throws UnknownHostException { + + remoteDeviceId = new RemoteDeviceId("netconf-topology", + new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9999)); + + final NetconfTopologySetup setup = mock(NetconfTopologySetup.class); + + final Props props = NetconfNodeActor.props(setup, remoteDeviceId, DEFAULT_SCHEMA_REPOSITORY, + DEFAULT_SCHEMA_REPOSITORY); + + system = ActorSystem.create(); + + masterRef = TestActorRef.create(system, props, "master_messages"); + } + + @After + public void teardown() { + JavaTestKit.shutdownActorSystem(system); + system = null; + } + + @Test + public void testInitDataMessages() throws Exception { + + final DOMDataBroker domDataBroker = mock(DOMDataBroker.class); + final List sourceIdentifiers = Lists.newArrayList(); + + /* Test init master data */ + + final Future initialDataToActor = + Patterns.ask(masterRef, new CreateInitialMasterActorData(domDataBroker, sourceIdentifiers), + TIMEOUT); + + final Object success = Await.result(initialDataToActor, TIMEOUT.duration()); + assertTrue(success instanceof MasterActorDataInitialized); + + + /* Test refresh master data */ + + final RemoteDeviceId remoteDeviceId2 = new RemoteDeviceId("netconf-topology2", + new InetSocketAddress(InetAddress.getByName("127.0.0.2"), 9999)); + + final NetconfTopologySetup setup2 = mock(NetconfTopologySetup.class); + + final Future refreshDataToActor = + Patterns.ask(masterRef, new RefreshSetupMasterActorData(setup2, remoteDeviceId2), + TIMEOUT); + + final Object success2 = Await.result(refreshDataToActor, TIMEOUT.duration()); + assertTrue(success2 instanceof MasterActorDataInitialized); + + } + + @Test + public void testRegisterMountPointMessage() throws Exception { + + final DOMDataBroker domDataBroker = mock(DOMDataBroker.class); + final List sourceIdentifiers = + Lists.newArrayList(SourceIdentifier.create("testID", Optional.absent())); + + // init master data + + final Future initialDataToActor = + Patterns.ask(masterRef, new CreateInitialMasterActorData(domDataBroker, sourceIdentifiers), + TIMEOUT); + + final Object successInit = Await.result(initialDataToActor, TIMEOUT.duration()); + + assertTrue(successInit instanceof MasterActorDataInitialized); + + // test if slave get right identifiers from master + + final Future registerMountPointFuture = + Patterns.ask(masterRef, new AskForMasterMountPoint(), + TIMEOUT); + + final RegisterMountPoint success = + (RegisterMountPoint) Await.result(registerMountPointFuture, TIMEOUT.duration()); + + assertEquals(sourceIdentifiers, success.getSourceIndentifiers()); + + } + + @Test + public void testYangTextSchemaSourceRequestMessage() throws Exception { + final SchemaRepository schemaRepository = mock(SchemaRepository.class); + final SourceIdentifier sourceIdentifier = SourceIdentifier.create("testID", Optional.absent()); + final Props props = NetconfNodeActor.props(mock(NetconfTopologySetup.class), remoteDeviceId, + DEFAULT_SCHEMA_REPOSITORY, schemaRepository); + + final ActorRef actorRefSchemaRepo = TestActorRef.create(system, props, "master_mocked_schema_repository"); + final ActorContext actorContext = mock(ActorContext.class); + doReturn(system.dispatcher()).when(actorContext).dispatcher(); + + final ProxyYangTextSourceProvider proxyYang = + new ProxyYangTextSourceProvider(actorRefSchemaRepo, actorContext); + // test if asking for source is resolved and sended back + + final YangTextSchemaSource yangTextSchemaSource = new YangTextSchemaSource(sourceIdentifier) { + @Override + protected MoreObjects.ToStringHelper addToStringAttributes(MoreObjects.ToStringHelper toStringHelper) { + return null; + } + + @Override + public InputStream openStream() throws IOException { + return new ByteArrayInputStream("YANG".getBytes()); + } + }; + + + final CheckedFuture result = + Futures.immediateCheckedFuture(yangTextSchemaSource); + + doReturn(result).when(schemaRepository).getSchemaSource(sourceIdentifier, YangTextSchemaSource.class); + + final Future resolvedSchema = + proxyYang.getYangTextSchemaSource(sourceIdentifier); + + final YangTextSchemaSourceSerializationProxy success = Await.result(resolvedSchema, TIMEOUT.duration()); + + assertEquals(sourceIdentifier, success.getRepresentation().getIdentifier()); + assertEquals("YANG", convertStreamToString(success.getRepresentation().openStream())); + + + // test if asking for source is missing + exception.expect(MissingSchemaSourceException.class); + + final SchemaSourceException schemaSourceException = + new MissingSchemaSourceException("Fail", sourceIdentifier); + + final CheckedFuture resultFail = + Futures.immediateFailedCheckedFuture(schemaSourceException); + + doReturn(resultFail).when(schemaRepository).getSchemaSource(sourceIdentifier, YangTextSchemaSource.class); + + final Future failedSchema = + proxyYang.getYangTextSchemaSource(sourceIdentifier); + + Await.result(failedSchema, TIMEOUT.duration()); + + } + + private String convertStreamToString(java.io.InputStream is) { + java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A"); + return s.hasNext() ? s.next() : ""; + } + +} diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java new file mode 100644 index 0000000000..9a3749c14e --- /dev/null +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java @@ -0,0 +1,271 @@ +/* + * 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.topology.singleton.impl; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.DELETE; +import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.WRITE; + +import com.google.common.util.concurrent.Futures; +import io.netty.util.concurrent.EventExecutor; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import javax.annotation.Nonnull; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.cluster.ActorSystemProvider; +import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; +import org.opendaylight.controller.config.threadpool.ThreadPool; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; +import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; +import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; +import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration; +import org.opendaylight.netconf.client.NetconfClientDispatcher; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; +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.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +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.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +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.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class NetconfTopologyManagerTest { + + private final String topologyId = "topologyID"; + private NetconfTopologyManager netconfTopologyManager; + + @Mock + private DataBroker dataBroker; + + @Mock + private ClusterSingletonServiceProvider clusterSingletonServiceProvider; + + @Before + public void setUp() { + initMocks(this); + + final RpcProviderRegistry rpcProviderRegistry = mock(RpcProviderRegistry.class); + final BindingAwareBroker bindingAwareBroker = mock(BindingAwareBroker.class); + final ScheduledThreadPool keepaliveExecutor = mock(ScheduledThreadPool.class); + final ThreadPool processingExecutor = mock(ThreadPool.class); + final Broker domBroker = mock(Broker.class); + final ActorSystemProvider actorSystemProvider = mock(ActorSystemProvider.class); + final EventExecutor eventExecutor = mock(EventExecutor.class); + final NetconfClientDispatcher clientDispatcher = mock(NetconfClientDispatcher.class); + + netconfTopologyManager = new NetconfTopologyManager(dataBroker, rpcProviderRegistry, + clusterSingletonServiceProvider, bindingAwareBroker, keepaliveExecutor, processingExecutor, domBroker, + actorSystemProvider, eventExecutor, clientDispatcher, topologyId); + } + @Test + public void testWriteConfiguration() throws Exception { + + final ClusterSingletonServiceRegistration clusterRegistration = mock(ClusterSingletonServiceRegistration.class); + + final Field fieldContexts = NetconfTopologyManager.class.getDeclaredField("contexts"); + fieldContexts.setAccessible(true); + @SuppressWarnings("unchecked") + final Map, NetconfTopologyContext> contexts = + (Map, NetconfTopologyContext>) fieldContexts.get(netconfTopologyManager); + + final Field fieldClusterRegistrations = NetconfTopologyManager.class.getDeclaredField("clusterRegistrations"); + fieldClusterRegistrations.setAccessible(true); + @SuppressWarnings("unchecked") + final Map, ClusterSingletonServiceRegistration> clusterRegistrations = + (Map, ClusterSingletonServiceRegistration>) + fieldClusterRegistrations.get(netconfTopologyManager); + + final Collection> changes = new ArrayList<>(); + + final InstanceIdentifier instanceIdentifier = NetconfTopologyUtils.createTopologyNodeListPath( + new NodeKey(new NodeId("node-id-1")),"topology-1"); + + final InstanceIdentifier instanceIdentifierDiferent = NetconfTopologyUtils.createTopologyNodeListPath( + new NodeKey(new NodeId("node-id-2")),"topology-2"); + + final DataTreeIdentifier rootIdentifier = + new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, instanceIdentifier); + + final DataTreeIdentifier rootIdentifierDifferent = + new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, instanceIdentifierDiferent); + + @SuppressWarnings("unchecked") + final DataObjectModification objectModification = mock(DataObjectModification.class); + + final NetconfNode netconfNode = new NetconfNodeBuilder() + .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) + .setPort(new PortNumber(9999)) + .setReconnectOnChangedSchema(true) + .setDefaultRequestTimeoutMillis(1000L) + .setBetweenAttemptsTimeoutMillis(100) + .setSchemaless(false) + .setTcpOnly(false) + .build(); + final Node node = new NodeBuilder().setNodeId(new NodeId("node-id")) + .addAugmentation(NetconfNode.class, netconfNode).build(); + + final Identifier key = new NodeKey(new NodeId("node-id")); + + @SuppressWarnings("unchecked") + final InstanceIdentifier.IdentifiableItem pathArgument = + new InstanceIdentifier.IdentifiableItem(Node.class, key); + + + // testing WRITE on two identical rootIdentifiers and one different + + changes.add(new CustomTreeModification(rootIdentifier, objectModification)); + changes.add(new CustomTreeModification(rootIdentifier, objectModification)); + changes.add(new CustomTreeModification(rootIdentifierDifferent, objectModification)); + + doReturn(WRITE).when(objectModification).getModificationType(); + doReturn(node).when(objectModification).getDataAfter(); + doReturn(pathArgument).when(objectModification).getIdentifier(); + doReturn(clusterRegistration).when(clusterSingletonServiceProvider).registerClusterSingletonService(any()); + + netconfTopologyManager.onDataTreeChanged(changes); + + verify(clusterSingletonServiceProvider, times(2)).registerClusterSingletonService(any()); + + // only two created contexts + assertEquals(2, contexts.size()); + assertTrue(contexts.containsKey(rootIdentifier.getRootIdentifier())); + assertTrue(contexts.containsKey(rootIdentifierDifferent.getRootIdentifier())); + + // only two created cluster registrations + assertEquals(2, contexts.size()); + assertTrue(clusterRegistrations.containsKey(rootIdentifier.getRootIdentifier())); + assertTrue(clusterRegistrations.containsKey(rootIdentifierDifferent.getRootIdentifier())); + + // after delete there should be no context and clustered registrations + doReturn(DELETE).when(objectModification).getModificationType(); + + doNothing().when(clusterRegistration).close(); + + netconfTopologyManager.onDataTreeChanged(changes); + + verify(clusterRegistration, times(2)).close(); + + // empty map of contexts + assertTrue(contexts.isEmpty()); + assertFalse(contexts.containsKey(rootIdentifier.getRootIdentifier())); + assertFalse(contexts.containsKey(rootIdentifierDifferent.getRootIdentifier())); + + // empty map of clustered registrations + assertTrue(clusterRegistrations.isEmpty()); + assertFalse(clusterRegistrations.containsKey(rootIdentifier.getRootIdentifier())); + assertFalse(clusterRegistrations.containsKey(rootIdentifierDifferent.getRootIdentifier())); + + } + + @Test + public void testRegisterDataTreeChangeListener() { + + final WriteTransaction wtx = mock(WriteTransaction.class); + + doReturn(wtx).when(dataBroker).newWriteOnlyTransaction(); + doNothing().when(wtx).merge(any(), any(), any()); + doReturn(Futures.immediateCheckedFuture(null)).when(wtx).submit(); + doReturn(null).when(dataBroker).registerDataChangeListener(any(), any(), any(), any()); + + netconfTopologyManager.init(); + + // verify if listener is called with right parameters = registered on right path + + verify(dataBroker, times(1)).registerDataTreeChangeListener( + new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, NetconfTopologyUtils + .createTopologyListPath(topologyId).child(Node.class)), netconfTopologyManager); + + } + + @Test + public void testClose() throws Exception { + + final Field fieldContexts = NetconfTopologyManager.class.getDeclaredField("contexts"); + fieldContexts.setAccessible(true); + @SuppressWarnings("unchecked") + final Map, NetconfTopologyContext> contexts = + (Map, NetconfTopologyContext>) fieldContexts.get(netconfTopologyManager); + + final Field fieldClusterRegistrations = NetconfTopologyManager.class.getDeclaredField("clusterRegistrations"); + fieldClusterRegistrations.setAccessible(true); + @SuppressWarnings("unchecked") + final Map, ClusterSingletonServiceRegistration> clusterRegistrations = + (Map, ClusterSingletonServiceRegistration>) + fieldClusterRegistrations.get(netconfTopologyManager); + + final InstanceIdentifier instanceIdentifier = NetconfTopologyUtils.createTopologyNodeListPath( + new NodeKey(new NodeId("node-id-1")),"topology-1"); + + + final NetconfTopologyContext context = mock(NetconfTopologyContext.class); + final ClusterSingletonServiceRegistration clusterRegistration = + mock(ClusterSingletonServiceRegistration.class); + contexts.put(instanceIdentifier, context); + clusterRegistrations.put(instanceIdentifier, clusterRegistration); + + doNothing().when(context).closeFinal(); + doNothing().when(clusterRegistration).close(); + + netconfTopologyManager.close(); + verify(context, times(1)).closeFinal(); + verify(clusterRegistration, times(1)).close(); + + assertTrue(contexts.isEmpty()); + assertTrue(clusterRegistrations.isEmpty()); + + } + + private class CustomTreeModification implements DataTreeModification { + + private final DataTreeIdentifier rootPath; + private final DataObjectModification rootNode; + + CustomTreeModification(DataTreeIdentifier rootPath, DataObjectModification rootNode) { + this.rootPath = rootPath; + this.rootNode = rootNode; + } + + @Nonnull + @Override + public DataTreeIdentifier getRootPath() { + return rootPath; + } + + @Nonnull + @Override + public DataObjectModification getRootNode() { + return rootNode; + } + } +} diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java new file mode 100644 index 0000000000..c1515abffb --- /dev/null +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/RemoteDeviceConnectorImplTest.java @@ -0,0 +1,271 @@ +/* + * 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.topology.singleton.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import io.netty.util.concurrent.EventExecutor; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.concurrent.ExecutorService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; +import org.opendaylight.controller.config.threadpool.ThreadPool; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; +import org.opendaylight.netconf.client.NetconfClientDispatcher; +import org.opendaylight.netconf.client.NetconfClientSessionListener; +import org.opendaylight.netconf.client.conf.NetconfClientConfiguration; +import org.opendaylight.netconf.client.conf.NetconfReconnectingClientConfiguration; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfConnectorDTO; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +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.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +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.netconf.node.credentials.Credentials; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.credentials.credentials.LoginPasswordBuilder; +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.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; + +public class RemoteDeviceConnectorImplTest { + + private static final NodeId NODE_ID = new NodeId("testing-node"); + private static final String TOPOLOGY_ID = "testing-topology"; + + @Mock + private DataBroker dataBroker; + + @Mock + private RpcProviderRegistry rpcProviderRegistry; + + @Mock + private ClusterSingletonServiceProvider clusterSingletonServiceProvider; + + @Mock + private BindingAwareBroker bindingAwareBroker; + + @Mock + private ScheduledThreadPool keepaliveExecutor; + + @Mock + private ThreadPool processingExecutor; + + @Mock + private Broker domBroker; + + @Mock + private ActorSystem actorSystem; + + @Mock + private EventExecutor eventExecutor; + + @Mock + private NetconfClientDispatcher clientDispatcher; + + private NetconfTopologySetup.NetconfTopologySetupBuilder builder; + private RemoteDeviceId remoteDeviceId; + + @Before + public void setUp() throws UnknownHostException { + initMocks(this); + + remoteDeviceId = new RemoteDeviceId(TOPOLOGY_ID, + new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9999)); + + builder = new NetconfTopologySetup.NetconfTopologySetupBuilder(); + builder.setDataBroker(dataBroker); + builder.setRpcProviderRegistry(rpcProviderRegistry); + builder.setClusterSingletonServiceProvider(clusterSingletonServiceProvider); + builder.setBindingAwareBroker(bindingAwareBroker); + builder.setKeepaliveExecutor(keepaliveExecutor); + builder.setProcessingExecutor(processingExecutor); + builder.setDomBroker(domBroker); + builder.setActorSystem(actorSystem); + builder.setEventExecutor(eventExecutor); + builder.setNetconfClientDispatcher(clientDispatcher); + builder.setTopologyId(TOPOLOGY_ID); + + } + + @Test + public void testStopRemoteDeviceConnection() { + final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build(); + final NetconfNode netconfNode = new NetconfNodeBuilder() + .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) + .setPort(new PortNumber(9999)) + .setReconnectOnChangedSchema(true) + .setDefaultRequestTimeoutMillis(1000L) + .setBetweenAttemptsTimeoutMillis(100) + .setSchemaless(false) + .setTcpOnly(false) + .setCredentials(credentials) + .build(); + final Node node = new NodeBuilder().setNodeId(NODE_ID).addAugmentation(NetconfNode.class, netconfNode).build(); + + builder.setNode(node); + + + final NetconfDeviceCommunicator communicator = mock (NetconfDeviceCommunicator.class); + final RemoteDeviceHandler salFacade = mock(RemoteDeviceHandler.class); + + final TestingRemoteDeviceConnectorImpl remoteDeviceConnection = + new TestingRemoteDeviceConnectorImpl(builder.build(), remoteDeviceId, communicator, salFacade); + + final ActorRef masterRef = mock(ActorRef.class); + + remoteDeviceConnection.startRemoteDeviceConnection(masterRef); + + remoteDeviceConnection.stopRemoteDeviceConnection(); + + verify(communicator, times(1)).close(); + verify(salFacade, times(1)).close(); + + } + + @Test + public void testMasterSalFacade() throws UnknownHostException { + final ExecutorService executorService = mock(ExecutorService.class); + doReturn(executorService).when(processingExecutor).getExecutor(); + + final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build(); + final NetconfNode netconfNode = new NetconfNodeBuilder() + .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) + .setPort(new PortNumber(9999)) + .setReconnectOnChangedSchema(true) + .setDefaultRequestTimeoutMillis(1000L) + .setBetweenAttemptsTimeoutMillis(100) + .setSchemaless(false) + .setTcpOnly(false) + .setCredentials(credentials) + .build(); + + final RemoteDeviceConnectorImpl remoteDeviceConnection = + new RemoteDeviceConnectorImpl(builder.build(), remoteDeviceId); + + final ActorRef masterRef = mock(ActorRef.class); + + final NetconfConnectorDTO connectorDTO = + remoteDeviceConnection.createDeviceCommunicator(NODE_ID, netconfNode, masterRef); + + assertTrue(connectorDTO.getFacade() instanceof MasterSalFacade); + } + + @Test + public void testKeapAliveFacade() throws UnknownHostException { + final ExecutorService executorService = mock(ExecutorService.class); + doReturn(executorService).when(processingExecutor).getExecutor(); + + final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build(); + final NetconfNode netconfNode = new NetconfNodeBuilder() + .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) + .setPort(new PortNumber(9999)) + .setReconnectOnChangedSchema(true) + .setDefaultRequestTimeoutMillis(1000L) + .setBetweenAttemptsTimeoutMillis(100) + .setSchemaless(false) + .setTcpOnly(false) + .setCredentials(credentials) + .setKeepaliveDelay(1L) + .build(); + + final RemoteDeviceId remoteDeviceId = new RemoteDeviceId(TOPOLOGY_ID, + new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9999)); + + final RemoteDeviceConnectorImpl remoteDeviceConnection = + new RemoteDeviceConnectorImpl(builder.build(), remoteDeviceId); + + final ActorRef masterRef = mock(ActorRef.class); + + final NetconfConnectorDTO connectorDTO = + remoteDeviceConnection.createDeviceCommunicator(NODE_ID, netconfNode, masterRef); + + assertTrue(connectorDTO.getFacade() instanceof KeepaliveSalFacade); + } + + @Test + public void testGetClientConfig() throws UnknownHostException { + final NetconfClientSessionListener listener = mock(NetconfClientSessionListener.class); + final Host host = new Host(new IpAddress(new Ipv4Address("127.0.0.1"))); + final PortNumber portNumber = new PortNumber(9999); + final NetconfNode testingNode = new NetconfNodeBuilder() + .setConnectionTimeoutMillis(1000L) + .setDefaultRequestTimeoutMillis(2000L) + .setHost(host) + .setPort(portNumber) + .setCredentials(new LoginPasswordBuilder() + .setUsername("testuser") + .setPassword("testpassword").build()) + .setTcpOnly(true) + .build(); + + final RemoteDeviceConnectorImpl remoteDeviceConnection = + new RemoteDeviceConnectorImpl(builder.build(), remoteDeviceId); + + final NetconfReconnectingClientConfiguration defaultClientConfig = + remoteDeviceConnection.getClientConfig(listener, testingNode); + + assertEquals(defaultClientConfig.getConnectionTimeoutMillis().longValue(), 1000L); + assertEquals(defaultClientConfig.getAddress(), new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9999)); + assertSame(defaultClientConfig.getSessionListener(), listener); + assertEquals(defaultClientConfig.getAuthHandler().getUsername(), "testuser"); + assertEquals(defaultClientConfig.getProtocol(), NetconfClientConfiguration.NetconfClientProtocol.TCP); + } + + @Test + public void testSchemaResourceDTO() throws UnknownHostException { + final ExecutorService executorService = mock(ExecutorService.class); + doReturn(executorService).when(processingExecutor).getExecutor(); + + final Credentials credentials = new LoginPasswordBuilder().setPassword("admin").setUsername("admin").build(); + final NetconfNode netconfNode = new NetconfNodeBuilder() + .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) + .setPort(new PortNumber(9999)) + .setReconnectOnChangedSchema(true) + .setDefaultRequestTimeoutMillis(1000L) + .setBetweenAttemptsTimeoutMillis(100) + .setSchemaless(false) + .setTcpOnly(false) + .setCredentials(credentials) + .setSchemaCacheDirectory("schemas-test") + .build(); + + final RemoteDeviceConnectorImpl remoteDeviceConnection = + new RemoteDeviceConnectorImpl(builder.build(), remoteDeviceId); + + final ActorRef masterRef = mock(ActorRef.class); + + remoteDeviceConnection.createDeviceCommunicator(NODE_ID, netconfNode, masterRef); + + assertTrue(remoteDeviceConnection.getSchemaResourcesDTOs().containsKey("schemas-test")); + } + +} diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/TestingRemoteDeviceConnectorImpl.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/TestingRemoteDeviceConnectorImpl.java new file mode 100644 index 0000000000..d4b4ec0492 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/TestingRemoteDeviceConnectorImpl.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.topology.singleton.impl; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; + +import akka.actor.ActorRef; +import com.google.common.util.concurrent.Futures; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfConnectorDTO; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; + +class TestingRemoteDeviceConnectorImpl extends RemoteDeviceConnectorImpl { + + private final NetconfDeviceCommunicator communicator; + private final RemoteDeviceHandler salFacade; + + TestingRemoteDeviceConnectorImpl(final NetconfTopologySetup netconfTopologyDeviceSetup, + final RemoteDeviceId remoteDeviceId, + final NetconfDeviceCommunicator communicator, + final RemoteDeviceHandler salFacade) { + super(netconfTopologyDeviceSetup, remoteDeviceId); + this.communicator = communicator; + this.salFacade = salFacade; + } + + @Override + public NetconfConnectorDTO createDeviceCommunicator(final NodeId nodeId, final NetconfNode node, + final ActorRef deviceContextActorRef) { + final NetconfConnectorDTO connectorDTO = new NetconfConnectorDTO(communicator, salFacade); + doReturn(Futures.immediateCheckedFuture(null)).when(communicator).initializeRemoteConnection(any(), any()); + + return connectorDTO; + } + +} diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/ReadOnlyTransactionTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/ReadOnlyTransactionTest.java new file mode 100644 index 0000000000..661c51149f --- /dev/null +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/ReadOnlyTransactionTest.java @@ -0,0 +1,262 @@ +/* + * 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.topology.singleton.impl.tx; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.pattern.Patterns; +import akka.testkit.JavaTestKit; +import akka.testkit.TestActorRef; +import akka.util.Timeout; +import com.google.common.base.Optional; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.impl.NetconfDOMDataBroker; +import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData; +import org.opendaylight.netconf.topology.singleton.messages.MasterActorDataInitialized; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import scala.concurrent.Await; +import scala.concurrent.Future; +import scala.concurrent.duration.Duration; + +public class ReadOnlyTransactionTest { + private static final Timeout TIMEOUT = new Timeout(Duration.create(5, "seconds")); + private static final int TIMEOUT_SEC = 5; + private static ActorSystem system; + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + private ActorRef masterRef; + private NetconfDOMDataBroker slaveDataBroker; + private DOMDataBroker masterDataBroker; + private List sourceIdentifiers; + + @Mock + private DOMDataReadOnlyTransaction readTx; + + @Before + public void setup() throws UnknownHostException { + initMocks(this); + + system = ActorSystem.create(); + + final RemoteDeviceId remoteDeviceId = new RemoteDeviceId("netconf-topology", + new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9999)); + + final NetconfTopologySetup setup = mock(NetconfTopologySetup.class); + final Props props = NetconfNodeActor.props(setup, remoteDeviceId, DEFAULT_SCHEMA_REPOSITORY, + DEFAULT_SCHEMA_REPOSITORY); + + masterRef = TestActorRef.create(system, props, "master_read"); + + sourceIdentifiers = Lists.newArrayList(); + + // Create master data broker + + final DOMDataBroker delegateDataBroker = mock(DOMDataBroker.class); + readTx = mock(DOMDataReadOnlyTransaction.class); + + doReturn(readTx).when(delegateDataBroker).newReadOnlyTransaction(); + + final NetconfDOMTransaction masterDOMTransactions = + new NetconfMasterDOMTransaction(delegateDataBroker); + + masterDataBroker = + new NetconfDOMDataBroker(system, remoteDeviceId, masterDOMTransactions); + + // Create slave data broker for testing proxy + + final NetconfDOMTransaction proxyDOMTransactions = + new NetconfProxyDOMTransaction(system, masterRef); + + slaveDataBroker = new NetconfDOMDataBroker(system, remoteDeviceId, proxyDOMTransactions); + + + } + + @After + public void teardown() { + JavaTestKit.shutdownActorSystem(system); + system = null; + } + + @Test + public void testRead() throws Exception { + + /* Initialize data on master */ + + initializeDataTest(); + + final YangInstanceIdentifier instanceIdentifier = YangInstanceIdentifier.EMPTY; + final LogicalDatastoreType storeType = LogicalDatastoreType.CONFIGURATION; + + // Message: EmptyReadResponse + + final CheckedFuture>, ReadFailedException> resultEmpty = + Futures.immediateCheckedFuture(Optional.absent()); + + doReturn(resultEmpty).when(readTx).read(storeType, instanceIdentifier); + + final CheckedFuture>, ReadFailedException> resultEmptyResponse = + slaveDataBroker.newReadOnlyTransaction().read(storeType, + instanceIdentifier); + + final Optional> resultEmptyMessage = + resultEmptyResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + assertEquals(resultEmptyMessage, Optional.absent()); + + // Message: NormalizedNodeMessage + + final NormalizedNode outputNode = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("TestQname"))) + .withChild(ImmutableNodes.leafNode(QName.create("NodeQname"), "foo")).build(); + + final CheckedFuture>, ReadFailedException> resultNormalizedNodeMessage = + Futures.immediateCheckedFuture(Optional.of(outputNode)); + + doReturn(resultNormalizedNodeMessage).when(readTx).read(storeType, instanceIdentifier); + + final CheckedFuture>, ReadFailedException> resultNodeMessageResponse = + slaveDataBroker.newReadOnlyTransaction().read(storeType, instanceIdentifier); + + final Optional> resultNodeMessage = + resultNodeMessageResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + assertTrue(resultNodeMessage.isPresent()); + assertEquals(resultNodeMessage.get(), outputNode); + + // Message: Throwable + + final ReadFailedException readFailedException = new ReadFailedException("Fail", null); + final CheckedFuture>, ReadFailedException> resultThrowable = + Futures.immediateFailedCheckedFuture(readFailedException); + + doReturn(resultThrowable).when(readTx).read(storeType, instanceIdentifier); + + final CheckedFuture>, ReadFailedException> resultThrowableResponse = + slaveDataBroker.newReadOnlyTransaction().read(storeType, instanceIdentifier); + + exception.expect(ReadFailedException.class); + resultThrowableResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + } + + @Test + public void testExist() throws Exception { + + /* Initialize data on master */ + + initializeDataTest(); + + final YangInstanceIdentifier instanceIdentifier = YangInstanceIdentifier.EMPTY; + final LogicalDatastoreType storeType = LogicalDatastoreType.CONFIGURATION; + + // Message: True + + final CheckedFuture resultTrue = + Futures.immediateCheckedFuture(true); + + doReturn(resultTrue).when(readTx).exists(storeType, instanceIdentifier); + + final CheckedFuture trueResponse = + slaveDataBroker.newReadOnlyTransaction().exists(storeType, instanceIdentifier); + + final Boolean trueMessage = trueResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + assertEquals(true, trueMessage); + + // Message: False + + final CheckedFuture resultFalse = Futures.immediateCheckedFuture(false); + + doReturn(resultFalse).when(readTx).exists(storeType, instanceIdentifier); + + final CheckedFuture falseResponse = + slaveDataBroker.newReadOnlyTransaction().exists(storeType, + instanceIdentifier); + + final Boolean falseMessage = falseResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + assertEquals(false, falseMessage); + + // Message: False, result null + + final CheckedFuture resultNull = Futures.immediateCheckedFuture(null); + + doReturn(resultNull).when(readTx).exists(storeType, instanceIdentifier); + + final CheckedFuture nullResponse = + slaveDataBroker.newReadOnlyTransaction().exists(storeType, + instanceIdentifier); + + final Boolean nullFalseMessage = nullResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + assertEquals(false, nullFalseMessage); + + // Message: Throwable + + final ReadFailedException readFailedException = new ReadFailedException("Fail", null); + final CheckedFuture resultThrowable = + Futures.immediateFailedCheckedFuture(readFailedException); + + doReturn(resultThrowable).when(readTx).exists(storeType, instanceIdentifier); + + final CheckedFuture resultThrowableResponse = + slaveDataBroker.newReadOnlyTransaction().exists(storeType, instanceIdentifier); + + exception.expect(ReadFailedException.class); + resultThrowableResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + } + + private void initializeDataTest() throws Exception { + final Future initialDataToActor = + Patterns.ask(masterRef, new CreateInitialMasterActorData(masterDataBroker, sourceIdentifiers), + TIMEOUT); + + final Object success = Await.result(initialDataToActor, TIMEOUT.duration()); + + assertTrue(success instanceof MasterActorDataInitialized); + } +} diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/WriteOnlyTransactionTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/WriteOnlyTransactionTest.java new file mode 100644 index 0000000000..97b0cec90a --- /dev/null +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/WriteOnlyTransactionTest.java @@ -0,0 +1,257 @@ +/* + * 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.topology.singleton.impl.tx; + +import static junit.framework.TestCase.assertNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.MockitoAnnotations.initMocks; +import static org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.actor.Props; +import akka.pattern.Patterns; +import akka.testkit.JavaTestKit; +import akka.testkit.TestActorRef; +import akka.util.Timeout; +import com.google.common.collect.Lists; +import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.UnknownHostException; +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +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.api.DOMDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataReadOnlyTransaction; +import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.netconf.topology.singleton.api.NetconfDOMTransaction; +import org.opendaylight.netconf.topology.singleton.impl.NetconfDOMDataBroker; +import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; +import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData; +import org.opendaylight.netconf.topology.singleton.messages.MasterActorDataInitialized; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; +import scala.concurrent.Await; +import scala.concurrent.Future; +import scala.concurrent.duration.Duration; + +public class WriteOnlyTransactionTest { + private static final Timeout TIMEOUT = new Timeout(Duration.create(5, "seconds")); + private static final int TIMEOUT_SEC = 5; + private static ActorSystem system; + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + private ActorRef masterRef; + private NetconfDOMDataBroker slaveDataBroker; + private DOMDataBroker masterDataBroker; + private List sourceIdentifiers; + + @Mock + private DOMDataWriteTransaction writeTx; + + @Before + public void setup() throws UnknownHostException { + initMocks(this); + + system = ActorSystem.create(); + + final RemoteDeviceId remoteDeviceId = new RemoteDeviceId("netconf-topology", + new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9999)); + + final NetconfTopologySetup setup = mock(NetconfTopologySetup.class); + final Props props = NetconfNodeActor.props(setup, remoteDeviceId, DEFAULT_SCHEMA_REPOSITORY, + DEFAULT_SCHEMA_REPOSITORY); + + masterRef = TestActorRef.create(system, props, "master_read"); + + sourceIdentifiers = Lists.newArrayList(); + + // Create master data broker + + final DOMDataBroker delegateDataBroker = mock(DOMDataBroker.class); + writeTx = mock(DOMDataWriteTransaction.class); + final DOMDataReadOnlyTransaction readTx = mock(DOMDataReadOnlyTransaction.class); + + doReturn(writeTx).when(delegateDataBroker).newWriteOnlyTransaction(); + doReturn(readTx).when(delegateDataBroker).newReadOnlyTransaction(); + + final NetconfDOMTransaction masterDOMTransactions = + new NetconfMasterDOMTransaction(delegateDataBroker); + + masterDataBroker = + new NetconfDOMDataBroker(system, remoteDeviceId, masterDOMTransactions); + + // Create slave data broker for testing proxy + + final NetconfDOMTransaction proxyDOMTransactions = + new NetconfProxyDOMTransaction(system, masterRef); + + slaveDataBroker = new NetconfDOMDataBroker(system, remoteDeviceId, proxyDOMTransactions); + + + } + + @After + public void teardown() { + JavaTestKit.shutdownActorSystem(system); + system = null; + } + + @Test + public void testPutMergeDeleteCalls() throws Exception { + + /* Initialize data on master */ + + initializeDataTest(); + + final YangInstanceIdentifier instanceIdentifier = YangInstanceIdentifier.EMPTY; + final LogicalDatastoreType storeType = LogicalDatastoreType.CONFIGURATION; + final NormalizedNode testNode = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("TestQname"))) + .withChild(ImmutableNodes.leafNode(QName.create("NodeQname"), "foo")).build(); + + // Test of invoking put on master through slave proxy + + doNothing().when(writeTx).put(storeType, instanceIdentifier, testNode); + slaveDataBroker.newWriteOnlyTransaction().put(storeType, instanceIdentifier, testNode); + + verify(writeTx, times(1)).put(storeType, instanceIdentifier, testNode); + + // Test of invoking merge on master through slave proxy + + doNothing().when(writeTx).merge(storeType, instanceIdentifier, testNode); + slaveDataBroker.newWriteOnlyTransaction().merge(storeType, instanceIdentifier, testNode); + + verify(writeTx, times(1)).merge(storeType, instanceIdentifier, testNode); + + // Test of invoking delete on master through slave proxy + + doNothing().when(writeTx).delete(storeType, instanceIdentifier); + slaveDataBroker.newWriteOnlyTransaction().delete(storeType, instanceIdentifier); + + verify(writeTx, times(1)).delete(storeType, instanceIdentifier); + + } + + @Test + public void testSubmit() throws Exception { + + /* Initialize data on master */ + + initializeDataTest(); + + // Without Tx + + final CheckedFuture resultSubmit = Futures.immediateCheckedFuture(null); + doReturn(resultSubmit).when(writeTx).submit(); + + final CheckedFuture resultSubmitResponse = + slaveDataBroker.newWriteOnlyTransaction().submit(); + + final Object result= resultSubmitResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + assertNull(result); + + // With Tx + + doNothing().when(writeTx).delete(any(), any()); + slaveDataBroker.newWriteOnlyTransaction().delete(LogicalDatastoreType.CONFIGURATION, + YangInstanceIdentifier.EMPTY); + + final CheckedFuture resultSubmitTx = Futures.immediateCheckedFuture(null); + doReturn(resultSubmitTx).when(writeTx).submit(); + + final CheckedFuture resultSubmitTxResponse = + slaveDataBroker.newWriteOnlyTransaction().submit(); + + final Object resultTx = resultSubmitTxResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + + assertNull(resultTx); + + slaveDataBroker.newWriteOnlyTransaction().delete(LogicalDatastoreType.CONFIGURATION, + YangInstanceIdentifier.EMPTY); + + final TransactionCommitFailedException throwable = new TransactionCommitFailedException("Fail", null); + final CheckedFuture resultThrowable = + Futures.immediateFailedCheckedFuture(throwable); + + doReturn(resultThrowable).when(writeTx).submit(); + + final CheckedFuture resultThrowableResponse = + slaveDataBroker.newWriteOnlyTransaction().submit(); + + exception.expect(TransactionCommitFailedException.class); + resultThrowableResponse.checkedGet(TIMEOUT_SEC, TimeUnit.SECONDS); + } + + @Test + public void testCancel() throws Exception { + + /* Initialize data on master */ + + initializeDataTest(); + + // Without Tx + + final Boolean resultFalseNoTx = slaveDataBroker.newWriteOnlyTransaction().cancel(); + assertEquals(false, resultFalseNoTx); + + // With Tx, readWriteTx test + + doNothing().when(writeTx).delete(any(), any()); + slaveDataBroker.newReadWriteTransaction().delete(LogicalDatastoreType.CONFIGURATION, + YangInstanceIdentifier.EMPTY); + + doReturn(true).when(writeTx).cancel(); + + final Boolean resultTrue = slaveDataBroker.newWriteOnlyTransaction().cancel(); + assertEquals(true, resultTrue); + + doReturn(false).when(writeTx).cancel(); + + final Boolean resultFalse = slaveDataBroker.newWriteOnlyTransaction().cancel(); + assertEquals(false, resultFalse); + + } + + private void initializeDataTest() throws Exception { + final Future initialDataToActor = + Patterns.ask(masterRef, new CreateInitialMasterActorData(masterDataBroker, sourceIdentifiers), + TIMEOUT); + + final Object success = Await.result(initialDataToActor, TIMEOUT.duration()); + + assertTrue(success instanceof MasterActorDataInitialized); + } + +} diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtilTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtilTest.java new file mode 100644 index 0000000000..9f8d153a87 --- /dev/null +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologyUtilTest.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.topology.singleton.impl.utils; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +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.IpAddress; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address; +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.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class NetconfTopologyUtilTest { + + @Test + public void testCreateRemoteDeviceId() { + final Host host = new Host(new IpAddress(new Ipv4Address("127.0.0.1"))); + final NetconfNode netconfNode = new NetconfNodeBuilder().setHost(host).setPort(new PortNumber(9999)).build(); + final NodeId nodeId = new NodeId("testing-node"); + final RemoteDeviceId id = NetconfTopologyUtils.createRemoteDeviceId(nodeId, netconfNode); + + assertEquals("testing-node", id.getName()); + assertEquals(host, id.getHost()); + assertEquals(9999, id.getAddress().getPort()); + } + + @Test + public void testCreateActorPath() { + final String actorPath = NetconfTopologyUtils.createActorPath("member", "name"); + assertEquals("member/user/name", actorPath); + } + + @Test + public void testCreateListPath() { + final InstanceIdentifier listPath = + NetconfTopologyUtils.createTopologyNodeListPath(new NodeKey(new NodeId("nodeId")), "topologyId"); + + assertEquals("nodeId", listPath.firstKeyOf(Node.class).getNodeId().getValue()); + assertEquals("topologyId", listPath.firstKeyOf(Topology.class).getTopologyId().getValue()); + + assertEquals("topologyId", NetconfTopologyUtils.createTopologyNodePath("topologyId"). + firstKeyOf(Topology.class).getTopologyId().getValue()); + } + +} diff --git a/netconf/pom.xml b/netconf/pom.xml index 0eed113225..db4a1c24d6 100644 --- a/netconf/pom.xml +++ b/netconf/pom.xml @@ -47,6 +47,7 @@ netconf-notifications-impl netconf-notifications-api netconf-topology + netconf-topology-singleton abstract-topology netconf-topology-config sal-netconf-connector diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java index b440b1d900..1fe3fafccf 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/sal/NetconfDeviceTopologyAdapter.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; @@ -29,6 +30,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev15 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.ConnectionStatus; 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.netconf.node.connection.status.ClusteredConnectionStatusBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.UnavailableCapabilities; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.UnavailableCapabilitiesBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability; @@ -51,7 +53,7 @@ import org.opendaylight.yangtools.yang.common.QName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class NetconfDeviceTopologyAdapter implements AutoCloseable { +public final class NetconfDeviceTopologyAdapter implements AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceTopologyAdapter.class); public static final Function, UnavailableCapability> UNAVAILABLE_CAPABILITY_TRANSFORMER = new Function, UnavailableCapability>() { @@ -134,6 +136,21 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable { commitTransaction(writeTx, "update"); } + public void updateClusteredDeviceData(boolean up, String masterAddress, NetconfDeviceCapabilities capabilities) { + final NetconfNode data = buildDataForNetconfClusteredNode(up, masterAddress, capabilities); + + final WriteTransaction writeTx = txChain.newWriteOnlyTransaction(); + LOG.trace( + "{}: Update device state transaction {} merging operational data started.", + id, writeTx.getIdentifier()); + writeTx.put(LogicalDatastoreType.OPERATIONAL, id.getTopologyBindingPath().augmentation(NetconfNode.class), data, true); + LOG.trace( + "{}: Update device state transaction {} merging operational data ended.", + id, writeTx.getIdentifier()); + + commitTransaction(writeTx, "update"); + } + public void setDeviceAsFailed(Throwable throwable) { String reason = (throwable != null && throwable.getMessage() != null) ? throwable.getMessage() : UNKNOWN_REASON; @@ -172,6 +189,30 @@ final class NetconfDeviceTopologyAdapter implements AutoCloseable { return netconfNodeBuilder.build(); } + private NetconfNode buildDataForNetconfClusteredNode(boolean up, String masterNodeAddress, NetconfDeviceCapabilities capabilities) { + List capabilityList = new ArrayList<>(); + capabilityList.addAll(capabilities.getNonModuleBasedCapabilities()); + capabilityList.addAll(FluentIterable.from(capabilities.getResolvedCapabilities()).transform(AVAILABLE_CAPABILITY_TRANSFORMER).toList()); + final AvailableCapabilitiesBuilder avCapabalitiesBuilder = new AvailableCapabilitiesBuilder(); + avCapabalitiesBuilder.setAvailableCapability(capabilityList); + + final UnavailableCapabilities unavailableCapabilities = + new UnavailableCapabilitiesBuilder().setUnavailableCapability(capabilities.getUnresolvedCapabilites() + .entrySet().stream().map(UNAVAILABLE_CAPABILITY_TRANSFORMER::apply) + .collect(Collectors.toList())).build(); + + final NetconfNodeBuilder netconfNodeBuilder = new NetconfNodeBuilder() + .setHost(id.getHost()) + .setPort(new PortNumber(id.getAddress().getPort())) + .setConnectionStatus(up ? ConnectionStatus.Connected : ConnectionStatus.Connecting) + .setAvailableCapabilities(avCapabalitiesBuilder.build()) + .setUnavailableCapabilities(unavailableCapabilities) + .setClusteredConnectionStatus( + new ClusteredConnectionStatusBuilder().setNetconfMasterNode(masterNodeAddress).build()); + + return netconfNodeBuilder.build(); + } + public void removeDeviceConfiguration() { final WriteTransaction writeTx = txChain.newWriteOnlyTransaction(); diff --git a/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang b/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang index 62b88b974f..b3a5f3ff84 100644 --- a/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang +++ b/netconf/sal-netconf-connector/src/main/yang/netconf-node-topology.yang @@ -153,6 +153,10 @@ module netconf-node-topology { } } } + leaf netconf-master-node { + config false; + type string; + } } leaf connected-message { -- 2.36.6