/**
* Copyright (c) 2015, 2017 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.openflowplugin.impl.device;
import com.google.common.collect.Iterators;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import io.netty.util.HashedWheelTimer;
import io.netty.util.internal.ConcurrentSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.openflowjava.protocol.api.connection.OutboundQueueHandlerRegistration;
import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
import org.opendaylight.openflowplugin.api.openflow.connection.OutboundQueueProvider;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager;
import org.opendaylight.openflowplugin.api.openflow.device.TranslatorLibrary;
import org.opendaylight.openflowplugin.api.openflow.statistics.ofpspecific.MessageSpy;
import org.opendaylight.openflowplugin.extension.api.ExtensionConverterProviderKeeper;
import org.opendaylight.openflowplugin.extension.api.core.extension.ExtensionConverterProvider;
import org.opendaylight.openflowplugin.impl.connection.OutboundQueueProviderImpl;
import org.opendaylight.openflowplugin.impl.device.initialization.DeviceInitializerProvider;
import org.opendaylight.openflowplugin.impl.device.listener.OpenflowProtocolListenerFullImpl;
import org.opendaylight.openflowplugin.impl.services.sal.SalRoleServiceImpl;
import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemovedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdatedBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
*/
public class DeviceManagerImpl implements DeviceManager, ExtensionConverterProviderKeeper {
private static final Logger LOG = LoggerFactory.getLogger(DeviceManagerImpl.class);
private long globalNotificationQuota;
private boolean switchFeaturesMandatory;
private boolean isFlowRemovedNotificationOn;
private boolean skipTableFeatures;
private static final int SPY_RATE = 10;
private final DataBroker dataBroker;
private final DeviceInitializerProvider deviceInitializerProvider;
private final ConvertorExecutor convertorExecutor;
private TranslatorLibrary translatorLibrary;
private final ConcurrentMap<DeviceInfo, DeviceContext> deviceContexts = new ConcurrentHashMap<>();
private final Set<DeviceInfo> notificationCreateNodeSend = new ConcurrentSet<>();
private long barrierIntervalNanos;
private int barrierCountLimit;
private ExtensionConverterProvider extensionConverterProvider;
private ScheduledThreadPoolExecutor spyPool;
private final NotificationPublishService notificationPublishService;
private final MessageSpy messageSpy;
private final HashedWheelTimer hashedWheelTimer;
private final boolean useSingleLayerSerialization;
public DeviceManagerImpl(@Nonnull final DataBroker dataBroker,
@Nonnull final MessageSpy messageSpy,
@Nullable final NotificationPublishService notificationPublishService,
@Nonnull final HashedWheelTimer hashedWheelTimer,
@Nonnull final ConvertorExecutor convertorExecutor,
@Nonnull final DeviceInitializerProvider deviceInitializerProvider,
final boolean useSingleLayerSerialization) {
this.dataBroker = dataBroker;
this.deviceInitializerProvider = deviceInitializerProvider;
this.useSingleLayerSerialization = useSingleLayerSerialization;
/* merge empty nodes to oper DS to predict any problems with missing parent for Node */
final WriteTransaction tx = dataBroker.newWriteOnlyTransaction();
final NodesBuilder nodesBuilder = new NodesBuilder();
nodesBuilder.setNode(Collections.<Node>emptyList());
tx.merge(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.create(Nodes.class), nodesBuilder.build());
try {
tx.submit().get();
} catch (ExecutionException | InterruptedException e) {
LOG.error("Creation of node failed.", e);
throw new IllegalStateException(e);
}
this.convertorExecutor = convertorExecutor;
this.hashedWheelTimer = hashedWheelTimer;
this.spyPool = new ScheduledThreadPoolExecutor(1);
this.notificationPublishService = notificationPublishService;
this.messageSpy = messageSpy;
}
@Override
public TranslatorLibrary oook() {
return translatorLibrary;
}
@Override
public void setTranslatorLibrary(final TranslatorLibrary translatorLibrary) {
this.translatorLibrary = translatorLibrary;
}
@Override
public void close() {
for (final Iterator<DeviceContext> iterator = Iterators.consumingIterator(deviceContexts.values().iterator());
iterator.hasNext();) {
final DeviceContext deviceCtx = iterator.next();
deviceCtx.shutdownConnection();
deviceCtx.shuttingDownDataStoreTransactions();
}
Optional.ofNullable(spyPool).ifPresent(ScheduledThreadPoolExecutor::shutdownNow);
spyPool = null;
}
@Override
public void initialize() {
spyPool.scheduleAtFixedRate(messageSpy, SPY_RATE, SPY_RATE, TimeUnit.SECONDS);
}
@Override
public void setExtensionConverterProvider(final ExtensionConverterProvider extensionConverterProvider) {
this.extensionConverterProvider = extensionConverterProvider;
}
@Override
public ExtensionConverterProvider getExtensionConverterProvider() {
return extensionConverterProvider;
}
@Override
public void setFlowRemovedNotificationOn(boolean isNotificationFlowRemovedOff) {
this.isFlowRemovedNotificationOn = isNotificationFlowRemovedOff;
}
@Override
public boolean isFlowRemovedNotificationOn() {
return this.isFlowRemovedNotificationOn;
}
@Override
public void setGlobalNotificationQuota(final long globalNotificationQuota) {
this.globalNotificationQuota = globalNotificationQuota;
}
@Override
public void setSwitchFeaturesMandatory(final boolean switchFeaturesMandatory) {
this.switchFeaturesMandatory = switchFeaturesMandatory;
}
@Override
public void setSkipTableFeatures(boolean skipTableFeaturesValue) {
skipTableFeatures = skipTableFeaturesValue;
}
@Override
public void setBarrierCountLimit(final int barrierCountLimit) {
this.barrierCountLimit = barrierCountLimit;
}
@Override
public void setBarrierInterval(final long barrierTimeoutLimit) {
this.barrierIntervalNanos = TimeUnit.MILLISECONDS.toNanos(barrierTimeoutLimit);
}
@Override
public CheckedFuture<Void, TransactionCommitFailedException> removeDeviceFromOperationalDS(final KeyedInstanceIdentifier<Node, NodeKey> ii) {
final WriteTransaction delWtx = dataBroker.newWriteOnlyTransaction();
delWtx.delete(LogicalDatastoreType.OPERATIONAL, ii);
final CheckedFuture<Void, TransactionCommitFailedException> delFuture = delWtx.submit();
Futures.addCallback(delFuture, new FutureCallback<Void>() {
@Override
public void onSuccess(final Void result) {
if (LOG.isDebugEnabled()) {
LOG.debug("Delete Node {} was successful", ii);
}
}
@Override
public void onFailure(@Nonnull final Throwable t) {
LOG.warn("Delete node {} failed with exception {}", ii, t);
}
});
return delFuture;
}
@Override
public CheckedFuture<Void, TransactionCommitFailedException> removeDeviceFromOperationalDS(final DeviceInfo deviceInfo) {
return this.removeDeviceFromOperationalDS(deviceInfo.getNodeInstanceIdentifier());
}
public DeviceContext createContext(@Nonnull final ConnectionContext connectionContext) {
LOG.info("ConnectionEvent: Device connected to controller, Device:{}, NodeId:{}",
connectionContext.getConnectionAdapter().getRemoteAddress(),
connectionContext.getDeviceInfo().getNodeId());
connectionContext.getConnectionAdapter().setPacketInFiltering(true);
final OutboundQueueProvider outboundQueueProvider
= new OutboundQueueProviderImpl(connectionContext.getDeviceInfo().getVersion());
connectionContext.setOutboundQueueProvider(outboundQueueProvider);
final OutboundQueueHandlerRegistration<OutboundQueueProvider> outboundQueueHandlerRegistration =
connectionContext.getConnectionAdapter().registerOutboundQueueHandler(
outboundQueueProvider,
barrierCountLimit,
barrierIntervalNanos);
connectionContext.setOutboundQueueHandleRegistration(outboundQueueHandlerRegistration);
final DeviceContext deviceContext = new DeviceContextImpl(
connectionContext,
dataBroker,
messageSpy,
translatorLibrary,
this,
convertorExecutor,
skipTableFeatures,
hashedWheelTimer,
useSingleLayerSerialization,
deviceInitializerProvider);
deviceContext.setSalRoleService(new SalRoleServiceImpl(deviceContext, deviceContext));
deviceContext.setSwitchFeaturesMandatory(switchFeaturesMandatory);
((ExtensionConverterProviderKeeper) deviceContext).setExtensionConverterProvider(extensionConverterProvider);
deviceContext.setNotificationPublishService(notificationPublishService);
deviceContexts.put(connectionContext.getDeviceInfo(), deviceContext);
updatePacketInRateLimiters();
final OpenflowProtocolListenerFullImpl messageListener = new OpenflowProtocolListenerFullImpl(
connectionContext.getConnectionAdapter(), deviceContext);
connectionContext.getConnectionAdapter().setMessageListener(messageListener);
return deviceContext;
}
private void updatePacketInRateLimiters() {
synchronized (deviceContexts) {
final int deviceContextsSize = deviceContexts.size();
if (deviceContextsSize > 0) {
long freshNotificationLimit = globalNotificationQuota / deviceContextsSize;
if (freshNotificationLimit < 100) {
freshNotificationLimit = 100;
}
if (LOG.isDebugEnabled()) {
LOG.debug("fresh notification limit = {}", freshNotificationLimit);
}
for (final DeviceContext deviceContext : deviceContexts.values()) {
deviceContext.updatePacketInRateLimit(freshNotificationLimit);
}
}
}
}
private void sendNodeRemovedNotification(final DeviceInfo deviceInfo) {
notificationCreateNodeSend.remove(deviceInfo);
NodeRemovedBuilder builder = new NodeRemovedBuilder();
builder.setNodeRef(new NodeRef(deviceInfo.getNodeInstanceIdentifier()));
if (LOG.isDebugEnabled()) {
LOG.debug("Publishing node removed notification for {}", deviceInfo.getLOGValue());
}
notificationPublishService.offerNotification(builder.build());
}
@Override
public void onDeviceRemoved(final DeviceInfo deviceInfo) {
this.sendNodeRemovedNotification(deviceInfo);
deviceContexts.remove(deviceInfo);
if (LOG.isDebugEnabled()) {
LOG.debug("Device context removed for node {}", deviceInfo.getLOGValue());
}
if (deviceContexts.size() > 0) {
this.updatePacketInRateLimiters();
}
}
@Override
public long getBarrierIntervalNanos() {
return barrierIntervalNanos;
}
@Override
public int getBarrierCountLimit() {
return barrierCountLimit;
}
@Override
public void sendNodeAddedNotification(@Nonnull final DeviceInfo deviceInfo) {
if (!notificationCreateNodeSend.contains(deviceInfo)) {
notificationCreateNodeSend.add(deviceInfo);
NodeUpdatedBuilder builder = new NodeUpdatedBuilder();
builder.setId(deviceInfo.getNodeId());
builder.setNodeRef(new NodeRef(deviceInfo.getNodeInstanceIdentifier()));
if (LOG.isDebugEnabled()) {
LOG.debug("Publishing node added notification for {}", deviceInfo.getLOGValue());
}
notificationPublishService.offerNotification(builder.build());
}
}
}