BUG-2794: incorporate openflowjava api changes to openflowplugin
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / HandshakeManagerImpl.java
1 /**
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.openflowplugin.openflow.md.core;
9
10 import java.util.List;
11 import java.util.concurrent.Future;
12 import java.util.concurrent.TimeUnit;
13 import java.util.concurrent.TimeoutException;
14
15 import org.opendaylight.openflowjava.protocol.api.connection.ConnectionAdapter;
16 import org.opendaylight.openflowplugin.ConnectionException;
17 import org.opendaylight.openflowplugin.api.openflow.md.core.ConnectionConductor;
18 import org.opendaylight.openflowplugin.api.openflow.md.core.ErrorHandler;
19 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeListener;
20 import org.opendaylight.openflowplugin.api.openflow.md.core.HandshakeManager;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesInputBuilder;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.GetFeaturesOutput;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloInput;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.HelloMessage;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.hello.Elements;
26 import org.opendaylight.yangtools.yang.common.RpcResult;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * @author mirehak
32  *
33  */
34 public class HandshakeManagerImpl implements HandshakeManager {
35     
36     private static final Logger LOG = LoggerFactory
37             .getLogger(HandshakeManagerImpl.class);
38     
39     private Short lastProposedVersion;
40     private Short lastReceivedVersion;
41     private final List<Short> versionOrder;
42     
43     private HelloMessage receivedHello;
44     private final ConnectionAdapter connectionAdapter;
45     private GetFeaturesOutput features;
46     private Short version;
47     private ErrorHandler errorHandler;
48     
49     private long maxTimeout = 8000;
50     private TimeUnit maxTimeoutUnit = TimeUnit.MILLISECONDS;
51     private Short highestVersion;
52
53     private Long activeXid;
54
55     private HandshakeListener handshakeListener;
56
57     private boolean useVersionBitmap;
58     
59     @Override
60     public void setReceivedHello(HelloMessage receivedHello) {
61         this.receivedHello = receivedHello;
62     }
63     
64     /**
65      * @param connectionAdapter 
66      * @param highestVersion 
67      * @param versionOrder
68      */
69     public HandshakeManagerImpl(ConnectionAdapter connectionAdapter, Short highestVersion, 
70             List<Short> versionOrder) {
71         this.highestVersion = highestVersion;
72         this.versionOrder = versionOrder;
73         this.connectionAdapter = connectionAdapter;
74     }
75     
76     @Override
77     public void setHandshakeListener(HandshakeListener handshakeListener) {
78         this.handshakeListener = handshakeListener;
79     }
80
81     @Override
82     public void shake() {
83
84         if (version != null) {
85             // Some switches respond with a second HELLO acknowledging our HELLO
86             // but we've already completed the handshake based on the negotiated
87             // version and have registered this switch.
88             LOG.debug("Hello recieved after handshake already settled ... ignoring.");
89             return;
90         }
91
92         LOG.trace("handshake STARTED");
93         setActiveXid(20L);
94         HelloMessage receivedHelloLoc = receivedHello;
95         
96         try {
97             if (receivedHelloLoc == null) {
98                 // first Hello sending
99                 sendHelloMessage(highestVersion, getNextXid());
100                 lastProposedVersion = highestVersion;
101                 LOG.trace("ret - firstHello+wait");
102                 return;
103             }
104
105             // process the 2. and later hellos
106             Short remoteVersion = receivedHelloLoc.getVersion();
107             List<Elements> elements = receivedHelloLoc.getElements();
108             setActiveXid(receivedHelloLoc.getXid());
109             List<Boolean> remoteVersionBitmap = MessageFactory.digVersions(elements);
110             LOG.debug("Hello message: version={}, bitmap={}, xid={}", remoteVersion, 
111                     remoteVersionBitmap, receivedHelloLoc.getXid());
112         
113             if (useVersionBitmap && remoteVersionBitmap != null) {
114                 // versionBitmap on both sides -> ONE STEP DECISION
115                 handleVersionBitmapNegotiation(elements);
116             } else { 
117                 // versionBitmap missing at least on one side -> STEP-BY-STEP NEGOTIATION applying 
118                 handleStepByStepVersionNegotiation(remoteVersion);
119             }
120         } catch (Exception ex) {
121             errorHandler.handleException(ex, null);
122             connectionAdapter.disconnect();
123             handshakeListener.onHandshakeFailure();
124             LOG.trace("ret - shake fail: {}", ex.getMessage());
125         }
126     }
127
128     /**
129      * @param remoteVersion
130      * @throws Exception 
131      */
132     private void handleStepByStepVersionNegotiation(Short remoteVersion) throws Exception {
133         LOG.debug("remoteVersion:{} lastProposedVersion:{}, highestVersion:{}", 
134                 remoteVersion, lastProposedVersion, highestVersion);
135         
136         if (lastProposedVersion == null) {
137             // first hello has not been sent yet, send it and either wait for next remote 
138             // version or proceed
139             lastProposedVersion = proposeNextVersion(remoteVersion);
140             sendHelloMessage(lastProposedVersion, getNextXid());
141         }
142         
143         if (remoteVersion == lastProposedVersion) {
144             postHandshake(lastProposedVersion, getNextXid());
145             LOG.trace("ret - OK - switch answered with lastProposedVersion");
146         } else {
147             checkNegotiationStalling(remoteVersion);
148
149             if (remoteVersion > (lastProposedVersion == null ? highestVersion : lastProposedVersion)) {
150                 // wait for next version
151                 LOG.trace("ret - wait");
152             } else {
153                 //propose lower version
154                 handleLowerVersionProposal(remoteVersion);
155             }
156         }
157     }
158
159     /**
160      * @param remoteVersion
161      * @throws Exception 
162      */
163     private void handleLowerVersionProposal(Short remoteVersion) throws Exception {
164         Short proposedVersion;
165         // find the version from header version field
166         proposedVersion = proposeNextVersion(remoteVersion);
167         lastProposedVersion = proposedVersion;
168         sendHelloMessage(proposedVersion, getNextXid());
169
170         if (proposedVersion != remoteVersion) {
171             LOG.trace("ret - sent+wait");
172         } else {
173             LOG.trace("ret - sent+OK");
174             postHandshake(proposedVersion, getNextXid());
175         }
176     }
177
178     /**
179      * @param elements
180      * @throws Exception 
181      */
182     private void handleVersionBitmapNegotiation(List<Elements> elements) throws Exception {
183         Short proposedVersion;
184         proposedVersion = proposeCommonBitmapVersion(elements);
185         if (lastProposedVersion == null) {
186             // first hello has not been sent yet
187             sendHelloMessage(proposedVersion, getNextXid());
188         }
189         postHandshake(proposedVersion, getNextXid());
190         LOG.trace("ret - OK - versionBitmap");
191     }
192     
193     /**
194      * 
195      * @return
196      */
197     private Long getNextXid() {
198         activeXid += 1; 
199         return activeXid;
200     }
201
202     /**
203      * @param xid
204      */
205     private void setActiveXid(Long xid) {
206         this.activeXid = xid;
207     }
208     
209     /**
210      * @param remoteVersion
211      */
212     private void checkNegotiationStalling(Short remoteVersion) {
213         if (lastReceivedVersion != null && lastReceivedVersion.equals(remoteVersion)) {
214             throw new IllegalStateException("version negotiation stalled: version = "+remoteVersion);
215         }
216         lastReceivedVersion = remoteVersion;
217     }
218
219     @Override
220     public GetFeaturesOutput getFeatures() {
221         return features;
222     }
223     
224     @Override
225     public Short getVersion() {
226         return version;
227     }
228
229     /**
230      * find common highest supported bitmap version
231      * @param list
232      * @return
233      */
234     protected Short proposeCommonBitmapVersion(List<Elements> list) {
235         Short supportedHighestVersion = null;
236         if((null != list) && (0 != list.size())) {
237             for(Elements element : list) {
238                 List<Boolean> bitmap = element.getVersionBitmap();
239                 // check for version bitmap
240                 for(short bitPos : ConnectionConductor.versionOrder) {
241                     // with all the version it should work.
242                     if(bitmap.get(bitPos % Integer.SIZE)) {
243                         supportedHighestVersion = bitPos;
244                         break;
245                     }
246                 }
247             }
248             
249             if(null == supportedHighestVersion) {
250                 throw new IllegalArgumentException("no common version found in versionBitmap");
251             }
252         }
253
254         return supportedHighestVersion;
255     }
256
257     /**
258      * find supported version based on remoteVersion
259      * @param remoteVersion
260      * @return
261      */
262     protected short proposeNextVersion(short remoteVersion) {
263         Short proposal = null;
264         for (short offer : versionOrder) {
265             if (offer <= remoteVersion) {
266                 proposal = offer;
267                 break;
268             }
269         }
270         if (proposal == null) {
271             throw new IllegalArgumentException("no equal or lower version found, unsupported version: "
272                     + remoteVersion);
273         }
274         return proposal;
275     }
276     
277     /**
278      * send hello reply without versionBitmap
279      * @param helloVersion
280      * @param helloXid
281      * @throws Exception 
282      */
283     private void sendHelloMessage(Short helloVersion, Long helloXid) throws Exception {
284         //Short highestVersion = ConnectionConductor.versionOrder.get(0);
285         //final Long helloXid = 21L;
286         HelloInput helloInput = MessageFactory.createHelloInput(helloVersion, helloXid, versionOrder);
287         
288         LOG.debug("sending hello message: version{}, xid={}, version bitmap={}", 
289                 helloVersion, helloXid, MessageFactory.digVersions(helloInput.getElements()));
290         
291         try {
292             RpcResult<Void> helloResult = connectionAdapter.hello(helloInput).get(maxTimeout, maxTimeoutUnit);
293             RpcUtil.smokeRpc(helloResult);
294             LOG.debug("FIRST HELLO sent.");
295         } catch (Exception e) {
296             if (LOG.isTraceEnabled()) {
297                 LOG.trace("FIRST HELLO sent.", e);
298             }
299             handshakeListener.onHandshakeFailure();
300             throw new ConnectionException("FIRST HELLO sending failed because of connection issue.");
301         }
302     }
303
304
305     /**
306      * after handshake set features, register to session
307      * @param proposedVersion
308      * @param xId
309      * @throws Exception 
310      */
311     protected void postHandshake(Short proposedVersion, Long xid) throws Exception {
312         // set version
313         version = proposedVersion;
314
315         LOG.debug("version set: {}", proposedVersion);
316         // request features
317         GetFeaturesInputBuilder featuresBuilder = new GetFeaturesInputBuilder();
318         featuresBuilder.setVersion(version).setXid(xid);
319         LOG.debug("sending feature request for version={} and xid={}", version, xid);
320         Future<RpcResult<GetFeaturesOutput>> featuresFuture = connectionAdapter
321                 .getFeatures(featuresBuilder.build());
322         LOG.debug("waiting for features");
323         try {
324             RpcResult<GetFeaturesOutput> rpcFeatures = 
325                     featuresFuture.get(maxTimeout, maxTimeoutUnit);
326             RpcUtil.smokeRpc(rpcFeatures);
327             
328             GetFeaturesOutput featureOutput =  rpcFeatures.getResult();
329             
330             LOG.debug("obtained features: datapathId={}",
331                     featureOutput.getDatapathId());
332             LOG.debug("obtained features: auxiliaryId={}",
333                     featureOutput.getAuxiliaryId());
334             LOG.trace("handshake SETTLED: version={}, datapathId={}, auxiliaryId={}", 
335                     version, featureOutput.getDatapathId(), featureOutput.getAuxiliaryId());
336             
337             handshakeListener.onHandshakeSuccessfull(featureOutput, proposedVersion);
338         } catch (TimeoutException e) {
339             // handshake failed
340             LOG.warn("issuing disconnect during handshake, reason: future expired", e);
341             connectionAdapter.disconnect();
342             handshakeListener.onHandshakeFailure();
343             throw e;
344         } catch (Exception e) {
345             // handshake failed
346             LOG.warn("issuing disconnect during handshake, reason - RPC: {}", e.getMessage(), e);
347             connectionAdapter.disconnect();
348             handshakeListener.onHandshakeFailure();
349             throw e;
350         }
351         
352         LOG.debug("postHandshake DONE");
353     }
354
355     @Override
356     public void setUseVersionBitmap(boolean useVersionBitmap) {
357         this.useVersionBitmap = useVersionBitmap;
358     }
359     
360     @Override
361     public void setErrorHandler(ErrorHandler errorHandler) {
362         this.errorHandler = errorHandler;
363     }
364 }