Bug-2827: role switch proposal
[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 import org.opendaylight.openflowplugin.api.openflow.md.core.session.SessionContext;
16 import org.opendaylight.openflowplugin.openflow.md.core.MessageFactory;
17 import org.opendaylight.openflowplugin.openflow.md.util.RoleUtil;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierInput;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.BarrierOutput;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.RoleRequestOutput;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.common.config.impl.rev140326.OfpRole;
22 import org.opendaylight.yangtools.yang.common.RpcResult;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * push role to device - basic step:<br/>
28  * <ul>
29  * <li>here we read generationId from device and</li>
30  * <li>push role request with incremented generationId</li>
31  * <li>{@link #call()} returns true if role request was successful</li>
32  * </ul>
33  */
34 final class RolePushTask implements Callable<Boolean> {
35
36     public static final Logger LOG = LoggerFactory
37             .getLogger(RolePushTask.class);
38
39     public static final long TIMEOUT = 2000;
40     public static final TimeUnit TIMEOUT_UNIT = TimeUnit.MILLISECONDS;
41     private OfpRole role;
42     private SessionContext session;
43     private int priority;
44     private int retryCounter;
45
46     /**
47      * @param role
48      * @param session
49      */
50     public RolePushTask(OfpRole role, SessionContext session) {
51         Preconditions.checkNotNull("OfpRole can not be empty.", role);
52         Preconditions.checkNotNull("Session context can not be empty.", session);
53         this.role = role;
54         this.session = session;
55     }
56
57     /**
58      * @return the retryCounter
59      */
60     public int getRetryCounter() {
61         return retryCounter;
62     }
63
64     /**
65      * @return the priority
66      */
67     public int getPriority() {
68         return priority;
69     }
70
71     /**
72      * @param priority the priority to set
73      */
74     public void setPriority(int priority) {
75         this.priority = priority;
76     }
77
78     @Override
79     public Boolean call() throws RolePushException {
80         if (!session.isValid()) {
81             String msg = "giving up role change: current session is invalid";
82             LOG.debug(msg);
83             throw new RolePushException(msg);
84         }
85
86         // adopt actual generationId from device (first shot failed and this is retry)
87         BigInteger generationId = null;
88         try {
89             generationId = RoleUtil.readGenerationIdFromDevice(session).get(TIMEOUT, TIMEOUT_UNIT);
90         } catch (Exception e) {
91             LOG.debug("generationId request failed: ", e);
92         }
93
94         if (generationId == null) {
95             String msg = "giving up role change: current generationId can not be read";
96             LOG.debug(msg);
97             throw new RolePushException(msg);
98         }
99
100         generationId = RoleUtil.getNextGenerationId(generationId);
101
102         // try to possess role on device
103         Future<RpcResult<RoleRequestOutput>> roleReply = RoleUtil.sendRoleChangeRequest(session, role, generationId);
104         // flush election result with barrier
105         BarrierInput barrierInput = MessageFactory.createBarrier(
106                 session.getFeatures().getVersion(), session.getNextXid());
107         Future<RpcResult<BarrierOutput>> barrierResult = session.getPrimaryConductor().getConnectionAdapter().barrier(barrierInput);
108         try {
109             barrierResult.get(TIMEOUT, TIMEOUT_UNIT);
110         } catch (Exception e) {
111             String msg = String.format("giving up role change: barrier after role change failed: %s", e.getMessage());
112             LOG.warn(msg);
113             throw new RolePushException(msg);
114         }
115         // after barrier replied there must be election result or error
116         try {
117             roleReply.get(0, TimeUnit.MILLISECONDS);
118         } catch (Exception e) {
119             // no election result received - let's retry
120             retryCounter += 1;
121             return false;
122         }
123
124         // here we expect that role on device is successfully possessed
125         return true;
126     }
127 }