Fix connection closing before initialization
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / role / RoleContextImpl.java
1 /**
2  * Copyright (c) 2015 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.impl.role;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Function;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.JdkFutureAdapters;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import io.netty.util.HashedWheelTimer;
18 import io.netty.util.TimerTask;
19
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.TimeUnit;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
26 import org.opendaylight.openflowplugin.api.OFConstants;
27 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
28 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
29 import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
30 import org.opendaylight.openflowplugin.api.openflow.device.handlers.ClusterInitializationPhaseHandler;
31 import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleService;
32 import org.opendaylight.openflowplugin.api.openflow.role.RoleContext;
33 import org.opendaylight.openflowplugin.api.openflow.role.RoleManager;
34 import org.opendaylight.openflowplugin.impl.rpc.AbstractRequestContext;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
41 import org.opendaylight.yangtools.yang.common.RpcResult;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * Role context try to make change device role on device
47  */
48 class RoleContextImpl implements RoleContext {
49
50     private static final Logger LOG = LoggerFactory.getLogger(RoleContextImpl.class);
51
52     // Timeout in seconds after what we will give up on propagating role
53     private static final int SET_ROLE_TIMEOUT = 10;
54
55     private SalRoleService salRoleService = null;
56     private final HashedWheelTimer hashedWheelTimer;
57     private final DeviceInfo deviceInfo;
58     private CONTEXT_STATE state;
59     private final RoleManager myManager;
60     private ClusterInitializationPhaseHandler clusterInitializationPhaseHandler;
61     private final LifecycleService lifecycleService;
62
63     RoleContextImpl(final DeviceInfo deviceInfo,
64                     final HashedWheelTimer hashedWheelTimer,
65                     final RoleManager myManager,
66                     final LifecycleService lifecycleService) {
67         this.deviceInfo = deviceInfo;
68         this.state = CONTEXT_STATE.WORKING;
69         this.myManager = myManager;
70         this.hashedWheelTimer = hashedWheelTimer;
71         this.lifecycleService = lifecycleService;
72     }
73
74     @Nullable
75     @Override
76     public <T> RequestContext<T> createRequestContext() {
77         return new AbstractRequestContext<T>(deviceInfo.reserveXidForDeviceMessage()) {
78             @Override
79             public void close() {
80             }
81         };
82     }
83
84     @Override
85     public void setSalRoleService(@Nonnull final SalRoleService salRoleService) {
86         Preconditions.checkNotNull(salRoleService);
87         this.salRoleService = salRoleService;
88     }
89
90     @Override
91     public CONTEXT_STATE getState() {
92         return this.state;
93     }
94
95     @Override
96     public void setState(CONTEXT_STATE state) {
97         this.state = state;
98     }
99
100     @Override
101     public ServiceGroupIdentifier getServiceIdentifier() {
102         return this.deviceInfo.getServiceIdentifier();
103     }
104
105     @Override
106     public DeviceInfo getDeviceInfo() {
107         return this.deviceInfo;
108     }
109
110     public void startupClusterServices() throws ExecutionException, InterruptedException {
111         Futures.addCallback(sendRoleChangeToDevice(OfpRole.BECOMEMASTER), new RpcResultFutureCallback());
112     }
113
114     @Override
115     public ListenableFuture<Void> stopClusterServices(final boolean deviceDisconnected) {
116
117         if (!deviceDisconnected) {
118             ListenableFuture<Void> future = Futures.transform(makeDeviceSlave(), new Function<RpcResult<SetRoleOutput>, Void>() {
119                 @Nullable
120                 @Override
121                 public Void apply(@Nullable RpcResult<SetRoleOutput> setRoleOutputRpcResult) {
122                     return null;
123                 }
124             });
125
126             Futures.addCallback(future, new FutureCallback<Void>() {
127                 @Override
128                 public void onSuccess(@Nullable Void aVoid) {
129                     if (LOG.isDebugEnabled()) {
130                         LOG.debug("Role SLAVE was successfully propagated on device, node {}", deviceInfo.getLOGValue());
131                     }
132                 }
133
134                 @Override
135                 public void onFailure(final Throwable throwable) {
136                     LOG.warn("Was not able to set role SLAVE to device on node {} ", deviceInfo.getLOGValue());
137                     LOG.trace("Error occurred on device role setting, probably connection loss: ", throwable);
138                     myManager.removeDeviceFromOperationalDS(deviceInfo);
139                 }
140             });
141             return future;
142         } else {
143             return myManager.removeDeviceFromOperationalDS(deviceInfo);
144         }
145     }
146
147     @Override
148     public ListenableFuture<RpcResult<SetRoleOutput>> makeDeviceSlave(){
149         return sendRoleChangeToDevice(OfpRole.BECOMESLAVE);
150     }
151
152     @VisibleForTesting
153     ListenableFuture<RpcResult<SetRoleOutput>> sendRoleChangeToDevice(final OfpRole newRole) {
154         if (LOG.isDebugEnabled()) {
155             LOG.debug("Sending new role {} to device {}", newRole, deviceInfo.getNodeId());
156         }
157         final Future<RpcResult<SetRoleOutput>> setRoleOutputFuture;
158         if (deviceInfo.getVersion() >= OFConstants.OFP_VERSION_1_3) {
159             final SetRoleInput setRoleInput = (new SetRoleInputBuilder()).setControllerRole(newRole)
160                     .setNode(new NodeRef(deviceInfo.getNodeInstanceIdentifier())).build();
161             setRoleOutputFuture = this.salRoleService.setRole(setRoleInput);
162             final TimerTask timerTask = timeout -> {
163                 if (!setRoleOutputFuture.isDone()) {
164                     LOG.warn("New role {} was not propagated to device {} during 5 sec", newRole, deviceInfo.getLOGValue());
165                     setRoleOutputFuture.cancel(true);
166                 }
167             };
168             hashedWheelTimer.newTimeout(timerTask, SET_ROLE_TIMEOUT, TimeUnit.SECONDS);
169         } else {
170             LOG.info("Device: {} with version: {} does not support role", deviceInfo.getLOGValue(), deviceInfo.getVersion());
171             return Futures.immediateFuture(null);
172         }
173         return JdkFutureAdapters.listenInPoolThread(setRoleOutputFuture);
174     }
175
176     @Override
177     public void setLifecycleInitializationPhaseHandler(final ClusterInitializationPhaseHandler handler) {
178         this.clusterInitializationPhaseHandler = handler;
179     }
180
181     @Override
182     public boolean onContextInstantiateService(final ConnectionContext connectionContext) {
183
184         if (connectionContext.getConnectionState().equals(ConnectionContext.CONNECTION_STATE.RIP)) {
185             LOG.warn("Connection on device {} was interrupted, will stop starting master services.", deviceInfo.getLOGValue());
186             return false;
187         }
188
189         Futures.addCallback(sendRoleChangeToDevice(OfpRole.BECOMEMASTER), new RpcResultFutureCallback());
190         return this.clusterInitializationPhaseHandler.onContextInstantiateService(connectionContext);
191     }
192
193     private class RpcResultFutureCallback implements FutureCallback<RpcResult<SetRoleOutput>> {
194         @Override
195         public void onSuccess(@Nullable RpcResult<SetRoleOutput> setRoleOutputRpcResult) {
196             if (LOG.isDebugEnabled()) {
197                 LOG.debug("Role MASTER was successfully set on device, node {}", deviceInfo.getLOGValue());
198             }
199         }
200
201         @Override
202         public void onFailure(final Throwable throwable) {
203             LOG.warn("Was not able to set MASTER role on device, node {}", deviceInfo.getLOGValue());
204             lifecycleService.closeConnection();
205         }
206     }
207 }