org.opendaylight.openflowplugin.openflow.md.core.HandshakeManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.openflowplugin.openflow.md.core.HandshakeManagerImpl.java

Source

/**
 * 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 java.util.List;
import java.util.Objects;
import java.util.concurrent.Future;

import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor;
import org.opendaylight.openflowplugin.api.openflow.md.core.ErrorHandler;
import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.JdkFutureAdapters;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;

/**
 * @author mirehak
 *
 */
public class HandshakeManagerImpl implements HandshakeManager {

    private static final long activeXID = 20L;

    private static final Logger LOG = LoggerFactory.getLogger(HandshakeManagerImpl.class);

    private Short lastProposedVersion;
    private Short lastReceivedVersion;
    private final List<Short> versionOrder;

    private final ConnectionAdapter connectionAdapter;
    private Short version;
    private ErrorHandler errorHandler;

    private Short highestVersion;

    private Long activeXid;

    private HandshakeListener handshakeListener;

    private boolean useVersionBitmap;

    /**
     * @param connectionAdapter connection adaptor for switch
     * @param highestVersion highest openflow version
     * @param versionOrder list of version in order for connection protocol negotiation
     */
    public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion,
            List<Short> versionOrder) {
        this.highestVersion = highestVersion;
        this.versionOrder = versionOrder;
        this.connectionAdapter = connectionAdapter;
    }

    @Override
    public void setHandshakeListener(HandshakeListener handshakeListener) {
        this.handshakeListener = handshakeListener;
    }

    @Override
    public synchronized void shake(HelloMessage receivedHello) {

        if (version != null) {
            // Some switches respond with a second HELLO acknowledging our HELLO
            // but we've already completed the handshake based on the negotiated
            // version and have registered this switch.
            LOG.debug("Hello recieved after handshake already settled ... ignoring.");
            return;
        }

        LOG.trace("handshake STARTED");
        setActiveXid(activeXID);

        try {
            if (receivedHello == null) {
                // first Hello sending
                sendHelloMessage(highestVersion, getNextXid());
                lastProposedVersion = highestVersion;
                LOG.trace("ret - firstHello+wait");
                return;
            }

            // process the 2. and later hellos
            Short remoteVersion = receivedHello.getVersion();
            List<Elements> elements = receivedHello.getElements();
            setActiveXid(receivedHello.getXid());
            List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
            LOG.debug("Hello message: version={}, xid={}, bitmap={}", remoteVersion, receivedHello.getXid(),
                    remoteVersionBitmap);

            if (useVersionBitmap && remoteVersionBitmap != null) {
                // versionBitmap on both sides -> ONE STEP DECISION
                handleVersionBitmapNegotiation(elements);
            } else {
                // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying
                handleStepByStepVersionNegotiation(remoteVersion);
            }
        } catch (Exception ex) {
            errorHandler.handleException(ex, null);
            LOG.trace("ret - shake fail - closing");
            handshakeListener.onHandshakeFailure();
        }
    }

    /**
     * @param remoteVersion remote version
     * @throws Exception exception
     */
    private void handleStepByStepVersionNegotiation(final Short remoteVersion) throws Exception {
        LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}", remoteVersion, lastProposedVersion,
                highestVersion);

        if (lastProposedVersion == null) {
            // first hello has not been sent yet, send it and either wait for next remote
            // version or proceed
            lastProposedVersion = proposeNextVersion(remoteVersion);
            final Long nextHelloXid = getNextXid();
            ListenableFuture<Void> helloResult = sendHelloMessage(lastProposedVersion, nextHelloXid);
            Futures.addCallback(helloResult, new FutureCallback<Void>() {
                @Override
                public void onSuccess(Void result) {
                    try {
                        stepByStepVersionSubStep(remoteVersion, lastProposedVersion);
                    } catch (Exception e) {
                        errorHandler.handleException(e, null);
                        handshakeListener.onHandshakeFailure();
                    }
                }

                @Override
                public void onFailure(Throwable t) {
                    LOG.info("hello sending seriously failed [{}]", nextHelloXid);
                    LOG.trace("detail of hello send problem", t);
                }
            });
        } else {
            stepByStepVersionSubStep(remoteVersion, lastProposedVersion);
        }
    }

    private void stepByStepVersionSubStep(Short remoteVersion, Short lastProposedVersion) throws Exception {
        if (remoteVersion.equals(lastProposedVersion)) {
            postHandshake(lastProposedVersion, getNextXid());
            LOG.trace("ret - OK - switch answered with lastProposedVersion");
        } else {
            checkNegotiationStalling(remoteVersion);

            if (remoteVersion > (lastProposedVersion == null ? highestVersion : this.lastProposedVersion)) {
                // wait for next version
                LOG.trace("ret - wait");
            } else {
                //propose lower version
                handleLowerVersionProposal(remoteVersion);
            }
        }
    }

    /**
     * @param remoteVersion remote version
     * @throws Exception exception
     */
    private void handleLowerVersionProposal(Short remoteVersion) throws Exception {
        Short proposedVersion;
        // find the version from header version field
        proposedVersion = proposeNextVersion(remoteVersion);
        lastProposedVersion = proposedVersion;
        sendHelloMessage(proposedVersion, getNextXid());

        if (!Objects.equals(proposedVersion, remoteVersion)) {
            LOG.trace("ret - sent+wait");
        } else {
            LOG.trace("ret - sent+OK");
            postHandshake(proposedVersion, getNextXid());
        }
    }

    /**
     * @param elements version elements
     * @throws Exception exception
     */
    private void handleVersionBitmapNegotiation(List<Elements> elements) throws Exception {
        final Short proposedVersion = proposeCommonBitmapVersion(elements);
        if (lastProposedVersion == null) {
            // first hello has not been sent yet
            Long nexHelloXid = getNextXid();
            ListenableFuture<Void> helloDone = sendHelloMessage(proposedVersion, nexHelloXid);
            Futures.addCallback(helloDone, new FutureCallback<Void>() {
                @Override
                public void onSuccess(Void result) {
                    LOG.trace("ret - DONE - versionBitmap");
                    postHandshake(proposedVersion, getNextXid());
                }

                @Override
                public void onFailure(Throwable t) {
                    // NOOP
                }
            });
            LOG.trace("next proposal [{}] with versionBitmap hooked ..", nexHelloXid);
        } else {
            LOG.trace("ret - DONE - versionBitmap");
            postHandshake(proposedVersion, getNextXid());
        }
    }

    /**
     *
     * @return next tx id
     */
    private Long getNextXid() {
        activeXid += 1;
        return activeXid;
    }

    /**
     * @param xid tx id
     */
    private void setActiveXid(Long xid) {
        this.activeXid = xid;
    }

    /**
     * @param remoteVersion remove version
     */
    private void checkNegotiationStalling(Short remoteVersion) {
        if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
            throw new IllegalStateException("version negotiation stalled: version = " + remoteVersion);
        }
        lastReceivedVersion = remoteVersion;
    }

    @Override
    public Short getVersion() {
        return version;
    }

    /**
     * find common highest supported bitmap version
     * @param list bitmap list
     * @return proposed bitmap value
     */
    protected Short proposeCommonBitmapVersion(List<Elements> list) {
        Short supportedHighestVersion = null;
        if ((null != list) && (0 != list.size())) {
            for (Elements element : list) {
                List<Boolean> bitmap = element.getVersionBitmap();
                // check for version bitmap
                for (short bitPos : ConnectionConductor.versionOrder) {
                    // with all the version it should work.
                    if (bitmap.get(bitPos % Integer.SIZE)) {
                        supportedHighestVersion = bitPos;
                        break;
                    }
                }
            }

            if (null == supportedHighestVersion) {
                LOG.trace("versionBitmap: no common version found");
                throw new IllegalArgumentException("no common version found in versionBitmap");
            }
        }

        return supportedHighestVersion;
    }

    /**
     * find supported version based on remoteVersion
     * @param remoteVersion openflow version supported by remote entity
     * @return openflow version
     */
    protected short proposeNextVersion(short remoteVersion) {
        Short proposal = null;
        for (short offer : versionOrder) {
            if (offer <= remoteVersion) {
                proposal = offer;
                break;
            }
        }
        if (proposal == null) {
            throw new IllegalArgumentException(
                    "no equal or lower version found, unsupported version: " + remoteVersion);
        }
        return proposal;
    }

    /**
     * send hello reply without versionBitmap
     * @param helloVersion initial hello version for openflow connection negotiation
     * @param helloXid transaction id
     * @throws Exception
     */
    private ListenableFuture<Void> sendHelloMessage(Short helloVersion, final Long helloXid) throws Exception {

        HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);

        final SettableFuture<Void> resultFtr = SettableFuture.create();

        LOG.debug("sending hello message: version{}, xid={}, version bitmap={}", helloVersion, helloXid,
                MessageFactory.digVersions(helloInput.getElements()));

        Future<RpcResult<Void>> helloResult = connectionAdapter.hello(helloInput);

        ListenableFuture<RpcResult<Void>> rpcResultListenableFuture = JdkFutureAdapters
                .listenInPoolThread(helloResult);
        Futures.addCallback(rpcResultListenableFuture, new FutureCallback<RpcResult<Void>>() {
            @Override
            public void onSuccess(RpcResult<Void> result) {
                if (result.isSuccessful()) {
                    LOG.debug("hello successfully sent, xid={}, addr={}", helloXid,
                            connectionAdapter.getRemoteAddress());
                    resultFtr.set(null);
                } else {
                    for (RpcError error : result.getErrors()) {
                        LOG.debug("hello sending failed [{}]: i:{} s:{} m:{}, addr:{}", helloXid, error.getInfo(),
                                error.getSeverity(), error.getMessage(), connectionAdapter.getRemoteAddress());
                        if (error.getCause() != null) {
                            LOG.trace("DETAIL of sending hello failure", error.getCause());
                        }
                    }
                    resultFtr.cancel(false);
                    handshakeListener.onHandshakeFailure();
                }
            }

            @Override
            public void onFailure(Throwable t) {
                LOG.warn("sending of hello failed seriously [{}, addr:{}]: {}", helloXid,
                        connectionAdapter.getRemoteAddress(), t.getMessage());
                LOG.trace("DETAIL of sending of hello failure:", t);
                resultFtr.cancel(false);
                handshakeListener.onHandshakeFailure();
            }
        });
        LOG.trace("sending hello message [{}] - result hooked ..", helloXid);
        return resultFtr;
    }

    /**
     * after handshake set features, register to session
     * @param proposedVersion proposed openflow version
     * @param xid transaction id
     */
    protected void postHandshake(final Short proposedVersion, final Long xid) {
        // set version
        version = proposedVersion;

        LOG.debug("version set: {}", proposedVersion);
        // request features
        GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
        featuresBuilder.setVersion(version).setXid(xid);
        LOG.debug("sending feature request for version={} and xid={}", version, xid);
        Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
                .getFeatures(featuresBuilder.build());

        Futures.addCallback(JdkFutureAdapters.listenInPoolThread(featuresFuture),
                new FutureCallback<RpcResult<GetFeaturesOutput>>() {
                    @Override
                    public void onSuccess(RpcResult<GetFeaturesOutput> rpcFeatures) {
                        LOG.trace("features are back");
                        if (rpcFeatures.isSuccessful()) {
                            GetFeaturesOutput featureOutput = rpcFeatures.getResult();

                            LOG.debug("obtained features: datapathId={}", featureOutput.getDatapathId());
                            LOG.debug("obtained features: auxiliaryId={}", featureOutput.getAuxiliaryId());
                            LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}", version,
                                    featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
                            handshakeListener.onHandshakeSuccessfull(featureOutput, proposedVersion);
                        } else {
                            // handshake failed
                            LOG.warn("issuing disconnect during handshake [{}]",
                                    connectionAdapter.getRemoteAddress());
                            for (RpcError rpcError : rpcFeatures.getErrors()) {
                                LOG.debug("handshake - features failure [{}]: i:{} | m:{} | s:{}", xid,
                                        rpcError.getInfo(), rpcError.getMessage(), rpcError.getSeverity(),
                                        rpcError.getCause());
                            }
                            handshakeListener.onHandshakeFailure();
                        }

                        LOG.debug("postHandshake DONE");
                    }

                    @Override
                    public void onFailure(Throwable t) {
                        LOG.warn("getting feature failed seriously [{}, addr:{}]: {}", xid,
                                connectionAdapter.getRemoteAddress(), t.getMessage());
                        LOG.trace("DETAIL of sending of hello failure:", t);
                    }
                });

        LOG.debug("future features [{}] hooked ..", xid);

    }

    @Override
    public void setUseVersionBitmap(boolean useVersionBitmap) {
        this.useVersionBitmap = useVersionBitmap;
    }

    @Override
    public void setErrorHandler(ErrorHandler errorHandler) {
        this.errorHandler = errorHandler;
    }
}