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