Merge "Fix checkstyle warnings for impl/role package"
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / role / RoleContextImpl.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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
9 package org.opendaylight.openflowplugin.impl.role;
10
11 import com.google.common.util.concurrent.FutureCallback;
12 import com.google.common.util.concurrent.Futures;
13 import com.google.common.util.concurrent.JdkFutureAdapters;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import com.google.common.util.concurrent.MoreExecutors;
16 import io.netty.util.HashedWheelTimer;
17 import io.netty.util.Timeout;
18 import io.netty.util.TimerTask;
19 import java.util.Collection;
20 import java.util.HashSet;
21 import java.util.Objects;
22 import java.util.concurrent.Future;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.atomic.AtomicReference;
25 import javax.annotation.Nonnull;
26 import javax.annotation.Nullable;
27 import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
28 import org.opendaylight.openflowplugin.api.OFConstants;
29 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
30 import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
31 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainMastershipState;
32 import org.opendaylight.openflowplugin.api.openflow.lifecycle.ContextChainMastershipWatcher;
33 import org.opendaylight.openflowplugin.api.openflow.role.RoleContext;
34 import org.opendaylight.openflowplugin.impl.rpc.AbstractRequestContext;
35 import org.opendaylight.openflowplugin.impl.services.util.RequestContextUtil;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInputBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
42 import org.opendaylight.yangtools.yang.common.RpcResult;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 public class RoleContextImpl implements RoleContext {
47     private static final Logger LOG = LoggerFactory.getLogger(RoleContextImpl.class);
48
49     // Timeout  after what we will give up on propagating role
50     private static final long SET_ROLE_TIMEOUT = 10000;
51
52     private final DeviceInfo deviceInfo;
53     private final HashedWheelTimer timer;
54     private final AtomicReference<ListenableFuture<RpcResult<SetRoleOutput>>> lastRoleFuture = new AtomicReference<>();
55     private final Collection<RequestContext<?>> requestContexts = new HashSet<>();
56     private final Timeout slaveTask;
57     private ContextChainMastershipWatcher contextChainMastershipWatcher;
58     private SalRoleService roleService;
59
60     RoleContextImpl(@Nonnull final DeviceInfo deviceInfo,
61                     @Nonnull final HashedWheelTimer timer,
62                     final long checkRoleMasterTimeout) {
63         this.deviceInfo = deviceInfo;
64         this.timer = timer;
65         slaveTask = timer.newTimeout((timerTask) -> makeDeviceSlave(), checkRoleMasterTimeout, TimeUnit.MILLISECONDS);
66
67         LOG.info("Started timer for setting SLAVE role on device {} if no role will be set in {}s.",
68                 deviceInfo,
69                 checkRoleMasterTimeout / 1000L);
70     }
71
72     @Override
73     public DeviceInfo getDeviceInfo() {
74         return deviceInfo;
75     }
76
77     @Override
78     public void setRoleService(final SalRoleService salRoleService) {
79         roleService = salRoleService;
80     }
81
82     @Override
83     public void registerMastershipWatcher(@Nonnull final ContextChainMastershipWatcher contextChainMastershipWatcher) {
84         this.contextChainMastershipWatcher = contextChainMastershipWatcher;
85     }
86
87     @Override
88     public void close() {
89         slaveTask.cancel();
90         changeLastRoleFuture(null);
91         requestContexts.forEach(requestContext -> RequestContextUtil
92                 .closeRequestContextWithRpcError(requestContext, "Connection closed."));
93         requestContexts.clear();
94     }
95
96     @Override
97     public void instantiateServiceInstance() {
98         final ListenableFuture<RpcResult<SetRoleOutput>> future = sendRoleChangeToDevice(OfpRole.BECOMEMASTER);
99         changeLastRoleFuture(future);
100         Futures.addCallback(future, new MasterRoleCallback(), MoreExecutors.directExecutor());
101     }
102
103     @Override
104     public ListenableFuture<Void> closeServiceInstance() {
105         changeLastRoleFuture(null);
106         return Futures.immediateFuture(null);
107     }
108
109     @Override
110     public <T> RequestContext<T> createRequestContext() {
111         final AbstractRequestContext<T> ret = new AbstractRequestContext<T>(deviceInfo.reserveXidForDeviceMessage()) {
112             @Override
113             public void close() {
114                 requestContexts.remove(this);
115             }
116         };
117
118         requestContexts.add(ret);
119         return ret;
120     }
121
122     @Nonnull
123     @Override
124     public ServiceGroupIdentifier getIdentifier() {
125         return deviceInfo.getServiceIdentifier();
126     }
127
128     private void changeLastRoleFuture(final ListenableFuture<RpcResult<SetRoleOutput>> newFuture) {
129         lastRoleFuture.getAndUpdate(lastFuture -> {
130             if (Objects.nonNull(lastFuture) && !lastFuture.isCancelled() && !lastFuture.isDone()) {
131                 lastFuture.cancel(true);
132             }
133
134             return newFuture;
135         });
136     }
137
138     private ListenableFuture<RpcResult<SetRoleOutput>> makeDeviceSlave() {
139         final ListenableFuture<RpcResult<SetRoleOutput>> future = sendRoleChangeToDevice(OfpRole.BECOMESLAVE);
140         changeLastRoleFuture(future);
141         Futures.addCallback(future, new SlaveRoleCallback(), MoreExecutors.directExecutor());
142         return future;
143     }
144
145     private ListenableFuture<RpcResult<SetRoleOutput>> sendRoleChangeToDevice(final OfpRole newRole) {
146         LOG.debug("Sending new role {} to device {}", newRole, deviceInfo);
147
148         if (deviceInfo.getVersion() >= OFConstants.OFP_VERSION_1_3) {
149             final SetRoleInput setRoleInput = new SetRoleInputBuilder()
150                     .setControllerRole(newRole)
151                     .setNode(new NodeRef(deviceInfo.getNodeInstanceIdentifier()))
152                     .build();
153
154             final Future<RpcResult<SetRoleOutput>> setRoleOutputFuture = roleService.setRole(setRoleInput);
155
156             final TimerTask timerTask = timeout -> {
157                 if (!setRoleOutputFuture.isDone()) {
158                     LOG.warn("New role {} was not propagated to device {} during {} sec", newRole,
159                             deviceInfo, SET_ROLE_TIMEOUT);
160                     setRoleOutputFuture.cancel(true);
161                 }
162             };
163
164             timer.newTimeout(timerTask, SET_ROLE_TIMEOUT, TimeUnit.MILLISECONDS);
165             return JdkFutureAdapters.listenInPoolThread(setRoleOutputFuture);
166         }
167
168         LOG.info("Device: {} with version: {} does not support role {}", deviceInfo, deviceInfo.getVersion(), newRole);
169         return Futures.immediateFuture(null);
170     }
171
172     private final class MasterRoleCallback implements FutureCallback<RpcResult<SetRoleOutput>> {
173         @Override
174         public void onSuccess(@Nullable RpcResult<SetRoleOutput> setRoleOutputRpcResult) {
175             slaveTask.cancel();
176             contextChainMastershipWatcher.onMasterRoleAcquired(
177                     deviceInfo,
178                     ContextChainMastershipState.MASTER_ON_DEVICE);
179             LOG.debug("Role MASTER was successfully set on device, node {}", deviceInfo);
180         }
181
182         @Override
183         public void onFailure(@Nonnull final Throwable throwable) {
184             slaveTask.cancel();
185             contextChainMastershipWatcher.onNotAbleToStartMastershipMandatory(
186                     deviceInfo,
187                     "Was not able to propagate MASTER role on device. Error: " + throwable.toString());
188         }
189     }
190
191     private final class SlaveRoleCallback implements FutureCallback<RpcResult<SetRoleOutput>> {
192         @Override
193         public void onSuccess(@Nullable final RpcResult<SetRoleOutput> result) {
194             slaveTask.cancel();
195             contextChainMastershipWatcher.onSlaveRoleAcquired(deviceInfo);
196             LOG.debug("Role SLAVE was successfully set on device, node {}", deviceInfo);
197         }
198
199         @Override
200         public void onFailure(@Nonnull final Throwable throwable) {
201             slaveTask.cancel();
202             contextChainMastershipWatcher.onSlaveRoleNotAcquired(deviceInfo,
203                     "Was not able to propagate SLAVE role on device. Error: " + throwable.toString());
204         }
205     }
206 }