/** * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowplugin.openflow.md.core; import com.google.common.util.concurrent.ForwardingBlockingQueue; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.MoreExecutors; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.opendaylight.openflowjava.protocol.spi.connection.SwitchConnectionProvider; import org.opendaylight.openflowplugin.api.OFConstants; import org.opendaylight.openflowplugin.api.openflow.md.core.IMDMessageTranslator; import org.opendaylight.openflowplugin.api.openflow.md.core.NotificationQueueWrapper; import org.opendaylight.openflowplugin.api.openflow.md.core.TranslatorKey; import org.opendaylight.openflowplugin.api.openflow.md.queue.PopListener; import org.opendaylight.openflowplugin.api.openflow.statistics.MessageSpy; import org.opendaylight.openflowplugin.extension.api.core.extension.ExtensionConverterProvider; import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.ConvertorExecutor; import org.opendaylight.openflowplugin.openflow.md.core.session.OFSessionUtil; import org.opendaylight.openflowplugin.openflow.md.core.translator.ErrorTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.ErrorV10Translator; import org.opendaylight.openflowplugin.openflow.md.core.translator.ExperimenterTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.FeaturesV10ToNodeConnectorUpdatedTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.FlowRemovedTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.MultiPartMessageDescToNodeUpdatedTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.MultiPartReplyPortToNodeConnectorUpdatedTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.MultipartReplyTableFeaturesToTableUpdatedTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.MultipartReplyTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.NotificationPlainTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.PacketInTranslator; import org.opendaylight.openflowplugin.openflow.md.core.translator.PacketInV10Translator; import org.opendaylight.openflowplugin.openflow.md.core.translator.PortStatusMessageToNodeConnectorUpdatedTranslator; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.NodeErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.AggregateFlowStatisticsUpdate; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.FlowsStatisticsUpdate; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.table.statistics.rev131215.FlowTableStatisticsUpdate; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupDescStatsUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupFeaturesUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.GroupStatisticsUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterConfigStatsUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterFeaturesUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.MeterStatisticsUpdated; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.BadActionErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.BadInstructionErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.BadMatchErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.BadRequestErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.ExperimenterErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.FlowModErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.GroupModErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.HelloFailedErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.MeterModErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.PortModErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.QueueOpErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.RoleRequestErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.SwitchConfigErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.TableFeaturesErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.node.error.service.rev140410.TableModErrorNotification; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.ErrorMessage; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.ExperimenterMessage; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FlowRemovedMessage; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReplyMessage; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.OfHeader; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PacketInMessage; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.PortStatusMessage; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.PacketReceived; import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.service.rev130709.TransmitPacketInput; import org.opendaylight.yang.gen.v1.urn.opendaylight.port.statistics.rev131214.NodeConnectorStatisticsUpdate; import org.opendaylight.yang.gen.v1.urn.opendaylight.queue.statistics.rev131216.QueueStatisticsUpdate; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.service.rev131026.TableUpdated; import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * */ public class MDController implements IMDController, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(MDController.class); private final ConvertorExecutor convertorExecutor; private Collection<SwitchConnectionProvider> switchConnectionProviders; private ConcurrentMap<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> messageTranslators; private Map<Class<? extends DataObject>, Collection<PopListener<DataObject>>> popListeners; private MessageSpy<DataContainer> messageSpyCounter; final private int OF10 = OFConstants.OFP_VERSION_1_0; final private int OF13 = OFConstants.OFP_VERSION_1_3; private ErrorHandlerSimpleImpl errorHandler; private ExtensionConverterProvider extensionConverterProvider; public MDController(ConvertorExecutor convertorExecutor) { this.convertorExecutor = convertorExecutor; } /** * @return translator mapping */ public Map<TranslatorKey, Collection<IMDMessageTranslator<OfHeader, List<DataObject>>>> getMessageTranslators() { return messageTranslators; } /** * provisioning of translator mapping */ public void init() { LOG.debug("init"); messageTranslators = new ConcurrentHashMap<>(); popListeners = new ConcurrentHashMap<>(); //TODO: move registration to factory addMessageTranslator(ErrorMessage.class, OF10, new ErrorV10Translator()); addMessageTranslator(ErrorMessage.class, OF13, new ErrorTranslator()); addMessageTranslator(FlowRemovedMessage.class, OF10, new FlowRemovedTranslator(convertorExecutor)); addMessageTranslator(FlowRemovedMessage.class, OF13, new FlowRemovedTranslator(convertorExecutor)); addMessageTranslator(PacketInMessage.class,OF10, new PacketInV10Translator()); addMessageTranslator(PacketInMessage.class,OF13, new PacketInTranslator(convertorExecutor)); addMessageTranslator(PortStatusMessage.class,OF10, new PortStatusMessageToNodeConnectorUpdatedTranslator()); addMessageTranslator(PortStatusMessage.class,OF13, new PortStatusMessageToNodeConnectorUpdatedTranslator()); addMessageTranslator(MultipartReplyMessage.class,OF13,new MultiPartReplyPortToNodeConnectorUpdatedTranslator()); addMessageTranslator(MultipartReplyMessage.class,OF10, new MultiPartMessageDescToNodeUpdatedTranslator()); addMessageTranslator(MultipartReplyMessage.class,OF13, new MultiPartMessageDescToNodeUpdatedTranslator()); addMessageTranslator(ExperimenterMessage.class, OF10, new ExperimenterTranslator()); addMessageTranslator(MultipartReplyMessage.class,OF10, new MultipartReplyTranslator(convertorExecutor)); addMessageTranslator(MultipartReplyMessage.class,OF13, new MultipartReplyTranslator(convertorExecutor)); addMessageTranslator(MultipartReplyMessage.class,OF13,new MultipartReplyTableFeaturesToTableUpdatedTranslator(convertorExecutor)); addMessageTranslator(GetFeaturesOutput.class,OF10, new FeaturesV10ToNodeConnectorUpdatedTranslator()); addMessageTranslator(NotificationQueueWrapper.class, OF10, new NotificationPlainTranslator()); addMessageTranslator(NotificationQueueWrapper.class, OF13, new NotificationPlainTranslator()); NotificationPopListener<DataObject> notificationPopListener = new NotificationPopListener<DataObject>(); notificationPopListener.setNotificationProviderService( OFSessionUtil.getSessionManager().getNotificationProviderService()); notificationPopListener.setMessageSpy(messageSpyCounter); //TODO: move registration to factory addMessagePopListener(NodeErrorNotification.class, notificationPopListener); addMessagePopListener(BadActionErrorNotification.class, notificationPopListener); addMessagePopListener(BadInstructionErrorNotification.class, notificationPopListener); addMessagePopListener(BadMatchErrorNotification.class, notificationPopListener); addMessagePopListener(BadRequestErrorNotification.class, notificationPopListener); addMessagePopListener(ExperimenterErrorNotification.class, notificationPopListener); addMessagePopListener(FlowModErrorNotification.class, notificationPopListener); addMessagePopListener(GroupModErrorNotification.class, notificationPopListener); addMessagePopListener(HelloFailedErrorNotification.class, notificationPopListener); addMessagePopListener(MeterModErrorNotification.class, notificationPopListener); addMessagePopListener(PortModErrorNotification.class, notificationPopListener); addMessagePopListener(QueueOpErrorNotification.class, notificationPopListener); addMessagePopListener(RoleRequestErrorNotification.class, notificationPopListener); addMessagePopListener(SwitchConfigErrorNotification.class, notificationPopListener); addMessagePopListener(TableFeaturesErrorNotification.class, notificationPopListener); addMessagePopListener(TableModErrorNotification.class, notificationPopListener); addMessagePopListener(NodeConnectorUpdated.class,notificationPopListener); addMessagePopListener(NodeConnectorRemoved.class,notificationPopListener); addMessagePopListener(PacketReceived.class,notificationPopListener); addMessagePopListener(TransmitPacketInput.class, notificationPopListener); addMessagePopListener(NodeUpdated.class, notificationPopListener); addMessagePopListener(NodeRemoved.class, notificationPopListener); addMessagePopListener(SwitchFlowRemoved.class, notificationPopListener); addMessagePopListener(TableUpdated.class, notificationPopListener); //Notification registration for flow statistics addMessagePopListener(FlowsStatisticsUpdate.class, notificationPopListener); addMessagePopListener(AggregateFlowStatisticsUpdate.class, notificationPopListener); //Notification registrations for group-statistics addMessagePopListener(GroupStatisticsUpdated.class, notificationPopListener); addMessagePopListener(GroupFeaturesUpdated.class, notificationPopListener); addMessagePopListener(GroupDescStatsUpdated.class, notificationPopListener); //Notification registrations for meter-statistics addMessagePopListener(MeterStatisticsUpdated.class, notificationPopListener); addMessagePopListener(MeterConfigStatsUpdated.class, notificationPopListener); addMessagePopListener(MeterFeaturesUpdated.class, notificationPopListener); //Notification registration for port-statistics addMessagePopListener(NodeConnectorStatisticsUpdate.class, notificationPopListener); //Notification registration for flow-table statistics addMessagePopListener(FlowTableStatisticsUpdate.class, notificationPopListener); //Notification registration for queue-statistics addMessagePopListener(QueueStatisticsUpdate.class, notificationPopListener); // Push the updated Listeners to Session Manager which will be then picked up by ConnectionConductor eventually OFSessionUtil.getSessionManager().setTranslatorMapping(messageTranslators); OFSessionUtil.getSessionManager().setPopListenerMapping(popListeners); OFSessionUtil.getSessionManager().setMessageSpy(messageSpyCounter); // prepare worker pool for rpc // TODO: get size from configSubsystem int rpcThreadLimit = 10; ListeningExecutorService rpcPoolDelegator = createRpcPoolSpyDecorated(rpcThreadLimit, messageSpyCounter); OFSessionUtil.getSessionManager().setRpcPool(rpcPoolDelegator); OFSessionUtil.getSessionManager().setExtensionConverterProvider(extensionConverterProvider); } /** * @param rpcThreadLimit * @param messageSpy * @return */ private static ListeningExecutorService createRpcPoolSpyDecorated(final int rpcThreadLimit, final MessageSpy<DataContainer> messageSpy) { final BlockingQueue<Runnable> delegate = new LinkedBlockingQueue<>(100000); final BlockingQueue<Runnable> queue = new ForwardingBlockingQueue<Runnable>() { @Override protected BlockingQueue<Runnable> delegate() { return delegate; } @Override public boolean offer(final Runnable r) { // ThreadPoolExecutor will spawn a new thread after core size is reached only // if the queue.offer returns false. return false; } }; ThreadPoolLoggingExecutor rpcPool = new ThreadPoolLoggingExecutor(rpcThreadLimit, rpcThreadLimit, 0L, TimeUnit.MILLISECONDS, queue, "OFRpc"); rpcPool.setRejectedExecutionHandler(new RejectedExecutionHandler() { @Override public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) { try { executor.getQueue().put(r); } catch (InterruptedException e) { throw new RejectedExecutionException("Interrupted while waiting on queue", e); } } }); ListeningExecutorService listeningRpcPool = MoreExecutors.listeningDecorator(rpcPool); RpcListeningExecutorService rpcPoolDecorated = new RpcListeningExecutorService(listeningRpcPool); rpcPoolDecorated.setMessageSpy(messageSpy); return rpcPoolDecorated; } /** * @param switchConnectionProviders * the switchConnectionProviders to set */ public void setSwitchConnectionProviders(final Collection<SwitchConnectionProvider> switchConnectionProviders) { this.switchConnectionProviders = switchConnectionProviders; } /** * Function called by dependency manager after "init ()" is called and after * the services provided by the class are registered in the service registry * */ public void start() { LOG.debug("starting .."); LOG.debug("switchConnectionProvider: " + switchConnectionProviders); // setup handler SwitchConnectionHandlerImpl switchConnectionHandler = new SwitchConnectionHandlerImpl(); switchConnectionHandler.setMessageSpy(messageSpyCounter); errorHandler = new ErrorHandlerSimpleImpl(); switchConnectionHandler.setErrorHandler(errorHandler); switchConnectionHandler.init(); List<ListenableFuture<Boolean>> starterChain = new ArrayList<>(switchConnectionProviders.size()); for (SwitchConnectionProvider switchConnectionPrv : switchConnectionProviders) { switchConnectionPrv.setSwitchConnectionHandler(switchConnectionHandler); ListenableFuture<Boolean> isOnlineFuture = switchConnectionPrv.startup(); starterChain.add(isOnlineFuture); } Future<List<Boolean>> srvStarted = Futures.allAsList(starterChain); } /** * Function called by the dependency manager before the services exported by * the component are unregistered, this will be followed by a "destroy ()" * calls * */ public void stop() { LOG.debug("stopping"); List<ListenableFuture<Boolean>> stopChain = new ArrayList<>(switchConnectionProviders.size()); try { for (SwitchConnectionProvider switchConnectionPrv : switchConnectionProviders) { ListenableFuture<Boolean> shutdown = switchConnectionPrv.shutdown(); stopChain.add(shutdown); } Futures.allAsList(stopChain).get(5000, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { LOG.warn("failed to stop MDController: {}", e.getMessage()); LOG.debug("failed to stop MDController.. ", e); } close(); } /** * Function called by the dependency manager when at least one dependency * become unsatisfied or when the component is shutting down because for * example bundle is being stopped. * */ public void destroy() { close(); } @Override public void addMessageTranslator(final Class<? extends DataObject> messageType, final int version, final IMDMessageTranslator<OfHeader, List<DataObject>> translator) { TranslatorKey tKey = new TranslatorKey(version, messageType.getName()); Collection<IMDMessageTranslator<OfHeader, List<DataObject>>> existingValues = messageTranslators.get(tKey); if (existingValues == null) { existingValues = new LinkedHashSet<>(); messageTranslators.put(tKey, existingValues); } existingValues.add(translator); LOG.debug("{} is now translated by {}", messageType, translator); } @Override public void removeMessageTranslator(final Class<? extends DataObject> messageType, final int version, final IMDMessageTranslator<OfHeader, List<DataObject>> translator) { TranslatorKey tKey = new TranslatorKey(version, messageType.getName()); Collection<IMDMessageTranslator<OfHeader, List<DataObject>>> values = messageTranslators.get(tKey); if (values != null) { values.remove(translator); if (values.isEmpty()) { messageTranslators.remove(tKey); } LOG.debug("{} is now removed from translators", translator); } } @Override public void addMessagePopListener(final Class<? extends DataObject> messageType, final PopListener<DataObject> popListener) { Collection<PopListener<DataObject>> existingValues = popListeners.get(messageType); if (existingValues == null) { existingValues = new LinkedHashSet<>(); popListeners.put(messageType, existingValues); } existingValues.add(popListener); LOG.debug("{} is now popListened by {}", messageType, popListener); } @Override public void removeMessagePopListener(final Class<? extends DataObject> messageType, final PopListener<DataObject> popListener) { Collection<PopListener<DataObject>> values = popListeners.get(messageType); if (values != null) { values.remove(popListener); if (values.isEmpty()) { popListeners.remove(messageType); } LOG.debug("{} is now removed from popListeners", popListener); } } /** * @param messageSpyCounter the messageSpyCounter to set */ public void setMessageSpyCounter( final MessageSpy<DataContainer> messageSpyCounter) { this.messageSpyCounter = messageSpyCounter; } @Override public void close() { LOG.debug("close"); messageSpyCounter = null; messageTranslators = null; popListeners = null; for (SwitchConnectionProvider switchConnectionPrv : switchConnectionProviders) { switchConnectionPrv.setSwitchConnectionHandler(null); } switchConnectionProviders = null; OFSessionUtil.releaseSessionManager(); errorHandler = null; } /** * @param extensionConverterProvider extension convertor provider */ public void setExtensionConverterProvider(ExtensionConverterProvider extensionConverterProvider) { this.extensionConverterProvider = extensionConverterProvider; } }