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