Bug 4957 Wiring new TxChainManager lifecycle
[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 javax.annotation.Nullable;
11 import java.util.concurrent.Future;
12
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.JdkFutureAdapters;
17 import com.google.common.util.concurrent.SettableFuture;
18 import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException;
19 import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
20 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidateRegistration;
21 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
22 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
23 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
24 import org.opendaylight.openflowplugin.api.openflow.device.DeviceContext;
25 import org.opendaylight.openflowplugin.api.openflow.device.RequestContext;
26 import org.opendaylight.openflowplugin.api.openflow.role.RoleContext;
27 import org.opendaylight.openflowplugin.api.openflow.role.RoleManager;
28 import org.opendaylight.openflowplugin.impl.rpc.AbstractRequestContext;
29 import org.opendaylight.openflowplugin.impl.services.SalRoleServiceImpl;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.OfpRole;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SalRoleService;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInput;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleInputBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.role.service.rev150727.SetRoleOutput;
36 import org.opendaylight.yangtools.yang.common.RpcResult;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 /**
41  * Created by kramesha on 9/12/15.
42  */
43 public class RoleContextImpl implements RoleContext {
44     private static final Logger LOG = LoggerFactory.getLogger(RoleContextImpl.class);
45
46     private final EntityOwnershipService entityOwnershipService;
47     private EntityOwnershipCandidateRegistration entityOwnershipCandidateRegistration;
48     private final RpcProviderRegistry rpcProviderRegistry;
49     private final DeviceContext deviceContext;
50     private final Entity entity;
51     private final OpenflowOwnershipListener openflowOwnershipListener;
52     private SalRoleService salRoleService;
53     private FutureCallback<Boolean> roleChangeCallback;
54
55     private final SettableFuture<OfpRole> initRoleChangeFuture;
56
57     public RoleContextImpl(final DeviceContext deviceContext, final RpcProviderRegistry rpcProviderRegistry,
58                            final EntityOwnershipService entityOwnershipService, final OpenflowOwnershipListener openflowOwnershipListener) {
59         this.entityOwnershipService = entityOwnershipService;
60         this.rpcProviderRegistry = rpcProviderRegistry;
61         this.deviceContext = deviceContext;
62         entity = new Entity(RoleManager.ENTITY_TYPE, deviceContext.getPrimaryConnectionContext().getNodeId().getValue());
63
64         this.openflowOwnershipListener =  openflowOwnershipListener;
65         salRoleService = new SalRoleServiceImpl(this, deviceContext);
66
67         initRoleChangeFuture = SettableFuture.create();
68     }
69
70     @Override
71     public Future<OfpRole> initialization() throws CandidateAlreadyRegisteredException {
72         LOG.debug("Initialization requestOpenflowEntityOwnership for entity {}", entity);
73         openflowOwnershipListener.registerRoleChangeListener(this);
74         entityOwnershipCandidateRegistration = entityOwnershipService.registerCandidate(entity);
75         LOG.info("RoleContextImpl : Candidate registered with ownership service for device :{}", deviceContext
76                 .getPrimaryConnectionContext().getNodeId().getValue());
77         return initRoleChangeFuture;
78     }
79
80     /**
81      * @deprecated not used but we are able to add here extra call for get EntityOwnershipState from
82      *             OpenflowOwnershipListener instead call it directly from RoleManager (here could be
83      *             add call salRoleService.setRole(setRoleInput);
84      */
85     @Override
86     @Deprecated
87     public void facilitateRoleChange(final FutureCallback<Boolean> roleChangeCallback) {
88         this.roleChangeCallback = roleChangeCallback;
89         if (!isDeviceConnected()) {
90             throw new IllegalStateException(
91                     "Device is disconnected. Giving up on Role Change:" + deviceContext.getDeviceState().getNodeId());
92         }
93     }
94
95     /**
96      * @deprecated not used
97      */
98     @Deprecated
99     private void requestOpenflowEntityOwnership() {
100
101         LOG.debug("requestOpenflowEntityOwnership for entity {}", entity);
102         try {
103             entityOwnershipCandidateRegistration = entityOwnershipService.registerCandidate(entity);
104
105             // The role change listener must be registered after registering a candidate
106             openflowOwnershipListener.registerRoleChangeListener(this);
107             LOG.info("RoleContextImpl : Candidate registered with ownership service for device :{}", deviceContext.getPrimaryConnectionContext().getNodeId().getValue());
108         } catch (final CandidateAlreadyRegisteredException e) {
109             // we can log and move for this error, as listener is present and role changes will be served.
110             LOG.error("Candidate - Entity already registered with Openflow candidate ", entity, e );
111         }
112     }
113
114     @Override
115     public void onRoleChanged(final OfpRole oldRole, final OfpRole newRole) {
116         LOG.trace("onRoleChanged method call for Entity {}", entity);
117
118         if (!isDeviceConnected()) {
119             // this can happen as after the disconnect, we still get a last messsage from EntityOwnershipService.
120             LOG.info("Device {} is disconnected from this node. Hence not attempting a role change.",
121                     deviceContext.getPrimaryConnectionContext().getNodeId());
122             if (!initRoleChangeFuture.isDone()) {
123                 LOG.debug("RoleChange is not valid for initialization Entity {} anymore - Device is disconnected", entity);
124                 initRoleChangeFuture.cancel(true);
125             }
126             return;
127         }
128
129         if (!initRoleChangeFuture.isDone()) {
130             LOG.debug("Initialization Role for entity {} is chosed {}", entity, newRole);
131             initRoleChangeFuture.set(newRole);
132             deviceContext.onInitClusterRoleChange(newRole);
133         }
134
135         LOG.debug("Role change received from ownership listener from {} to {} for device:{}", oldRole, newRole,
136                 deviceContext.getPrimaryConnectionContext().getNodeId());
137
138         final SetRoleInput setRoleInput = (new SetRoleInputBuilder())
139                 .setControllerRole(newRole)
140                 .setNode(new NodeRef(deviceContext.getDeviceState().getNodeInstanceIdentifier()))
141                 .build();
142
143         final Future<RpcResult<SetRoleOutput>> setRoleOutputFuture = salRoleService.setRole(setRoleInput);
144
145         Futures.addCallback(JdkFutureAdapters.listenInPoolThread(setRoleOutputFuture), new FutureCallback<RpcResult<SetRoleOutput>>() {
146             @Override
147             public void onSuccess(final RpcResult<SetRoleOutput> setRoleOutputRpcResult) {
148                 LOG.debug("Rolechange {} successful made on switch :{}", newRole,
149                         deviceContext.getPrimaryConnectionContext().getNodeId());
150                 deviceContext.getDeviceState().setRole(newRole);
151                         deviceContext.onClusterRoleChange(newRole);
152                 if (roleChangeCallback != null) {
153                     roleChangeCallback.onSuccess(true);
154                 }
155             }
156
157             @Override
158             public void onFailure(final Throwable throwable) {
159                 LOG.error("Error in setRole {} for device {} ", newRole,
160                         deviceContext.getPrimaryConnectionContext().getNodeId(), throwable);
161                 if (roleChangeCallback != null) {
162                     roleChangeCallback.onFailure(throwable);
163                 }
164             }
165         });
166     }
167
168     @Override
169     public void close() throws Exception {
170         if (entityOwnershipCandidateRegistration != null) {
171             LOG.debug("Closing EntityOwnershipCandidateRegistration for {}", entity);
172             entityOwnershipCandidateRegistration.close();
173         }
174         if (OfpRole.BECOMESLAVE.equals(deviceContext.getDeviceState().getRole())
175                 || deviceContext.getDeviceState() == null) {
176             // FIXME : there still stay small time window when we can unregistered
177             //         master DeviceCtx (RoleCtx) without the DS clean action
178             //         - RoleCtx doasn't send/receive RpcResult<SetRoleOutput> for
179             //           Master change role for/from Device
180             openflowOwnershipListener.unregisterRoleChangeListener(this);
181         }
182     }
183
184     @Override
185     public void onDeviceContextClosed(final DeviceContext deviceContext) {
186         try {
187             LOG.debug("onDeviceContextClosed called");
188             this.close();
189         } catch (final Exception e) {
190             LOG.error("Exception in onDeviceContextClosed of RoleContext", e);
191         }
192     }
193
194     @Override
195     public Entity getEntity() {
196         return entity;
197     }
198
199     @Override
200     public void onDeviceDisconnectedFromCluster(final boolean removeNodeFromDS) {
201         LOG.debug("Called onDeviceDisconnectedFromCluster in DeviceContext for entity:{}", entity);
202         deviceContext.onDeviceDisconnectedFromCluster(removeNodeFromDS);
203     }
204
205     private boolean isDeviceConnected() {
206         return ConnectionContext.CONNECTION_STATE.WORKING.equals(
207                 deviceContext.getPrimaryConnectionContext().getConnectionState());
208     }
209
210     @Nullable
211     @Override
212     public <T> RequestContext<T> createRequestContext() {
213         final AbstractRequestContext<T> ret = new AbstractRequestContext<T>(deviceContext.getReservedXid()) {
214             @Override
215             public void close() {
216             }
217         };
218         return ret;
219     }
220
221     @VisibleForTesting
222     public void setSalRoleService(final SalRoleService salRoleService) {
223         this.salRoleService = salRoleService;
224     }
225 }