From a29553293e059ea656ec0bb3339d6e261ecf081b Mon Sep 17 00:00:00 2001 From: Vaclav Demcak Date: Sat, 28 Mar 2015 18:43:10 +0100 Subject: [PATCH] PostHandshakeNodeProducer help class Note: PostHandshake process has to populate complex static information about new FlowCapableNode before add DataStore process. But whole postHandsake process contains a lot of functionality which is related only this short time window so we put it to one class together. This new helper has to be forgotten after succesfull add to DataStore process and it doesn't need to be a part of DeviceManager code. * settableFuture produce new Node with all device info and features * collecting Devide description * collecting Device meterFutures * collecting Device grupFutures * collecting Device tableFutures (TODO : add DataStore model and finish impl) (Tests and possible fixies for this class will be a part of DeviceManager#deviceConnected method implemenation and its test suite) Change-Id: I84fdd3004f211590d5066711518490cd134d6573 Signed-off-by: Vaclav Demcak --- .../device/PostHandshakeNodeProducer.java | 362 ++++++++++++++++++ 1 file changed, 362 insertions(+) create mode 100644 openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/PostHandshakeNodeProducer.java diff --git a/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/PostHandshakeNodeProducer.java b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/PostHandshakeNodeProducer.java new file mode 100644 index 0000000000..1e1e4778e5 --- /dev/null +++ b/openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/device/PostHandshakeNodeProducer.java @@ -0,0 +1,362 @@ +/** + * 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 java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Future; +import javax.annotation.Nonnull; +import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext; +import org.opendaylight.openflowplugin.api.openflow.device.XidGenerator; +import org.opendaylight.openflowplugin.impl.common.MultipartRequestInputFactory; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.Counter32; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeBuilder; +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.group.statistics.rev131111.NodeGroupFeaturesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.statistics.rev131111.group.features.GroupFeaturesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.Chaining; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.ChainingChecks; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupAll; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupCapability; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupFf; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupIndirect; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupSelect; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.SelectLiveness; +import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.SelectWeight; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeatures; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.NodeMeterFeaturesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.statistics.rev131111.nodes.node.MeterFeaturesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterBand; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterBandDrop; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterBandDscpRemark; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterBurst; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterCapability; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterKbps; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterPktps; +import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterStats; +import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.common.types.rev130731.ActionType; +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.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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * openflowplugin-impl + * org.opendaylight.openflowplugin.impl.device + * + * Class is a helper to prepare FlowCapableNode with whole internal future structure. + * Everything is realized by {@link ListenableFuture} objects. + * + * @author Vaclav Demcak + * + * Created: Mar 27, 2015 + */ +class PostHandshakeNodeProducer { + + private static final Logger LOG = LoggerFactory.getLogger(PostHandshakeNodeProducer.class); + + private final Map> futures; + private final XidGenerator xidGenerator; + private final ConnectionContext connectionContext; + private SettableFuture deviceFuture; + + public PostHandshakeNodeProducer (@Nonnull final ConnectionContext connectionContext, + @Nonnull final XidGenerator xidGenerator) { + this.xidGenerator = Preconditions.checkNotNull(xidGenerator); + this.connectionContext = Preconditions.checkNotNull(connectionContext); + futures = new ConcurrentHashMap<>(); + } + + public ListenableFuture prepareFlowCapabeNode() { + deviceFuture = SettableFuture.create(); + final NodeBuilder nodeBuilder = new NodeBuilder(); + nodeBuilder.setId(connectionContext.getNodeId()); + nodeBuilder.setKey(new NodeKey(connectionContext.getNodeId())); + final Short version = connectionContext.getFeatures().getVersion(); + + final long nodeDescXid = xidGenerator.generate().getValue(); + futures.put(nodeDescXid, queryDescription(connectionContext, nodeDescXid, nodeBuilder)); + + final long meterFutureXid = xidGenerator.generate().getValue(); + futures.put(meterFutureXid, queryMeterFuture(connectionContext, meterFutureXid, nodeBuilder)); + + final long groupFutureXid = xidGenerator.generate().getValue(); + futures.put(groupFutureXid, queryGroupFuture(connectionContext, groupFutureXid, nodeBuilder)); + + final long tableFutureXid = xidGenerator.generate().getValue(); + futures.put(tableFutureXid, queryTableFuture(connectionContext, tableFutureXid, nodeBuilder)); + + connectionContext.getConnectionAdapter().multipartRequest(MultipartRequestInputFactory + .makeMultipartRequestInput(nodeDescXid, version, MultipartType.OFPMPDESC)); + connectionContext.getConnectionAdapter().multipartRequest(MultipartRequestInputFactory + .makeMultipartRequestInput(meterFutureXid, version, MultipartType.OFPMPMETERFEATURES)); + connectionContext.getConnectionAdapter().multipartRequest(MultipartRequestInputFactory + .makeMultipartRequestInput(groupFutureXid, version, MultipartType.OFPMPGROUPFEATURES)); + connectionContext.getConnectionAdapter().multipartRequest(MultipartRequestInputFactory + .makeMultipartRequestInput(tableFutureXid, version, MultipartType.OFPMPTABLEFEATURES)); + + return deviceFuture; + } + + private ListenableFuture> queryTableFuture(final ConnectionContext connectionContext, + final long tableFutureXid, final NodeBuilder nodeBuilder) { + final ListenableFuture> nodeTableFuture = connectionContext.registerMultipartMsg(tableFutureXid); + Futures.addCallback(nodeTableFuture, new FutureCallback>() { + + @Override + public void onSuccess(final Collection result) { + Preconditions.checkArgument(result != null && ( ! result.isEmpty()), "Node table future info result is null or empty!"); + final FlowCapableNode flowCapNode = nodeBuilder.getAugmentation(FlowCapableNode.class); + final FlowCapableNodeBuilder flowCapAugBuilder = flowCapNode != null + ? new FlowCapableNodeBuilder(flowCapNode) : new FlowCapableNodeBuilder(); + for (final MultipartReply reply : result) { + final MultipartReplyTableFeaturesCase replyBody = (MultipartReplyTableFeaturesCase) reply.getMultipartReplyBody(); + final MultipartReplyTableFeatures tableFutures = replyBody.getMultipartReplyTableFeatures(); + // FIXME : add DataStore model in future commits in this chain + } + checkForFinalization(tableFutureXid, nodeBuilder); + } + + @Override + public void onFailure(final Throwable t) { + LOG.info("Failed to retrieve node table future info: {}", t.getMessage()); + checkForFinalization(tableFutureXid, nodeBuilder); + } + }); + + return nodeTableFuture; + } + + private ListenableFuture> queryGroupFuture(final ConnectionContext connectionContext, + final long groupFutureXid, final NodeBuilder nodeBuilder) { + final ListenableFuture> nodeGroupFuture = connectionContext.registerMultipartMsg(groupFutureXid); + Futures.addCallback(nodeGroupFuture, new FutureCallback>() { + + @Override + public void onSuccess(final Collection result) { + Preconditions.checkArgument(result != null && ( ! result.isEmpty()), "Node group future info result is null or empty!"); + final NodeGroupFeaturesBuilder nodeGroupFeaturesBuilder = new NodeGroupFeaturesBuilder(); + final GroupFeaturesBuilder groupFeature = new GroupFeaturesBuilder(); + for (final MultipartReply reply : result) { + final MultipartReplyGroupFeaturesCase replyBody = (MultipartReplyGroupFeaturesCase) reply.getMultipartReplyBody(); + final MultipartReplyGroupFeatures groupReplyFutures = replyBody.getMultipartReplyGroupFeatures(); + groupFeature.setMaxGroups(groupReplyFutures.getMaxGroups()); + final List> supportedGroups = new ArrayList<>(); + if(groupReplyFutures.getTypes().isOFPGTALL()){ + supportedGroups.add(GroupAll.class); + } + if(groupReplyFutures.getTypes().isOFPGTSELECT()){ + supportedGroups.add(GroupSelect.class); + } + if(groupReplyFutures.getTypes().isOFPGTINDIRECT()){ + supportedGroups.add(GroupIndirect.class); + } + if(groupReplyFutures.getTypes().isOFPGTFF()){ + supportedGroups.add(GroupFf.class); + } + groupFeature.setGroupTypesSupported(supportedGroups); + + final List> gCapability = new ArrayList<>(); + if(groupReplyFutures.getCapabilities().isOFPGFCCHAINING()){ + gCapability.add(Chaining.class); + } + if(groupReplyFutures.getCapabilities().isOFPGFCCHAININGCHECKS()){ + gCapability.add(ChainingChecks.class); + } + if(groupReplyFutures.getCapabilities().isOFPGFCSELECTLIVENESS()){ + gCapability.add(SelectLiveness.class); + } + if(groupReplyFutures.getCapabilities().isOFPGFCSELECTWEIGHT()){ + gCapability.add(SelectWeight.class); + } + groupFeature.setGroupCapabilitiesSupported(gCapability); + /* TODO : + * My recommendation would be, its good to have a respective model of + * 'type bits', which will generate a class where all these flags will eventually + * be stored as boolean. It will be convenient for application to check the + * supported action, rather then doing bitmap operation. + */ + final List supportActionByGroups = new ArrayList<>(); + for (final ActionType actionType : groupReplyFutures.getActionsBitmap()) { + long supportActionBitmap = 0; + supportActionBitmap |= actionType.isOFPATOUTPUT()?(1 << 0): 0; + supportActionBitmap |= actionType.isOFPATCOPYTTLOUT()?(1 << 11): 0; + supportActionBitmap |= actionType.isOFPATCOPYTTLIN()?(1 << 12): 0; + supportActionBitmap |= actionType.isOFPATSETMPLSTTL()?(1 << 15): 0; + supportActionBitmap |= actionType.isOFPATDECMPLSTTL()?(1 << 16): 0; + supportActionBitmap |= actionType.isOFPATPUSHVLAN()?(1 << 17): 0; + supportActionBitmap |= actionType.isOFPATPOPVLAN()?(1 << 18): 0; + supportActionBitmap |= actionType.isOFPATPUSHMPLS()?(1 << 19): 0; + supportActionBitmap |= actionType.isOFPATPOPMPLS()?(1 << 20): 0; + supportActionBitmap |= actionType.isOFPATSETQUEUE()?(1 << 21): 0; + supportActionBitmap |= actionType.isOFPATGROUP()?(1 << 22): 0; + supportActionBitmap |= actionType.isOFPATSETNWTTL()?(1 << 23): 0; + supportActionBitmap |= actionType.isOFPATDECNWTTL()?(1 << 24): 0; + supportActionBitmap |= actionType.isOFPATSETFIELD()?(1 << 25): 0; + supportActionBitmap |= actionType.isOFPATPUSHPBB()?(1 << 26): 0; + supportActionBitmap |= actionType.isOFPATPOPPBB()?(1 << 27): 0; + supportActionByGroups.add(Long.valueOf(supportActionBitmap)); + } + groupFeature.setActions(supportActionByGroups); + } + nodeGroupFeaturesBuilder.setGroupFeatures(groupFeature.build()); + nodeBuilder.addAugmentation(NodeGroupFeatures.class, nodeGroupFeaturesBuilder.build()); + checkForFinalization(groupFutureXid, nodeBuilder); + } + + @Override + public void onFailure(final Throwable t) { + LOG.info("Failed to retrieve node group future info: {}", t.getMessage()); + checkForFinalization(groupFutureXid, nodeBuilder); + } + }); + + return nodeGroupFuture; + } + + private ListenableFuture> queryMeterFuture(final ConnectionContext connectionContext, + final long meterFutureXid, final NodeBuilder nodeBuilder) { + final ListenableFuture> nodeMeterFuture = connectionContext.registerMultipartMsg(meterFutureXid); + Futures.addCallback(nodeMeterFuture, new FutureCallback>() { + + @Override + public void onSuccess(final Collection result) { + Preconditions.checkArgument(result != null && ( ! result.isEmpty()), "Node meter future info result is null or empty!"); + final NodeMeterFeaturesBuilder nodeMeterFeaturesBuilder = new NodeMeterFeaturesBuilder(); + final MeterFeaturesBuilder meterFeature = new MeterFeaturesBuilder(); + for (final MultipartReply reply : result) { + final MultipartReplyMeterFeaturesCase replyBody = (MultipartReplyMeterFeaturesCase) reply.getMultipartReplyBody(); + final MultipartReplyMeterFeatures meterReplyFutures = replyBody.getMultipartReplyMeterFeatures(); + meterFeature.setMaxBands(meterReplyFutures.getMaxBands()); + meterFeature.setMaxColor(meterReplyFutures.getMaxColor()); + meterFeature.setMaxMeter(new Counter32(meterReplyFutures.getMaxMeter())); + final List> meterBandTypes = new ArrayList<>(); + if (meterReplyFutures.getBandTypes().isOFPMBTDROP()) { + meterBandTypes.add(MeterBandDrop.class); + } + if (meterReplyFutures.getBandTypes().isOFPMBTDSCPREMARK()) { + meterBandTypes.add(MeterBandDscpRemark.class); + } + meterFeature.setMeterBandSupported(Collections.unmodifiableList(meterBandTypes)); + + final List> mCapability = new ArrayList<>(); + if (meterReplyFutures.getCapabilities().isOFPMFBURST()) { + mCapability.add(MeterBurst.class); + } + if(meterReplyFutures.getCapabilities().isOFPMFKBPS()){ + mCapability.add(MeterKbps.class); + + } + if(meterReplyFutures.getCapabilities().isOFPMFPKTPS()){ + mCapability.add(MeterPktps.class); + + } + if(meterReplyFutures.getCapabilities().isOFPMFSTATS()){ + mCapability.add(MeterStats.class); + + } + meterFeature.setMeterCapabilitiesSupported(Collections.unmodifiableList(mCapability)); + } + nodeMeterFeaturesBuilder.setMeterFeatures(meterFeature.build()); + nodeBuilder.addAugmentation(NodeMeterFeatures.class, nodeMeterFeaturesBuilder.build()); + checkForFinalization(meterFutureXid, nodeBuilder); + } + + @Override + public void onFailure(final Throwable t) { + LOG.info("Failed to retrieve node meter future info: {}", t.getMessage()); + checkForFinalization(meterFutureXid, nodeBuilder); + } + }); + + return nodeMeterFuture; + } + + private ListenableFuture> queryDescription(final ConnectionContext connectionContext, + final long nodeDescXid, final NodeBuilder nodeBuilder) { + final ListenableFuture> nodeDesc = connectionContext.registerMultipartMsg(nodeDescXid); + final Short nrOfTables = connectionContext.getFeatures().getTables(); + Futures.addCallback(nodeDesc, new FutureCallback>() { + + @Override + public void onSuccess(final Collection result) { + Preconditions.checkArgument(result != null && ( ! result.isEmpty()), "Node static info result is null or empty!"); + final FlowCapableNode flowCapNode = nodeBuilder.getAugmentation(FlowCapableNode.class); + final FlowCapableNodeBuilder flowCapAugBuilder = flowCapNode != null + ? new FlowCapableNodeBuilder(flowCapNode) : new FlowCapableNodeBuilder(); + final List tables = flowCapAugBuilder.getTable(); + for (int i = 0; i < nrOfTables.intValue(); i++) { + final Short id = Short.valueOf(((short) i)); + tables.add(new TableBuilder().setId(id).setKey(new TableKey(id)).build()); + } + flowCapAugBuilder.setTable(tables); + for (final MultipartReply reply : result) { + final MultipartReplyDescCase replyBody = (MultipartReplyDescCase) reply.getMultipartReplyBody(); + final MultipartReplyDesc description = replyBody.getMultipartReplyDesc(); + flowCapAugBuilder.setDescription(choiseValues(flowCapAugBuilder.getDescription(), description.getDpDesc())); + flowCapAugBuilder.setHardware(choiseValues(flowCapAugBuilder.getHardware(), description.getHwDesc())); + flowCapAugBuilder.setManufacturer(choiseValues(flowCapAugBuilder.getManufacturer(), description.getMfrDesc())); + flowCapAugBuilder.setSoftware(choiseValues(flowCapAugBuilder.getSoftware(), description.getSwDesc())); + flowCapAugBuilder.setSerialNumber(choiseValues(flowCapAugBuilder.getSerialNumber(), description.getSerialNum())); + } + nodeBuilder.addAugmentation(FlowCapableNode.class, flowCapAugBuilder.build()); + checkForFinalization(nodeDescXid, nodeBuilder); + } + + @Override + public void onFailure(final Throwable t) { + LOG.info("Failed to retrieve node static info: {}", t.getMessage()); + checkForFinalization(nodeDescXid, nodeBuilder); + } + }); + + return nodeDesc; + } + + private void checkForFinalization(final long xid, final NodeBuilder nodeBuilder) { + futures.remove(xid); + if (futures.isEmpty()) { + deviceFuture.set(nodeBuilder.build()); + } + } + + private static T choiseValues(final T actual, final T newValue) { + if (actual == null || newValue != null) { + return newValue; + } else { + return actual; + } + } + +} -- 2.36.6