diff options
author | herbert <herbert.eiselt@highstreet-technologies.com> | 2020-01-30 10:39:37 +0100 |
---|---|---|
committer | herbert <herbert.eiselt@highstreet-technologies.com> | 2020-02-01 13:30:27 +0100 |
commit | cbbc3520048bbc539b137cac011776506b3e0f70 (patch) | |
tree | 8b72e076b091001e88286a59e5b79479a9194560 /sdnr/wt/netconfnode-state-service/provider/src/main/java | |
parent | 84cf604e3f06942a62ae0478327ec6100787761e (diff) |
SDN-R add updated app
add updated netconfnode-state-service
Issue-ID: SDNC-1033
Signed-off-by: herbert <herbert.eiselt@highstreet-technologies.com>
Change-Id: If1639bb61dc58e90b769c0dd100d49eeddf83fc9
Signed-off-by: herbert <herbert.eiselt@highstreet-technologies.com>
Diffstat (limited to 'sdnr/wt/netconfnode-state-service/provider/src/main/java')
11 files changed, 1687 insertions, 0 deletions
diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/GenericTransactionUtils.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/GenericTransactionUtils.java new file mode 100644 index 000000000..93f230a23 --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/GenericTransactionUtils.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl; + +import com.google.common.base.Preconditions; +import com.google.common.util.concurrent.FluentFuture; +import java.util.NoSuchElementException; +import java.util.Optional; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.onap.ccsdk.features.sdnr.wt.common.util.StackTrace; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.TransactionUtils; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.ReadTransaction; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class GenericTransactionUtils implements TransactionUtils { + static final Logger LOG = LoggerFactory.getLogger(GenericTransactionUtils.class); + + /** + * Deliver the data back or null. Warning + * + * @param <T> SubType of the DataObject to be handled + * @param dataBroker for accessing data + * @param dataStoreType to address datastore + * @param iid id to access data + * @return null or object + */ + @Override + @Nullable + public <T extends DataObject> T readData(DataBroker dataBroker, LogicalDatastoreType dataStoreType, + InstanceIdentifier<T> iid) { + + AtomicBoolean noErrorIndication = new AtomicBoolean(); + AtomicReference<String> statusText = new AtomicReference<>(); + + @Nullable T obj = readDataOptionalWithStatus(dataBroker, dataStoreType, iid, noErrorIndication, statusText); + + if (!noErrorIndication.get()) { + LOG.warn("Read transaction for identifier " + iid + " failed with status " + statusText.get()); + } + + return obj; + } + + /** + * Deliver the data back or null + * + * @param <T> SubType of the DataObject to be handled + * @param dataBroker for accessing data + * @param dataStoreType to address datastore + * @param iid id to access data + * @param noErrorIndication (Output) true if data could be read and are available and is not null + * @param statusIndicator (Output) String with status indications during the read. + * @return null or object + */ + @Override + @SuppressWarnings("null") + public @Nullable <T extends DataObject> T readDataOptionalWithStatus(DataBroker dataBroker, + LogicalDatastoreType dataStoreType, InstanceIdentifier<T> iid, AtomicBoolean noErrorIndication, + AtomicReference<String> statusIndicator) { + + @Nullable T data = null; + noErrorIndication.set(false); + + statusIndicator.set("Preconditions"); + Preconditions.checkNotNull(dataBroker); + + int retry = 0; + int retryDelayMilliseconds = 2000; + int maxRetries = 5; // 0 no Retry + + do { + if (retry > 0) { + try { + LOG.debug("Sleep {}ms", retryDelayMilliseconds); + Thread.sleep(retryDelayMilliseconds); + } catch (InterruptedException e) { + LOG.debug("Sleep interrupted", e); + Thread.currentThread().interrupt(); + } + } + + LOG.debug("Sending message with retry {} ", retry); + statusIndicator.set("Create Read Transaction"); + + try (ReadTransaction readTransaction = dataBroker.newReadOnlyTransaction();) { + @NonNull FluentFuture<Optional<T>> od = readTransaction.read(dataStoreType, iid); + statusIndicator.set("Read done"); + if (od != null) { + statusIndicator.set("Unwrap checkFuture done"); + Optional<T> optionalData = od.get(); + if (optionalData != null) { + statusIndicator.set("Unwrap optional done"); + data = optionalData.orElse(null); + statusIndicator.set("Read transaction done"); + noErrorIndication.set(true); + } + } + + readTransaction.close(); + } catch (CancellationException | ExecutionException | InterruptedException | NoSuchElementException e) { + statusIndicator.set(StackTrace.toString(e)); + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + } + + } while (noErrorIndication.get() == false && retry++ < maxRetries); + + return data; + } + + +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/NetconfAccessorImpl.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/NetconfAccessorImpl.java new file mode 100644 index 000000000..654b9d7d9 --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/NetconfAccessorImpl.java @@ -0,0 +1,164 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; +import java.util.Optional; +import javax.annotation.Nonnull; +import org.eclipse.jdt.annotation.NonNull; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.Capabilities; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfAccessor; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.TransactionUtils; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.MountPoint; +import org.opendaylight.mdsal.binding.api.NotificationService; +import org.opendaylight.mdsal.binding.api.RpcConsumerRegistry; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInput; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionInputBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.CreateSubscriptionOutput; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.NotificationsService; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714.StreamNameType; +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.ConnectionStatus; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.NotificationListener; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfAccessorImpl implements NetconfAccessor { + + private static final Logger log = LoggerFactory.getLogger(NetconfAccessorImpl.class); + + private final NodeId nodeId; + private final DataBroker dataBroker; + private final TransactionUtils transactionUtils; + private final MountPoint mountpoint; + private final NetconfNode netconfNode; + private final Capabilities capabilities; + + /** + * Contains all data to access and manage netconf device + * @param nodeId of managed netconf node + * @param netconfNode information + * @param dataBroker to access node + * @param mountpoint of netconfNode + * @param transactionUtils with read an write functions + */ + public NetconfAccessorImpl(NodeId nodeId, NetconfNode netconfNode, DataBroker dataBroker, MountPoint mountpoint, + TransactionUtils transactionUtils) { + super(); + this.nodeId = nodeId; + this.netconfNode = netconfNode; + this.dataBroker = dataBroker; + this.mountpoint = mountpoint; + this.transactionUtils = transactionUtils; + + ConnectionStatus csts = netconfNode != null ? netconfNode.getConnectionStatus() : null; + this.capabilities = Capabilities.getAvailableCapabilities(csts != null ? netconfNode : null); + } + + /** + * @param nodeId with uuid of managed netconf node + * @param dataBroker to access node + */ + public NetconfAccessorImpl(String nodeId, NetconfNode netconfNode, DataBroker dataBroker, MountPoint mountpoint, + TransactionUtils transactionUtils) { + this(new NodeId(nodeId), netconfNode, dataBroker, mountpoint, transactionUtils); + } + + @Override + public NodeId getNodeId() { + return nodeId; + } + + @Override + public DataBroker getDataBroker() { + return dataBroker; + } + @Override + public MountPoint getMountpoint() { + return mountpoint; + } + @Override + public TransactionUtils getTransactionUtils() { + return transactionUtils; + } + @Override + public NetconfNode getNetconfNode() { + return netconfNode; + } + @Override + public Capabilities getCapabilites() { + return capabilities; + } + + @Override + public @NonNull <T extends NotificationListener> ListenerRegistration<NotificationListener> doRegisterNotificationListener(@NonNull T listener) { + log.info("Begin register listener for Mountpoint {}", mountpoint.getIdentifier().toString()); + final Optional<NotificationService> optionalNotificationService = mountpoint + .getService(NotificationService.class); + final NotificationService notificationService = optionalNotificationService.get(); + final ListenerRegistration<NotificationListener> ranListenerRegistration = notificationService + .registerNotificationListener(listener); + log.info("End registration listener for Mountpoint {} Listener: {} Result: {}", + mountpoint.getIdentifier().toString(), optionalNotificationService, ranListenerRegistration); + return ranListenerRegistration; + } + + @Override + public ListenableFuture<RpcResult<CreateSubscriptionOutput>> registerNotificationsStream(String streamName) { + + String failMessage = ""; + final Optional<RpcConsumerRegistry> optionalRpcConsumerService = + mountpoint.getService(RpcConsumerRegistry.class); + if (optionalRpcConsumerService.isPresent()) { + final RpcConsumerRegistry rpcConsumerRegitry = optionalRpcConsumerService.get(); + @Nonnull + final NotificationsService rpcService = rpcConsumerRegitry.getRpcService(NotificationsService.class); + + final CreateSubscriptionInputBuilder createSubscriptionInputBuilder = new CreateSubscriptionInputBuilder(); + createSubscriptionInputBuilder.setStream(new StreamNameType(streamName)); + log.info("Event listener triggering notification stream {} for node {}", streamName, nodeId); + try { + CreateSubscriptionInput createSubscriptionInput = createSubscriptionInputBuilder.build(); + if (createSubscriptionInput == null) { + failMessage = "createSubscriptionInput is null for mountpoint " + nodeId; + } else { + return rpcService.createSubscription(createSubscriptionInput); + } + } catch (NullPointerException e) { + failMessage = "createSubscription failed"; + } + } else { + failMessage = "No RpcConsumerRegistry avaialble."; + } + log.warn(failMessage); + RpcResultBuilder<CreateSubscriptionOutput> result = RpcResultBuilder.failed(); + result.withError(ErrorType.APPLICATION, failMessage); + SettableFuture<RpcResult<CreateSubscriptionOutput>> res = SettableFuture.create(); + res.set(result.build()); + return res; + } + + +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/NetconfNodeStateServiceImpl.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/NetconfNodeStateServiceImpl.java new file mode 100644 index 000000000..041ab9a23 --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/NetconfNodeStateServiceImpl.java @@ -0,0 +1,625 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.annotation.Nullable; + +import org.eclipse.jdt.annotation.NonNull; +import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.IEntityDataProvider; +import org.onap.ccsdk.features.sdnr.wt.dataprovider.model.StatusChangedHandler.StatusKey; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfAccessor; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfNodeConnectListener; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfNodeStateListener; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.NetconfNodeStateService; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.TransactionUtils; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.VesNotificationListener; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlAkka.AkkaConfig; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlAkka.ClusterConfig; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlGeo.GeoConfig; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.rpc.NetconfnodeStateServiceRpcApiImpl; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.rpc.RpcApigetStateCallback; +import org.opendaylight.mdsal.binding.api.ClusteredDataTreeChangeListener; +import org.opendaylight.mdsal.binding.api.DataBroker; +import org.opendaylight.mdsal.binding.api.DataObjectModification; +import org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType; +import org.opendaylight.mdsal.binding.api.DataTreeChangeListener; +import org.opendaylight.mdsal.binding.api.DataTreeIdentifier; +import org.opendaylight.mdsal.binding.api.DataTreeModification; +import org.opendaylight.mdsal.binding.api.MountPoint; +import org.opendaylight.mdsal.binding.api.MountPointService; +import org.opendaylight.mdsal.binding.api.NotificationPublishService; +import org.opendaylight.mdsal.binding.api.RpcProviderService; +import org.opendaylight.mdsal.common.api.LogicalDatastoreType; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; +import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration; +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.ConnectionStatus; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.ClusteredConnectionStatus; +import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.network.topology.topology.topology.types.TopologyNetconf; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusOutputBuilder; +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.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class NetconfNodeStateServiceImpl implements NetconfNodeStateService, RpcApigetStateCallback, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeStateServiceImpl.class); + private static final String APPLICATION_NAME = "NetconfNodeStateService"; + @SuppressWarnings("unused") + private static final String CONFIGURATIONFILE = "etc/netconfnode-status-service.properties"; + + + @SuppressWarnings("null") + private static final @NonNull InstanceIdentifier<Topology> NETCONF_TOPO_IID = + InstanceIdentifier.create(NetworkTopology.class).child(Topology.class, + new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName()))); + + @SuppressWarnings("null") + private static final @NonNull InstanceIdentifier<Node> NETCONF_NODE_TOPO_IID = + InstanceIdentifier.create(NetworkTopology.class) + .child(Topology.class, new TopologyKey(new TopologyId(TopologyNetconf.QNAME.getLocalName()))) + .child(Node.class); + + private static final DataTreeIdentifier<Node> NETCONF_NODE_TOPO_TREE_ID = + DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, NETCONF_NODE_TOPO_IID); + + // Name of ODL controller NETCONF instance + private static final NodeId CONTROLLER = new NodeId("controller-config"); + private static final TransactionUtils TRANSACTIONUTILS = new GenericTransactionUtils(); + + // -- OSGi services, provided + private DataBroker dataBroker; + private MountPointService mountPointService; + private RpcProviderService rpcProviderRegistry; + private IEntityDataProvider iEntityDataProvider; + @SuppressWarnings("unused") + private NotificationPublishService notificationPublishService; + @SuppressWarnings("unused") + private ClusterSingletonServiceProvider clusterSingletonServiceProvider; + + // -- Parameter + private ListenerRegistration<L1> listenerL1; + private ListenerRegistration<L2> listenerL2; + @SuppressWarnings("unused") + private ClusterSingletonServiceRegistration cssRegistration; + + private NetconfnodeStateServiceRpcApiImpl rpcApiService; + + /** Indication if init() function called and fully executed **/ + private Boolean initializationSuccessful; + + /** List of all registered listeners **/ + private final List<NetconfNodeConnectListener> netconfNodeConnectListenerList; + + /** List of all registered listeners **/ + private final List<NetconfNodeStateListener> netconfNodeStateListenerList; + + /** List of all registered listeners **/ + private final List<VesNotificationListener> vesNotificationListenerList; + + /** Indicates if running in cluster configuration **/ + private boolean isCluster; + + /** Indicates the name of the cluster **/ + private String clusterName; + + /** Blueprint **/ + public NetconfNodeStateServiceImpl() { + LOG.info("Creating provider for {}", APPLICATION_NAME); + + this.dataBroker = null; + this.mountPointService = null; + this.rpcProviderRegistry = null; + this.notificationPublishService = null; + this.clusterSingletonServiceProvider = null; + + this.listenerL1 = null; + this.listenerL2 = null; + this.initializationSuccessful= false; + this.netconfNodeConnectListenerList = new CopyOnWriteArrayList<>(); + this.netconfNodeStateListenerList = new CopyOnWriteArrayList<>(); + this.vesNotificationListenerList = new CopyOnWriteArrayList<>(); + } + + public void setDataBroker(DataBroker dataBroker) { + this.dataBroker = dataBroker; + } + + public void setRpcProviderRegistry(RpcProviderService rpcProviderRegistry) { + this.rpcProviderRegistry = rpcProviderRegistry; + } + + public void setNotificationPublishService(NotificationPublishService notificationPublishService) { + this.notificationPublishService = notificationPublishService; + } + + public void setMountPointService(MountPointService mountPointService) { + this.mountPointService = mountPointService; + } + public void setClusterSingletonService(ClusterSingletonServiceProvider clusterSingletonService) { + this.clusterSingletonServiceProvider = clusterSingletonService; + } + public void setEntityDataProvider(IEntityDataProvider iEntityDataProvider) { + this.iEntityDataProvider = iEntityDataProvider; + } + + /** Blueprint initialization **/ + public void init() { + + LOG.info("Session Initiated start {}", APPLICATION_NAME); + + // Start RPC Service + this.rpcApiService = new NetconfnodeStateServiceRpcApiImpl(rpcProviderRegistry, vesNotificationListenerList); + // Get configuration + // ConfigurationFileRepresentation config = new ConfigurationFileRepresentation(CONFIGURATIONFILE); + // Akka setup + AkkaConfig akkaConfig = getAkkaConfig(); + this.isCluster = akkaConfig == null ? false : akkaConfig.isCluster(); + this.clusterName = akkaConfig == null ? "" : akkaConfig.getClusterConfig().getClusterSeedNodeName("abc"); + + // Provide status information + ClusterConfig cc = akkaConfig==null?null:akkaConfig.getClusterConfig(); + this.iEntityDataProvider.setStatus(StatusKey.CLUSTER_SIZE,cc==null?"1":String.format("%d",cc.getClusterSize())); + + // RPC Service for specific services + this.rpcApiService.setStatusCallback(this); + + LOG.debug("start NetconfSubscriptionManager Service"); + //this.netconfChangeListener = new NetconfChangeListener(this, dataBroker); + //this.netconfChangeListener.register(); + //DataTreeIdentifier<Node> treeId = new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, NETCONF_NODE_TOPO_IID); + + listenerL1 = dataBroker.registerDataTreeChangeListener(NETCONF_NODE_TOPO_TREE_ID, new L1()); + listenerL2 = dataBroker.registerDataTreeChangeListener(NETCONF_NODE_TOPO_TREE_ID, new L2()); + + this.initializationSuccessful = true; + + LOG.info("Session Initiated end. Initialization done {}", initializationSuccessful); + + } + /** Blueprint destroy-method method */ + public void destroy() { + close(); + } + + /** + * Getter + * @return NetconfnodeStateServiceRpcApiImpl + */ + public NetconfnodeStateServiceRpcApiImpl getNetconfnodeStateServiceRpcApiImpl() { + return rpcApiService; + } + + @Override + public GetStatusOutputBuilder getStatus(GetStatusInput input) { + return new GetStatusOutputBuilder(); + } + + @Override + public <L extends NetconfNodeConnectListener> @NonNull ListenerRegistration<L> registerNetconfNodeConnectListener( + final @NonNull L netconfNodeConnectListener) { + LOG.info("Register connect listener {}",netconfNodeConnectListener.getClass().getName()); + netconfNodeConnectListenerList.add(netconfNodeConnectListener); + + return new ListenerRegistration<L>() { + @Override + public @NonNull L getInstance() { + return netconfNodeConnectListener; + } + + @Override + public void close() { + LOG.info("Remove connect listener {}",netconfNodeConnectListener); + netconfNodeConnectListenerList.remove(netconfNodeConnectListener); + } + }; + } + + @Override + public <L extends NetconfNodeStateListener> @NonNull ListenerRegistration<L> registerNetconfNodeStateListener( + @NonNull L netconfNodeStateListener) { + LOG.info("Register state listener {}",netconfNodeStateListener.getClass().getName()); + netconfNodeStateListenerList.add(netconfNodeStateListener); + + return new ListenerRegistration<L>() { + @Override + public @NonNull L getInstance() { + return netconfNodeStateListener; + } + + @Override + public void close() { + LOG.info("Remove state listener {}",netconfNodeStateListener); + netconfNodeStateListenerList.remove(netconfNodeStateListener); + } + }; + } + + @Override + public <L extends VesNotificationListener> @NonNull ListenerRegistration<L> registerVesNotifications( + @NonNull L vesNotificationListener) { + LOG.info("Register Ves notification listener {}",vesNotificationListener.getClass().getName()); + vesNotificationListenerList.add(vesNotificationListener); + + return new ListenerRegistration<L>() { + @Override + public @NonNull L getInstance() { + return vesNotificationListener; + } + + @Override + public void close() { + LOG.info("Remove Ves notification listener {}",vesNotificationListener); + vesNotificationListenerList.remove(vesNotificationListener); + } + }; + } + + @Override + public void close() { + LOG.info("Closing start ..."); + try { + close(rpcApiService, listenerL1, listenerL2); + } catch (Exception e) { + LOG.debug("Closing", e); + } + LOG.info("Closing done"); + } + + /** + * Used to close all Services, that should support AutoCloseable Pattern + * + * @param toClose + * @throws Exception + */ + private void close(AutoCloseable... toCloseList) throws Exception { + for (AutoCloseable element : toCloseList) { + if (element != null) { + element.close(); + } + } + } + + /** + * Indication if init() of this bundle successfully done. + * @return true if init() was successful. False if not done or not successful. + */ + public boolean isInitializationSuccessful() { + return this.initializationSuccessful; + } + + /*------------------------------------------------------------------------------------------- + * Functions for interface DeviceManagerService + */ + + /** + * For each mounted device a mountpoint is created and this listener is called. + * Mountpoint was created or existing. Managed device is now fully connected to node/mountpoint. + * @param nNodeId id of the mountpoint + * @param netconfNode mountpoint contents + */ + private void enterConnectedState(NodeId nNodeId, NetconfNode netconfNode) { + + String mountPointNodeName = nNodeId.getValue(); + LOG.info("Access connected state for mountpoint {}", mountPointNodeName); + + boolean preConditionMissing = false; + if (mountPointService == null) { + preConditionMissing = true; + LOG.warn("No mountservice available."); + } + if (!initializationSuccessful) { + preConditionMissing = true; + LOG.warn("Devicemanager initialization still pending."); + } + if (preConditionMissing) { + return; + } + + boolean isNetconfNodeMaster = isNetconfNodeMaster(netconfNode); + LOG.info("isNetconfNodeMaster indication {} for mountpoint {}", isNetconfNodeMaster, mountPointNodeName); + if (isNetconfNodeMaster) { + + InstanceIdentifier<Node> instanceIdentifier = NETCONF_TOPO_IID.child(Node.class, + new NodeKey(new NodeId(mountPointNodeName))); + + Optional<MountPoint> optionalMountPoint = null; + int timeout = 10000; + while (!(optionalMountPoint = mountPointService.getMountPoint(instanceIdentifier)).isPresent() + && timeout > 0) { + LOG.info("Event listener waiting for mount point for Netconf device :: Name : {}", mountPointNodeName); + sleepMs(1000); + timeout -= 1000; + } + + if (!optionalMountPoint.isPresent()) { + LOG.warn("Event listener timeout while waiting for mount point for Netconf device :: Name : {} ", + mountPointNodeName); + } else { + // Mountpoint is present for sure + MountPoint mountPoint = optionalMountPoint.get(); + // BindingDOMDataBrokerAdapter.BUILDER_FACTORY; + LOG.info("Mountpoint with id: {}", mountPoint.getIdentifier()); + + Optional<DataBroker> optionalNetconfNodeDatabroker = mountPoint.getService(DataBroker.class); + + if (!optionalNetconfNodeDatabroker.isPresent()) { + LOG.info("Slave mountpoint {} without databroker", mountPointNodeName); + } else { + LOG.info("Master mountpoint {}", mountPointNodeName); + DataBroker netconfNodeDataBroker = optionalNetconfNodeDatabroker.get(); + NetconfAccessor acessor = new NetconfAccessorImpl(nNodeId, netconfNode, netconfNodeDataBroker, + mountPoint, TRANSACTIONUTILS); + /* + * --> Call Listers for onConnect() Indication + for (all) + */ + netconfNodeConnectListenerList.forEach(item -> { + try { + item.onEnterConnected(acessor); + } catch (Exception e) { + LOG.info("Exception during onEnterConnected listener call", e); + } + }); + + LOG.info("Connect indication forwarded for {}", mountPointNodeName); + } + } + } + } + + /** + * Leave the connected status to a non connected or removed status for master mountpoint + * @param action that occurred + * @param nNodeId id of the mountpoint + * @param netconfNode mountpoint contents or not available on remove + */ + private void leaveConnectedState(NodeId nNodeId, Optional<NetconfNode> optionalNetconfNode) { + String mountPointNodeName = nNodeId.getValue(); + LOG.info("netconfNode id {}", mountPointNodeName); + + InstanceIdentifier<Node> instanceIdentifier = NETCONF_TOPO_IID.child(Node.class, + new NodeKey(new NodeId(mountPointNodeName))); + Optional<MountPoint> optionalMountPoint = mountPointService.getMountPoint(instanceIdentifier); + if (optionalMountPoint.isPresent()) { + Optional<DataBroker> optionalNetconfNodeDatabroker = optionalMountPoint.get().getService(DataBroker.class); + if (optionalNetconfNodeDatabroker.isPresent()) { + LOG.info("Master mountpoint {}", mountPointNodeName); + netconfNodeConnectListenerList.forEach(item -> { + try { + if (item != null) { + item.onLeaveConnected(nNodeId, optionalNetconfNode); + } else { + LOG.warn("Unexpeced null item during onleave"); + } + } catch (Exception e) { + LOG.info("Exception during onLeaveConnected listener call", e); + } + }); + } + } + } + + // ---- onDataTreeChangedHandler + + private void onDataTreeChangedHandler(@NonNull Collection<DataTreeModification<Node>> changes) { + for (final DataTreeModification<Node> change : changes) { + + final DataObjectModification<Node> root = change.getRootNode(); + //if (LOG.isTraceEnabled()) { + LOG.info /*trace*/("Handle this modificationType:{} path:{} root:{}", root.getModificationType(), + change.getRootPath(), root); + //} + + // Catch potential nullpointer exceptions .. + try { + ModificationType modificationTyp = root.getModificationType(); + Node node = modificationTyp == ModificationType.DELETE ? root.getDataBefore() + : root.getDataAfter(); + NodeId nodeId = node != null ? node.getNodeId() : null; + if (nodeId != null) { + if (nodeId.equals(CONTROLLER)) { + // Do not forward any controller related events to devicemanager + LOG.debug("Stop processing for [{}]", nodeId); + } else { + if (modificationTyp != null) { + switch (modificationTyp) { + case SUBTREE_MODIFIED: // Create or modify sub level node + case WRITE: // Create or modify top level node + // Treat an overwrite as an update + // leaveconnected state.before = connected; state.after != connected + // enterConnected state.after == connected + // => Here create or update by checking root.getDataBefore() != null + + boolean connectedBefore, connectedAfter; + NetconfNode nNodeAfter = getNetconfNode(root.getDataAfter()); + connectedAfter = isConnected(nNodeAfter); + if (root.getDataBefore() != null) { + // It is an update + NetconfNode nodeBefore = getNetconfNode(root.getDataBefore()); + connectedBefore = isConnected(nodeBefore); + } else { + // It is a create + connectedBefore = false; + } + + LOG.info( + "L1 NETCONF Node change with id:{} ConnectedBefore:{} connectedAfter {}:cluster status:{} akkaIsCluster:{}", + nodeId, connectedBefore, connectedAfter, + getClusteredConnectionStatus(nNodeAfter), isCluster); + + if (!connectedBefore && connectedAfter) { + netconfNodeStateListenerList.forEach(item -> { + try { + item.onCreated(nodeId, nNodeAfter); + } catch (Exception e) { + LOG.info("Exception during onCreated listener call", e); + } + }); + enterConnectedState(nodeId, nNodeAfter); + } else { + LOG.debug("State change {} {}", connectedBefore, connectedAfter); + if (connectedBefore && !connectedAfter) { + leaveConnectedState(nodeId, Optional.of(nNodeAfter)); + } + netconfNodeStateListenerList.forEach(item -> { + try { + item.onStateChange(nodeId, nNodeAfter); + } catch (Exception e) { + LOG.info("Exception during onStateChange listener call", e); + } + }); + } + // doProcessing(update ? Action.UPDATE : Action.CREATE, nodeId, root); + break; + case DELETE: + // Node removed + // leaveconnected state.before = connected; + leaveConnectedState(nodeId, Optional.empty()); + netconfNodeStateListenerList.forEach(item -> { + try { + item.onRemoved(nodeId); + } catch (Exception e) { + LOG.info("Exception during onRemoved listener call", e); + } + }); + // doProcessing(Action.REMOVE, nodeId, root); + break; + } + } + } + } + } catch (NullPointerException e) { + LOG.info("Data not available at ", e); + } + } //for + } + + // ---- subclasses for listeners + + /** + * Clustered listener function to select the right node from + * DataObjectModification. Called at all nodes. + */ + private class L1 implements ClusteredDataTreeChangeListener<Node> { + @Override + public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) { + LOG.info("L1 TreeChange enter changes:{}", changes.size()); + onDataTreeChangedHandler(changes); + LOG.info("L1 TreeChange leave"); + } + } + + /** + * Data change, called at leader/master + */ + private class L2 implements DataTreeChangeListener<Node> { + + @Override + public void onDataTreeChanged(@NonNull Collection<DataTreeModification<Node>> changes) { + LOG.info("L2 TreeChange enter changes:{}", changes.size()); + // Do nothing + LOG.info("L2 TreeChange leave"); + } + } + + /* --- private helpers --- */ + private static @Nullable NetconfNode getNetconfNode(Node node) { + return node != null ? node.augmentation(NetconfNode.class) : null; + } + + private static boolean isConnected(NetconfNode nNode) { + return nNode != null ? ConnectionStatus.Connected.equals(nNode.getConnectionStatus()) : false; + } + + private static @Nullable ClusteredConnectionStatus getClusteredConnectionStatus(NetconfNode node) { + return node != null ? node.getClusteredConnectionStatus() : null; + } + + /* -- LOG related functions -- */ + + /** Analyze configuration **/ + private static @Nullable AkkaConfig getAkkaConfig() { + AkkaConfig akkaConfig; + try { + akkaConfig = AkkaConfig.load(); + LOG.debug("akka.conf loaded: " + akkaConfig.toString()); + } catch (Exception e1) { + akkaConfig = null; + LOG.warn("problem loading akka.conf: " + e1.getMessage()); + } + if (akkaConfig != null && akkaConfig.isCluster()) { + LOG.info("cluster mode detected"); + if (GeoConfig.fileExists()) { + try { + LOG.debug("try to load geoconfig"); + GeoConfig.load(); + } catch (Exception err) { + LOG.warn("problem loading geoconfig: " + err.getMessage()); + } + } else { + LOG.debug("no geoconfig file found"); + } + } else { + LOG.info("single node mode detected"); + } + return akkaConfig; + } + + private boolean isNetconfNodeMaster(NetconfNode nNode) { + if (this.isCluster) { + LOG.debug("check if me is responsible for node"); + ClusteredConnectionStatus ccs = nNode.getClusteredConnectionStatus(); + @SuppressWarnings("null") + @NonNull String masterNodeName = ccs == null || ccs.getNetconfMasterNode() == null ? "null" : ccs.getNetconfMasterNode(); + LOG.debug("sdnMasterNode=" + masterNodeName + " and sdnMyNode=" + this.clusterName); + if (!masterNodeName.equals(this.clusterName)) { + LOG.debug("netconf change but me is not master for this node"); + return false; + } + } + return true; + } + + + private void sleepMs(int milliseconds) { + try { + Thread.sleep(milliseconds); + } catch (InterruptedException e) { + LOG.debug("Interrupted sleep"); + // Restore interrupted state... + Thread.currentThread().interrupt(); + } + } + + +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/AkkaConfig.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/AkkaConfig.java new file mode 100644 index 000000000..d0ea0eb69 --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/AkkaConfig.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlAkka; + +import java.io.File; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; + +public class AkkaConfig { + + @SuppressWarnings("unused") + private static final Logger LOG = LoggerFactory.getLogger(AkkaConfig.class); + + private static final String DEFAULT_FILENAME = "configuration/initial/akka.conf"; + private final String filename; + private ClusterConfig cluserConfig; + + public ClusterConfig getClusterConfig() { + return this.cluserConfig; + } + + private AkkaConfig(String filename) { + this.filename = filename; + } + + public AkkaConfig() { + this(null); + } + + @Override + public String toString() { + return "AkkaConfig [filename=" + filename + ", cluserConfig=" + cluserConfig + "]"; + } + + private void loadFromFile() throws Exception { + Config cfg = ConfigFactory.parseFile(new File(this.filename)); + this.cluserConfig = new ClusterConfig(cfg.getConfig("odl-cluster-data").getConfig("akka").getConfig("cluster")); + } + + public boolean isCluster() { + return this.cluserConfig != null ? this.cluserConfig.isCluster() : false; + } + + public boolean isClusterAndFirstNode() { + return isSingleNode() || isCluster() && getClusterConfig().getRoleMemberIndex() == 1; + } + + public static AkkaConfig load() throws Exception { + return load(DEFAULT_FILENAME); + } + + public static AkkaConfig load(String filename) throws Exception { + AkkaConfig cfg = new AkkaConfig(filename); + cfg.loadFromFile(); + return cfg; + } + + public boolean isSingleNode() { + return !this.isCluster(); + } + public static AkkaConfig parse(String content) throws Exception { + Config cfg = ConfigFactory.parseString(content); + AkkaConfig c = new AkkaConfig(); + c.cluserConfig=new ClusterConfig(cfg.getConfig("odl-cluster-data").getConfig("akka").getConfig("cluster")); + return c; + } +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/ClusterConfig.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/ClusterConfig.java new file mode 100644 index 000000000..7f8d6cc78 --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/ClusterConfig.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlAkka; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlGeo.ClusterRoleInfo; +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlGeo.ClusterRoleInfoCollection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.typesafe.config.Config; + +public class ClusterConfig { + + private static final Logger LOG = LoggerFactory.getLogger(ClusterConfig.class); + + private final List<ClusterNodeInfo> seedNodes; + private final ClusterRoleInfoCollection roles; + private ClusterNodeInfo ismeInfo; + + public static ClusterConfig defaultSingleNodeConfig() + { + ClusterConfig cfg=new ClusterConfig(); + cfg.ismeInfo=ClusterNodeInfo.defaultSingleNodeInfo(); + cfg.seedNodes.add(cfg.ismeInfo); + cfg.roles.add(ClusterRoleInfo.defaultSingleNodeRole()); + return cfg; + } + public ClusterConfig() + { + this.seedNodes = new ArrayList<>(); + this.roles = new ClusterRoleInfoCollection(); + + } + public ClusterConfig(Config o) throws Exception { + { + this.seedNodes = new ArrayList<>(); + this.roles = new ClusterRoleInfoCollection(); + List<String> a = o.getStringList("seed-nodes"); + for (int i = 0; i < a.size(); i++) { + ClusterNodeInfo info = new ClusterNodeInfo(a.get(i)); + this.seedNodes.add(info); + } + a = o.getStringList("roles"); + for (int i = 0; i < a.size(); i++) { + ClusterRoleInfo s = new ClusterRoleInfo(a.get(i)); + this.roles.add(s); + } + int idx = this.roles.get(0).getIndex() - 1; + if (idx >= 0 && idx < this.seedNodes.size()) { + this.ismeInfo = this.seedNodes.get(idx); + } else { + this.ismeInfo = null; + } + } + + } + + public boolean isCluster() { + return this.seedNodes != null ? this.seedNodes.size() > 1 : false; + } + + public boolean isMe(ClusterNodeInfo i) { + return this.ismeInfo != null ? this.ismeInfo.equals(i) : false; + } + + public List<ClusterNodeInfo> getSeedNodes() { + return this.seedNodes; + } + + public String getHostName(String defaultValue) { + if (getRoleMemberIndex() > 0 && getRoleMemberIndex() <= seedNodes.size()) { + return this.seedNodes.get(getRoleMemberIndex()-1).getRemoteAddress(); + } else { + LOG.warn("Seednode not available for roleMemberIndex {}. Using default {}",getRoleMember(), defaultValue); + return defaultValue; + } + } + + public String getDBClusterName(String defaultValue) { + String r = null; + if (this.seedNodes != null && this.seedNodes.size() > 0) { + r = String.format("cluster-%s.%d", this.seedNodes.get(0).getRemoteAddress(), this.seedNodes.get(0).getPort()); + } + if (r == null || r.isEmpty()) { + r = defaultValue; + } + return r; + } + public String getClusterSeedNodeName() { + return this.getClusterSeedNodeName(""); + } + public String getClusterSeedNodeName(String defaultValue) { + int idx=this.getRoleMemberIndex()-1; + String r=null; + if(this.seedNodes!=null && idx>=0 && this.seedNodes.size()>0 && this.seedNodes.size()>idx) + { + r=this.seedNodes.get(idx).getSeedNodeName(); + } + if (r == null || r.isEmpty()) { + r = defaultValue; + } + return r; + } + public int getRoleMemberIndex() { + + ClusterRoleInfo role=this.roles.get("member"); + return role!=null?role.getIndex():0; + } + public ClusterRoleInfo getRoleMember() { + return this.roles.get("member"); + } + + @Override + public String toString() { + return "ClusterConfig [seedNodes=" + seedNodes + ", roles=" + roles + ", ismeInfo=" + ismeInfo + "]"; + } + + public int getClusterSize() { + return this.seedNodes == null ? 0 : this.seedNodes.size(); + } + + +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/ClusterNodeInfo.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/ClusterNodeInfo.java new file mode 100644 index 000000000..ef161ad12 --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlAkka/ClusterNodeInfo.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlAkka; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ClusterNodeInfo { + private final String protocol; + private final String clusterName; + private final String remoteAdr; + private final int port; + private final String seedNodeName; + + public static ClusterNodeInfo defaultSingleNodeInfo() { + return new ClusterNodeInfo("akka.tcp","opendaylight-cluster-data","127.0.0.1",2550); + } + + public ClusterNodeInfo(String s) throws Exception { + final String regex = "([a-z.]*):\\/\\/([a-zA-Z0-9-]*)@([a-zA-Z0-9.-]*):([0-9]*)"; + final Pattern pattern = Pattern.compile(regex); + final Matcher matcher = pattern.matcher(s); + if (!matcher.find()) { + throw new Exception("invalid seedNode format"); + } + this.seedNodeName = matcher.group(); + this.protocol = matcher.group(1); + this.clusterName = matcher.group(2); + this.remoteAdr = matcher.group(3); + this.port = Integer.parseInt(matcher.group(4)); + } + + public ClusterNodeInfo(String protocol, String clustername, String remoteadr, int port) { + this.protocol=protocol; + this.clusterName=clustername; + this.remoteAdr=remoteadr; + this.port=port; + this.seedNodeName=this.protocol+"://"+this.clusterName+"@"+this.remoteAdr+":"+this.port; + } + + public String getProtocol() { + return protocol; + } + + public String getClusterName() { + return clusterName; + } + + public String getRemoteAddress() { + return remoteAdr; + } + public String getSeedNodeName() { + return seedNodeName; + } + + public int getPort() { + return port; + } + + @Override + public String toString() { + return "ClusterNodeInfo [protocol=" + protocol + ", clusterName=" + clusterName + ", remoteAdr=" + remoteAdr + + ", port=" + port + ", seedNodeName=" + seedNodeName + "]"; + } + +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/ClusterRoleInfo.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/ClusterRoleInfo.java new file mode 100644 index 000000000..994ef548b --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/ClusterRoleInfo.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlGeo; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ClusterRoleInfo { + private final String Role; + private final int Index; + + public ClusterRoleInfo(String s) throws Exception { + final String regex = "([a-zA-Z]*)-([0-9]*)"; + final Pattern pattern = Pattern.compile(regex); + final Matcher matcher = pattern.matcher(s); + if (!matcher.find()) { + throw new Exception("unexpected role format:"+s); + } + this.Role = matcher.group(1); + this.Index = Integer.parseInt(matcher.group(2)); + } + + private ClusterRoleInfo(String role, int idx) { + this.Role=role; + this.Index=idx; + } + + public static ClusterRoleInfo defaultSingleNodeRole() { + return new ClusterRoleInfo("member",1); + } + + public String getRole() { + return Role; + } + public int getIndex() { + return Index; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Index; + result = prime * result + (Role == null ? 0 : Role.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ClusterRoleInfo other = (ClusterRoleInfo) obj; + if (Index != other.Index) { + return false; + } + if (Role == null) { + if (other.Role != null) { + return false; + } + } else if (!Role.equals(other.Role)) { + return false; + } + return true; + } + @Override + public String toString() { + return "ClusterRoleInfo [Role=" + Role + ", Index=" + Index + "]"; + } +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/ClusterRoleInfoCollection.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/ClusterRoleInfoCollection.java new file mode 100644 index 000000000..478ed8394 --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/ClusterRoleInfoCollection.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlGeo; + +import java.util.ArrayList; + +public class ClusterRoleInfoCollection extends ArrayList<ClusterRoleInfo> { + private static final long serialVersionUID = 1L; + + public ClusterRoleInfo get(String role) { + for (ClusterRoleInfo info : this) { + if (info.getRole().equals(role)) { + return info; + } + } + return null; + } + + public boolean contains(ClusterRoleInfo info) { + if (info == null) { + return false; + } + for (ClusterRoleInfo i : this) { + if (i.equals(info)) { + return true; + } + } + return false; + } +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/GeoConfig.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/GeoConfig.java new file mode 100644 index 000000000..c25f3264b --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/conf/odlGeo/GeoConfig.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.conf.odlGeo; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; + +public class GeoConfig { + + private static final String DEFAULT_FILENAME = "configuration/initial/geo.conf"; + private static final String LUMINA_ROOTNODENAME = "lumina-geo-cluster"; + private final String filename; + private final String rootNodename; + private ClusterRoleInfoCollection primaryRoles; + private ClusterRoleInfoCollection secondayRoles; + private RolesTable rolesTable; + + private GeoConfig() { + this(null); + } + + private GeoConfig(String filename) { + this(filename, LUMINA_ROOTNODENAME); + } + + private GeoConfig(String filename, String rootNodeName) { + this.filename = filename; + this.rootNodename = rootNodeName; + } + + public static boolean fileExists() { + File f = new File(DEFAULT_FILENAME); + return f.exists(); + } + + public static GeoConfig load() throws Exception { + return load(DEFAULT_FILENAME); + } + + public static GeoConfig load(String filename) throws Exception { + GeoConfig cfg = new GeoConfig(filename); + cfg._load(); + return cfg; + } + + private void _load() throws Exception { + this._load(ConfigFactory.parseFile(new File(this.filename))); + } + + private void _load(Config cfg) throws Exception { + this.primaryRoles = new ClusterRoleInfoCollection(); + List<String> a = cfg.getConfig(this.rootNodename).getStringList("primary_roles"); + + for (int i = 0; i < a.size(); i++) { + ClusterRoleInfo s = new ClusterRoleInfo(a.get(i)); + this.primaryRoles.add(s); + } + this.secondayRoles = new ClusterRoleInfoCollection(); + a = cfg.getConfig(this.rootNodename).getStringList("secondary_roles"); + for (int i = 0; i < a.size(); i++) { + ClusterRoleInfo s = new ClusterRoleInfo(a.get(i)); + this.secondayRoles.add(s); + } + this.checkDuplicateRoleEntries(); + this.rolesTable = new RolesTable(cfg.getConfig(this.rootNodename).getConfigList("ip_roles_table")); + } + + private void checkDuplicateRoleEntries() throws Exception { + ClusterRoleInfoCollection duplicateEntries = new ClusterRoleInfoCollection(); + for (ClusterRoleInfo primaryRole : this.primaryRoles) { + if (this.secondayRoles.contains(primaryRole)) { + duplicateEntries.add(primaryRole); + } + } + if (duplicateEntries.size() > 0) { + throw new Exception("duplicate entries found: " + duplicateEntries.toString()); + } + + } + + public static GeoConfig parse(String content) throws Exception { + GeoConfig cfg = new GeoConfig(); + cfg._load(ConfigFactory.parseString(content)); + return cfg; + } + + public ClusterRoleInfoCollection getPrimaryRoles() { + return this.primaryRoles; + } + + public ClusterRoleInfoCollection getSecondaryRoles() { + return this.secondayRoles; + } + + public boolean isPrimary(ClusterRoleInfo roleMember) { + return !this.isSecondary(roleMember); + } + + private boolean isSecondary(ClusterRoleInfo roleMember) { + if (roleMember == null) { + return false; + } + for (ClusterRoleInfo info : this.secondayRoles) { + if (info.equals(roleMember)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "GeoConfig [filename=" + filename + ", rootNodename=" + rootNodename + ", primaryRoles=" + primaryRoles + + ", secondayRoles=" + secondayRoles + ", rolesTable=" + rolesTable + "]"; + } + + public static class RolesTableEntry { + private final ClusterRoleInfo role; + private final String ip; + + public RolesTableEntry(Config c) throws Exception { + this.role = new ClusterRoleInfo(c.getString("role")); + this.ip = c.getString("ip"); + } + + @Override + public String toString() { + return "RolesTableEntry [role=" + role + ", ip=" + ip + "]"; + } + } + public static class RolesTable extends ArrayList<RolesTableEntry> { + private static final long serialVersionUID = -9146218864237487506L; + + public RolesTable(List<? extends Config> configList) throws Exception { + for (Config c : configList) { + this.add(new RolesTableEntry(c)); + } + } + + } + + +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/rpc/NetconfnodeStateServiceRpcApiImpl.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/rpc/NetconfnodeStateServiceRpcApiImpl.java new file mode 100644 index 000000000..9215887ff --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/rpc/NetconfnodeStateServiceRpcApiImpl.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.rpc; + +import org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.VesNotificationListener; +import org.opendaylight.mdsal.binding.api.RpcProviderService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.AttributeChangeNotificationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.FaultNotificationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusOutputBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.NetconfnodeStateService; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.PushAttributeChangeNotificationInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.PushAttributeChangeNotificationOutput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.PushFaultNotificationInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.PushFaultNotificationOutput; +import org.opendaylight.yangtools.concepts.ObjectRegistration; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorType; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.util.concurrent.ListenableFuture; +import java.util.List; + +public class NetconfnodeStateServiceRpcApiImpl implements NetconfnodeStateService, AutoCloseable { + + private static final Logger LOG = LoggerFactory.getLogger(NetconfnodeStateServiceRpcApiImpl.class); + + private final ObjectRegistration<NetconfnodeStateServiceRpcApiImpl> rpcReg; + private RpcApigetStateCallback getStatusCallback; + private final List<VesNotificationListener> vesNotificationListenerList; + + public NetconfnodeStateServiceRpcApiImpl(final RpcProviderService rpcProviderRegistry, + List<VesNotificationListener> vesNotificationListenerList) { + + this.vesNotificationListenerList = vesNotificationListenerList; + + // Register ourselves as the REST API RPC implementation + LOG.info("Register RPC Service " + NetconfnodeStateService.class.getSimpleName()); + rpcReg = rpcProviderRegistry.registerRpcImplementation(NetconfnodeStateService.class, this); + this.getStatusCallback = null; + } + + public NetconfnodeStateServiceRpcApiImpl setStatusCallback(RpcApigetStateCallback getStatusCallback) { + this.getStatusCallback = getStatusCallback; + return this; + } + + @Override + public void close() throws Exception { + LOG.info("Close RPC Service"); + if (rpcReg != null) { + rpcReg.close(); + } + } + + /*------------------------------- + * Interfaces for getting information + */ + @Override + public ListenableFuture<RpcResult<GetStatusOutput>> getStatus(GetStatusInput input) { + + LOG.info("RPC Request: getStatus input: {}", input); + RpcResultBuilder<GetStatusOutput> result; + + try { + GetStatusOutputBuilder outputBuilder = new GetStatusOutputBuilder(); + getStatusCallback.getStatus(input); + result = RpcResultBuilder.success(outputBuilder); + } catch (Exception e) { + result = RpcResultBuilder.failed(); + result.withError(ErrorType.APPLICATION, "Exception", e); + } + return result.buildFuture(); + } + + @Override + public ListenableFuture<RpcResult<PushFaultNotificationOutput>> pushFaultNotification( + PushFaultNotificationInput input) { + + RpcResultBuilder<PushFaultNotificationOutput> result; + try { + FaultNotificationBuilder faultNotificationBuilder = new FaultNotificationBuilder(); + faultNotificationBuilder.fieldsFrom(input); + vesNotificationListenerList.forEach(item -> item.onNotification(faultNotificationBuilder.build())); + result = RpcResultBuilder.success(); + } catch (Exception e) { + result = RpcResultBuilder.failed(); + result.withError(ErrorType.APPLICATION, "Exception", e); + } + return result.buildFuture(); + } + + @Override + public ListenableFuture<RpcResult<PushAttributeChangeNotificationOutput>> pushAttributeChangeNotification( + PushAttributeChangeNotificationInput input) { + RpcResultBuilder<PushAttributeChangeNotificationOutput> result; + try { + AttributeChangeNotificationBuilder attributeChangeNotificationBuilder = new AttributeChangeNotificationBuilder(); + attributeChangeNotificationBuilder.fieldsFrom(input); + vesNotificationListenerList.forEach(item -> item.onNotification(attributeChangeNotificationBuilder.build())); + result = RpcResultBuilder.success(); + } catch (Exception e) { + result = RpcResultBuilder.failed(); + result.withError(ErrorType.APPLICATION, "Exception", e); + } + return result.buildFuture(); + } +} diff --git a/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/rpc/RpcApigetStateCallback.java b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/rpc/RpcApigetStateCallback.java new file mode 100644 index 000000000..1434cf41a --- /dev/null +++ b/sdnr/wt/netconfnode-state-service/provider/src/main/java/org/onap/ccsdk/features/sdnr/wt/netconfnodestateservice/impl/rpc/RpcApigetStateCallback.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + ******************************************************************************/ + +package org.onap.ccsdk.features.sdnr.wt.netconfnodestateservice.impl.rpc; + +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusInput; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netconfnode.state.rev191011.GetStatusOutputBuilder; + +public interface RpcApigetStateCallback { + + GetStatusOutputBuilder getStatus(GetStatusInput input); +} |