Merge "Bug 4957 No empty transaction for every connection fix"
[openflowplugin.git] / openflowplugin / src / main / java / org / opendaylight / openflowplugin / openflow / md / core / session / RolePushTask.java
1 /**
2  * Copyright (c) 2014 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.session;
9
10 import com.google.common.base.Preconditions;
11 import java.math.BigInteger;
12 import java.util.concurrent.Callable;
13 import java.util.concurrent.Future;
14 import java.util.concurrent.TimeUnit;
15
16 import org.opendaylight.openflowplugin.api.OFConstants;
17 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionContext;
18 import org.opendaylight.openflowplugin.openflow.md.core.MessageFactory;
19 import org.opendaylight.openflowplugin.openflow.md.util.RoleUtil;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierInput;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierOutput;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.common.config.impl.rev140326.OfpRole;
24 import org.opendaylight.yangtools.yang.common.RpcResult;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27 import java.util.Date;
28
29 /**
30  * push role to device - basic step:
31  * <ul>
32  * <li>here we read generationId from device and</li>
33  * <li>push role request with incremented generationId</li>
34  * <li>{@link #call()} returns true if role request was successful</li>
35  * </ul>
36  */
37 //final class RolePushTask implements Callable<Boolean> {
38 public class RolePushTask implements Callable<Boolean> {
39
40     private static final Logger LOG = LoggerFactory
41             .getLogger(RolePushTask.class);
42
43     public static final long TIMEOUT = 7000;
44     public static final TimeUnit TIMEOUT_UNIT = TimeUnit.MILLISECONDS;
45     private OfpRole role;
46     private SessionContext session;
47     private int priority;
48     private int retryCounter;
49
50     /**
51      * @param role openflow controller role
52      * @param session switch session context
53      */
54     public RolePushTask(OfpRole role, SessionContext session) {
55         Preconditions.checkNotNull("OfpRole can not be empty.", role);
56         Preconditions.checkNotNull("Session context can not be empty.", session);
57         this.role = role;
58         this.session = session;
59     }
60
61     /**
62      * @return the retryCounter
63      */
64     public int getRetryCounter() {
65         return retryCounter;
66     }
67
68     /**
69      * @return the priority
70      */
71     public int getPriority() {
72         return priority;
73     }
74
75     /**
76      * @param priority the priority to set
77      */
78     public void setPriority(int priority) {
79         this.priority = priority;
80     }
81
82     @Override
83     public Boolean call() throws RolePushException {
84         if (session.getPrimaryConductor().getVersion() == OFConstants.OFP_VERSION_1_0) {
85             LOG.info("OpenFlow 1.0 devices don't support multi controller features, skipping role push.");
86             return true;
87         }
88         if (!session.isValid()) {
89             String msg = "Giving up role change: current session is invalid";
90             LOG.error(msg);
91             throw new RolePushException(msg);
92         }
93
94         // adopt actual generationId from device (first shot failed and this is retry)
95         BigInteger generationId = null;
96         String dpId = new BigInteger(session.getSessionKey().getId()).toString();
97         LOG.info("Pushing {} role configuration to device openflow:{}",
98                 role==OfpRole.BECOMEMASTER?"MASTER":"SLAVE", dpId);
99         try {
100             Date date = new Date();
101             Future<BigInteger> generationIdFuture = RoleUtil.readGenerationIdFromDevice(session);
102             // flush election result with barrier
103             BarrierInput barrierInput = MessageFactory.createBarrier(
104                 session.getFeatures().getVersion(), session.getNextXid());
105             Future<RpcResult<BarrierOutput>> barrierResult = session.getPrimaryConductor().getConnectionAdapter().barrier(barrierInput);
106             try {
107                 barrierResult.get(TIMEOUT, TIMEOUT_UNIT);
108             } catch (Exception e) {
109                 String msg = String.format("Giving up role change: barrier after read generation-id failed : %s", e.getMessage());
110                 LOG.warn(msg);
111                 throw new RolePushException(msg);
112             }
113             try {
114                 generationId = generationIdFuture.get(0, TIMEOUT_UNIT);
115             } catch (Exception e) {
116                 String msg = String.format("Giving up role change: read generation-id failed %s", e.getMessage());
117                 throw new RolePushException(msg);
118             }
119
120             LOG.info("Received generation-id {} for role change request from device {}",
121                     generationId, dpId);
122         } catch (Exception e) {
123             LOG.error("Role push request failed for device {}",session.getSessionKey().getId(), e);
124         }
125
126         if (generationId == null) {
127             LOG.error("Generation ID is NULL for device {}",session.getSessionKey().getId());
128             String msg = "Giving up role change: current generation-id can not be read";
129             throw new RolePushException(msg);
130         }
131
132         generationId = RoleUtil.getNextGenerationId(generationId);
133         LOG.info("Pushing role change {} config request with generation-id {} to device {}",
134                 role==OfpRole.BECOMEMASTER?"MASTER":"SLAVE", generationId, dpId);
135
136
137         // try to possess role on device
138         Future<RpcResult<RoleRequestOutput>> roleReply = RoleUtil.sendRoleChangeRequest(session, role, generationId);
139         // flush election result with barrier
140         BarrierInput barrierInput = MessageFactory.createBarrier(
141                 session.getFeatures().getVersion(), session.getNextXid());
142         Future<RpcResult<BarrierOutput>> barrierResult = session.getPrimaryConductor().getConnectionAdapter().barrier(barrierInput);
143         try {
144             barrierResult.get(TIMEOUT, TIMEOUT_UNIT);
145         } catch (Exception e) {
146             String msg = String.format("Giving up role change: barrier after role change failed: %s", e.getMessage());
147             LOG.warn(msg);
148             throw new RolePushException(msg);
149         }
150         // after barrier replied there must be election result or error
151         try {
152             roleReply.get(0, TimeUnit.MILLISECONDS);
153         } catch (Exception e) {
154             // no election result received - let's retry
155             retryCounter += 1;
156             return false;
157         }
158
159         // here we expect that role on device is successfully possessed
160         LOG.info("Successfully pushing {} role to the device openflow:{}",
161                 role==OfpRole.BECOMEMASTER?"MASTER":"SLAVE", dpId);
162         return true;
163     }
164 }