/** * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.openflowplugin.impl.device; import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import io.netty.util.HashedWheelTimer; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.annotation.CheckForNull; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext; import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext; import org.opendaylight.openflowplugin.api.openflow.device.DeviceManager; import org.opendaylight.openflowplugin.api.openflow.device.DeviceState; import org.opendaylight.openflowplugin.api.openflow.device.RequestContext; import org.opendaylight.openflowplugin.api.openflow.device.RequestContextStack; import org.opendaylight.openflowplugin.api.openflow.device.TranslatorLibrary; import org.opendaylight.openflowplugin.api.openflow.device.Xid; import org.opendaylight.openflowplugin.api.openflow.device.handlers.DeviceInitializationPhaseHandler; import org.opendaylight.openflowplugin.api.openflow.device.handlers.MultiMsgCollector; import org.opendaylight.openflowplugin.api.openflow.rpc.RpcManager; import org.opendaylight.openflowplugin.impl.common.MultipartRequestInputFactory; import org.opendaylight.openflowplugin.impl.common.NodeStaticReplyTranslatorUtil; import org.opendaylight.openflowplugin.impl.device.listener.OpenflowProtocolListenerFullImpl; import org.opendaylight.openflowplugin.impl.rpc.RequestContextImpl; import org.opendaylight.openflowplugin.impl.services.OFJResult2RequestCtxFuture; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.NodeGroupFeatures; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeatures; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.MultipartType; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.MultipartReply; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.MultipartReplyBody; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyDescCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyGroupFeaturesCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyMeterFeaturesCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.MultipartReplyTableFeaturesCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.desc._case.MultipartReplyDesc; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.group.features._case.MultipartReplyGroupFeatures; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.meter.features._case.MultipartReplyMeterFeatures; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.multipart.reply.multipart.reply.body.multipart.reply.table.features._case.MultipartReplyTableFeatures; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * */ public class DeviceManagerImpl implements DeviceManager { private static final Logger LOG = LoggerFactory.getLogger(DeviceManagerImpl.class); private static final long TICK_DURATION = 500; // 0.5 sec. private final RpcManager rpcManager; private final DataBroker dataBroker; private final HashedWheelTimer hashedWheelTimer; private RequestContextStack dummyRequestContextStack; private TranslatorLibrary translatorLibrary; private DeviceInitializationPhaseHandler deviceInitPhaseHandler; public DeviceManagerImpl(@Nonnull final RpcManager rpcManager, @Nonnull final DataBroker dataBroker) { this.rpcManager = Preconditions.checkNotNull(rpcManager); this.dataBroker = Preconditions.checkNotNull(dataBroker); hashedWheelTimer = new HashedWheelTimer(TICK_DURATION, TimeUnit.MILLISECONDS, 10); dummyRequestContextStack = new RequestContextStack() { @Override public void forgetRequestContext(final RequestContext requestContext) { //NOOP } @Override public SettableFuture> storeOrFail(final RequestContext data) { return data.getFuture(); } @Override public RequestContext createRequestContext() { return new RequestContextImpl<>(this); } }; } @Override public void setDeviceInitializationPhaseHandler(final DeviceInitializationPhaseHandler handler) { deviceInitPhaseHandler = handler; } @Override public void onDeviceContextLevelUp(final DeviceContext deviceContext) { // final phase - we have to add new Device to MD-SAL DataStore Preconditions.checkNotNull(deviceContext != null); ((DeviceContextImpl) deviceContext).submitTransaction(); } @Override public void deviceConnected(@CheckForNull final ConnectionContext connectionContext) { Preconditions.checkArgument(connectionContext != null); final DeviceState deviceState = new DeviceStateImpl(connectionContext.getFeatures(), connectionContext.getNodeId()); final DeviceContextImpl deviceContext = new DeviceContextImpl(connectionContext, deviceState, dataBroker, hashedWheelTimer); deviceContext.setTranslatorLibrary(translatorLibrary); final OpenflowProtocolListenerFullImpl messageListener = new OpenflowProtocolListenerFullImpl( connectionContext.getConnectionAdapter(), deviceContext); connectionContext.getConnectionAdapter().setMessageListener(messageListener); final ListenableFuture>> replyDesc = getNodeStaticInfo(messageListener, MultipartType.OFPMPDESC, deviceContext, deviceState.getNodeInstanceIdentifier(), deviceState.getVersion()); final ListenableFuture>> replyMeterFeature = getNodeStaticInfo(messageListener, MultipartType.OFPMPMETERFEATURES, deviceContext, deviceState.getNodeInstanceIdentifier(), deviceState.getVersion()); final ListenableFuture>> replyGroupFeatures = getNodeStaticInfo(messageListener, MultipartType.OFPMPGROUPFEATURES, deviceContext, deviceState.getNodeInstanceIdentifier(), deviceState.getVersion()); /* final ListenableFuture>> replyTableFeatures = getNodeStaticInfo(messageListener, MultipartType.OFPMPTABLEFEATURES, deviceContext, deviceState.getNodeInstanceIdentifier(), deviceState.getVersion()); */ /* final ListenableFuture>>> deviceFeaturesFuture = Futures.allAsList(Arrays.asList(replyDesc, replyMeterFeature, replyGroupFeatures, replyTableFeatures)); */ final ListenableFuture>>> deviceFeaturesFuture = Futures.allAsList(Arrays.asList(replyDesc, replyMeterFeature, replyGroupFeatures)); Futures.addCallback(deviceFeaturesFuture, new FutureCallback>>>() { @Override public void onSuccess(final List>> result) { // wake up statistics deviceInitPhaseHandler.onDeviceContextLevelUp(deviceContext); } @Override public void onFailure(final Throwable t) { // FIXME : remove session } }); } @Override public TranslatorLibrary oook() { return translatorLibrary; } @Override public void setTranslatorLibrary(final TranslatorLibrary translatorLibrary) { this.translatorLibrary = translatorLibrary; } private ListenableFuture>> getNodeStaticInfo(final MultiMsgCollector multiMsgCollector, final MultipartType type, final DeviceContext deviceContext, final InstanceIdentifier nodeII, final short version) { final Xid xid = deviceContext.getNextXid(); final RequestContext> requestContext = dummyRequestContextStack.createRequestContext(); multiMsgCollector.registerMultipartXid(xid.getValue()); Futures.addCallback(requestContext.getFuture(), new FutureCallback>>() { @Override public void onSuccess(final RpcResult> rpcResult) { final List result = rpcResult.getResult(); if (result != null) { translateAndWriteReply(type, deviceContext, nodeII, result); } else { final Iterator rpcErrorIterator = rpcResult.getErrors().iterator(); while (rpcErrorIterator.hasNext()) { final Throwable t = rpcErrorIterator.next().getCause(); LOG.info("Failed to retrieve static node {} info: {}", type, t.getMessage()); LOG.trace("Detailed error:", t); } if (MultipartType.OFPMPTABLE.equals(type)) { makeEmptyTables(deviceContext, nodeII, deviceContext.getPrimaryConnectionContext().getFeatures().getTables()); } } } @Override public void onFailure(final Throwable t) { LOG.info("Failed to retrieve static node {} info: {}", type, t.getMessage()); } }); final Future> rpcFuture = deviceContext.getPrimaryConnectionContext().getConnectionAdapter() .multipartRequest(MultipartRequestInputFactory.makeMultipartRequestInput(xid.getValue(), version, type)); OFJResult2RequestCtxFuture OFJResult2RequestCtxFuture = new OFJResult2RequestCtxFuture(requestContext, deviceContext); OFJResult2RequestCtxFuture.processResultFromOfJava(rpcFuture); return requestContext.getFuture(); } // FIXME : remove after ovs tableFeatures fix private static void makeEmptyTables(final DeviceContext dContext, final InstanceIdentifier nodeII, final Short nrOfTables) { for (int i = 0; i < nrOfTables; i++) { final short tId = (short) i; final InstanceIdentifier tableII = nodeII.augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tId)); dContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, tableII, new TableBuilder().setId(tId).build()); } } private static void translateAndWriteReply(final MultipartType type, final DeviceContext dContext, final InstanceIdentifier nodeII, final Collection result) { for (final MultipartReply reply : result) { final MultipartReplyBody body = reply.getMultipartReplyBody(); switch (type) { case OFPMPDESC: Preconditions.checkArgument(body instanceof MultipartReplyDescCase); final MultipartReplyDesc replyDesc = ((MultipartReplyDescCase) body).getMultipartReplyDesc(); final FlowCapableNode fcNode = NodeStaticReplyTranslatorUtil.nodeDescTranslator(replyDesc); final InstanceIdentifier fNodeII = nodeII.augmentation(FlowCapableNode.class); dContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, fNodeII, fcNode); break; case OFPMPTABLEFEATURES: Preconditions.checkArgument(body instanceof MultipartReplyTableFeaturesCase); final MultipartReplyTableFeatures tableFeatures = ((MultipartReplyTableFeaturesCase) body).getMultipartReplyTableFeatures(); final List tables = NodeStaticReplyTranslatorUtil.nodeTableFeatureTranslator(tableFeatures); for (final TableFeatures table : tables) { final Short tableId = table.getTableId(); final InstanceIdentifier
tableII = nodeII.augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tableId)); dContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, tableII, new TableBuilder().setId(tableId).setTableFeatures(Collections.singletonList(table)).build()); } break; case OFPMPMETERFEATURES: Preconditions.checkArgument(body instanceof MultipartReplyMeterFeaturesCase); final MultipartReplyMeterFeatures meterFeatures = ((MultipartReplyMeterFeaturesCase) body).getMultipartReplyMeterFeatures(); final NodeMeterFeatures mFeature = NodeStaticReplyTranslatorUtil.nodeMeterFeatureTranslator(meterFeatures); final InstanceIdentifier mFeatureII = nodeII.augmentation(NodeMeterFeatures.class); dContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, mFeatureII, mFeature); break; case OFPMPGROUPFEATURES: Preconditions.checkArgument(body instanceof MultipartReplyGroupFeaturesCase); final MultipartReplyGroupFeatures groupFeatures = ((MultipartReplyGroupFeaturesCase) body).getMultipartReplyGroupFeatures(); final NodeGroupFeatures gFeature = NodeStaticReplyTranslatorUtil.nodeGroupFeatureTranslator(groupFeatures); final InstanceIdentifier gFeatureII = nodeII.augmentation(NodeGroupFeatures.class); dContext.writeToTransaction(LogicalDatastoreType.OPERATIONAL, gFeatureII, gFeature); break; default: throw new IllegalArgumentException("Unnexpected MultipartType " + type); } } } }